fix(#2089): wire adapter into install path + address all review findings
MEDIUM fixes (code review):
- Wire resolveManagedHookEvents + resolveHookScripts + buildHookBusEntries
from imperative-hook-bus.cts into writeCursorHooksJson — the install path
is now truly descriptor-driven (reads hostBehaviors.managedHookEvents),
not a hardcoded constant that happens to match the descriptor. bin/install.js
passes the descriptor list via opts.managedHookEvents.
- buildHookBusEntries is now consumed (was dead code); entry-building is no
longer duplicated inline.
- Remove try/finally from cursor-hook-bus-upgrade.test.cjs test bodies
(violated CONTRIBUTING.md L342; redundant with t.after cleanup).
LOW fixes:
- Remove dead require('fs')/require('path') from gsd-cursor-pre-tool.js
- Fix resolveManagedHookEvents docstring (all-invalid fallback behavior)
- Add src/runtime-hooks-surface.cts to the AC2 source-guard file list
Security review: no CRITICAL/HIGH/MEDIUM findings (3 LOW are pre-existing
#777 baseline patterns, not regressions).
This commit is contained in:
@@ -114,68 +114,60 @@ test('all 6 hook scripts exist under hooks/', () => {
|
||||
test('reconcileCursorHooksJson writes all 6 managed events into hooks.json', (t) => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cursor-hook-bus-'));
|
||||
t.after(() => cleanup(tmpDir));
|
||||
try {
|
||||
const hooksJsonPath = path.join(tmpDir, 'hooks.json');
|
||||
const managedEntries = {};
|
||||
for (const ev of EXPECTED_EVENTS) {
|
||||
managedEntries[ev] = {
|
||||
type: 'command',
|
||||
command: `node /fake/${ev}.js`,
|
||||
[GSD_CURSOR_HOOK_MARKER]: true,
|
||||
};
|
||||
}
|
||||
const result = reconcileCursorHooksJson(hooksJsonPath, managedEntries);
|
||||
assert.ok(result.changed, 'first write must report changed=true');
|
||||
const hooksJsonPath = path.join(tmpDir, 'hooks.json');
|
||||
const managedEntries = {};
|
||||
for (const ev of EXPECTED_EVENTS) {
|
||||
managedEntries[ev] = {
|
||||
type: 'command',
|
||||
command: `node /fake/${ev}.js`,
|
||||
[GSD_CURSOR_HOOK_MARKER]: true,
|
||||
};
|
||||
}
|
||||
const result = reconcileCursorHooksJson(hooksJsonPath, managedEntries);
|
||||
assert.ok(result.changed, 'first write must report changed=true');
|
||||
|
||||
const written = JSON.parse(fs.readFileSync(hooksJsonPath, 'utf8'));
|
||||
const hookTable = written.hooks;
|
||||
assert.ok(hookTable && typeof hookTable === 'object');
|
||||
for (const ev of EXPECTED_EVENTS) {
|
||||
assert.ok(Array.isArray(hookTable[ev]),
|
||||
`hooks.json must have a ${ev} array`);
|
||||
assert.equal(hookTable[ev].length, 1,
|
||||
`${ev} must have exactly 1 managed entry`);
|
||||
assert.equal(hookTable[ev][0][GSD_CURSOR_HOOK_MARKER], true,
|
||||
`${ev} entry must carry the GSD managed marker`);
|
||||
}
|
||||
} finally {
|
||||
cleanup(tmpDir);
|
||||
const written = JSON.parse(fs.readFileSync(hooksJsonPath, 'utf8'));
|
||||
const hookTable = written.hooks;
|
||||
assert.ok(hookTable && typeof hookTable === 'object');
|
||||
for (const ev of EXPECTED_EVENTS) {
|
||||
assert.ok(Array.isArray(hookTable[ev]),
|
||||
`hooks.json must have a ${ev} array`);
|
||||
assert.equal(hookTable[ev].length, 1,
|
||||
`${ev} must have exactly 1 managed entry`);
|
||||
assert.equal(hookTable[ev][0][GSD_CURSOR_HOOK_MARKER], true,
|
||||
`${ev} entry must carry the GSD managed marker`);
|
||||
}
|
||||
});
|
||||
|
||||
test('reconcileCursorHooksJson preserves user entries across all 6 events', (t) => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cursor-hook-bus-'));
|
||||
t.after(() => cleanup(tmpDir));
|
||||
try {
|
||||
const hooksJsonPath = path.join(tmpDir, 'hooks.json');
|
||||
// Seed with user-owned entries in two events.
|
||||
const seed = {
|
||||
version: 1,
|
||||
hooks: {
|
||||
sessionStart: [{ type: 'command', command: 'user-start.sh' }],
|
||||
preToolUse: [{ type: 'command', command: 'user-pre.sh' }],
|
||||
},
|
||||
const hooksJsonPath = path.join(tmpDir, 'hooks.json');
|
||||
// Seed with user-owned entries in two events.
|
||||
const seed = {
|
||||
version: 1,
|
||||
hooks: {
|
||||
sessionStart: [{ type: 'command', command: 'user-start.sh' }],
|
||||
preToolUse: [{ type: 'command', command: 'user-pre.sh' }],
|
||||
},
|
||||
};
|
||||
fs.writeFileSync(hooksJsonPath, JSON.stringify(seed, null, 2) + '\n');
|
||||
|
||||
const managedEntries = {};
|
||||
for (const ev of EXPECTED_EVENTS) {
|
||||
managedEntries[ev] = {
|
||||
type: 'command',
|
||||
command: `node /gsd/${ev}.js`,
|
||||
[GSD_CURSOR_HOOK_MARKER]: true,
|
||||
};
|
||||
fs.writeFileSync(hooksJsonPath, JSON.stringify(seed, null, 2) + '\n');
|
||||
|
||||
const managedEntries = {};
|
||||
for (const ev of EXPECTED_EVENTS) {
|
||||
managedEntries[ev] = {
|
||||
type: 'command',
|
||||
command: `node /gsd/${ev}.js`,
|
||||
[GSD_CURSOR_HOOK_MARKER]: true,
|
||||
};
|
||||
}
|
||||
reconcileCursorHooksJson(hooksJsonPath, managedEntries);
|
||||
|
||||
const written = JSON.parse(fs.readFileSync(hooksJsonPath, 'utf8'));
|
||||
// sessionStart: 1 user + 1 managed
|
||||
assert.equal(written.hooks.sessionStart.length, 2);
|
||||
// preToolUse: 1 user + 1 managed
|
||||
assert.equal(written.hooks.preToolUse.length, 2);
|
||||
// postToolUse: 1 managed only
|
||||
assert.equal(written.hooks.postToolUse.length, 1);
|
||||
} finally {
|
||||
cleanup(tmpDir);
|
||||
}
|
||||
reconcileCursorHooksJson(hooksJsonPath, managedEntries);
|
||||
|
||||
const written = JSON.parse(fs.readFileSync(hooksJsonPath, 'utf8'));
|
||||
// sessionStart: 1 user + 1 managed
|
||||
assert.equal(written.hooks.sessionStart.length, 2);
|
||||
// preToolUse: 1 user + 1 managed
|
||||
assert.equal(written.hooks.preToolUse.length, 2);
|
||||
// postToolUse: 1 managed only
|
||||
assert.equal(written.hooks.postToolUse.length, 1);
|
||||
});
|
||||
|
||||
@@ -118,7 +118,7 @@ test('no `runtime === "cursor"` string-equality branch remains in the install so
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '')
|
||||
.replace(/\/\/[^\r\n]*/g, '')
|
||||
.replace(/`[^`]*`/g, '');
|
||||
for (const rel of ['bin/install.js', 'src/install-engine.cts', 'src/runtime-artifact-conversion.cts']) {
|
||||
for (const rel of ['bin/install.js', 'src/install-engine.cts', 'src/runtime-artifact-conversion.cts', 'src/runtime-hooks-surface.cts']) {
|
||||
const src = fs.readFileSync(path.join(__dirname, '..', rel), 'utf8');
|
||||
const offenders = strip(src).match(/runtime\s*[!=]==\s*'cursor'/g) || [];
|
||||
assert.deepEqual(offenders, [], `AC2: no hardcoded runtime==='cursor' branch may remain in ${rel}; found: ${offenders.join(', ')}`);
|
||||
|
||||
Reference in New Issue
Block a user