feat(#777): register Cursor-native hooks (.cursor/hooks.json) for session-start/post-tool parity (#831)

* feat(#777): register Cursor-native hooks (.cursor/hooks.json) for session-start/post-tool parity

- Add gsd-cursor-session-start.js: injects STATE.md presence reminder (or
  new-project nudge) into Cursor sessions via the sessionStart hook event
- Add gsd-cursor-post-tool.js: emits an additional_context nudge when
  write-class tool calls touch .planning/ files (postToolUse hook event)
- Add 'cursor-hooks-json' installSurface to runtime-config-adapter-registry;
  writeCursorHooksJson/reconcileCursorHooksJson write the canonical
  { version: 1, hooks: { sessionStart, postToolUse } } JSON shape with
  idempotent reconciliation that preserves user-owned hook entries
- Hook scripts are copied with /gsd:→gsd- rewrite so installed files
  contain no colon-form slash-command refs (bug-376 invariant)
- 20 new tests in tests/cursor-hooks.test.cjs cover all reconciler paths,
  entry helpers, removal, runtime adapter surface, and hook script behavior
- Update CONTEXT.md, ARCHITECTURE.md, installer-migrations.md, and
  000-first-time-baseline.cts to include Cursor hooks.json surface

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#777): build hooks/dist on demand in bug-376 test for scoped/windows CI

hooks/dist is gitignored and only produced by `npm run build:hooks`.
The CI scoped (ubuntu-latest/node-22) and windows (windows-latest/node-24)
test jobs do NOT run build:hooks before executing tests, so bug-376's
prerequisite suite was failing with "hooks/dist not found" on both legs.

Add ensureHooksDist() helper (mirrors bug-3357 pattern) that builds
hooks/dist on demand in the before() hooks of prerequisite and Suite 3.
Also add ensureHooksDist() call to Suite 3's before() so the snapshot
step is also hermetic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-07 19:22:48 -04:00
committed by GitHub
parent e04e757672
commit 1040fb792e
17 changed files with 985 additions and 25 deletions

View File

@@ -31,7 +31,7 @@ const EXPECTED_TABLE = [
{ runtime: 'codex', installSurface: 'codex-toml', writesSharedSettings: false, finishPermissionWriter: null },
{ runtime: 'copilot', installSurface: 'copilot-instructions', writesSharedSettings: false, finishPermissionWriter: null },
{ runtime: 'cline', installSurface: 'cline-rules', writesSharedSettings: false, finishPermissionWriter: null },
{ runtime: 'cursor', installSurface: 'profile-marker-only', writesSharedSettings: false, finishPermissionWriter: null },
{ runtime: 'cursor', installSurface: 'cursor-hooks-json', writesSharedSettings: false, finishPermissionWriter: null },
{ runtime: 'windsurf', installSurface: 'profile-marker-only', writesSharedSettings: false, finishPermissionWriter: null },
{ runtime: 'trae', installSurface: 'profile-marker-only', writesSharedSettings: false, finishPermissionWriter: null },
];
@@ -160,8 +160,8 @@ describe('installSurface correctness', () => {
assert.strictEqual(resolveRuntimeConfigIntent('cline').installSurface, 'cline-rules');
});
test('cursor -> "profile-marker-only"', () => {
assert.strictEqual(resolveRuntimeConfigIntent('cursor').installSurface, 'profile-marker-only');
test('cursor -> "cursor-hooks-json"', () => {
assert.strictEqual(resolveRuntimeConfigIntent('cursor').installSurface, 'cursor-hooks-json');
});
test('windsurf -> "profile-marker-only"', () => {
@@ -236,10 +236,11 @@ describe('INSTALL_SURFACES export', () => {
'codex-toml',
'copilot-instructions',
'cline-rules',
'cursor-hooks-json',
'profile-marker-only',
]);
test('INSTALL_SURFACES contains exactly the 5 surface strings', () => {
test('INSTALL_SURFACES contains exactly the 6 surface strings', () => {
assert.deepStrictEqual(new Set(INSTALL_SURFACES), EXPECTED_SURFACES);
});
});