@devrik-tools/claude-gates 0.1.2 → 0.2.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/.claude-plugin/marketplace.json +7 -1
- package/cli/__tests__/config.test.mjs +113 -101
- package/cli/__tests__/install.test.mjs +102 -0
- package/cli/__tests__/materialize.test.mjs +95 -0
- package/cli/__tests__/registry-gates-consistency.test.mjs +52 -44
- package/cli/__tests__/task.test.mjs +124 -0
- package/cli/__tests__/version-consistency.test.mjs +44 -0
- package/cli/config.mjs +124 -113
- package/cli/index.mjs +100 -97
- package/cli/init.mjs +259 -256
- package/cli/install.mjs +150 -80
- package/cli/materialize.mjs +102 -51
- package/cli/registry.mjs +136 -127
- package/cli/task.mjs +140 -0
- package/package.json +2 -1
- package/plugins/gates/.claude-plugin/plugin.json +8 -2
- package/plugins/gates/hooks/__tests__/ask-adoption.test.mjs +83 -0
- package/plugins/gates/hooks/__tests__/doctor.test.mjs +85 -0
- package/plugins/gates/hooks/__tests__/wiring-check.test.mjs +65 -0
- package/plugins/gates/hooks/ask-adoption.mjs +147 -0
- package/plugins/gates/hooks/doctor.mjs +207 -0
- package/plugins/gates/hooks/gates/audit-before-build/audit-before-build.edge.test.mjs +83 -0
- package/plugins/gates/hooks/gates/audit-before-build/index.mjs +88 -88
- package/plugins/gates/hooks/gates/audit-before-build/test.mjs +117 -92
- package/plugins/gates/hooks/gates/autonomous-mode/autonomous.edge.test.mjs +104 -0
- package/plugins/gates/hooks/gates/autonomous-mode/index.mjs +48 -45
- package/plugins/gates/hooks/gates/bash-commands/bash-commands.edge.test.mjs +165 -0
- package/plugins/gates/hooks/gates/bash-commands/index.mjs +285 -268
- package/plugins/gates/hooks/gates/brief-before-delegate/brief-before-delegate.edge.test.mjs +151 -0
- package/plugins/gates/hooks/gates/brief-before-delegate/index.mjs +265 -177
- package/plugins/gates/hooks/gates/circuit-breaker/circuit-breaker.edge.test.mjs +207 -0
- package/plugins/gates/hooks/gates/circuit-breaker/index.mjs +501 -456
- package/plugins/gates/hooks/gates/circuit-breaker/test.mjs +161 -143
- package/plugins/gates/hooks/gates/dependency-skills/dependency-skills.edge.test.mjs +69 -0
- package/plugins/gates/hooks/gates/dependency-skills/index.mjs +126 -118
- package/plugins/gates/hooks/gates/diagnosis-before-patch/diagnosis-before-patch.edge.test.mjs +68 -0
- package/plugins/gates/hooks/gates/diagnosis-before-patch/index.mjs +43 -49
- package/plugins/gates/hooks/gates/feature-catalog/edge.edge.test.mjs +83 -0
- package/plugins/gates/hooks/gates/feature-catalog/index.mjs +83 -100
- package/plugins/gates/hooks/gates/force-parallel/index.mjs +119 -0
- package/plugins/gates/hooks/gates/force-parallel/test.mjs +88 -0
- package/plugins/gates/hooks/gates/forge-flow/forge-flow.edge.test.mjs +155 -0
- package/plugins/gates/hooks/gates/forge-flow/index.mjs +134 -112
- package/plugins/gates/hooks/gates/implementation-pipeline/edge.edge.test.mjs +66 -0
- package/plugins/gates/hooks/gates/implementation-pipeline/index.mjs +187 -186
- package/plugins/gates/hooks/gates/intent-flow/index.mjs +260 -238
- package/plugins/gates/hooks/gates/intent-flow/intent-flow.edge.test.mjs +96 -0
- package/plugins/gates/hooks/gates/intent-flow/test.mjs +165 -136
- package/plugins/gates/hooks/gates/lint-commit/index.mjs +149 -0
- package/plugins/gates/hooks/gates/lint-commit/test.mjs +85 -0
- package/plugins/gates/hooks/gates/mandatory-flow/edge.edge.test.mjs +100 -0
- package/plugins/gates/hooks/gates/mandatory-flow/index.mjs +180 -166
- package/plugins/gates/hooks/gates/neutral-spanish/index.mjs +79 -85
- package/plugins/gates/hooks/gates/neutral-spanish/neutral-spanish.edge.test.mjs +80 -0
- package/plugins/gates/hooks/gates/never-assume/index.mjs +58 -55
- package/plugins/gates/hooks/gates/never-assume/never-assume.edge.test.mjs +71 -0
- package/plugins/gates/hooks/gates/never-assume/test.mjs +95 -78
- package/plugins/gates/hooks/gates/no-blocking/index.mjs +148 -142
- package/plugins/gates/hooks/gates/no-blocking/no-blocking.edge.test.mjs +109 -0
- package/plugins/gates/hooks/gates/no-memory-dependency/index.mjs +123 -120
- package/plugins/gates/hooks/gates/no-memory-dependency/no-memory-dependency.edge.test.mjs +83 -0
- package/plugins/gates/hooks/gates/no-reconfirm/no-reconfirm.edge.test.mjs +91 -0
- package/plugins/gates/hooks/gates/protected-paths/index.mjs +144 -147
- package/plugins/gates/hooks/gates/protected-paths/protected-paths.edge.test.mjs +126 -0
- package/plugins/gates/hooks/gates/recurrence-lock/index.mjs +89 -64
- package/plugins/gates/hooks/gates/recurrence-lock/recurrence-lock.edge.test.mjs +102 -0
- package/plugins/gates/hooks/gates/reuse-before-build/edge.edge.test.mjs +94 -0
- package/plugins/gates/hooks/gates/reuse-before-build/index.mjs +159 -150
- package/plugins/gates/hooks/gates/risk-level/index.mjs +263 -203
- package/plugins/gates/hooks/gates/risk-level/risk-level.edge.test.mjs +116 -0
- package/plugins/gates/hooks/gates/risk-level/test.mjs +153 -125
- package/plugins/gates/hooks/gates/root-cause-first/index.mjs +56 -50
- package/plugins/gates/hooks/gates/root-cause-first/root-cause-first.edge.test.mjs +58 -0
- package/plugins/gates/hooks/gates/root-whitelist/index.mjs +116 -111
- package/plugins/gates/hooks/gates/root-whitelist/root-whitelist.edge.test.mjs +97 -0
- package/plugins/gates/hooks/gates/rule-skill-autodiscovery/index.mjs +184 -99
- package/plugins/gates/hooks/gates/rule-skill-autodiscovery/rule-skill-autodiscovery.edge.test.mjs +126 -0
- package/plugins/gates/hooks/gates/sdd-specs/edge.edge.test.mjs +130 -0
- package/plugins/gates/hooks/gates/sdd-specs/index.mjs +256 -251
- package/plugins/gates/hooks/gates/stop-pending/index.mjs +159 -0
- package/plugins/gates/hooks/gates/stop-pending/test.mjs +114 -0
- package/plugins/gates/hooks/gates/test-after-implementation/index.mjs +101 -101
- package/plugins/gates/hooks/gates/test-after-implementation/test-after-implementation.edge.test.mjs +72 -0
- package/plugins/gates/hooks/gates/test-matrix/edge.edge.test.mjs +63 -0
- package/plugins/gates/hooks/gates/test-matrix/index.mjs +187 -181
- package/plugins/gates/hooks/gates/test-matrix/test.mjs +114 -87
- package/plugins/gates/hooks/gates/tool-map/edge.edge.test.mjs +62 -0
- package/plugins/gates/hooks/gates/tool-map/index.mjs +143 -140
- package/plugins/gates/hooks/hooks.json +346 -286
- package/plugins/gates/hooks/lib/__tests__/config.test.mjs +173 -154
- package/plugins/gates/hooks/lib/__tests__/hook-io.test.mjs +210 -154
- package/plugins/gates/hooks/lib/__tests__/signals.test.mjs +114 -0
- package/plugins/gates/hooks/lib/config.mjs +172 -165
- package/plugins/gates/hooks/lib/hook-io-config.edge.test.mjs +189 -0
- package/plugins/gates/hooks/lib/hook-io.mjs +318 -208
- package/plugins/gates/hooks/lib/signals.mjs +127 -0
- package/plugins/gates/hooks/wiring-check.mjs +227 -0
- package/plugins/tasks/.claude-plugin/plugin.json +14 -0
- package/plugins/tasks/hooks/__tests__/register-requests.test.mjs +100 -0
- package/plugins/tasks/hooks/__tests__/session-tasks.test.mjs +95 -0
- package/plugins/tasks/hooks/hooks.json +26 -0
- package/plugins/tasks/hooks/lib/__tests__/task-store.test.mjs +180 -132
- package/plugins/tasks/hooks/lib/task-store.mjs +197 -159
- package/plugins/tasks/hooks/register-requests.mjs +141 -108
- package/plugins/tasks/hooks/session-tasks.mjs +108 -0
- package/registry.json +793 -686
|
@@ -1,140 +1,143 @@
|
|
|
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';
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
'.
|
|
35
|
-
'.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (!
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
if (!
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
toolMapFile,
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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';
|
|
18
|
+
import {
|
|
19
|
+
runGate,
|
|
20
|
+
warn,
|
|
21
|
+
toolInGroups,
|
|
22
|
+
writtenContentOf,
|
|
23
|
+
writtenPathOf,
|
|
24
|
+
} from '../../lib/hook-io.mjs';
|
|
25
|
+
|
|
26
|
+
const GATE_ID = 'tool-map';
|
|
27
|
+
const CONFIG_KEY = 'maintainToolMap';
|
|
28
|
+
|
|
29
|
+
const DEFAULT_TOOL_MAP_FILE = join('.ai', 'tool-map.json');
|
|
30
|
+
const JSON_INDENT = 2;
|
|
31
|
+
const MAX_AUDIT_LENGTH = 300;
|
|
32
|
+
|
|
33
|
+
const EXECUTABLE_EXTENSIONS = new Set([
|
|
34
|
+
'.js',
|
|
35
|
+
'.mjs',
|
|
36
|
+
'.cjs',
|
|
37
|
+
'.ts',
|
|
38
|
+
'.py',
|
|
39
|
+
'.sh',
|
|
40
|
+
'.ps1',
|
|
41
|
+
]);
|
|
42
|
+
const TOOL_FOLDERS = ['scripts/', 'hooks/', 'tools/', 'gates/'];
|
|
43
|
+
|
|
44
|
+
// Phrases that mark a line as the author's audit statement. Matched against one line at a
|
|
45
|
+
// time (no wrapping `.*`, which backtracks): the matching line is captured whole.
|
|
46
|
+
const AUDIT_PHRASE_PATTERN =
|
|
47
|
+
/(already exists|no existing tool|there is no plugin|audited|nothing does this|justification:)/i;
|
|
48
|
+
|
|
49
|
+
/** The first line of `content` that states an audit, trimmed — or null when none does. */
|
|
50
|
+
function auditLineOf(content) {
|
|
51
|
+
for (const line of content.split('\n')) {
|
|
52
|
+
if (AUDIT_PHRASE_PATTERN.test(line)) return line.trim();
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function projectRootOf(startDirectory) {
|
|
58
|
+
let current = startDirectory;
|
|
59
|
+
while (true) {
|
|
60
|
+
if (existsSync(join(current, '.git'))) return current;
|
|
61
|
+
const parent = dirname(current);
|
|
62
|
+
if (parent === current) return null;
|
|
63
|
+
current = parent;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isExecutableToolPath(filePath) {
|
|
68
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
69
|
+
const inToolFolder = TOOL_FOLDERS.some((folder) =>
|
|
70
|
+
normalized.includes(folder),
|
|
71
|
+
);
|
|
72
|
+
return (
|
|
73
|
+
inToolFolder && EXECUTABLE_EXTENSIONS.has(extname(filePath).toLowerCase())
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function readMap(path) {
|
|
78
|
+
if (!existsSync(path)) return { tools: [] };
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
81
|
+
return Array.isArray(parsed.tools) ? parsed : { tools: [] };
|
|
82
|
+
} catch {
|
|
83
|
+
return { tools: [] };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Appends the tool's record to the map file, warning (never blocking) if it cannot. */
|
|
88
|
+
function recordTool(mapPath, normalizedPath, auditLine, toolMapFile) {
|
|
89
|
+
const map = readMap(mapPath);
|
|
90
|
+
if (map.tools.some((tool) => tool.path === normalizedPath)) return; // already recorded
|
|
91
|
+
|
|
92
|
+
map.tools.push({
|
|
93
|
+
path: normalizedPath,
|
|
94
|
+
audit: auditLine.slice(0, MAX_AUDIT_LENGTH),
|
|
95
|
+
recordedAt: new Date().toISOString(),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
mkdirSync(dirname(mapPath), { recursive: true });
|
|
100
|
+
writeFileSync(
|
|
101
|
+
mapPath,
|
|
102
|
+
`${JSON.stringify(map, null, JSON_INDENT)}\n`,
|
|
103
|
+
'utf8',
|
|
104
|
+
);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
warn(
|
|
107
|
+
GATE_ID,
|
|
108
|
+
`Could not record this tool in ${toolMapFile}: ${error?.message ?? error}. ` +
|
|
109
|
+
'The build proceeds, but the discovery was not remembered.',
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
runGate(
|
|
115
|
+
{
|
|
116
|
+
id: GATE_ID,
|
|
117
|
+
configKey: CONFIG_KEY,
|
|
118
|
+
enabledByDefault: false,
|
|
119
|
+
defaultParams: { toolMapFile: DEFAULT_TOOL_MAP_FILE },
|
|
120
|
+
},
|
|
121
|
+
({ toolName, toolInput, parameters }) => {
|
|
122
|
+
if (!toolInGroups(toolName, ['write'])) return;
|
|
123
|
+
|
|
124
|
+
const filePath = writtenPathOf(toolInput);
|
|
125
|
+
if (!isExecutableToolPath(filePath)) return;
|
|
126
|
+
|
|
127
|
+
// Only a build that declared its audit is worth recording: that line IS the reason
|
|
128
|
+
// this tool exists and what it was checked against.
|
|
129
|
+
const content = writtenContentOf(toolInput);
|
|
130
|
+
const auditLine = auditLineOf(content);
|
|
131
|
+
if (!auditLine) return;
|
|
132
|
+
|
|
133
|
+
const root = projectRootOf(process.cwd());
|
|
134
|
+
if (!root) return;
|
|
135
|
+
const toolMapFile = parameters.toolMapFile ?? DEFAULT_TOOL_MAP_FILE;
|
|
136
|
+
recordTool(
|
|
137
|
+
join(root, toolMapFile),
|
|
138
|
+
filePath.replace(/\\/g, '/'),
|
|
139
|
+
auditLine,
|
|
140
|
+
toolMapFile,
|
|
141
|
+
);
|
|
142
|
+
},
|
|
143
|
+
);
|