@devrik-tools/claude-gates 0.4.0 → 0.7.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 (57) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/README.es.md +39 -4
  3. package/README.md +34 -5
  4. package/cli/config.mjs +126 -124
  5. package/cli/init.mjs +303 -276
  6. package/cli/install.mjs +281 -175
  7. package/cli/materialize.mjs +103 -102
  8. package/cli/registry.mjs +139 -136
  9. package/cli/smoke-fixtures.json +65 -0
  10. package/cli/task.mjs +140 -140
  11. package/package.json +1 -1
  12. package/plugins/gates/.claude-plugin/plugin.json +1 -1
  13. package/plugins/gates/hooks/ask-adoption.mjs +147 -147
  14. package/plugins/gates/hooks/doctor.mjs +207 -207
  15. package/plugins/gates/hooks/gates/atomic-commit/index.mjs +229 -0
  16. package/plugins/gates/hooks/gates/audit-before-build/index.mjs +110 -88
  17. package/plugins/gates/hooks/gates/autonomous-mode/index.mjs +50 -50
  18. package/plugins/gates/hooks/gates/bash-commands/index.mjs +215 -215
  19. package/plugins/gates/hooks/gates/brief-approved/index.mjs +216 -0
  20. package/plugins/gates/hooks/gates/brief-before-delegate/index.mjs +269 -265
  21. package/plugins/gates/hooks/gates/capability-map/index.mjs +701 -0
  22. package/plugins/gates/hooks/gates/circuit-breaker/index.mjs +527 -501
  23. package/plugins/gates/hooks/gates/diagnosis-before-patch/index.mjs +48 -43
  24. package/plugins/gates/hooks/gates/feature-catalog/index.mjs +83 -83
  25. package/plugins/gates/hooks/gates/force-parallel/index.mjs +134 -119
  26. package/plugins/gates/hooks/gates/forge-flow/index.mjs +134 -134
  27. package/plugins/gates/hooks/gates/implementation-pipeline/index.mjs +187 -187
  28. package/plugins/gates/hooks/gates/intent-flow/index.mjs +260 -260
  29. package/plugins/gates/hooks/gates/lint-commit/index.mjs +152 -149
  30. package/plugins/gates/hooks/gates/mandatory-flow/index.mjs +180 -180
  31. package/plugins/gates/hooks/gates/never-assume/index.mjs +59 -58
  32. package/plugins/gates/hooks/gates/no-blocking/index.mjs +163 -148
  33. package/plugins/gates/hooks/gates/no-coauthor/index.mjs +127 -0
  34. package/plugins/gates/hooks/gates/no-lint-suppression/index.mjs +183 -0
  35. package/plugins/gates/hooks/gates/protected-paths/index.mjs +149 -144
  36. package/plugins/gates/hooks/gates/recurrence-lock/index.mjs +91 -89
  37. package/plugins/gates/hooks/gates/reuse-before-build/index.mjs +263 -159
  38. package/plugins/gates/hooks/gates/risk-level/index.mjs +265 -263
  39. package/plugins/gates/hooks/gates/root-cause-first/index.mjs +57 -56
  40. package/plugins/gates/hooks/gates/root-whitelist/index.mjs +211 -131
  41. package/plugins/gates/hooks/gates/rule-skill-autodiscovery/index.mjs +181 -184
  42. package/plugins/gates/hooks/gates/sdd-specs/index.mjs +256 -256
  43. package/plugins/gates/hooks/gates/staged-lint/index.mjs +187 -0
  44. package/plugins/gates/hooks/gates/stop-pending/index.mjs +169 -164
  45. package/plugins/gates/hooks/gates/test-matrix/index.mjs +187 -187
  46. package/plugins/gates/hooks/gates/tool-map/index.mjs +168 -143
  47. package/plugins/gates/hooks/hooks.json +61 -0
  48. package/plugins/gates/hooks/lib/config.mjs +179 -172
  49. package/plugins/gates/hooks/lib/hook-io.mjs +367 -357
  50. package/plugins/gates/hooks/lib/signals.mjs +172 -127
  51. package/plugins/gates/hooks/wiring-check.mjs +227 -227
  52. package/plugins/tasks/.claude-plugin/plugin.json +1 -1
  53. package/plugins/tasks/hooks/hooks.json +26 -26
  54. package/plugins/tasks/hooks/lib/task-store.mjs +217 -197
  55. package/plugins/tasks/hooks/register-requests.mjs +145 -145
  56. package/plugins/tasks/hooks/session-tasks.mjs +108 -108
  57. package/registry.json +192 -1
