@marcomigozzi/trailhead 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.it.md +142 -29
  2. package/README.md +141 -28
  3. package/bin/base-preflight-docs.test.js +65 -0
  4. package/bin/execute-split-docs.test.js +146 -0
  5. package/bin/isolation-answer-docs.test.js +41 -0
  6. package/bin/isolation-copy-docs.test.js +28 -0
  7. package/bin/lib/codex-projection.js +17 -10
  8. package/bin/lib/codex-projection.test.js +80 -11
  9. package/bin/lib/install-verify.js +10 -5
  10. package/bin/lib/install-verify.test.js +51 -0
  11. package/bin/report-status-docs.test.js +195 -0
  12. package/bin/resolve-helpers-docs.test.js +36 -0
  13. package/bin/scope-confirm-skip-docs.test.js +70 -0
  14. package/bin/trailhead.js +40 -28
  15. package/bin/trailhead.test.js +503 -6
  16. package/bin/uat-setup-docs.test.js +65 -0
  17. package/package.json +4 -1
  18. package/plugins/trailhead/.claude-plugin/plugin.json +3 -2
  19. package/plugins/trailhead/agents/trailhead-code-review.md +15 -2
  20. package/plugins/trailhead/agents/trailhead-codebase-map.md +20 -1
  21. package/plugins/trailhead/agents/trailhead-debug.md +20 -1
  22. package/plugins/trailhead/agents/trailhead-executor.md +29 -2
  23. package/plugins/trailhead/agents/trailhead-fix.md +29 -2
  24. package/plugins/trailhead/agents/trailhead-plan.md +20 -1
  25. package/plugins/trailhead/agents/trailhead-research.md +22 -3
  26. package/plugins/trailhead/agents/trailhead-verify.md +15 -2
  27. package/plugins/trailhead/commands/panel.md +10 -0
  28. package/plugins/trailhead/hooks/hooks.json +3 -18
  29. package/plugins/trailhead/hooks/lib/base-preflight.test.js +513 -0
  30. package/plugins/trailhead/hooks/lib/body-sources.js +795 -0
  31. package/plugins/trailhead/hooks/lib/commit-message-check.js +1 -1
  32. package/plugins/trailhead/hooks/lib/commit-message-check.test.js +3 -2
  33. package/plugins/trailhead/hooks/lib/commit-msg-module-repo.test.js +116 -0
  34. package/plugins/trailhead/hooks/lib/commit-msg-sync.test.js +100 -9
  35. package/plugins/trailhead/hooks/lib/dashboard.test.js +409 -0
  36. package/plugins/trailhead/hooks/lib/fixtures/commit-msg-legacy-js +104 -0
  37. package/plugins/trailhead/hooks/lib/gh-subcommand.js +92 -10
  38. package/plugins/trailhead/hooks/lib/gh-subcommand.test.js +55 -0
  39. package/plugins/trailhead/hooks/lib/isolation-answer.test.js +258 -0
  40. package/plugins/trailhead/hooks/lib/isolation-copy.test.js +298 -0
  41. package/plugins/trailhead/hooks/lib/issue-body.test.js +470 -0
  42. package/plugins/trailhead/hooks/lib/mockup-link.js +21 -5
  43. package/plugins/trailhead/hooks/lib/report-status.test.js +366 -0
  44. package/plugins/trailhead/hooks/lib/shell-scan.js +532 -65
  45. package/plugins/trailhead/hooks/lib/shell-scan.test.js +434 -0
  46. package/plugins/trailhead/hooks/lib/uat-backfill.test.js +150 -0
  47. package/plugins/trailhead/hooks/lib/uat-setup.test.js +340 -0
  48. package/plugins/trailhead/hooks/lib/unmodelled.js +345 -0
  49. package/plugins/trailhead/hooks/lib/unmodelled.test.js +173 -0
  50. package/plugins/trailhead/hooks/lib/work-state.test.js +297 -0
  51. package/plugins/trailhead/hooks/mod/README.md +219 -0
  52. package/plugins/trailhead/hooks/mod/map-pane-view.ts +606 -0
  53. package/plugins/trailhead/hooks/mod/map-pane.tsx +832 -0
  54. package/plugins/trailhead/hooks/mod/register.tsx +293 -0
  55. package/plugins/trailhead/hooks/mod/work-state-prompt.ts +69 -0
  56. package/plugins/trailhead/hooks/mod/work-state-view.ts +110 -0
  57. package/plugins/trailhead/hooks/trailhead-bash-guards.js +140 -0
  58. package/plugins/trailhead/hooks/trailhead-bash-guards.test.js +233 -0
  59. package/plugins/trailhead/hooks/trailhead-body-guard.js +145 -61
  60. package/plugins/trailhead/hooks/trailhead-body-guard.test.js +224 -3
  61. package/plugins/trailhead/hooks/trailhead-commit-guard.js +41 -14
  62. package/plugins/trailhead/hooks/trailhead-commit-guard.test.js +2 -1
  63. package/plugins/trailhead/hooks/trailhead-install-guard.js +187 -42
  64. package/plugins/trailhead/hooks/trailhead-install-guard.test.js +182 -2
  65. package/plugins/trailhead/hooks/trailhead-mockup-link-guard.js +48 -11
  66. package/plugins/trailhead/hooks/trailhead-mockup-link-guard.test.js +75 -3
  67. package/plugins/trailhead/hooks/trailhead-search-guard.js +59 -13
  68. package/plugins/trailhead/hooks/trailhead-search-guard.test.js +68 -3
  69. package/plugins/trailhead/hooks/trailhead-secret-guard.js +541 -57
  70. package/plugins/trailhead/hooks/trailhead-secret-guard.test.js +1000 -4
  71. package/plugins/trailhead/hooks/trailhead-secret-read-guard.js +171 -6
  72. package/plugins/trailhead/hooks/trailhead-secret-read-guard.test.js +170 -0
  73. package/plugins/trailhead/skills/_shared/choices.md +7 -3
  74. package/plugins/trailhead/skills/_shared/configuration-reference.md +15 -3
  75. package/plugins/trailhead/skills/_shared/load-first.md +3 -3
  76. package/plugins/trailhead/skills/_shared/multi-map.md +3 -3
  77. package/plugins/trailhead/skills/_shared/session-handoff.md +1 -1
  78. package/plugins/trailhead/skills/_shared/substrate-commands.md +22 -11
  79. package/plugins/trailhead/skills/_shared/substrate.md +6 -4
  80. package/plugins/trailhead/skills/_shared/teamwork.md +17 -6
  81. package/plugins/trailhead/skills/_shared/techniques/acceptance-testing.md +17 -9
  82. package/plugins/trailhead/skills/_shared/techniques/code-review.md +1 -1
  83. package/plugins/trailhead/skills/_shared/techniques/codebase-map.md +1 -1
  84. package/plugins/trailhead/skills/_shared/techniques/debug.md +2 -0
  85. package/plugins/trailhead/skills/_shared/techniques/prototype.md +1 -1
  86. package/plugins/trailhead/skills/_shared/techniques/research.md +4 -2
  87. package/plugins/trailhead/skills/_shared/techniques/verify.md +1 -1
  88. package/plugins/trailhead/skills/_shared/techniques.md +12 -2
  89. package/plugins/trailhead/skills/trailhead/SKILL.md +3 -3
  90. package/plugins/trailhead/skills/trailhead-capture/SKILL.md +1 -1
  91. package/plugins/trailhead/skills/trailhead-capture/references/capture.md +2 -2
  92. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +1 -1
  93. package/plugins/trailhead/skills/trailhead-chart/references/charting.md +1 -1
  94. package/plugins/trailhead/skills/trailhead-manage/SKILL.md +1 -1
  95. package/plugins/trailhead/skills/trailhead-manage/references/auditing.md +1 -1
  96. package/plugins/trailhead/skills/trailhead-view/SKILL.md +8 -6
  97. package/plugins/trailhead/skills/trailhead-work/SKILL.md +16 -13
  98. package/plugins/trailhead/skills/trailhead-work/references/auto.md +6 -4
  99. package/plugins/trailhead/skills/trailhead-work/references/executor-brief.md +29 -0
  100. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +28 -10
  101. package/plugins/trailhead/templates/trailhead-base-preflight.js +305 -0
  102. package/plugins/trailhead/templates/trailhead-commit-msg +38 -102
  103. package/plugins/trailhead/templates/trailhead-commit-msg-sync.js +58 -27
  104. package/plugins/trailhead/templates/trailhead-commit-msg.cjs +114 -0
  105. package/plugins/trailhead/templates/trailhead-dashboard.js +340 -0
  106. package/plugins/trailhead/templates/trailhead-isolation-answer.js +227 -0
  107. package/plugins/trailhead/templates/trailhead-isolation-copy.js +239 -0
  108. package/plugins/trailhead/templates/trailhead-issue-body.js +459 -0
  109. package/plugins/trailhead/templates/trailhead-report-status.js +274 -0
  110. package/plugins/trailhead/templates/trailhead-uat-backfill.js +114 -0
  111. package/plugins/trailhead/templates/trailhead-uat-setup.js +385 -0
  112. package/plugins/trailhead/templates/trailhead-work-state.js +217 -0
  113. package/plugins/trailhead/tests/map-pane-ui.test.ts +1921 -0
  114. package/plugins/trailhead/tests/map-pane-view.test.ts +853 -0
  115. package/plugins/trailhead/tests/mod.test.ts +14 -0
  116. package/plugins/trailhead/tests/work-state-prompt-hook.test.ts +207 -0
  117. package/plugins/trailhead/tests/work-state-prompt.test.ts +109 -0
  118. package/plugins/trailhead/tests/work-state-ui.test.ts +365 -0
  119. package/plugins/trailhead/tests/work-state-view.test.ts +110 -0
  120. package/plugins/trailhead/types/index.d.ts +103 -0
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ // Docs test for #253: the skill prose names the Resolve bookkeeping helpers as the
3
+ // default path (trailhead-issue-body.js, trailhead-dashboard.js, and the one `clear`
4
+ // that also removes the session-ticket marker), while keeping the by-hand fallback.
5
+ // Run: node resolve-helpers-docs.test.js
6
+ const assert = require('assert');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ const skills = path.join(__dirname, '..', 'plugins', 'trailhead', 'skills');
11
+ const read = (rel) => fs.readFileSync(path.join(skills, rel), 'utf8');
12
+
13
+ let passed = 0;
14
+ const ok = (name, cond) => { assert.ok(cond, name); passed++; };
15
+
16
+ // file -> strings it must carry
17
+ const EXPECT = {
18
+ '_shared/substrate.md': ['trailhead-issue-body.js', 'trailhead-dashboard.js', 'when a helper is missing'],
19
+ '_shared/substrate-commands.md': ['trailhead-issue-body.js append --issue <map> --section "Decisions so far"', 'trailhead-dashboard.js', '--set-last-audit', 'fully generated'],
20
+ 'trailhead-work/references/ticket-engines.md': ['trailhead-issue-body.js append --issue <map> --section "Decisions so far"', 'trailhead-dashboard.js', 'trailhead-work-state.js clear --repo <working-root>'],
21
+ 'trailhead-work/SKILL.md': ['trailhead-issue-body.js append --issue <map> --section "Decisions so far"', 'trailhead-dashboard.js', 'trailhead-work-state.js clear --repo <working-root>',
22
+ 'trailhead-issue-body.js remove --issue <map> --section "Not yet specified" --match "<unique text of the entry>"'],
23
+ '_shared/session-handoff.md': ['trailhead-work-state.js clear --repo <working-root>', 'removes both'],
24
+ '_shared/teamwork.md': ['trailhead-issue-body.js append --issue <map> --section "Decisions so far"', 'sessionTicket', 'when the helper is missing'],
25
+ 'trailhead-view/SKILL.md': ['trailhead-dashboard.js', '--dry-run'],
26
+ 'trailhead-manage/references/auditing.md': ['trailhead-dashboard.js --set-last-audit'],
27
+ '_shared/configuration-reference.md': ['trailhead-issue-body.js replace --issue <conventions> --section :preamble'],
28
+ };
29
+
30
+ for (const [rel, needles] of Object.entries(EXPECT)) {
31
+ const text = read(rel);
32
+ for (const needle of needles) ok(`${rel} names: ${needle}`, text.includes(needle));
33
+ ok(`${rel} has no em-dash`, !text.includes('—'));
34
+ }
35
+
36
+ console.log(`resolve-helpers-docs.test.js: ${passed} checks passed`);
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ // Docs test for #244: the ticket-start scope confirm is skipped on a ticket this session
3
+ // already shaped. The rule is stated once (Work the map step 3); the quick bullet and the
4
+ // choices table only point at it.
5
+ // Run: node scope-confirm-skip-docs.test.js
6
+ const assert = require('assert');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ const skills = path.join(__dirname, '..', 'plugins', 'trailhead', 'skills');
11
+ const read = (rel) => fs.readFileSync(path.join(skills, rel), 'utf8');
12
+
13
+ let passed = 0;
14
+ const ok = (name, cond) => { assert.ok(cond, name); passed++; };
15
+
16
+ const ANCHOR = 'Skip the confirm on a ticket this session already shaped';
17
+ const DEFINITION = `**${ANCHOR}.**`;
18
+
19
+ const walk = (dir) => fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) =>
20
+ e.isDirectory() ? walk(path.join(dir, e.name)) : [path.join(dir, e.name)]);
21
+
22
+ // 1. the bold definition exists exactly once, in trailhead-work/SKILL.md
23
+ const defs = walk(skills).filter((f) => fs.readFileSync(f, 'utf8').includes(DEFINITION));
24
+ ok('definition appears in exactly one file', defs.length === 1);
25
+ ok('definition lives in trailhead-work/SKILL.md', defs[0] === path.join(skills, 'trailhead-work', 'SKILL.md'));
26
+ const work = read('trailhead-work/SKILL.md');
27
+ ok('definition appears once in SKILL.md', work.split(DEFINITION).length === 2);
28
+
29
+ // 2. step 3 carries the normative fragments
30
+ const lines = work.split('\n');
31
+ const s = lines.findIndex((l) => l.startsWith('3. **Brief the ticket and confirm scope'));
32
+ const e = lines.findIndex((l, i) => i > s && l.startsWith('4. '));
33
+ ok('step 3 found', s >= 0 && e > s);
34
+ const step3 = lines.slice(s, e).join('\n');
35
+ ok('step 3 carries the definition', step3.includes(DEFINITION));
36
+ // match the fragments in the rule sub-bullet only, not the whole of step 3
37
+ const rule = lines.slice(s, e).find((l) => l.includes(DEFINITION));
38
+ for (const frag of [
39
+ "from the user's own request for it",
40
+ 'a Grilling round on this ticket concluded',
41
+ 'created in bulk',
42
+ 'earlier session',
43
+ '/clear',
44
+ 'resume',
45
+ 'one-line',
46
+ 'never inferred from tracker metadata',
47
+ '`auto` is unchanged',
48
+ ]) ok(`rule carries: ${frag}`, rule.includes(frag));
49
+
50
+ // 3. the quick bullet only points at the rule
51
+ const quick = lines.find((l) => l.startsWith('- **Briefs and confirms scope first'));
52
+ ok('quick bullet found', !!quick);
53
+ ok('quick bullet references the anchor', quick.includes(ANCHOR));
54
+ ok('quick bullet gates the menu on the rule', quick.includes('Whether the menu is posed at all follows'));
55
+ ok('quick "<text>" gets the note, no menu', quick.includes('one-line note, no menu'));
56
+ ok('quick bullet does not restate bulk exclusion', !quick.includes('created in bulk'));
57
+ ok('quick bullet does not restate grill trigger', !quick.includes('a Grilling round on this ticket concluded'));
58
+
59
+ // 4. the choices row only points at the rule
60
+ const row = read('_shared/choices.md').split('\n').find((l) => l.startsWith('| Ticket-start scope confirm'));
61
+ ok('choices row found', !!row);
62
+ ok('choices row references the anchor', row.includes(ANCHOR));
63
+ ok('choices row does not restate bulk exclusion', !row.includes('created in bulk'));
64
+
65
+ // 5. no em-dash in the touched files
66
+ for (const rel of ['trailhead-work/SKILL.md', '_shared/choices.md'])
67
+ ok(`${rel} has no em-dash`, !read(rel).includes('\u2014'));
68
+ ok('this test has no em-dash', !fs.readFileSync(__filename, 'utf8').includes('\u2014'));
69
+
70
+ console.log(`scope-confirm-skip-docs.test.js: ${passed} checks passed`);
package/bin/trailhead.js CHANGED
@@ -243,18 +243,27 @@ function claudePaths(configDir) {
243
243
  settings: path.join(configDir, 'settings.json'),
244
244
  };
