* feat(shell-projection): route persistent PATH hints through projection seam * refactor(shell-projection): unify managed hook command policy * fix(install): guard malformed settings hooks during uninstall * chore(changeset): add fragment for pr 3450 * fix(install): guard malformed settings hook entries
81 lines
2.6 KiB
JavaScript
81 lines
2.6 KiB
JavaScript
'use strict';
|
|
|
|
const { isManagedHookCommand } = require('../shell-command-projection.cjs');
|
|
|
|
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) {
|
|
return isManagedHookCommand(command, {
|
|
surface: 'codex-hooks-json',
|
|
includeLegacyAliases: true,
|
|
configDir,
|
|
});
|
|
}
|
|
|
|
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 (pruned.changed && isStructurallyEmpty(pruned.value)) changed = true;
|
|
else next.push(pruned.value);
|
|
}
|
|
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 (pruned.changed && isStructurallyEmpty(pruned.value)) changed = true;
|
|
else next[key] = pruned.value;
|
|
}
|
|
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',
|
|
},
|
|
];
|
|
},
|
|
};
|