Files
msd-core/get-shit-done/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs
2026-05-11 10:44:57 -04:00

82 lines
2.8 KiB
JavaScript

'use strict';
const path = require('path');
function isStructurallyEmpty(value) {
if (value === null || value === undefined) return true;
if (Array.isArray(value)) return value.length === 0;
if (typeof value !== 'object') return false;
for (const _key in value) return false;
return true;
}
function isManagedCodexHookCommand(command, configDir) {
if (typeof command !== 'string') return false;
if (typeof configDir !== 'string' || configDir.length === 0) return false;
const normalizedCommand = command.replace(/\\/g, '/');
const managedHooksDir = `${path.join(configDir, 'hooks').replace(/\\/g, '/')}/`;
if (!normalizedCommand.includes(managedHooksDir)) return false;
return /(^|[\\/\s"'])(gsd-check-update\.js|gsd-update-check\.js)(?=$|[\s"'])/.test(normalizedCommand);
}
function pruneLegacyCodexHooksJsonValue(value, configDir) {
if (Array.isArray(value)) {
let changed = false;
const next = [];
for (const item of value) {
const pruned = pruneLegacyCodexHooksJsonValue(item, configDir);
if (pruned.changed) changed = true;
if (!isStructurallyEmpty(pruned.value)) next.push(pruned.value);
else changed = true;
}
return { value: next, changed };
}
if (value && typeof value === 'object') {
if (isManagedCodexHookCommand(value.command, configDir)) {
return { value: null, changed: true };
}
let changed = false;
const next = {};
for (const [key, child] of Object.entries(value)) {
const pruned = pruneLegacyCodexHooksJsonValue(child, configDir);
if (pruned.changed) changed = true;
if (!isStructurallyEmpty(pruned.value)) next[key] = pruned.value;
else changed = true;
}
return { value: next, changed };
}
return { value, changed: false };
}
module.exports = {
id: '2026-05-11-codex-legacy-hooks-json',
title: 'Remove legacy Codex hooks.json GSD hook registrations',
description: 'Remove legacy Codex hooks.json GSD hook registrations after config.toml migration.',
introducedIn: '1.50.0',
runtimes: ['codex'],
scopes: ['global', 'local'],
destructive: true,
runtimeContract: 'docs/installer-migrations.md#runtime-configuration-contract-registry Codex row',
plan: ({ configDir, readJson }) => {
const hooksJson = readJson('hooks.json');
if (!hooksJson.exists || hooksJson.error) return [];
const pruned = pruneLegacyCodexHooksJsonValue(hooksJson.value, configDir);
if (!pruned.changed) return [];
return [
{
type: 'rewrite-json',
relPath: 'hooks.json',
value: pruned.value,
deleteIfEmpty: true,
reason: 'legacy Codex hooks.json GSD registration retired by installer migration',
ownershipEvidence: 'pruned command matches generated GSD hook command under the install hooks directory',
},
];
},
};