245
245
  }
246
- const HOOK_FILES = ['trailhead-commit-guard.js', 'trailhead-issue-injection-scanner.js', 'trailhead-secret-guard.js', 'trailhead-install-guard.js', 'trailhead-search-guard.js', 'trailhead-secret-read-guard.js', 'trailhead-body-guard.js', 'trailhead-mockup-link-guard.js', 'trailhead-mockup-link-stop.js', 'trailhead-check-update.js'];
246
+ // The five Bash guard modules. They are shipped, but only require()d by the
247
+ // merged trailhead-bash-guards.js (one process per Bash call), never registered
248
+ // on their own. Legacy installs registered each one; the installers strip those.
249
+ const BASH_GUARD_MODULES = ['trailhead-commit-guard.js', 'trailhead-secret-guard.js', 'trailhead-install-guard.js', 'trailhead-search-guard.js', 'trailhead-body-guard.js'];
250
+ // Every hook script copied into the hooks dir (shipped runtime files).
251
+ const HOOK_FILES = [...BASH_GUARD_MODULES, 'trailhead-bash-guards.js', 'trailhead-issue-injection-scanner.js', 'trailhead-secret-read-guard.js', 'trailhead-mockup-link-guard.js', 'trailhead-mockup-link-stop.js', 'trailhead-check-update.js'];
252
+ // The registered entrypoints: the shipped files that must appear in
253
+ // settings.json / hooks.json (everything but the require()d guard modules).
254
+ const ENTRYPOINT_FILES = HOOK_FILES.filter((f) => !BASH_GUARD_MODULES.includes(f));
247
255
  // Runtime libs the hook scripts require (trailhead-commit-guard.js does
