@devrik-tools/claude-gates 0.7.2 → 0.9.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 (83) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.es.md +129 -11
  3. package/README.md +120 -10
  4. package/cli/doctor.mjs +132 -0
  5. package/cli/evidence.mjs +78 -0
  6. package/cli/hooks-manifest.mjs +89 -0
  7. package/cli/index.mjs +124 -6
  8. package/cli/init.mjs +97 -3
  9. package/cli/log.mjs +70 -0
  10. package/cli/materialize.mjs +36 -2
  11. package/cli/registry.mjs +12 -1
  12. package/cli/selection.mjs +18 -0
  13. package/cli/smoke-fixtures.json +159 -25
  14. package/cli/task.mjs +104 -11
  15. package/cli/toggle.mjs +125 -0
  16. package/package.json +4 -3
  17. package/plugins/gates/.claude-plugin/plugin.json +1 -1
  18. package/plugins/gates/hooks/doctor.mjs +47 -1
  19. package/plugins/gates/hooks/gates/atomic-commit/index.mjs +92 -119
  20. package/plugins/gates/hooks/gates/audit-before-build/index.mjs +101 -66
  21. package/plugins/gates/hooks/gates/autonomous-mode/index.mjs +8 -8
  22. package/plugins/gates/hooks/gates/autonomous-mode/stop.mjs +13 -64
  23. package/plugins/gates/hooks/gates/bash-commands/index.mjs +186 -163
  24. package/plugins/gates/hooks/gates/block-remote-publish/index.mjs +100 -101
  25. package/plugins/gates/hooks/gates/brief-approved/index.mjs +71 -140
  26. package/plugins/gates/hooks/gates/brief-before-delegate/index.mjs +56 -137
  27. package/plugins/gates/hooks/gates/capability-map/index.mjs +280 -506
  28. package/plugins/gates/hooks/gates/circuit-breaker/index.mjs +94 -215
  29. package/plugins/gates/hooks/gates/circuit-breaker/track.mjs +285 -0
  30. package/plugins/gates/hooks/gates/dependency-skills/index.mjs +127 -46
  31. package/plugins/gates/hooks/gates/diagnosis-before-patch/index.mjs +81 -19
  32. package/plugins/gates/hooks/gates/engram-first/index.mjs +41 -0
  33. package/plugins/gates/hooks/gates/engram-first/session-start.mjs +64 -0
  34. package/plugins/gates/hooks/gates/engram-first/shared.mjs +90 -0
  35. package/plugins/gates/hooks/gates/engram-first/stop.mjs +23 -0
  36. package/plugins/gates/hooks/gates/engram-first/track.mjs +97 -0
  37. package/plugins/gates/hooks/gates/feature-catalog/index.mjs +156 -50
  38. package/plugins/gates/hooks/gates/force-parallel/index.mjs +53 -95
  39. package/plugins/gates/hooks/gates/forge-flow/index.mjs +51 -83
  40. package/plugins/gates/hooks/gates/implementation-pipeline/index.mjs +40 -80
  41. package/plugins/gates/hooks/gates/intent-flow/index.mjs +23 -149
  42. package/plugins/gates/hooks/gates/library-docs/index.mjs +338 -0
  43. package/plugins/gates/hooks/gates/library-docs/track.mjs +95 -0
  44. package/plugins/gates/hooks/gates/lint-commit/index.mjs +88 -97
  45. package/plugins/gates/hooks/gates/mandatory-flow/index.mjs +48 -95
  46. package/plugins/gates/hooks/gates/neutral-spanish/index.mjs +69 -31
  47. package/plugins/gates/hooks/gates/never-assume/index.mjs +23 -18
  48. package/plugins/gates/hooks/gates/no-blocking/index.mjs +127 -86
  49. package/plugins/gates/hooks/gates/no-coauthor/index.mjs +108 -88
  50. package/plugins/gates/hooks/gates/no-explanatory-comments/index.mjs +281 -0
  51. package/plugins/gates/hooks/gates/no-lint-suppression/index.mjs +170 -110
  52. package/plugins/gates/hooks/gates/no-memory-dependency/index.mjs +46 -77
  53. package/plugins/gates/hooks/gates/no-reconfirm/index.mjs +85 -47
  54. package/plugins/gates/hooks/gates/no-trivial-scripts/index.mjs +114 -0
  55. package/plugins/gates/hooks/gates/protected-paths/index.mjs +137 -90
  56. package/plugins/gates/hooks/gates/recurrence-lock/index.mjs +95 -51
  57. package/plugins/gates/hooks/gates/require-monitor/index.mjs +126 -0
  58. package/plugins/gates/hooks/gates/require-task-split/index.mjs +88 -0
  59. package/plugins/gates/hooks/gates/reuse-before-build/index.mjs +72 -216
  60. package/plugins/gates/hooks/gates/risk-level/index.mjs +51 -204
  61. package/plugins/gates/hooks/gates/root-cause-first/index.mjs +34 -17
  62. package/plugins/gates/hooks/gates/root-whitelist/index.mjs +160 -95
  63. package/plugins/gates/hooks/gates/rule-skill-autodiscovery/index.mjs +196 -97
  64. package/plugins/gates/hooks/gates/sdd-specs/index.mjs +90 -143
  65. package/plugins/gates/hooks/gates/staged-lint/index.mjs +98 -106
  66. package/plugins/gates/hooks/gates/stop-pending/index.mjs +47 -148
  67. package/plugins/gates/hooks/gates/test-after-implementation/index.mjs +78 -68
  68. package/plugins/gates/hooks/gates/test-matrix/index.mjs +53 -88
  69. package/plugins/gates/hooks/gates/tool-map/index.mjs +80 -110
  70. package/plugins/gates/hooks/hooks.json +152 -42
  71. package/plugins/gates/hooks/lib/config.mjs +6 -3
  72. package/plugins/gates/hooks/lib/delegation.mjs +352 -0
  73. package/plugins/gates/hooks/lib/gate-log.mjs +141 -0
  74. package/plugins/gates/hooks/lib/git.mjs +262 -0
  75. package/plugins/gates/hooks/lib/hook-io.mjs +451 -83
  76. package/plugins/gates/hooks/lib/session-state.mjs +150 -0
  77. package/plugins/gates/hooks/lib/signals.mjs +18 -13
  78. package/plugins/gates/hooks/lib/testing.mjs +148 -0
  79. package/plugins/gates/hooks/lib/tools.mjs +144 -0
  80. package/plugins/tasks/.claude-plugin/plugin.json +1 -1
  81. package/plugins/tasks/hooks/lib/task-store.mjs +12 -6
  82. package/plugins/tasks/hooks/register-requests.mjs +37 -10
  83. package/registry.json +249 -38