@@ -1,159 +1,263 @@
1
- // reuse-before-build — do not reinvent the wheel. Before building a new script, gate,
2
- // verifier or generic helper, consult the per-project tool map: a record of what already
3
- // solves a given need, local first, cloud second. This gate is the deterministic half —
4
- // it consults the map and objects when a build is starting without evidence of a check.
5
- // It never reaches the network: the cloud search (npm, marketplaces) is done by the
6
- // assistant or the `claude-gates tool-map` CLI, so the hook stays self-contained.
7
- //
8
- // ── What a project can configure (params) ───────────────────────────────────────────
9
- // toolMapFile path to the per-project tool map, relative to the project root.
10
- // Default .ai/tool-map.json.
11
- //
12
- // ── How it decides (deterministic, no judgment) ─────────────────────────────────────
13
- // A write that creates an executable tool (by extension) inside a tools folder, or a
14
- // delegation prompt that asks to build one, must carry evidence that the wheel was
15
- // checked: either an audit phrase in the text, or the need already recorded in the tool
16
- // map. Absent both, the gate blocks with an actionable message — consult the map, then
17
- // (if truly absent) search local→cloud and record the result with the CLI.
18
-
19
- import { existsSync, readFileSync } from 'node:fs';
20
- import { dirname, extname, join } from 'node:path';
21
- import {
22
- runGate,
23
- deny,
24
- toolInGroups,
25
- writtenContentOf,
26
- writtenPathOf,
27
- delegationPromptOf,
28
- } from '../../lib/hook-io.mjs';
29
-
30
- const GATE_ID = 'reuse-before-build';
31
- const CONFIG_KEY = 'requireReuseCheckBeforeBuilding';
32
-
33
- const DEFAULT_TOOL_MAP_FILE = join('.ai', 'tool-map.json');
34
- const EXECUTABLE_EXTENSIONS = new Set([
35
- '.js',
36
- '.mjs',
37
- '.cjs',
38
- '.ts',
39
- '.py',
40
- '.sh',
41
- '.ps1',
42
- ]);
43
- const TOOL_FOLDERS = ['scripts/', 'hooks/', 'tools/', 'gates/'];
44
- // A map word must be this long to count as a real signal (skip "the", "csv" is 3 but rare).
45
- const MIN_SIGNIFICANT_WORD_LENGTH = 4;
46
-
47
- // A verb of creation next to a tool noun. Deliberately narrow: "write a report" or
48
- // "create a folder" do not match because the noun is not one of these.
49
- const BUILD_INTENT_PATTERN =
50
- /\b(write|create|build|implement|add)\s+(a |the )?(new )?(script|verifier|gate|hook|linter|checker|tool)\b/i;
51
- // Evidence the wheel was already checked: any phrase stating the audit result.
52
- const AUDIT_DONE_PATTERN =
53
- /(already exists|no existing tool|there is no plugin|audited|checked whether|nothing does this|justification:)/i;
54
-
55
- function projectRootOf(startDirectory) {
56
- let current = startDirectory;
57
- while (true) {
58
- if (existsSync(join(current, '.git'))) return current;
59
- const parent = dirname(current);
60
- if (parent === current) return null;
61
- current = parent;
62
- }
63
- }
64
-
65
- /**
66
- * Whether the tool map already records a need matching the given text. The map is a JSON
67
- * blob; a cheap lowercased contains-check over the significant words of the build target
68
- * is enough — the gate does not parse the map's shape, so the CLI is free to evolve it.
69
- * A word of 4+ chars found in the map counts as "this need is already tracked".
70
- */
71
- function needRecordedInMap(startDirectory, toolMapFile, targetText) {
72
- const root = projectRootOf(startDirectory);
73
- if (!root) return false;
74
- const path = join(root, toolMapFile);
75
- if (!existsSync(path)) return false;
76
- let map;
77
- try {
78
- map = readFileSync(path, 'utf8').toLowerCase();
79
- } catch {
80
- return false;
81
- }
82
- const words = targetText
83
- .toLowerCase()
84
- .split(/[^a-z0-9]+/)
85
- .filter((word) => word.length >= MIN_SIGNIFICANT_WORD_LENGTH);
86
- return words.some((word) => map.includes(word));
87
- }
88
-
89
- /** Whether the path has a real path SEGMENT equal to one of the tool-folder names
90
- * (e.g. `tools`), not merely a substring match -- `src/mytools/thing.mjs` contains the
91
- * substring "tools/" but its actual segments are `src`, `mytools`, `thing.mjs`, none of
92
- * which is `tools`. Splitting on the separator and comparing whole segments closes that
93
- * false positive without allowing a matching false negative. */
94
- function isExecutableToolPath(filePath) {
95
- const segments = filePath.replace(/\\/g, '/').split('/');
96
- const toolFolderNames = TOOL_FOLDERS.map((folder) => folder.replace(/\/$/, ''));
97
- const inToolFolder = segments.some((segment) =>
98
- toolFolderNames.includes(segment),
99
- );
100
- return (
101
- inToolFolder && EXECUTABLE_EXTENSIONS.has(extname(filePath).toLowerCase())
102
- );
103
- }
104
-
105
- const DENY_MESSAGE =
106
- 'Do not reinvent the wheel. Before building this, consult the project tool map and ' +
107
- 'confirm nothing already covers it, auditing in this order: (1) LOCAL — the repo and ' +
108
- 'installed deps (search by name and usage, read the manifest); (2) CONTEXT7 — resolve ' +
109
- 'the candidate library and read its docs to confirm whether it truly covers the need; ' +
110
- '(3) WEB — search npm and plugin marketplaces for a maintained package. If it is ' +
111
- 'genuinely absent, record the finding (e.g. `claude-gates tool-map add`) and state the ' +
112
- 'audit in the text, so the exploration is not repeated next time.';
113
-
114
- /** A build is allowed when its text declares an audit or the need is already in the map. */
115
- function buildIsCleared(text, cwd, toolMapFile) {
116
- return (
117
- AUDIT_DONE_PATTERN.test(text) || needRecordedInMap(cwd, toolMapFile, text)
118
- );
119
- }
120
-
121
- /** Delegation: block a build-a-tool prompt that is not cleared. */
122
- function checkDelegation(toolInput, cwd, toolMapFile) {
123
- const prompt = delegationPromptOf(toolInput);
124
- if (!BUILD_INTENT_PATTERN.test(prompt)) return;
125
- if (buildIsCleared(prompt, cwd, toolMapFile)) return;
126
- deny(GATE_ID, DENY_MESSAGE);
127
- }
128
-
129
- /** Write: block a new executable tool that is not cleared by its content or the map. */
130
- function checkWrite(toolInput, cwd, toolMapFile) {
131
- const rawPath = writtenPathOf(toolInput);
132
- const filePath = rawPath.replace(/\\/g, '/');
133
- if (!isExecutableToolPath(filePath)) return;
134
- // Editing an EXISTING file is not building a new tool only creation triggers the audit.
135
- // A file already on disk is an edit, so it is allowed (lets the gates maintain themselves).
136
- if (rawPath && existsSync(rawPath)) return;
137
- const content = writtenContentOf(toolInput);
138
- if (AUDIT_DONE_PATTERN.test(content)) return;
139
- if (needRecordedInMap(cwd, toolMapFile, filePath)) return;
140
- deny(GATE_ID, DENY_MESSAGE);
141
- }
142
-
143
- runGate(
144
- {
145
- id: GATE_ID,
146
- configKey: CONFIG_KEY,
147
- enabledByDefault: false,
148
- defaultParams: { toolMapFile: DEFAULT_TOOL_MAP_FILE },
149
- },
150
- ({ toolName, toolInput, parameters }) => {
151
- const toolMapFile = parameters.toolMapFile ?? DEFAULT_TOOL_MAP_FILE;
152
- const cwd = process.cwd();
153
- if (toolInGroups(toolName, ['delegation'])) {
154
- checkDelegation(toolInput, cwd, toolMapFile);
155
- } else if (toolInGroups(toolName, ['write'])) {
156
- checkWrite(toolInput, cwd, toolMapFile);
157
- }
158
- },
159
- );
1
+ // reuse-before-build — do not reinvent the wheel. Before building a new script, gate,
2
+ // verifier, helper, composable, component or generic utility, consult what already solves
3
+ // the need: the per-project tool map (a record of past findings), the installed dependencies
4
+ // (package.json), and declared by the author a local→cloud audit. This gate is the
5
+ // deterministic half: it objects when a build is starting without evidence of a check.
6
+ // It never reaches the network: the cloud search (npm, marketplaces) is the assistant's or
7
+ // the CLI's job, so the hook stays self-contained.
8
+ //
9
+ // ── What a project can configure (params) ───────────────────────────────────────────
10
+ // toolMapFile path to the per-project tool map, relative to the project root.
11
+ // Default .ai/tool-map.json.
12
+ // toolFolders folder names that mark a file as a "tool/helper". Replaces the built-in
13
+ // list wholesale.
14
+ // toolNamePatterns regex sources; a new file whose BASENAME matches any is treated as a
15
+ // generic helper regardless of its folder (useFoo, fooHelper, fooUtil...).
16
+ // toolExtensions file extensions considered code that can reinvent a wheel.
17
+ //
18
+ // ── How it decides (deterministic, no judgment) ─────────────────────────────────────
19
+ // A write that creates a tool/helper (by folder, by name pattern, or by a helper-ish
20
+ // extension), or a delegation prompt that asks to build one, must carry evidence the wheel
21
+ // was checked: (a) an audit phrase in the text, OR (b) the tool's own name already recorded
22
+ // in the tool map, OR (c) an installed dependency whose name matches the tool's name (there
23
+ // is already a package that plausibly covers it — reuse it or justify not to). Absent all
24
+ // three, the gate blocks with an actionable message.
25
+ //
26
+ // ── What changed vs the first version (closed holes) ────────────────────────────────
27
+ // · Map match was a lax "any 4+-letter word of the target appears anywhere in the JSON",
28
+ // which produced false "already covered" (a `parser.mjs` cleared by any unrelated entry
29
+ // mentioning "parser"). Now it matches the tool's own basename as a whole token against
30
+ // the recorded tool paths — structural, not substring-of-prose.
31
+ // · Scope was only scripts/hooks/tools/gates + executable extensions. Now it also covers
32
+ // lib/utils/helpers/composables/components/services AND name patterns (useX, XHelper,
33
+ // XUtil, XService) anywhere, which is where a wheel is most often reinvented.
34
+ // · Build-intent detection was English-only prose; now it uses the bilingual BUILD_INTENT.
35
+ // · Adds an installed-dependency check: a matching package.json dep also clears the build.
36
+
37
+ import { existsSync, readFileSync } from 'node:fs';
38
+ import { basename, dirname, extname, join } from 'node:path';
39
+ import {
40
+ runGate,
41
+ deny,
42
+ toolInGroups,
43
+ writtenContentOf,
44
+ writtenPathOf,
45
+ delegationPromptOf,
46
+ } from '../../lib/hook-io.mjs';
47
+ import { isBuildIntent } from '../../lib/signals.mjs';
48
+
49
+ const GATE_ID = 'reuse-before-build';
50
+ const CONFIG_KEY = 'requireReuseCheckBeforeBuilding';
51
+
52
+ const DEFAULT_TOOL_MAP_FILE = join('.ai', 'tool-map.json');
53
+
54
+ // Code that can reinvent a wheel — broad, because a helper is a helper in any language.
55
+ const DEFAULT_TOOL_EXTENSIONS = [
56
+ '.js',
57
+ '.mjs',
58
+ '.cjs',
59
+ '.ts',
60
+ '.tsx',
61
+ '.jsx',
62
+ '.vue',
63
+ '.py',
64
+ '.sh',
65
+ '.ps1',
66
+ '.rb',
67
+ '.go',
68
+ ];
69
+
70
+ // Folders whose files are "tools/helpers" worth a reuse check. Beyond the original four,
71
+ // this now includes the places generic reusable code actually lives.
72
+ const DEFAULT_TOOL_FOLDERS = [
73
+ 'scripts',
74
+ 'hooks',
75
+ 'tools',
76
+ 'gates',
77
+ 'lib',
78
+ 'libs',
79
+ 'utils',
80
+ 'util',
81
+ 'helpers',
82
+ 'helper',
83
+ 'composables',
84
+ 'components',
85
+ 'services',
86
+ 'shared',
87
+ 'common',
88
+ ];
89
+
90
+ // A file whose BASENAME matches one of these is a generic helper wherever it lives
91
+ // useDebounce.ts in features/x/ is still a candidate for "does this already exist?".
92
+ const DEFAULT_TOOL_NAME_PATTERNS = [
93
+ String.raw`^use[A-Z]`, // React/Vue hook/composable: useFoo
94
+ String.raw`[-.]?helpers?\.`, // foo.helper.ts, foo-helpers.js
95
+ String.raw`[-.]?utils?\.`, // foo.util.ts, string-utils.js
96
+ String.raw`[-.]?service\.`, // foo.service.ts
97
+ String.raw`[-.]?wrapper\.`, // foo.wrapper.ts
98
+ ];
99
+
100
+ // Evidence the wheel was already checked: any phrase stating the audit result. Bilingual.
101
+ const AUDIT_DONE_PATTERN =
102
+ /(already exists|no existing tool|there is no plugin|audited|checked whether|nothing does this|justification:|ya existe|no existe (?:una |la )?herramienta|no hay plugin|audit[eé]|verifiqu[eé] que no)/i;
103
+
104
+ function projectRootOf(startDirectory) {
105
+ let current = startDirectory;
106
+ while (true) {
107
+ if (existsSync(join(current, '.git'))) return current;
108
+ const parent = dirname(current);
109
+ if (parent === current) return null;
110
+ current = parent;
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Whether the tool map already records THIS tool, matched structurally: the new file's
116
+ * basename (without extension) is compared as a whole `-`/`.`-delimited token against the
117
+ * basenames of the recorded tool paths. This replaces the old "any 4+-letter word appears
118
+ * anywhere in the JSON blob", which cleared unrelated builds by coincidental word overlap.
119
+ */
120
+ function toolAlreadyInMap(startDirectory, toolMapFile, toolBaseName) {
121
+ const root = projectRootOf(startDirectory);
122
+ if (!root) return false;
123
+ const path = join(root, toolMapFile);
124
+ if (!existsSync(path)) return false;
125
+ let parsed;
126
+ try {
127
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
128
+ } catch {
129
+ return false;
130
+ }
131
+ if (!Array.isArray(parsed.tools)) return false;
132
+ const target = toolBaseName.toLowerCase();
133
+ return parsed.tools.some((tool) => {
134
+ const recorded = basename(String(tool.path ?? ''))
135
+ .replace(/\.[^.]+$/, '')
136
+ .toLowerCase();
137
+ return recorded === target;
138
+ });
139
+ }
140
+
141
+ /** Whether an installed dependency's name matches the tool's name (a package likely covers
142
+ * it). Normalizes scoped names (@scope/foo → foo) and matches whole, so `parser` is cleared
143
+ * by a `parser`/`@x/parser` dep but not by an unrelated `body-parser`. */
144
+ function installedDependencyCovers(startDirectory, toolBaseName) {
145
+ const root = projectRootOf(startDirectory);
146
+ if (!root) return false;
147
+ const path = join(root, 'package.json');
148
+ if (!existsSync(path)) return false;
149
+ let parsed;
150
+ try {
151
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
152
+ } catch {
153
+ return false;
154
+ }
155
+ const names = [
156
+ ...Object.keys(parsed.dependencies ?? {}),
157
+ ...Object.keys(parsed.devDependencies ?? {}),
158
+ ];
159
+ const target = toolBaseName.toLowerCase();
160
+ return names.some((name) => {
161
+ const bare = name.replace(/^@[^/]+\//, '').toLowerCase();
162
+ return bare === target;
163
+ });
164
+ }
165
+
166
+ function hasToolExtension(filePath, toolExtensions) {
167
+ return toolExtensions.includes(extname(filePath).toLowerCase());
168
+ }
169
+
170
+ /** A real path SEGMENT equal to one of the tool-folder names (not a mere substring, so
171
+ * `src/mytools/x` does not match `tools`). */
172
+ function inToolFolder(filePath, toolFolders) {
173
+ const segments = filePath.replace(/\\/g, '/').split('/');
174
+ return segments.some((segment) =>
175
+ toolFolders.includes(segment.toLowerCase()),
176
+ );
177
+ }
178
+
179
+ function matchesToolNamePattern(fileName, toolNamePatterns) {
180
+ return toolNamePatterns.some((source) => {
181
+ try {
182
+ return new RegExp(source).test(fileName);
183
+ } catch {
184
+ return false;
185
+ }
186
+ });
187
+ }
188
+
189
+ /** Whether a new file at this path should be treated as a reusable tool/helper. */
190
+ function isToolLikePath(filePath, parameters) {
191
+ if (!hasToolExtension(filePath, parameters.toolExtensions)) return false;
192
+ const fileName = basename(filePath);
193
+ return (
194
+ inToolFolder(filePath, parameters.toolFolders) ||
195
+ matchesToolNamePattern(fileName, parameters.toolNamePatterns)
196
+ );
197
+ }
198
+
199
+ const DENY_MESSAGE =
200
+ 'Do not reinvent the wheel. Before building this, confirm nothing already covers it, ' +
201
+ 'auditing in this order: (1) LOCAL — the repo and installed deps (search by name and ' +
202
+ 'usage, read the manifest); (2) CONTEXT7 — resolve the candidate library and read its ' +
203
+ 'docs to confirm whether it truly covers the need; (3) WEB — search npm and plugin ' +
204
+ 'marketplaces for a maintained package. If it is genuinely absent, record the finding ' +
205
+ '(e.g. `claude-gates tool-map add`) and state the audit in the text, so the exploration ' +
206
+ 'is not repeated next time.';
207
+
208
+ /** A build is cleared when its text declares an audit, its name is already recorded in the
209
+ * map, or an installed dependency covers it. `toolBaseName` is used for the last two. */
210
+ function buildIsCleared(text, cwd, parameters, toolBaseName) {
211
+ if (AUDIT_DONE_PATTERN.test(text)) return true;
212
+ if (
213
+ toolBaseName &&
214
+ toolAlreadyInMap(cwd, parameters.toolMapFile, toolBaseName)
215
+ )
216
+ return true;
217
+ if (toolBaseName && installedDependencyCovers(cwd, toolBaseName)) return true;
218
+ return false;
219
+ }
220
+
221
+ /** Delegation: block a build-a-tool prompt that is not cleared. No file name is known from a
222
+ * prompt, so only the audit-phrase evidence applies there. */
223
+ function checkDelegation(toolInput, cwd, parameters) {
224
+ const prompt = delegationPromptOf(toolInput);
225
+ if (!isBuildIntent(prompt)) return;
226
+ if (buildIsCleared(prompt, cwd, parameters, null)) return;
227
+ deny(GATE_ID, DENY_MESSAGE);
228
+ }
229
+
230
+ /** Write: block a new tool-like file that is not cleared by content, map, or an installed dep. */
231
+ function checkWrite(toolInput, cwd, parameters) {
232
+ const rawPath = writtenPathOf(toolInput);
233
+ const filePath = rawPath.replace(/\\/g, '/');
234
+ if (!isToolLikePath(filePath, parameters)) return;
235
+ // Editing an EXISTING file is not building a new tool — only creation triggers the audit.
236
+ if (rawPath && existsSync(rawPath)) return;
237
+ const content = writtenContentOf(toolInput);
238
+ const toolBaseName = basename(filePath).replace(/\.[^.]+$/, '');
239
+ if (buildIsCleared(content, cwd, parameters, toolBaseName)) return;
240
+ deny(GATE_ID, DENY_MESSAGE);
241
+ }
242
+
243
+ runGate(
244
+ {
245
+ id: GATE_ID,
246
+ configKey: CONFIG_KEY,
247
+ enabledByDefault: false,
248
+ defaultParams: {
249
+ toolMapFile: DEFAULT_TOOL_MAP_FILE,
250
+ toolFolders: DEFAULT_TOOL_FOLDERS,
251
+ toolNamePatterns: DEFAULT_TOOL_NAME_PATTERNS,
252
+ toolExtensions: DEFAULT_TOOL_EXTENSIONS,
253
+ },
254
+ },
255
+ ({ toolName, toolInput, parameters }) => {
256
+ const cwd = process.cwd();
257
+ if (toolInGroups(toolName, ['delegation'])) {
258
+ checkDelegation(toolInput, cwd, parameters);
259
+ } else if (toolInGroups(toolName, ['write'])) {
260
+ checkWrite(toolInput, cwd, parameters);
261
+ }
262
+ },
263
+ );