248
- // require('./lib/commit-message-check.js'); trailhead-search-guard.js and
249
- // trailhead-secret-read-guard.js both do require('./lib/shell-scan.js'), #135
250
- // follow-up; trailhead-body-guard.js and trailhead-secret-guard.js both do
251
- // require('./lib/gh-subcommand.js'), #153 follow-up; trailhead-check-update.js
256
+ // require('./lib/commit-message-check.js'); several guards (grep `lib/shell-scan`:
257
+ // search, secret-read, secret, install, lib/unmodelled.js) do
258
+ // require('./lib/shell-scan.js'), #135 follow-up; trailhead-body-guard.js and trailhead-secret-guard.js both do
259
+ // require('./lib/gh-subcommand.js'), #153 follow-up; trailhead-secret-guard.js
260
+ // also does require('./lib/body-sources.js'), #210; trailhead-check-update.js
252
261
  // does require('./lib/model-defaults-review.js'), which in turn reads the
253
262
  // sibling model-defaults.json, #186; both mockup-link hooks do
254
263
  // require('./lib/mockup-link.js')). Copied by name, never a recursive
255
264
  // sweep: the Claude hooks/lib dir is shared with other plugins, so we must
256
265
  // not clobber a co-tenant's lib nor ship our own *.test.js files.
