trackfw 6.7.0 → 6.8.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.
- package/package.json +1 -1
- package/src/generators/hooks.js +202 -56
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trackfw",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.8.0",
|
|
4
4
|
"description": "Governance CLI for AI-native software delivery and AI coding agents. Enforces traceability: ADR → REQ → ROADMAP → kanban. Native support for Codex, Claude Code, Gemini CLI, Cursor, Copilot, Windsurf, Amazon Q, OpenCode, and Kiro.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
package/src/generators/hooks.js
CHANGED
|
@@ -85,6 +85,30 @@ function mergeClaudeHookArray(existing, matcher, command) {
|
|
|
85
85
|
return arr
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
// migrateHookCommand rewrites a legacy hook command to a new one, in place, for every entry
|
|
89
|
+
// matching the given matcher inside a "matcher + hooks[].command" shaped array -- the format
|
|
90
|
+
// shared by Claude, Codex and Gemini's merge-based settings files (PreToolUse/PostToolUse/
|
|
91
|
+
// PermissionRequest/Notification/BeforeTool/AfterTool). Used to fix settings files already written
|
|
92
|
+
// by an older trackfw before a command string changes -- without this, re-running
|
|
93
|
+
// `trackfw init`/`update` only ever appends the new (fixed) command alongside the stale one (merge
|
|
94
|
+
// dedup in mergeClaudeHookArray keys on the exact command string, so it can't tell "same guard, new
|
|
95
|
+
// path" from "a different hook"), leaving the broken entry in place to keep firing and failing
|
|
96
|
+
// forever. Originally written for Claude only (hence the doc comment history); generalized
|
|
97
|
+
// (ROADMAP-2026-08-11 ML-1A) so Codex/Gemini injectors can call it too, ahead of the
|
|
98
|
+
// mechanism-specific string changes those CLIs' waves make. Must always be called before the
|
|
99
|
+
// corresponding mergeClaudeHookArray call for the same matcher, or the merge's exact-string dedup
|
|
100
|
+
// will append a duplicate instead of rewriting in place.
|
|
101
|
+
function migrateHookCommand(existing, matcher, oldCommand, newCommand) {
|
|
102
|
+
const arr = Array.isArray(existing) ? existing : []
|
|
103
|
+
for (const item of arr) {
|
|
104
|
+
if (!item || item.matcher !== matcher) continue
|
|
105
|
+
const innerHooks = Array.isArray(item.hooks) ? item.hooks : []
|
|
106
|
+
for (const h of innerHooks) {
|
|
107
|
+
if (h && h.command === oldCommand) h.command = newCommand
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
88
112
|
// ---------------------------------------------------------------------------
|
|
89
113
|
// Scripts content
|
|
90
114
|
// ---------------------------------------------------------------------------
|
|
@@ -416,9 +440,86 @@ function generateGlobalCredentialGuardScript(home) {
|
|
|
416
440
|
console.log(' ✓ .trackfw/scripts/trackfw-credential-guard.sh')
|
|
417
441
|
}
|
|
418
442
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
443
|
+
// SIGNAL_CMD_*/CLEANUP_CMD_* — split per-CLI (ROADMAP-2026-08-11 ML-2A) from what used to be two
|
|
444
|
+
// constants (SIGNAL_CMD/CLEANUP_CMD) shared by all 6 CLI injectors. Mutating a shared constant to
|
|
445
|
+
// fix one CLI's path-resolution mechanism silently changed the emission of the other 5 -- Wave 0 of
|
|
446
|
+
// that roadmap proved Cursor and Copilot are *already correct* (Cursor hooks run from the project
|
|
447
|
+
// root; Copilot emits the native "cwd": "." field), so an accidental shared-constant edit there
|
|
448
|
+
// would be a regression in verified-good wiring, not just scope creep. Each constant starts equal
|
|
449
|
+
// to the pre-split literal; only the Claude ones (ML-2A, this change) move to the
|
|
450
|
+
// $CLAUDE_PROJECT_DIR-pinned form below. GUARD_CMD was later split too (ML-3A) once Codex's guard
|
|
451
|
+
// command needed to change -- see the GUARD_CMD_* block below.
|
|
452
|
+
// ROADMAP-2026-08-11 ML-3A: Codex CLI does not expose a project-root env var for repo-local hooks
|
|
453
|
+
// (unlike Claude's $CLAUDE_PROJECT_DIR or Gemini's $GEMINI_PROJECT_DIR) -- the only documented
|
|
454
|
+
// mechanism is shell substitution. Per ADR-2026-08-11 ("Codex — alterar, com dependência explícita
|
|
455
|
+
// de shell e git"), the command is wrapped in literal double quotes around
|
|
456
|
+
// `$(git rev-parse --show-toplevel)`, matching every repo-local hook example in the official Codex
|
|
457
|
+
// docs (https://developers.openai.com/codex/config-advanced): "For repo-local hooks, prefer
|
|
458
|
+
// resolving from the git root instead of using a relative path such as `.codex/hooks/...`."
|
|
459
|
+
const CODEX_ROOT = '"$(git rev-parse --show-toplevel)'
|
|
460
|
+
|
|
461
|
+
const SIGNAL_CMD_CLAUDE = '$CLAUDE_PROJECT_DIR/scripts/trackfw-attention-signal.sh'
|
|
462
|
+
const SIGNAL_CMD_CODEX = CODEX_ROOT + '/scripts/trackfw-attention-signal.sh"'
|
|
463
|
+
// $GEMINI_PROJECT_DIR (ROADMAP-2026-08-11 ML-4A): distinct from the session-following
|
|
464
|
+
// $GEMINI_CWD, documented and used in 100% of the Gemini CLI's official hook command
|
|
465
|
+
// examples (ADR-2026-08-11, "Gemini CLI — alterar, por argumento de assimetria"). Expanded
|
|
466
|
+
// by the Gemini CLI runtime itself -- no shell substitution needed, no literal quotes.
|
|
467
|
+
const SIGNAL_CMD_GEMINI = '$GEMINI_PROJECT_DIR/scripts/trackfw-attention-signal.sh'
|
|
468
|
+
const SIGNAL_CMD_KIRO = 'scripts/trackfw-attention-signal.sh'
|
|
469
|
+
const SIGNAL_CMD_COPILOT = 'scripts/trackfw-attention-signal.sh'
|
|
470
|
+
const SIGNAL_CMD_CURSOR = 'scripts/trackfw-attention-signal.sh'
|
|
471
|
+
|
|
472
|
+
const CLEANUP_CMD_CLAUDE = '$CLAUDE_PROJECT_DIR/scripts/trackfw-attention-cleanup.sh'
|
|
473
|
+
const CLEANUP_CMD_CODEX = CODEX_ROOT + '/scripts/trackfw-attention-cleanup.sh"'
|
|
474
|
+
const CLEANUP_CMD_GEMINI = '$GEMINI_PROJECT_DIR/scripts/trackfw-attention-cleanup.sh'
|
|
475
|
+
const CLEANUP_CMD_KIRO = 'scripts/trackfw-attention-cleanup.sh'
|
|
476
|
+
const CLEANUP_CMD_COPILOT = 'scripts/trackfw-attention-cleanup.sh'
|
|
477
|
+
const CLEANUP_CMD_CURSOR = 'scripts/trackfw-attention-cleanup.sh'
|
|
478
|
+
|
|
479
|
+
// Pre-ML-2A literal value of SIGNAL_CMD_CLAUDE/CLEANUP_CMD_CLAUDE, kept only as the `oldCommand`
|
|
480
|
+
// argument to the migration calls in injectClaudeHooks below (rewrites settings.json entries
|
|
481
|
+
// written by a pre-ML-2A trackfw in place, mirroring the GUARD_CMD_CLAUDE migration pattern).
|
|
482
|
+
const SIGNAL_CMD_CLAUDE_LEGACY = 'scripts/trackfw-attention-signal.sh'
|
|
483
|
+
const CLEANUP_CMD_CLAUDE_LEGACY = 'scripts/trackfw-attention-cleanup.sh'
|
|
484
|
+
|
|
485
|
+
// Pre-ML-3A literal value of SIGNAL_CMD_CODEX/CLEANUP_CMD_CODEX, kept only as the `oldCommand`
|
|
486
|
+
// argument to the migration calls in injectCodexHooks below.
|
|
487
|
+
const SIGNAL_CMD_CODEX_LEGACY = 'scripts/trackfw-attention-signal.sh'
|
|
488
|
+
const CLEANUP_CMD_CODEX_LEGACY = 'scripts/trackfw-attention-cleanup.sh'
|
|
489
|
+
|
|
490
|
+
// Pre-ML-4A literal value of SIGNAL_CMD_GEMINI/CLEANUP_CMD_GEMINI/GUARD_CMD_GEMINI, kept only as
|
|
491
|
+
// the `oldCommand` argument to the migration calls in injectGeminiHooks below.
|
|
492
|
+
const SIGNAL_CMD_GEMINI_LEGACY = 'scripts/trackfw-attention-signal.sh'
|
|
493
|
+
const CLEANUP_CMD_GEMINI_LEGACY = 'scripts/trackfw-attention-cleanup.sh'
|
|
494
|
+
const GUARD_CMD_GEMINI_LEGACY = 'scripts/trackfw-credential-guard.sh'
|
|
495
|
+
|
|
496
|
+
// GUARD_CMD_* -- split per-CLI (ROADMAP-2026-08-11 ML-3A) from what used to be one constant
|
|
497
|
+
// (GUARD_CMD) shared by Codex/Gemini/Kiro/Copilot/Cursor (Claude already had its own,
|
|
498
|
+
// GUARD_CMD_CLAUDE, since ML-2A). Same rationale as the SIGNAL_CMD_*/CLEANUP_CMD_* split above:
|
|
499
|
+
// Cursor and Copilot are verified-correct wiring (ML-0A), so a shared-constant edit made for Codex
|
|
500
|
+
// would have silently regressed them. Each constant starts equal to the pre-split literal; only
|
|
501
|
+
// GUARD_CMD_CODEX (this ML) moves to the new form below.
|
|
502
|
+
// Legacy relative-path literal, pre-dating both the ML-2A Claude fix and the ML-3A Codex fix --
|
|
503
|
+
// kept only as the `oldCommand` argument to migration calls (Claude's migration below, and Codex's
|
|
504
|
+
// until this ML rewrites it), mirroring the SIGNAL_CMD_CLAUDE_LEGACY/CLEANUP_CMD_CLAUDE_LEGACY
|
|
505
|
+
// pattern above.
|
|
506
|
+
const GUARD_CMD_LEGACY = 'scripts/trackfw-credential-guard.sh'
|
|
507
|
+
const GUARD_CMD_CODEX = CODEX_ROOT + '/scripts/trackfw-credential-guard.sh"'
|
|
508
|
+
const GUARD_CMD_GEMINI = '$GEMINI_PROJECT_DIR/scripts/trackfw-credential-guard.sh'
|
|
509
|
+
const GUARD_CMD_KIRO = 'scripts/trackfw-credential-guard.sh'
|
|
510
|
+
const GUARD_CMD_COPILOT = 'scripts/trackfw-credential-guard.sh'
|
|
511
|
+
const GUARD_CMD_CURSOR = 'scripts/trackfw-credential-guard.sh'
|
|
512
|
+
|
|
513
|
+
// Claude Code only (2026-08-09 fix, reported in production against the CMDB project): Claude Code
|
|
514
|
+
// resolves a bare relative hook command against the hook's *dynamic* cwd (tracks `cd`s the agent
|
|
515
|
+
// runs during the session), not the project root -- confirmed against
|
|
516
|
+
// https://code.claude.com/docs/en/hooks: "Handlers run in the current directory... cwd is dynamic".
|
|
517
|
+
// Any Bash/Read/Write/Edit call after the agent `cd`s into a subdirectory (e.g. a monorepo package)
|
|
518
|
+
// made the hook fail with "No such file or directory". $CLAUDE_PROJECT_DIR is the env var Claude
|
|
519
|
+
// Code guarantees stays pinned to the project root regardless of cwd drift (same doc) -- used here
|
|
520
|
+
// instead of GUARD_CMD, matching the pattern this project's own custom hooks already relied on
|
|
521
|
+
// successfully in practice.
|
|
522
|
+
const GUARD_CMD_CLAUDE = '$CLAUDE_PROJECT_DIR/scripts/trackfw-credential-guard.sh'
|
|
422
523
|
|
|
423
524
|
// ---------------------------------------------------------------------------
|
|
424
525
|
// Global credential-guard dedup (ROADMAP-2026-08-06 Wave 3/ML-3A)
|
|
@@ -551,22 +652,41 @@ function injectClaudeHooks(cwd) {
|
|
|
551
652
|
const data = readJSON(filePath)
|
|
552
653
|
|
|
553
654
|
if (!data.hooks) data.hooks = {}
|
|
554
|
-
|
|
655
|
+
|
|
656
|
+
// Migration (ROADMAP-2026-08-11 ML-2A): rewrite any stale relative-path attention-signal/cleanup
|
|
657
|
+
// command from an older trackfw run before merging the $CLAUDE_PROJECT_DIR-pinned one below, so
|
|
658
|
+
// upgrading doesn't just append a second, still-cwd-fragile entry alongside the fixed one (same
|
|
659
|
+
// "No such file or directory" bug class as the credential-guard fix below, and the same
|
|
660
|
+
// migrate-before-merge ordering requirement -- mergeClaudeHookArray's dedup keys on the exact
|
|
661
|
+
// command string, so it can't tell "same hook, new path" from "a different hook").
|
|
662
|
+
migrateHookCommand(data.hooks.PreToolUse, 'AskUserQuestion', SIGNAL_CMD_CLAUDE_LEGACY, SIGNAL_CMD_CLAUDE)
|
|
663
|
+
migrateHookCommand(data.hooks.PostToolUse, 'AskUserQuestion', CLEANUP_CMD_CLAUDE_LEGACY, CLEANUP_CMD_CLAUDE)
|
|
664
|
+
|
|
665
|
+
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'AskUserQuestion', SIGNAL_CMD_CLAUDE)
|
|
666
|
+
|
|
667
|
+
// Rewrite any stale relative-path credential-guard command from an older trackfw run before
|
|
668
|
+
// merging the fixed one below, so upgrading doesn't just append a second, still-broken entry
|
|
669
|
+
// alongside the new one (see GUARD_CMD_CLAUDE comment for the "No such file or directory" bug).
|
|
670
|
+
for (const matcher of ['Bash', 'Read', 'Write|Edit']) {
|
|
671
|
+
migrateHookCommand(data.hooks.PreToolUse, matcher, GUARD_CMD_LEGACY, GUARD_CMD_CLAUDE)
|
|
672
|
+
migrateHookCommand(data.hooks.PostToolUse, matcher, GUARD_CMD_LEGACY, GUARD_CMD_CLAUDE)
|
|
673
|
+
}
|
|
674
|
+
|
|
555
675
|
// Dedup (ROADMAP-2026-08-06 Wave 3/ML-3A, extended ADR-2026-08-06 emenda 7/ROADMAP-2026-08-08
|
|
556
676
|
// Wave 2 to Read/Write|Edit): skip project-scope credential-guard when the global one is
|
|
557
677
|
// already installed for this CLI.
|
|
558
678
|
if (!globalCredentialGuardInstalledClaude()) {
|
|
559
|
-
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'Bash',
|
|
679
|
+
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'Bash', GUARD_CMD_CLAUDE)
|
|
560
680
|
// ADR-2026-08-06 emenda 7 (2026-08-08): Read/Write/Edit coverage — extraction via direct
|
|
561
681
|
// file read, or materialization via write/edit, never went through the hook before.
|
|
562
|
-
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'Read',
|
|
563
|
-
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'Write|Edit',
|
|
682
|
+
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'Read', GUARD_CMD_CLAUDE)
|
|
683
|
+
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'Write|Edit', GUARD_CMD_CLAUDE)
|
|
564
684
|
}
|
|
565
|
-
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'AskUserQuestion',
|
|
685
|
+
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'AskUserQuestion', CLEANUP_CMD_CLAUDE)
|
|
566
686
|
if (!globalCredentialGuardInstalledClaude()) {
|
|
567
|
-
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'Bash',
|
|
568
|
-
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'Read',
|
|
569
|
-
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'Write|Edit',
|
|
687
|
+
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'Bash', GUARD_CMD_CLAUDE)
|
|
688
|
+
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'Read', GUARD_CMD_CLAUDE)
|
|
689
|
+
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'Write|Edit', GUARD_CMD_CLAUDE)
|
|
570
690
|
}
|
|
571
691
|
|
|
572
692
|
writeJSON(filePath, data)
|
|
@@ -596,19 +716,30 @@ function injectCodexHooks(cwd) {
|
|
|
596
716
|
const data = readJSON(filePath)
|
|
597
717
|
|
|
598
718
|
if (!data.hooks) data.hooks = {}
|
|
599
|
-
|
|
719
|
+
|
|
720
|
+
// Migration wiring (ROADMAP-2026-08-11 ML-1A, strings updated in ML-3A): rewrites any stale
|
|
721
|
+
// relative-path entry from before this fix in place, so `trackfw update` doesn't just append the
|
|
722
|
+
// new $(git rev-parse ...) entry alongside the still-cwd-fragile old one.
|
|
723
|
+
migrateHookCommand(data.hooks.PermissionRequest, '.*', SIGNAL_CMD_CODEX_LEGACY, SIGNAL_CMD_CODEX)
|
|
724
|
+
migrateHookCommand(data.hooks.PreToolUse, 'Bash', GUARD_CMD_LEGACY, GUARD_CMD_CODEX)
|
|
725
|
+
migrateHookCommand(data.hooks.PreToolUse, 'apply_patch', GUARD_CMD_LEGACY, GUARD_CMD_CODEX)
|
|
726
|
+
migrateHookCommand(data.hooks.PostToolUse, '.*', CLEANUP_CMD_CODEX_LEGACY, CLEANUP_CMD_CODEX)
|
|
727
|
+
migrateHookCommand(data.hooks.PostToolUse, 'Bash', GUARD_CMD_LEGACY, GUARD_CMD_CODEX)
|
|
728
|
+
migrateHookCommand(data.hooks.PostToolUse, 'apply_patch', GUARD_CMD_LEGACY, GUARD_CMD_CODEX)
|
|
729
|
+
|
|
730
|
+
data.hooks.PermissionRequest = mergeClaudeHookArray(data.hooks.PermissionRequest, '.*', SIGNAL_CMD_CODEX)
|
|
600
731
|
// Dedup (ROADMAP-2026-08-06 Wave 3/ML-3A, extended ADR-2026-08-06 emenda 7/ROADMAP-2026-08-08
|
|
601
732
|
// Wave 2 to apply_patch): skip project-scope credential-guard when the global one is already
|
|
602
733
|
// installed for this CLI.
|
|
603
734
|
const skipCodexCG = globalCredentialGuardInstalledCodex()
|
|
604
735
|
if (!skipCodexCG) {
|
|
605
|
-
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'Bash',
|
|
606
|
-
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'apply_patch',
|
|
736
|
+
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'Bash', GUARD_CMD_CODEX)
|
|
737
|
+
data.hooks.PreToolUse = mergeClaudeHookArray(data.hooks.PreToolUse, 'apply_patch', GUARD_CMD_CODEX)
|
|
607
738
|
}
|
|
608
|
-
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, '.*',
|
|
739
|
+
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, '.*', CLEANUP_CMD_CODEX)
|
|
609
740
|
if (!skipCodexCG) {
|
|
610
|
-
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'Bash',
|
|
611
|
-
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'apply_patch',
|
|
741
|
+
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'Bash', GUARD_CMD_CODEX)
|
|
742
|
+
data.hooks.PostToolUse = mergeClaudeHookArray(data.hooks.PostToolUse, 'apply_patch', GUARD_CMD_CODEX)
|
|
612
743
|
}
|
|
613
744
|
|
|
614
745
|
writeJSON(filePath, data)
|
|
@@ -645,21 +776,36 @@ function injectGeminiHooks(cwd) {
|
|
|
645
776
|
const data = readJSON(filePath)
|
|
646
777
|
|
|
647
778
|
if (!data.hooks) data.hooks = {}
|
|
648
|
-
|
|
779
|
+
|
|
780
|
+
// Migration wiring (ROADMAP-2026-08-11 ML-1A): old === new is a functional no-op today, but
|
|
781
|
+
// proves the call point exists and runs before the merge below. The wave that changes the
|
|
782
|
+
// Gemini command strings (ML-4A) updates the oldCommand argument here instead of adding this
|
|
783
|
+
// call from scratch -- without it, the merge's exact-string dedup would append a duplicate
|
|
784
|
+
// alongside the stale entry.
|
|
785
|
+
migrateHookCommand(data.hooks.Notification, 'ToolPermission', SIGNAL_CMD_GEMINI_LEGACY, SIGNAL_CMD_GEMINI)
|
|
786
|
+
migrateHookCommand(data.hooks.BeforeTool, 'run_shell_command', GUARD_CMD_GEMINI_LEGACY, GUARD_CMD_GEMINI)
|
|
787
|
+
migrateHookCommand(data.hooks.BeforeTool, 'read_file|read_many_files', GUARD_CMD_GEMINI_LEGACY, GUARD_CMD_GEMINI)
|
|
788
|
+
migrateHookCommand(data.hooks.BeforeTool, 'write_file|replace', GUARD_CMD_GEMINI_LEGACY, GUARD_CMD_GEMINI)
|
|
789
|
+
migrateHookCommand(data.hooks.AfterTool, '*', CLEANUP_CMD_GEMINI_LEGACY, CLEANUP_CMD_GEMINI)
|
|
790
|
+
migrateHookCommand(data.hooks.AfterTool, 'run_shell_command', GUARD_CMD_GEMINI_LEGACY, GUARD_CMD_GEMINI)
|
|
791
|
+
migrateHookCommand(data.hooks.AfterTool, 'read_file|read_many_files', GUARD_CMD_GEMINI_LEGACY, GUARD_CMD_GEMINI)
|
|
792
|
+
migrateHookCommand(data.hooks.AfterTool, 'write_file|replace', GUARD_CMD_GEMINI_LEGACY, GUARD_CMD_GEMINI)
|
|
793
|
+
|
|
794
|
+
data.hooks.Notification = mergeClaudeHookArray(data.hooks.Notification, 'ToolPermission', SIGNAL_CMD_GEMINI)
|
|
649
795
|
// Dedup (ROADMAP-2026-08-06 Wave 3/ML-3A, extended ADR-2026-08-06 emenda 7/ROADMAP-2026-08-08
|
|
650
796
|
// Wave 2 to read_file|read_many_files / write_file|replace): skip project-scope
|
|
651
797
|
// credential-guard when the global one is already installed.
|
|
652
798
|
const skipGeminiCG = globalCredentialGuardInstalledGemini()
|
|
653
799
|
if (!skipGeminiCG) {
|
|
654
|
-
data.hooks.BeforeTool = mergeClaudeHookArray(data.hooks.BeforeTool, 'run_shell_command',
|
|
655
|
-
data.hooks.BeforeTool = mergeClaudeHookArray(data.hooks.BeforeTool, 'read_file|read_many_files',
|
|
656
|
-
data.hooks.BeforeTool = mergeClaudeHookArray(data.hooks.BeforeTool, 'write_file|replace',
|
|
800
|
+
data.hooks.BeforeTool = mergeClaudeHookArray(data.hooks.BeforeTool, 'run_shell_command', GUARD_CMD_GEMINI)
|
|
801
|
+
data.hooks.BeforeTool = mergeClaudeHookArray(data.hooks.BeforeTool, 'read_file|read_many_files', GUARD_CMD_GEMINI)
|
|
802
|
+
data.hooks.BeforeTool = mergeClaudeHookArray(data.hooks.BeforeTool, 'write_file|replace', GUARD_CMD_GEMINI)
|
|
657
803
|
}
|
|
658
|
-
data.hooks.AfterTool = mergeClaudeHookArray(data.hooks.AfterTool, '*',
|
|
804
|
+
data.hooks.AfterTool = mergeClaudeHookArray(data.hooks.AfterTool, '*', CLEANUP_CMD_GEMINI)
|
|
659
805
|
if (!skipGeminiCG) {
|
|
660
|
-
data.hooks.AfterTool = mergeClaudeHookArray(data.hooks.AfterTool, 'run_shell_command',
|
|
661
|
-
data.hooks.AfterTool = mergeClaudeHookArray(data.hooks.AfterTool, 'read_file|read_many_files',
|
|
662
|
-
data.hooks.AfterTool = mergeClaudeHookArray(data.hooks.AfterTool, 'write_file|replace',
|
|
806
|
+
data.hooks.AfterTool = mergeClaudeHookArray(data.hooks.AfterTool, 'run_shell_command', GUARD_CMD_GEMINI)
|
|
807
|
+
data.hooks.AfterTool = mergeClaudeHookArray(data.hooks.AfterTool, 'read_file|read_many_files', GUARD_CMD_GEMINI)
|
|
808
|
+
data.hooks.AfterTool = mergeClaudeHookArray(data.hooks.AfterTool, 'write_file|replace', GUARD_CMD_GEMINI)
|
|
663
809
|
}
|
|
664
810
|
|
|
665
811
|
writeJSON(filePath, data)
|
|
@@ -695,14 +841,14 @@ function injectKiroHooks(cwd) {
|
|
|
695
841
|
description: 'Signals trackfw board when agent executes a tool',
|
|
696
842
|
trigger: 'PreToolUse',
|
|
697
843
|
matcher: '*',
|
|
698
|
-
action: { type: 'command', command:
|
|
844
|
+
action: { type: 'command', command: SIGNAL_CMD_KIRO },
|
|
699
845
|
},
|
|
700
846
|
{
|
|
701
847
|
name: 'trackfw-attention-cleanup',
|
|
702
848
|
description: 'Clears trackfw board attention after tool completes',
|
|
703
849
|
trigger: 'PostToolUse',
|
|
704
850
|
matcher: '*',
|
|
705
|
-
action: { type: 'command', command:
|
|
851
|
+
action: { type: 'command', command: CLEANUP_CMD_KIRO },
|
|
706
852
|
},
|
|
707
853
|
]
|
|
708
854
|
|
|
@@ -716,14 +862,14 @@ function injectKiroHooks(cwd) {
|
|
|
716
862
|
description: 'Blocks/warns on possible plaintext credential materialization before a shell command executes',
|
|
717
863
|
trigger: 'PreToolUse',
|
|
718
864
|
matcher: 'shell',
|
|
719
|
-
action: { type: 'command', command:
|
|
865
|
+
action: { type: 'command', command: GUARD_CMD_KIRO },
|
|
720
866
|
},
|
|
721
867
|
{
|
|
722
868
|
name: 'trackfw-credential-guard-post',
|
|
723
869
|
description: 'Warns on possible plaintext credential materialization after a shell command executes',
|
|
724
870
|
trigger: 'PostToolUse',
|
|
725
871
|
matcher: 'shell',
|
|
726
|
-
action: { type: 'command', command:
|
|
872
|
+
action: { type: 'command', command: GUARD_CMD_KIRO },
|
|
727
873
|
},
|
|
728
874
|
// Read/Write coverage (ADR-2026-08-06 emenda 7, 2026-08-08): "read" and "write" are the
|
|
729
875
|
// documented Kiro tool-category aliases (fs_read/fs_write), same pattern as "shell" above.
|
|
@@ -732,28 +878,28 @@ function injectKiroHooks(cwd) {
|
|
|
732
878
|
description: 'Blocks/warns on possible plaintext credential materialization before a file read',
|
|
733
879
|
trigger: 'PreToolUse',
|
|
734
880
|
matcher: 'read',
|
|
735
|
-
action: { type: 'command', command:
|
|
881
|
+
action: { type: 'command', command: GUARD_CMD_KIRO },
|
|
736
882
|
},
|
|
737
883
|
{
|
|
738
884
|
name: 'trackfw-credential-guard-read-post',
|
|
739
885
|
description: 'Warns on possible plaintext credential materialization after a file read',
|
|
740
886
|
trigger: 'PostToolUse',
|
|
741
887
|
matcher: 'read',
|
|
742
|
-
action: { type: 'command', command:
|
|
888
|
+
action: { type: 'command', command: GUARD_CMD_KIRO },
|
|
743
889
|
},
|
|
744
890
|
{
|
|
745
891
|
name: 'trackfw-credential-guard-write-pre',
|
|
746
892
|
description: 'Blocks/warns on possible plaintext credential materialization before a file write',
|
|
747
893
|
trigger: 'PreToolUse',
|
|
748
894
|
matcher: 'write',
|
|
749
|
-
action: { type: 'command', command:
|
|
895
|
+
action: { type: 'command', command: GUARD_CMD_KIRO },
|
|
750
896
|
},
|
|
751
897
|
{
|
|
752
898
|
name: 'trackfw-credential-guard-write-post',
|
|
753
899
|
description: 'Warns on possible plaintext credential materialization after a file write',
|
|
754
900
|
trigger: 'PostToolUse',
|
|
755
901
|
matcher: 'write',
|
|
756
|
-
action: { type: 'command', command:
|
|
902
|
+
action: { type: 'command', command: GUARD_CMD_KIRO },
|
|
757
903
|
},
|
|
758
904
|
)
|
|
759
905
|
}
|
|
@@ -797,19 +943,19 @@ function injectKiroHooks(cwd) {
|
|
|
797
943
|
function injectCopilotHooks(cwd) {
|
|
798
944
|
const filePath = path.join(cwd, '.github', 'hooks', 'trackfw-attention.json')
|
|
799
945
|
|
|
800
|
-
const preToolUse = [{ type: 'command', bash:
|
|
801
|
-
const postToolUse = [{ type: 'command', bash:
|
|
946
|
+
const preToolUse = [{ type: 'command', bash: SIGNAL_CMD_COPILOT, cwd: '.', timeoutSec: 10 }]
|
|
947
|
+
const postToolUse = [{ type: 'command', bash: CLEANUP_CMD_COPILOT, cwd: '.', timeoutSec: 10 }]
|
|
802
948
|
|
|
803
949
|
// Dedup (ROADMAP-2026-08-06 Wave 3/ML-3A, extended ADR-2026-08-06 emenda 7/ROADMAP-2026-08-08
|
|
804
950
|
// Wave 2 to view / create|edit): skip project-scope credential-guard entries when the global
|
|
805
951
|
// one is already installed.
|
|
806
952
|
if (!globalCredentialGuardInstalledCopilot()) {
|
|
807
|
-
preToolUse.push({ type: 'command', matcher: 'bash', bash:
|
|
808
|
-
preToolUse.push({ type: 'command', matcher: 'view', bash:
|
|
809
|
-
preToolUse.push({ type: 'command', matcher: 'create|edit', bash:
|
|
810
|
-
postToolUse.push({ type: 'command', matcher: 'bash', bash:
|
|
811
|
-
postToolUse.push({ type: 'command', matcher: 'view', bash:
|
|
812
|
-
postToolUse.push({ type: 'command', matcher: 'create|edit', bash:
|
|
953
|
+
preToolUse.push({ type: 'command', matcher: 'bash', bash: GUARD_CMD_COPILOT, cwd: '.', timeoutSec: 10 })
|
|
954
|
+
preToolUse.push({ type: 'command', matcher: 'view', bash: GUARD_CMD_COPILOT, cwd: '.', timeoutSec: 10 })
|
|
955
|
+
preToolUse.push({ type: 'command', matcher: 'create|edit', bash: GUARD_CMD_COPILOT, cwd: '.', timeoutSec: 10 })
|
|
956
|
+
postToolUse.push({ type: 'command', matcher: 'bash', bash: GUARD_CMD_COPILOT, cwd: '.', timeoutSec: 10 })
|
|
957
|
+
postToolUse.push({ type: 'command', matcher: 'view', bash: GUARD_CMD_COPILOT, cwd: '.', timeoutSec: 10 })
|
|
958
|
+
postToolUse.push({ type: 'command', matcher: 'create|edit', bash: GUARD_CMD_COPILOT, cwd: '.', timeoutSec: 10 })
|
|
813
959
|
}
|
|
814
960
|
|
|
815
961
|
const data = {
|
|
@@ -895,29 +1041,29 @@ function injectCursorHooks(cwd) {
|
|
|
895
1041
|
// Migrate any legacy top-level preToolUse/postToolUse trackfw entries
|
|
896
1042
|
// (written by trackfw before this ML) into the nested, real hooks.
|
|
897
1043
|
if (!Array.isArray(data.hooks.preToolUse)) data.hooks.preToolUse = []
|
|
898
|
-
if (!hasEntry(data.hooks.preToolUse, 'command',
|
|
899
|
-
data.hooks.preToolUse.push({ command:
|
|
1044
|
+
if (!hasEntry(data.hooks.preToolUse, 'command', SIGNAL_CMD_CURSOR)) {
|
|
1045
|
+
data.hooks.preToolUse.push({ command: SIGNAL_CMD_CURSOR })
|
|
900
1046
|
}
|
|
901
|
-
removeKnownCommandFromLegacyTopLevelArray(data, 'preToolUse',
|
|
1047
|
+
removeKnownCommandFromLegacyTopLevelArray(data, 'preToolUse', SIGNAL_CMD_CURSOR)
|
|
902
1048
|
|
|
903
1049
|
if (!Array.isArray(data.hooks.postToolUse)) data.hooks.postToolUse = []
|
|
904
|
-
if (!hasEntry(data.hooks.postToolUse, 'command',
|
|
905
|
-
data.hooks.postToolUse.push({ command:
|
|
1050
|
+
if (!hasEntry(data.hooks.postToolUse, 'command', CLEANUP_CMD_CURSOR)) {
|
|
1051
|
+
data.hooks.postToolUse.push({ command: CLEANUP_CMD_CURSOR })
|
|
906
1052
|
}
|
|
907
|
-
removeKnownCommandFromLegacyTopLevelArray(data, 'postToolUse',
|
|
1053
|
+
removeKnownCommandFromLegacyTopLevelArray(data, 'postToolUse', CLEANUP_CMD_CURSOR)
|
|
908
1054
|
|
|
909
1055
|
// Dedup (ROADMAP-2026-08-06 Wave 3/ML-3A, extended ADR-2026-08-06 emenda 7/ROADMAP-2026-08-08
|
|
910
1056
|
// Wave 2 to Read/Write via the generic preToolUse/postToolUse events): skip project-scope
|
|
911
1057
|
// credential-guard entries when the global one is already installed.
|
|
912
1058
|
if (!globalCredentialGuardInstalledCursor()) {
|
|
913
1059
|
if (!Array.isArray(data.hooks.beforeShellExecution)) data.hooks.beforeShellExecution = []
|
|
914
|
-
if (!hasEntry(data.hooks.beforeShellExecution, 'command',
|
|
915
|
-
data.hooks.beforeShellExecution.push({ command:
|
|
1060
|
+
if (!hasEntry(data.hooks.beforeShellExecution, 'command', GUARD_CMD_CURSOR)) {
|
|
1061
|
+
data.hooks.beforeShellExecution.push({ command: GUARD_CMD_CURSOR })
|
|
916
1062
|
}
|
|
917
1063
|
|
|
918
1064
|
if (!Array.isArray(data.hooks.afterShellExecution)) data.hooks.afterShellExecution = []
|
|
919
|
-
if (!hasEntry(data.hooks.afterShellExecution, 'command',
|
|
920
|
-
data.hooks.afterShellExecution.push({ command:
|
|
1065
|
+
if (!hasEntry(data.hooks.afterShellExecution, 'command', GUARD_CMD_CURSOR)) {
|
|
1066
|
+
data.hooks.afterShellExecution.push({ command: GUARD_CMD_CURSOR })
|
|
921
1067
|
}
|
|
922
1068
|
|
|
923
1069
|
// Read/Write coverage (ADR-2026-08-06 emenda 7, 2026-08-08): wired via the generic
|
|
@@ -928,19 +1074,19 @@ function injectCursorHooks(cwd) {
|
|
|
928
1074
|
// unfiltered signal entry and these matcher-scoped guard entries share the same array, so
|
|
929
1075
|
// dedup must also check `matcher`.
|
|
930
1076
|
const hasGuardMatcherEntry = (arr, matcher) =>
|
|
931
|
-
Array.isArray(arr) && arr.some(e => e && e.command ===
|
|
1077
|
+
Array.isArray(arr) && arr.some(e => e && e.command === GUARD_CMD_CURSOR && e.matcher === matcher)
|
|
932
1078
|
|
|
933
1079
|
if (!hasGuardMatcherEntry(data.hooks.preToolUse, 'Read')) {
|
|
934
|
-
data.hooks.preToolUse.push({ command:
|
|
1080
|
+
data.hooks.preToolUse.push({ command: GUARD_CMD_CURSOR, matcher: 'Read' })
|
|
935
1081
|
}
|
|
936
1082
|
if (!hasGuardMatcherEntry(data.hooks.preToolUse, 'Write')) {
|
|
937
|
-
data.hooks.preToolUse.push({ command:
|
|
1083
|
+
data.hooks.preToolUse.push({ command: GUARD_CMD_CURSOR, matcher: 'Write' })
|
|
938
1084
|
}
|
|
939
1085
|
if (!hasGuardMatcherEntry(data.hooks.postToolUse, 'Read')) {
|
|
940
|
-
data.hooks.postToolUse.push({ command:
|
|
1086
|
+
data.hooks.postToolUse.push({ command: GUARD_CMD_CURSOR, matcher: 'Read' })
|
|
941
1087
|
}
|
|
942
1088
|
if (!hasGuardMatcherEntry(data.hooks.postToolUse, 'Write')) {
|
|
943
|
-
data.hooks.postToolUse.push({ command:
|
|
1089
|
+
data.hooks.postToolUse.push({ command: GUARD_CMD_CURSOR, matcher: 'Write' })
|
|
944
1090
|
}
|
|
945
1091
|
}
|
|
946
1092
|
|