@@ -1,20 +1,12 @@
1
- // tool-map — maintains the per-project tool map so a discovery is never repeated. It is
2
- // the write half of the discovery pair (reuse-before-build is the read half). When a new
3
- // tool is created WITH its audit declared, this gate records what that tool covers into
4
- // .ai/tool-map.json, so next time reuse-before-build finds it and does not block.
5
- //
6
- // It never reaches the network and takes no judgment: it only appends a deterministic
7
- // record (path + the audit line the author already wrote) to a JSON file. The cloud search
8
- // that decides "does something already exist?" is the assistant's / the CLI's job. This
9
- // gate never denies it observes a legitimate build and remembers it. It warns once when
10
- // it could not record, so a silent write failure does not go unnoticed.
11
- //
12
- // ── What a project can configure (params) ───────────────────────────────────────────
13
- // toolMapFile path to the per-project tool map, relative to the project root.
14
- // Default .ai/tool-map.json.
15
-
16
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
17
- import { dirname, extname, join } from 'node:path';
1
+ // tool-map — the write half of the discovery pair (reuse-before-build reads). When a tool
2
+ // is created WITH its audit declared, the tool and that audit line are recorded in the
3
+ // project's tool map so the discovery is never repeated. Never denies; warns once when it
4
+ // cannot record. What counts as a tool and as an audit line is the same lib definition
5
+ // reuse-before-build uses, so the map records exactly what the read half accepts.
6
+
7
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
8
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
9
+ import { projectRootOf, readJsonOrNull } from '../../lib/config.mjs';
18
10
  import {
19
11
  runGate,
20
12
  warn,
@@ -22,6 +14,13 @@ import {
22
14
  writtenContentOf,
23
15
  writtenPathOf,
24
16
  } from '../../lib/hook-io.mjs';
17
+ import {
18
+ DEFAULT_TOOL_EXTENSIONS,
19
+ DEFAULT_TOOL_FOLDERS,
20
+ DEFAULT_TOOL_NAME_PATTERNS,
21
+ hasAuditEvidence,
22
+ isToolPath,
23
+ } from '../../lib/tools.mjs';
25
24
 
26
25
  const GATE_ID = 'tool-map';
27
26
  const CONFIG_KEY = 'maintainToolMap';
@@ -30,107 +29,73 @@ const DEFAULT_TOOL_MAP_FILE = join('.ai', 'tool-map.json');
30
29
  const JSON_INDENT = 2;
31
30
  const MAX_AUDIT_LENGTH = 300;
32
31
 
33
- // Kept in sync with reuse-before-build's scope (its read half): a file this gate would NOT
34
- // record is a file reuse-before-build would still flag, and the pair must agree on what
35
- // counts as a tool/helper. Extensions and folders are broad because a reusable helper lives
36
- // in many places (lib/utils/composables/components), not only the four original tool dirs.
37
- const EXECUTABLE_EXTENSIONS = new Set([
38
- '.js',
39
- '.mjs',
40
- '.cjs',
41
- '.ts',
42
- '.tsx',
43
- '.jsx',
44
- '.vue',
45
- '.py',
46
- '.sh',
47
- '.ps1',
48
- '.rb',
49
- '.go',
50
- ]);
51
- const TOOL_FOLDERS = [
52
- 'scripts/',
53
- 'hooks/',
54
- 'tools/',
55
- 'gates/',
56
- 'lib/',
57
- 'libs/',
58
- 'utils/',
59
- 'util/',
60
- 'helpers/',
61
- 'helper/',
62
- 'composables/',
63
- 'components/',
64
- 'services/',
65
- 'shared/',
66
- 'common/',
67
- ];
68
-
69
- // Phrases that mark a line as the author's audit statement. Matched against one line at a
70
- // time (no wrapping `.*`, which backtracks): the matching line is captured whole.
71
- const AUDIT_PHRASE_PATTERN =
72
- /(already exists|no existing tool|there is no plugin|audited|nothing does this|justification:)/i;
32
+ // Same rule as reuse-before-build: a map path outside the project root is ignored.
33
+ function toolMapPathFor(root, toolMapFile) {
34
+ const resolved = resolve(root, String(toolMapFile || DEFAULT_TOOL_MAP_FILE));
35
+ const relativePath = relative(root, resolved);
36
+ const inside =
37
+ relativePath !== '' &&
38
+ !relativePath.startsWith('..') &&
39
+ !isAbsolute(relativePath);
40
+ return inside ? resolved : join(root, DEFAULT_TOOL_MAP_FILE);
41
+ }
73
42
 
74
- /** The first line of `content` that states an audit, trimmed — or null when none does. */
75
43
  function auditLineOf(content) {
76
- for (const line of content.split('\n')) {
77
- if (AUDIT_PHRASE_PATTERN.test(line)) return line.trim();
78
- }
79
- return null;
44
+ return content
45
+ .split('\n')
46
+ .map((line) => line.trim())
47
+ .find((line) => hasAuditEvidence(line));
80
48
  }
81
49
 
82
- function projectRootOf(startDirectory) {
83
- let current = startDirectory;
84
- while (true) {
85
- if (existsSync(join(current, '.git'))) return current;
86
- const parent = dirname(current);
87
- if (parent === current) return null;
88
- current = parent;
89
- }
50
+ function normalizePath(path) {
51
+ return String(path ?? '')
52
+ .replace(/\\/g, '/')
53
+ .replace(/^\.\//, '');
90
54
  }
91
55
 
92
- function isExecutableToolPath(filePath) {
93
- const normalized = filePath.replace(/\\/g, '/');
94
- const inToolFolder = TOOL_FOLDERS.some((folder) =>
95
- normalized.includes(folder),
96
- );
97
- return (
98
- inToolFolder && EXECUTABLE_EXTENSIONS.has(extname(filePath).toLowerCase())
99
- );
56
+ function rootRelativePath(root, rawPath) {
57
+ const absolute = isAbsolute(rawPath) ? rawPath : join(root, rawPath);
58
+ const relativePath = normalizePath(relative(root, absolute));
59
+ return relativePath.startsWith('..') ? normalizePath(rawPath) : relativePath;
100
60
  }
101
61
 
102
62
  function readMap(path) {
103
- if (!existsSync(path)) return { tools: [] };
104
- try {
105
- const parsed = JSON.parse(readFileSync(path, 'utf8'));
106
- return Array.isArray(parsed.tools) ? parsed : { tools: [] };
107
- } catch {
108
- return { tools: [] };
109
- }
63
+ const parsed = readJsonOrNull(path);
64
+ return Array.isArray(parsed?.tools) ? parsed : { tools: [] };
110
65
  }
111
66
 
112
- /** Appends the tool's record to the map file, warning (never blocking) if it cannot. */
113
- function recordTool(mapPath, normalizedPath, auditLine, toolMapFile) {
67
+ // The file being recorded does not exist yet at PreToolUse time, so it is exempt from
68
+ // the prune; every other entry must still point at a real file.
69
+ function recordTool(root, mapPath, toolPath, auditLine) {
114
70
  const map = readMap(mapPath);
115
- if (map.tools.some((tool) => tool.path === normalizedPath)) return; // already recorded
116
-
117
- map.tools.push({
118
- path: normalizedPath,
119
- audit: auditLine.slice(0, MAX_AUDIT_LENGTH),
120
- recordedAt: new Date().toISOString(),
71
+ const kept = map.tools.filter((tool) => {
72
+ const recorded = normalizePath(tool?.path);
73
+ return recorded !== toolPath && existsSync(join(root, recorded));
121
74
  });
122
-
75
+ const unchanged =
76
+ kept.length === map.tools.length &&
77
+ map.tools.some((tool) => normalizePath(tool?.path) === toolPath);
78
+ if (unchanged) return;
79
+
80
+ const tools = [
81
+ ...kept,
82
+ {
83
+ path: toolPath,
84
+ audit: auditLine.slice(0, MAX_AUDIT_LENGTH),
85
+ recordedAt: new Date().toISOString(),
86
+ },
87
+ ];
123
88
  try {
124
89
  mkdirSync(dirname(mapPath), { recursive: true });
125
90
  writeFileSync(
126
91
  mapPath,
127
- `${JSON.stringify(map, null, JSON_INDENT)}\n`,
92
+ `${JSON.stringify({ ...map, tools }, null, JSON_INDENT)}\n`,
128
93
  'utf8',
129
94
  );
130
95
  } catch (error) {
131
96
  warn(
132
97
  CONFIG_KEY,
133
- `Could not record this tool in ${toolMapFile}: ${error?.message ?? error}. ` +
98
+ `Could not record this tool in ${mapPath}: ${error?.message ?? error}. ` +
134
99
  'The build proceeds, but the discovery was not remembered.',
135
100
  );
136
101
  }
@@ -141,28 +106,33 @@ runGate(
141
106
  id: GATE_ID,
142
107
  configKey: CONFIG_KEY,
143
108
  enabledByDefault: false,
144
- defaultParams: { toolMapFile: DEFAULT_TOOL_MAP_FILE },
109
+ defaultParams: {
110
+ toolMapFile: DEFAULT_TOOL_MAP_FILE,
111
+ toolFolders: DEFAULT_TOOL_FOLDERS,
112
+ toolExtensions: DEFAULT_TOOL_EXTENSIONS,
113
+ toolNamePatterns: DEFAULT_TOOL_NAME_PATTERNS,
114
+ },
145
115
  },
146
- ({ toolName, toolInput, parameters }) => {
116
+ ({ toolName, toolInput, parameters, cwd }) => {
147
117
  if (!toolInGroups(toolName, ['write'])) return;
148
118
 
149
- const filePath = writtenPathOf(toolInput);
150
- if (!isExecutableToolPath(filePath)) return;
119
+ const rawPath = writtenPathOf(toolInput);
120
+ const isTool = isToolPath(rawPath, {
121
+ folders: parameters.toolFolders,
122
+ extensions: parameters.toolExtensions,
123
+ namePatterns: parameters.toolNamePatterns,
124
+ });
125
+ if (!isTool) return;
151
126
 
152
- // Only a build that declared its audit is worth recording: that line IS the reason
153
- // this tool exists and what it was checked against.
154
- const content = writtenContentOf(toolInput);
155
- const auditLine = auditLineOf(content);
127
+ const auditLine = auditLineOf(writtenContentOf(toolInput));
156
128
  if (!auditLine) return;
157
129
 
158
- const root = projectRootOf(process.cwd());
159
- if (!root) return;
160
- const toolMapFile = parameters.toolMapFile ?? DEFAULT_TOOL_MAP_FILE;
130
+ const root = projectRootOf(cwd) ?? cwd;
161
131
  recordTool(
162
- join(root, toolMapFile),
163
- filePath.replace(/\\/g, '/'),
132
+ root,
133
+ toolMapPathFor(root, parameters.toolMapFile),
134
+ rootRelativePath(root, rawPath),
164
135
  auditLine,
165
- toolMapFile,
166
136
  );
167
137
  },
168
138
  );
@@ -2,7 +2,7 @@
2
2
  "hooks": {
3
3
  "PreToolUse": [
4
4
  {
5
- "matcher": "Bash|run_command|Agent|Task|invoke_subagent|mcp__.*",
5
+ "matcher": "Bash|run_command|PowerShell|Agent|Task|invoke_subagent|mcp__.*",
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
@@ -12,7 +12,7 @@
12
12
  ]
13
13
  },
14
14
  {
15
- "matcher": "Bash|run_command|Agent|Task|invoke_subagent|mcp__.*",
15
+ "matcher": "Bash|run_command|PowerShell|Agent|Task|invoke_subagent|mcp__.*",
16
16
  "hooks": [
17
17
  {
18
18
  "type": "command",
@@ -22,7 +22,7 @@
22
22
  ]
23
23
  },
24
24
  {
25
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|mcp__.*",
25
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|PowerShell|mcp__.*",
26
26
  "hooks": [
27
27
  {
28
28
  "type": "command",
@@ -32,7 +32,7 @@
32
32
  ]
33
33
  },
34
34
  {
35
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|mcp__.*",
35
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|PowerShell|mcp__.*",
36
36
  "hooks": [
37
37
  {
38
38
  "type": "command",
@@ -42,7 +42,7 @@
42
42
  ]
43
43
  },
44
44
  {
45
- "matcher": "Bash|run_command|Agent|Task|invoke_subagent|mcp__.*",
45
+ "matcher": "Bash|run_command|PowerShell|Agent|Task|invoke_subagent|mcp__.*",
46
46
  "hooks": [
47
47
  {
48
48
  "type": "command",
@@ -51,6 +51,16 @@
51
51
  }
52
52
  ]
53
53
  },
54
+ {
55
+ "matcher": "Bash|run_command|PowerShell|Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|mcp__ide__executeCode|Monitor|mcp__.*",
56
+ "hooks": [
57
+ {
58
+ "type": "command",
59
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/require-monitor/index.mjs\"",
60
+ "timeout": 30
61
+ }
62
+ ]
63
+ },
54
64
  {
55
65
  "matcher": "Agent|Task|invoke_subagent|mcp__.*",
56
66
  "hooks": [
@@ -102,7 +112,17 @@
102
112
  ]
103
113
  },
104
114
  {
105
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
115
+ "matcher": "Agent|Task|invoke_subagent|mcp__.*",
116
+ "hooks": [
117
+ {
118
+ "type": "command",
119
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/force-parallel/index.mjs\"",
120
+ "timeout": 30
121
+ }
122
+ ]
123
+ },
124
+ {
125
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|PowerShell|mcp__.*",
106
126
  "hooks": [
107
127
  {
108
128
  "type": "command",
@@ -112,7 +132,7 @@
112
132
  ]
113
133
  },
114
134
  {
115
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
135
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
116
136
  "hooks": [
117
137
  {
118
138
  "type": "command",
@@ -162,7 +182,7 @@
162
182
  ]
163
183
  },
164
184
  {
165
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
185
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|PowerShell|mcp__.*",
166
186
  "hooks": [
167
187
  {
168
188
  "type": "command",
@@ -172,7 +192,7 @@
172
192
  ]
173
193
  },
174
194
  {
175
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
195
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
176
196
  "hooks": [
177
197
  {
178
198
  "type": "command",
@@ -182,7 +202,7 @@
182
202
  ]
183
203
  },
184
204
  {
185
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
205
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
186
206
  "hooks": [
187
207
  {
188
208
  "type": "command",
@@ -192,7 +212,7 @@
192
212
  ]
193
213
  },
194
214
  {
195
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
215
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
196
216
  "hooks": [
197
217
  {
198
218
  "type": "command",
@@ -202,7 +222,7 @@
202
222
  ]
203
223
  },
204
224
  {
205
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|mcp__ide__executeCode|Agent|Task|invoke_subagent|mcp__.*",
225
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|PowerShell|mcp__ide__executeCode|Agent|Task|invoke_subagent|mcp__.*",
206
226
  "hooks": [
207
227
  {
208
228
  "type": "command",
@@ -212,7 +232,7 @@
212
232
  ]
213
233
  },
214
234
  {
215
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|mcp__ide__executeCode|Agent|Task|invoke_subagent|mcp__.*",
235
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|PowerShell|mcp__ide__executeCode|Agent|Task|invoke_subagent|mcp__.*",
216
236
  "hooks": [
217
237
  {
218
238
  "type": "command",
@@ -222,7 +242,7 @@
222
242
  ]
223
243
  },
224
244
  {
225
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
245
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
226
246
  "hooks": [
227
247
  {
228
248
  "type": "command",
@@ -242,7 +262,7 @@
242
262
  ]
243
263
  },
244
264
  {
245
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
265
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
246
266
  "hooks": [
247
267
  {
248
268
  "type": "command",
@@ -252,7 +272,7 @@
252
272
  ]
253
273
  },
254
274
  {
255
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
275
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
256
276
  "hooks": [
257
277
  {
258
278
  "type": "command",
@@ -262,113 +282,174 @@
262
282
  ]
263
283
  },
264
284
  {
265
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
285
+ "matcher": "Bash|run_command|PowerShell|mcp__.*",
266
286
  "hooks": [
267
287
  {
268
288
  "type": "command",
269
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/reuse-before-build/index.mjs\"",
289
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/lint-commit/index.mjs\"",
290
+ "timeout": 65
291
+ }
292
+ ]
293
+ },
294
+ {
295
+ "matcher": "Bash|run_command|PowerShell|mcp__.*",
296
+ "hooks": [
297
+ {
298
+ "type": "command",
299
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/staged-lint/index.mjs\"",
300
+ "timeout": 65
301
+ }
302
+ ]
303
+ },
304
+ {
305
+ "matcher": "Bash|run_command|PowerShell|mcp__.*",
306
+ "hooks": [
307
+ {
308
+ "type": "command",
309
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/atomic-commit/index.mjs\"",
270
310
  "timeout": 30
271
311
  }
272
312
  ]
273
313
  },
274
314
  {
275
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
315
+ "matcher": "Bash|run_command|PowerShell|Agent|Task|invoke_subagent|mcp__.*",
276
316
  "hooks": [
277
317
  {
278
318
  "type": "command",
279
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/tool-map/index.mjs\"",
319
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/no-coauthor/index.mjs\"",
280
320
  "timeout": 30
281
321
  }
282
322
  ]
283
323
  },
284
324
  {
285
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|mcp__.*",
325
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
286
326
  "hooks": [
287
327
  {
288
328
  "type": "command",
289
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/forge-flow/index.mjs\"",
329
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/no-lint-suppression/index.mjs\"",
290
330
  "timeout": 30
291
331
  }
292
332
  ]
293
333
  },
294
334
  {
295
- "matcher": "AskUserQuestion|mcp__.*",
335
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
296
336
  "hooks": [
297
337
  {
298
338
  "type": "command",
299
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/autonomous-mode/index.mjs\"",
339
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/no-explanatory-comments/index.mjs\"",
300
340
  "timeout": 30
301
341
  }
302
342
  ]
303
343
  },
304
344
  {
305
- "matcher": "Bash|run_command|mcp__.*",
345
+ "matcher": "Bash|run_command|PowerShell|mcp__.*",
306
346
  "hooks": [
307
347
  {
308
348
  "type": "command",
309
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/lint-commit/index.mjs\"",
310
- "timeout": 65
349
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/no-trivial-scripts/index.mjs\"",
350
+ "timeout": 30
311
351
  }
312
352
  ]
313
353
  },
314
354
  {
315
- "matcher": "Bash|run_command|mcp__.*",
355
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Agent|Task|invoke_subagent|mcp__.*",
316
356
  "hooks": [
317
357
  {
318
358
  "type": "command",
319
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/staged-lint/index.mjs\"",
320
- "timeout": 65
359
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/reuse-before-build/index.mjs\"",
360
+ "timeout": 30
321
361
  }
322
362
  ]
323
363
  },
324
364
  {
325
- "matcher": "Agent|Task|invoke_subagent|mcp__.*",
365
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
326
366
  "hooks": [
327
367
  {
328
368
  "type": "command",
329
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/force-parallel/index.mjs\"",
369
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/tool-map/index.mjs\"",
330
370
  "timeout": 30
331
371
  }
332
372
  ]
333
373
  },
334
374
  {
335
- "matcher": "Bash|run_command|Agent|Task|invoke_subagent|mcp__.*",
375
+ "matcher": "WebSearch|WebFetch|mcp__.*",
336
376
  "hooks": [
337
377
  {
338
378
  "type": "command",
339
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/no-coauthor/index.mjs\"",
379
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/engram-first/index.mjs\"",
340
380
  "timeout": 30
341
381
  }
342
382
  ]
343
383
  },
344
384
  {
345
- "matcher": "Bash|run_command|mcp__.*",
385
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|PowerShell|mcp__.*",
346
386
  "hooks": [
347
387
  {
348
388
  "type": "command",
349
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/atomic-commit/index.mjs\"",
389
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/library-docs/index.mjs\"",
350
390
  "timeout": 30
351
391
  }
352
392
  ]
353
393
  },
354
394
  {
355
- "matcher": "Write|Edit|NotebookEdit|write_to_file|replace_file_content|mcp__.*",
395
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|PowerShell|mcp__.*",
356
396
  "hooks": [
357
397
  {
358
398
  "type": "command",
359
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/no-lint-suppression/index.mjs\"",
399
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/forge-flow/index.mjs\"",
400
+ "timeout": 30
401
+ }
402
+ ]
403
+ },
404
+ {
405
+ "matcher": "AskUserQuestion|mcp__.*",
406
+ "hooks": [
407
+ {
408
+ "type": "command",
409
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/autonomous-mode/index.mjs\"",
410
+ "timeout": 30
411
+ }
412
+ ]
413
+ },
414
+ {
415
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|write_to_file|replace_file_content|Bash|run_command|PowerShell|mcp__ide__executeCode|mcp__.*",
416
+ "hooks": [
417
+ {
418
+ "type": "command",
419
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/require-task-split/index.mjs\"",
360
420
  "timeout": 30
361
421
  }
362
422
  ]
363
423
  }
364
424
  ],
365
- "UserPromptSubmit": [
425
+ "PostToolUse": [
366
426
  {
427
+ "matcher": "Agent|Task|invoke_subagent|mcp__.*",
367
428
  "hooks": [
368
429
  {
369
430
  "type": "command",
370
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/capability-map/index.mjs\"",
371
- "timeout": 10
431
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/circuit-breaker/track.mjs\"",
432
+ "timeout": 30
433
+ }
434
+ ]
435
+ },
436
+ {
437
+ "matcher": "WebSearch|WebFetch|mcp__.*",
438
+ "hooks": [
439
+ {
440
+ "type": "command",
441
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/engram-first/track.mjs\"",
442
+ "timeout": 30
443
+ }
444
+ ]
445
+ },
446
+ {
447
+ "matcher": "WebSearch|WebFetch|mcp__.*",
448
+ "hooks": [
449
+ {
450
+ "type": "command",
451
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/library-docs/track.mjs\"",
452
+ "timeout": 30
372
453
  }
373
454
  ]
374
455
  }
@@ -378,7 +459,7 @@
378
459
  "hooks": [
379
460
  {
380
461
  "type": "command",
381
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/stop-pending/index.mjs\"",
462
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/engram-first/stop.mjs\"",
382
463
  "timeout": 30
383
464
  }
384
465
  ]
@@ -391,9 +472,27 @@
391
472
  "timeout": 30
392
473
  }
393
474
  ]
475
+ },
476
+ {
477
+ "hooks": [
478
+ {
479
+ "type": "command",
480
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/stop-pending/index.mjs\"",
481
+ "timeout": 30
482
+ }
483
+ ]
394
484
  }
395
485
  ],
396
486
  "SessionStart": [
487
+ {
488
+ "hooks": [
489
+ {
490
+ "type": "command",
491
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/engram-first/session-start.mjs\"",
492
+ "timeout": 30
493
+ }
494
+ ]
495
+ },
397
496
  {
398
497
  "hooks": [
399
498
  {
@@ -421,6 +520,17 @@
421
520
  }
422
521
  ]
423
522
  }
523
+ ],
524
+ "UserPromptSubmit": [
525
+ {
526
+ "hooks": [
527
+ {
528
+ "type": "command",
529
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gates/capability-map/index.mjs\"",
530
+ "timeout": 10
531
+ }
532
+ ]
533
+ }
424
534
  ]
425
535
  }
426
536
  }