257
- const HOOK_LIB_FILES = ['commit-message-check.js', 'shell-scan.js', 'gh-subcommand.js', 'model-defaults-review.js', 'model-defaults.json', 'mockup-link.js'];
266
+ const HOOK_LIB_FILES = ['commit-message-check.js', 'shell-scan.js', 'gh-subcommand.js', 'body-sources.js', 'unmodelled.js', 'model-defaults-review.js', 'model-defaults.json', 'mockup-link.js'];
258
267
  // search-guard is Claude-Code-specific (#169), and so is mockup-link-guard
259
268
  // (it gates the AskUserQuestion tool, which Codex lacks): excluded from both
260
269
  // the Codex hook copy and the Codex verify spec's expected hookFiles, so a
@@ -264,6 +273,10 @@ const CODEX_HOOK_EXCLUDE = ['trailhead-search-guard.js', 'trailhead-mockup-link-
264
273
  // Claude Code gates the AskUserQuestion call instead, so it is neither copied
265
274
  // nor expected on a Claude install.
266
275
  const CLAUDE_HOOK_EXCLUDE = ['trailhead-mockup-link-stop.js'];
276
+ const claudeShippedHooks = () => HOOK_FILES.filter((f) => !CLAUDE_HOOK_EXCLUDE.includes(f));
277
+ const claudeEntrypoints = () => ENTRYPOINT_FILES.filter((f) => !CLAUDE_HOOK_EXCLUDE.includes(f));
278
+ const codexShippedHooks = () => HOOK_FILES.filter((f) => !CODEX_HOOK_EXCLUDE.includes(f));
279
+ const codexEntrypoints = () => ENTRYPOINT_FILES.filter((f) => !CODEX_HOOK_EXCLUDE.includes(f));
267
280
 
268
281
  // Copy the hook scripts and their runtime libs into a destination hooks dir
269
282
  // (Claude's ~/.claude/hooks or Codex's skills/trailhead/hooks). Shared by both
@@ -357,6 +370,12 @@ function stripHook(s, event, substr) {
357
370
  s.hooks[event] = s.hooks[event].filter((g) => (g.hooks || []).length);
358
371
  }
359
372
 
373
+ // Drop the per-guard PreToolUse registrations an earlier install made (and the
374
+ // pre-#169 Codex search-guard one): the merged entrypoint replaces all of them.
375
+ function stripLegacyBashGuards(s) {
376
+ for (const f of BASH_GUARD_MODULES) stripHook(s, 'PreToolUse', f);
377
+ }
378
+
360
379
  function uninstallClaude(configDir) {
361
380
  const P = claudePaths(configDir);
362
381
  // Sweep every trailhead-owned skill dir (current set + any stale dir a prior
@@ -371,12 +390,9 @@ function uninstallClaude(configDir) {
371
390
  // with other plugins, so never rmrf the whole dir.
372
391
  HOOK_LIB_FILES.forEach((f) => rmrf(path.join(P.hooksDir, 'lib', f)));
373
392
  const s = readJSON(P.settings);
374
- stripHook(s, 'PreToolUse', 'trailhead-commit-guard.js');
375
- stripHook(s, 'PreToolUse', 'trailhead-secret-guard.js');
376
- stripHook(s, 'PreToolUse', 'trailhead-install-guard.js');
377
- stripHook(s, 'PreToolUse', 'trailhead-search-guard.js');
393
+ stripLegacyBashGuards(s);
394
+ stripHook(s, 'PreToolUse', 'trailhead-bash-guards.js');
378
395
  stripHook(s, 'PreToolUse', 'trailhead-secret-read-guard.js');
379
- stripHook(s, 'PreToolUse', 'trailhead-body-guard.js');
380
396
  stripHook(s, 'PreToolUse', 'trailhead-mockup-link-guard.js');
381
397
  stripHook(s, 'PostToolUse', 'trailhead-issue-injection-scanner.js');
382
398
  stripHook(s, 'SessionStart', 'trailhead-check-update.js');
@@ -419,12 +435,11 @@ function installClaude(configDir, { useSymlink }) {
419
435
  }
420
436
 
421
437
  const s = readJSON(P.settings);
438
+ // Migration: an earlier install registered each Bash guard on its own; the
439
+ // merged entrypoint below replaces all five.
440
+ stripLegacyBashGuards(s);
422
441
  const added = [
423
- addHook(s, 'PreToolUse', 'Bash', hookCmd('trailhead-commit-guard.js')),
424
- addHook(s, 'PreToolUse', 'Bash', hookCmd('trailhead-secret-guard.js')),
425
- addHook(s, 'PreToolUse', 'Bash', hookCmd('trailhead-install-guard.js')),
426
- addHook(s, 'PreToolUse', 'Bash', hookCmd('trailhead-search-guard.js')),
427
- addHook(s, 'PreToolUse', 'Bash', hookCmd('trailhead-body-guard.js')),
442
+ addHook(s, 'PreToolUse', 'Bash', hookCmd('trailhead-bash-guards.js')),
428
443
  addHook(s, 'PreToolUse', 'Read|Bash', hookCmd('trailhead-secret-read-guard.js')),
429
444
  addHook(s, 'PreToolUse', 'AskUserQuestion', hookCmd('trailhead-mockup-link-guard.js')),
430
445
  addHook(s, 'PostToolUse', 'Bash', hookCmd('trailhead-issue-injection-scanner.js')),
@@ -438,7 +453,8 @@ function installClaude(configDir, { useSymlink }) {
438
453
  const spec = claudeVerifySpec(configDir, {
439
454
  skillDirs: shippedSkillDirs(),
440
455
  sharedFiles: readSharedCoreFiles(),
441
- hookFiles: HOOK_FILES.filter((f) => !CLAUDE_HOOK_EXCLUDE.includes(f)),
456
+ hookFiles: claudeShippedHooks(),
457
+ entrypointFiles: claudeEntrypoints(),
442
458
  hookLibFiles: HOOK_LIB_FILES,
443
459
  env: process.env,
444
460
  home: os.homedir(),
@@ -653,7 +669,7 @@ function installCodex(configDir, { useSymlink }) {
653
669
  // Migration: strip a pre-#169 search-guard registration. Its target is
654
670
  // always gone now (search-guard is never copied to Codex), so a prior
655
671
  // install's stale entry would otherwise fail the post-install check below.
656
- stripHook(h, 'PreToolUse', 'trailhead-search-guard.js');
672
+ stripLegacyBashGuards(h);
657
673
  for (const e of codexHookEntries(L.hooksScriptsDir)) addHook(h, e.event, e.matcher, e.command);
658
674
  writeJSON(L.hooksJson, h);
659
675
 
@@ -709,7 +725,8 @@ function installCodex(configDir, { useSymlink }) {
709
725
  const spec = codexVerifySpec(configDir, {
710
726
  skillDirs: shippedSkillDirs(),
711
727
  sharedFiles: readSharedCoreFiles(),
712
- hookFiles: HOOK_FILES.filter((f) => !CODEX_HOOK_EXCLUDE.includes(f)),
728
+ hookFiles: codexShippedHooks(),
729
+ entrypointFiles: codexEntrypoints(),
713
730
  hookLibFiles: HOOK_LIB_FILES,
714
731
  env: process.env,
715
732
  home: os.homedir(),
@@ -772,16 +789,11 @@ function uninstallCodex(configDir) {
772
789
  // settings.json flags alone.
773
790
  if (fs.existsSync(L.hooksJson)) {
774
791
  const h = readJSON(L.hooksJson);
775
- stripHook(h, 'PreToolUse', 'trailhead-commit-guard.js');
776
- stripHook(h, 'PreToolUse', 'trailhead-secret-guard.js');
777
- stripHook(h, 'PreToolUse', 'trailhead-install-guard.js');
778
- // search-guard is no longer projected to Codex (#169: it is
779
- // Claude-Code-specific), so it is never registered by a current install;
780
- // this strip is retained only to clean up a stale entry left by a
781
- // pre-#169 install that did register it.
782
- stripHook(h, 'PreToolUse', 'trailhead-search-guard.js');
792
+ // The merged Bash guard entry, plus every legacy per-guard registration
793
+ // (commit, secret, install, body, and the pre-#169 search-guard one).
794
+ stripHook(h, 'PreToolUse', 'trailhead-bash-guards.js');
795
+ stripLegacyBashGuards(h);
783
796
  stripHook(h, 'PreToolUse', 'trailhead-secret-read-guard.js');
784
- stripHook(h, 'PreToolUse', 'trailhead-body-guard.js');
785
797
  stripHook(h, 'PostToolUse', 'trailhead-issue-injection-scanner.js');
786
798
  stripHook(h, 'Stop', 'trailhead-mockup-link-stop.js');
787
799
  stripHook(h, 'SessionStart', 'trailhead-check-update.js');