claudify-agent 1.0.1 → 1.0.3
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/QUICK-REFERENCE.md +213 -213
- package/.claude/cost-dashboard.md +162 -162
- package/.claude/docs/COST-OPTIMIZATION.md +402 -403
- package/.claude/docs/MCP-INTEGRATION-GUIDE.md +348 -348
- package/.claude/docs/PLAN-MODE-OPTIMIZATION.md +835 -836
- package/.claude/hooks/auto-learning-review.ts +200 -195
- package/.claude/hooks/auto-lint-fix.ts +60 -104
- package/.claude/hooks/auto-security-review.ts +314 -354
- package/.claude/hooks/auto-validate.ts +249 -348
- package/.claude/hooks/lib/claude-hook.ts +168 -0
- package/.claude/hooks/plan-quality-tracker.ts +598 -719
- package/.claude/rules/anti-patterns.md +22 -22
- package/.claude/rules/context-tier-1.md +41 -41
- package/.claude/rules/context-tier-2.md +285 -285
- package/.claude/rules/general.md +25 -25
- package/.claude/rules/mcp-auto-use.md +46 -46
- package/.claude/rules/parallel-exploration-auto.md +30 -31
- package/.claude/rules/plan-mode-execution.md +289 -289
- package/.claude/rules/plan-mode-research.md +34 -34
- package/.claude/rules/plan-mode-structure.md +166 -166
- package/.claude/rules/plan-mode-validation.md +129 -130
- package/.claude/rules/security-critical.md +19 -19
- package/.claude/rules/tool-auto-selection.md +45 -45
- package/.claude/rules/tool-strategy.md +22 -22
- package/.claude/scripts/security-scan.js +1 -1
- package/.claude/security-dashboard.md +142 -142
- package/.claude/settings.json +50 -0
- package/.claude/skills/debug-optimizer/SKILL.md +420 -399
- package/.claude/skills/learning-loop/SKILL.md +313 -308
- package/.claude/skills/mcp-mastery/SKILL.md +218 -214
- package/.claude/skills/nextjs-guide/SKILL.md +687 -687
- package/.claude/skills/parallel-exploration/SKILL.md +197 -193
- package/.claude/skills/plan-mode-mastery/SKILL.md +268 -263
- package/.claude/skills/python-guide/SKILL.md +822 -822
- package/.claude/skills/react-guide/SKILL.md +762 -762
- package/.claude/skills/{security.md → security-guide/SKILL.md} +10 -0
- package/.claude/skills/solidity-guide/SKILL.md +5 -0
- package/.claude/skills/tool-selection/SKILL.md +433 -428
- package/.claude/skills/vite-guide/SKILL.md +445 -445
- package/CLAUDE.md +1 -0
- package/README.md +74 -35
- package/README.vi.md +106 -0
- package/bin/cli.js +13 -4
- package/package.json +13 -1
- package/.claude/skills/debug-optimizer.md +0 -12
- package/.claude/skills/nextjs.md +0 -13
- package/.claude/skills/react.md +0 -12
|
@@ -1,348 +1,249 @@
|
|
|
1
|
-
// .
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
import { execSync } from "child_process";
|
|
5
|
-
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
6
|
-
import { join } from "path";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
const
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
validations.push({ name: '
|
|
197
|
-
console.error(
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
execSync(testCommand, {
|
|
251
|
-
stdio: 'pipe',
|
|
252
|
-
encoding: 'utf-8',
|
|
253
|
-
timeout: 120000 // 120s timeout for test suites
|
|
254
|
-
});
|
|
255
|
-
validations.push({ name: 'Tests', passed: true });
|
|
256
|
-
console.error("[Auto-Validate] ✓ Tests passed");
|
|
257
|
-
} catch (error: any) {
|
|
258
|
-
validations.push({
|
|
259
|
-
name: 'Tests',
|
|
260
|
-
passed: false,
|
|
261
|
-
error: error.stdout?.toString() || error.stderr?.toString() || error.message
|
|
262
|
-
});
|
|
263
|
-
console.error("[Auto-Validate] ✗ Tests failed");
|
|
264
|
-
}
|
|
265
|
-
} else {
|
|
266
|
-
validations.push({ name: 'Tests', passed: true, skipped: true });
|
|
267
|
-
console.error("[Auto-Validate] ⚠ Tests skipped (no test script found)");
|
|
268
|
-
}
|
|
269
|
-
} else {
|
|
270
|
-
if (!validationConfig.enableTests) {
|
|
271
|
-
validations.push({ name: 'Tests', passed: true, skipped: true });
|
|
272
|
-
console.error("[Auto-Validate] ⚠ Tests disabled in config");
|
|
273
|
-
} else {
|
|
274
|
-
validations.push({ name: 'Tests', passed: true, skipped: true });
|
|
275
|
-
console.error("[Auto-Validate] ⚠ Tests skipped (no test files detected)");
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// 4. Validate MCP tools (if MCP tools were used)
|
|
280
|
-
if (validationConfig.enableMCPValidation && conversation) {
|
|
281
|
-
const mcpResults = validateMCPTools(conversation);
|
|
282
|
-
|
|
283
|
-
if (mcpResults.length > 0) {
|
|
284
|
-
const failedMCPTools = mcpResults.filter(r => !r.success);
|
|
285
|
-
|
|
286
|
-
if (failedMCPTools.length > 0) {
|
|
287
|
-
validations.push({
|
|
288
|
-
name: 'MCP Tools',
|
|
289
|
-
passed: false,
|
|
290
|
-
error: `Failed tools: ${failedMCPTools.map(t => t.tool).join(', ')}`
|
|
291
|
-
});
|
|
292
|
-
console.error(`[Auto-Validate] ✗ ${failedMCPTools.length} MCP tool(s) failed validation`);
|
|
293
|
-
} else {
|
|
294
|
-
validations.push({ name: 'MCP Tools', passed: true });
|
|
295
|
-
console.error(`[Auto-Validate] ✓ ${mcpResults.length} MCP tool(s) validated successfully`);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
} else if (!validationConfig.enableMCPValidation) {
|
|
299
|
-
console.error("[Auto-Validate] ⚠ MCP validation disabled in config");
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Generate validation report (if enabled)
|
|
303
|
-
const failedValidations = validations.filter(v => !v.passed && !v.skipped);
|
|
304
|
-
const passedCount = validations.filter(v => v.passed && !v.skipped).length;
|
|
305
|
-
const skippedCount = validations.filter(v => v.skipped).length;
|
|
306
|
-
|
|
307
|
-
const reportPath = join(process.cwd(), reportingConfig.reportDirectory, 'auto-validation-report.md');
|
|
308
|
-
|
|
309
|
-
if (reportingConfig.generateReports) {
|
|
310
|
-
const validationReport = `## Auto-Validation Report
|
|
311
|
-
|
|
312
|
-
Generated: ${new Date().toISOString()}
|
|
313
|
-
Conversation: ${input.conversation_id}
|
|
314
|
-
|
|
315
|
-
## Results
|
|
316
|
-
|
|
317
|
-
${validations.map(v => {
|
|
318
|
-
if (v.skipped) {
|
|
319
|
-
return `⚠️ ${v.name} (skipped)`;
|
|
320
|
-
}
|
|
321
|
-
return `${v.passed ? '✅' : '❌'} ${v.name}${v.error ? `:\n\`\`\`${v.error}\`\`\`` : ''}`;
|
|
322
|
-
}).join('\n\n')}
|
|
323
|
-
|
|
324
|
-
## Summary
|
|
325
|
-
|
|
326
|
-
Total: ${validations.length} checks
|
|
327
|
-
Passed: ${passedCount}
|
|
328
|
-
Failed: ${failedValidations.length}
|
|
329
|
-
Skipped: ${skippedCount}
|
|
330
|
-
${failedValidations.length === 0 ? '\n✓ All validations passed!' : '\n⚠️ Some validations failed. Please fix the issues above.'}
|
|
331
|
-
`;
|
|
332
|
-
|
|
333
|
-
writeFileSync(reportPath, validationReport);
|
|
334
|
-
console.error(`[Auto-Validate] Report saved to ${reportPath}`);
|
|
335
|
-
} else {
|
|
336
|
-
console.error("[Auto-Validate] ⚠ Report generation disabled in config");
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Provide feedback
|
|
340
|
-
if (failedValidations.length > 0) {
|
|
341
|
-
const output: StopHookOutput = {
|
|
342
|
-
followup_message: `⚠️ Validation failed: ${failedValidations.map(v => v.name).join(', ')}. See ${reportPath} for details.`,
|
|
343
|
-
};
|
|
344
|
-
console.log(JSON.stringify(output));
|
|
345
|
-
} else {
|
|
346
|
-
console.error("[Auto-Validate] ✓ All validations passed");
|
|
347
|
-
console.log(JSON.stringify({}));
|
|
348
|
-
}
|
|
1
|
+
// .claude/hooks/auto-validate.ts
|
|
2
|
+
// Stop hook — unified validation: lint + typecheck + tests + MCP validation
|
|
3
|
+
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import {
|
|
8
|
+
readHookInput,
|
|
9
|
+
loadCurrentTurn,
|
|
10
|
+
editedFiles,
|
|
11
|
+
stateDir,
|
|
12
|
+
blockOrNotify,
|
|
13
|
+
done,
|
|
14
|
+
type ToolUse,
|
|
15
|
+
type ExecError,
|
|
16
|
+
} from "./lib/claude-hook.ts";
|
|
17
|
+
|
|
18
|
+
interface ValidationResult {
|
|
19
|
+
name: string;
|
|
20
|
+
passed: boolean;
|
|
21
|
+
error?: string;
|
|
22
|
+
skipped?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface MCPToolResult {
|
|
26
|
+
tool: string;
|
|
27
|
+
success: boolean;
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ValidationConfig {
|
|
32
|
+
enableLint?: boolean;
|
|
33
|
+
enableTypecheck?: boolean;
|
|
34
|
+
enableTests?: boolean;
|
|
35
|
+
enableMCPValidation?: boolean;
|
|
36
|
+
testCommand?: string;
|
|
37
|
+
lintCommand?: string;
|
|
38
|
+
typecheckCommand?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface HooksConfig {
|
|
42
|
+
validation?: ValidationConfig;
|
|
43
|
+
reporting?: {
|
|
44
|
+
generateReports?: boolean;
|
|
45
|
+
reportDirectory?: string;
|
|
46
|
+
appendReports?: boolean;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const input = await readHookInput();
|
|
51
|
+
const projectDir = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd();
|
|
52
|
+
|
|
53
|
+
// Load optional configuration
|
|
54
|
+
const configPath = join(stateDir(input), 'hooks.config.json');
|
|
55
|
+
const config: HooksConfig = existsSync(configPath)
|
|
56
|
+
? JSON.parse(readFileSync(configPath, 'utf-8'))
|
|
57
|
+
: {};
|
|
58
|
+
|
|
59
|
+
const validationConfig = {
|
|
60
|
+
enableLint: config.validation?.enableLint !== false,
|
|
61
|
+
enableTypecheck: config.validation?.enableTypecheck !== false,
|
|
62
|
+
enableTests: config.validation?.enableTests !== false,
|
|
63
|
+
enableMCPValidation: config.validation?.enableMCPValidation !== false,
|
|
64
|
+
lintCommand: config.validation?.lintCommand,
|
|
65
|
+
typecheckCommand: config.validation?.typecheckCommand,
|
|
66
|
+
testCommand: config.validation?.testCommand
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const reportingConfig = {
|
|
70
|
+
generateReports: config.reporting?.generateReports !== false,
|
|
71
|
+
reportDirectory: config.reporting?.reportDirectory || '.claude',
|
|
72
|
+
appendReports: config.reporting?.appendReports !== false
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Helper functions
|
|
76
|
+
function hasScript(scriptName: string): boolean {
|
|
77
|
+
try {
|
|
78
|
+
const pkgPath = join(projectDir, 'package.json');
|
|
79
|
+
if (!existsSync(pkgPath)) return false;
|
|
80
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
81
|
+
return !!pkg.scripts?.[scriptName];
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function validateMCPTools(toolUses: ToolUse[]): MCPToolResult[] {
|
|
88
|
+
return toolUses
|
|
89
|
+
.filter(t => t.name.startsWith('mcp__'))
|
|
90
|
+
.map(t => ({ tool: t.name, success: !t.isError }));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const TEST_FILE = /(\.(test|spec)\.[cm]?[jt]sx?$)|__tests__|_test\.go$|\.t\.sol$/;
|
|
94
|
+
|
|
95
|
+
// Only validate turns in which Claude edited files
|
|
96
|
+
const turn = loadCurrentTurn(input.transcript_path);
|
|
97
|
+
const changedFiles = turn ? editedFiles(turn.toolUses) : [];
|
|
98
|
+
|
|
99
|
+
if (changedFiles.length === 0) {
|
|
100
|
+
console.error("[Auto-Validate] No code changes detected, skipping validation");
|
|
101
|
+
done();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.error("[Auto-Validate] Running automatic validation...");
|
|
105
|
+
|
|
106
|
+
// Automatic validation sequence
|
|
107
|
+
const validations: ValidationResult[] = [];
|
|
108
|
+
|
|
109
|
+
// Run one check; a tool that isn't installed (exit 127) is skipped, not failed
|
|
110
|
+
function runCheck(name: string, command: string, timeout: number): void {
|
|
111
|
+
try {
|
|
112
|
+
execSync(command, { cwd: projectDir, stdio: 'pipe', encoding: 'utf-8', timeout });
|
|
113
|
+
validations.push({ name, passed: true });
|
|
114
|
+
console.error(`[Auto-Validate] ✓ ${name} passed`);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
const error = err as ExecError;
|
|
117
|
+
if (error.status === 127) {
|
|
118
|
+
validations.push({ name, passed: true, skipped: true });
|
|
119
|
+
console.error(`[Auto-Validate] ⚠ ${name} skipped (command not found — is it installed?)`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const output = [error.stdout?.toString(), error.stderr?.toString()].filter(Boolean).join('\n').trim();
|
|
123
|
+
validations.push({ name, passed: false, error: output || error.message });
|
|
124
|
+
console.error(`[Auto-Validate] ✗ ${name} failed`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 1. Run ESLint (with graceful degradation)
|
|
129
|
+
if (validationConfig.enableLint) {
|
|
130
|
+
const lintCommand = validationConfig.lintCommand || (hasScript('lint') ? 'npm run lint' : null);
|
|
131
|
+
|
|
132
|
+
if (lintCommand) {
|
|
133
|
+
runCheck('ESLint', lintCommand, 60000);
|
|
134
|
+
} else {
|
|
135
|
+
validations.push({ name: 'ESLint', passed: true, skipped: true });
|
|
136
|
+
console.error("[Auto-Validate] ⚠ ESLint skipped (no lint script found)");
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
validations.push({ name: 'ESLint', passed: true, skipped: true });
|
|
140
|
+
console.error("[Auto-Validate] ⚠ ESLint disabled in config");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 2. Run typecheck (with graceful degradation)
|
|
144
|
+
if (validationConfig.enableTypecheck) {
|
|
145
|
+
const typecheckCommand = validationConfig.typecheckCommand || (hasScript('typecheck') ? 'npm run typecheck' : null);
|
|
146
|
+
|
|
147
|
+
if (typecheckCommand) {
|
|
148
|
+
runCheck('Typecheck', typecheckCommand, 60000);
|
|
149
|
+
} else {
|
|
150
|
+
validations.push({ name: 'Typecheck', passed: true, skipped: true });
|
|
151
|
+
console.error("[Auto-Validate] ⚠ Typecheck skipped (no typecheck script found)");
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
validations.push({ name: 'Typecheck', passed: true, skipped: true });
|
|
155
|
+
console.error("[Auto-Validate] ⚠ Typecheck disabled in config");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 3. Run tests (if test files detected or configured)
|
|
159
|
+
const hasTestFiles = changedFiles.some(f => TEST_FILE.test(f));
|
|
160
|
+
const shouldRunTests = validationConfig.enableTests && (hasTestFiles || hasScript('test'));
|
|
161
|
+
|
|
162
|
+
if (shouldRunTests) {
|
|
163
|
+
const testCommand = validationConfig.testCommand || 'npm run test';
|
|
164
|
+
|
|
165
|
+
if (hasScript('test') || validationConfig.testCommand) {
|
|
166
|
+
runCheck('Tests', testCommand, 120000); // 120s timeout for test suites
|
|
167
|
+
} else {
|
|
168
|
+
validations.push({ name: 'Tests', passed: true, skipped: true });
|
|
169
|
+
console.error("[Auto-Validate] ⚠ Tests skipped (no test script found)");
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
if (!validationConfig.enableTests) {
|
|
173
|
+
validations.push({ name: 'Tests', passed: true, skipped: true });
|
|
174
|
+
console.error("[Auto-Validate] ⚠ Tests disabled in config");
|
|
175
|
+
} else {
|
|
176
|
+
validations.push({ name: 'Tests', passed: true, skipped: true });
|
|
177
|
+
console.error("[Auto-Validate] ⚠ Tests skipped (no test files detected)");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 4. Validate MCP tools (if MCP tools were used)
|
|
182
|
+
if (validationConfig.enableMCPValidation && turn) {
|
|
183
|
+
const mcpResults = validateMCPTools(turn.toolUses);
|
|
184
|
+
|
|
185
|
+
if (mcpResults.length > 0) {
|
|
186
|
+
const failedMCPTools = mcpResults.filter(r => !r.success);
|
|
187
|
+
|
|
188
|
+
if (failedMCPTools.length > 0) {
|
|
189
|
+
validations.push({
|
|
190
|
+
name: 'MCP Tools',
|
|
191
|
+
passed: false,
|
|
192
|
+
error: `Failed tools: ${failedMCPTools.map(t => t.tool).join(', ')}`
|
|
193
|
+
});
|
|
194
|
+
console.error(`[Auto-Validate] ✗ ${failedMCPTools.length} MCP tool(s) failed validation`);
|
|
195
|
+
} else {
|
|
196
|
+
validations.push({ name: 'MCP Tools', passed: true });
|
|
197
|
+
console.error(`[Auto-Validate] ✓ ${mcpResults.length} MCP tool(s) validated successfully`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} else if (!validationConfig.enableMCPValidation) {
|
|
201
|
+
console.error("[Auto-Validate] ⚠ MCP validation disabled in config");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Generate validation report (if enabled)
|
|
205
|
+
const failedValidations = validations.filter(v => !v.passed && !v.skipped);
|
|
206
|
+
const passedCount = validations.filter(v => v.passed && !v.skipped).length;
|
|
207
|
+
const skippedCount = validations.filter(v => v.skipped).length;
|
|
208
|
+
|
|
209
|
+
const reportPath = join(projectDir, reportingConfig.reportDirectory, 'auto-validation-report.md');
|
|
210
|
+
|
|
211
|
+
if (reportingConfig.generateReports) {
|
|
212
|
+
const validationReport = `## Auto-Validation Report
|
|
213
|
+
|
|
214
|
+
Generated: ${new Date().toISOString()}
|
|
215
|
+
Session: ${input.session_id}
|
|
216
|
+
Changed files: ${changedFiles.join(", ")}
|
|
217
|
+
|
|
218
|
+
## Results
|
|
219
|
+
|
|
220
|
+
${validations.map(v => {
|
|
221
|
+
if (v.skipped) {
|
|
222
|
+
return `⚠️ ${v.name} (skipped)`;
|
|
223
|
+
}
|
|
224
|
+
return `${v.passed ? '✅' : '❌'} ${v.name}${v.error ? `:\n\`\`\`${v.error}\`\`\`` : ''}`;
|
|
225
|
+
}).join('\n\n')}
|
|
226
|
+
|
|
227
|
+
## Summary
|
|
228
|
+
|
|
229
|
+
Total: ${validations.length} checks
|
|
230
|
+
Passed: ${passedCount}
|
|
231
|
+
Failed: ${failedValidations.length}
|
|
232
|
+
Skipped: ${skippedCount}
|
|
233
|
+
${failedValidations.length === 0 ? '\n✓ All validations passed!' : '\n⚠️ Some validations failed. Please fix the issues above.'}
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
writeFileSync(reportPath, validationReport);
|
|
237
|
+
console.error(`[Auto-Validate] Report saved to ${reportPath}`);
|
|
238
|
+
} else {
|
|
239
|
+
console.error("[Auto-Validate] ⚠ Report generation disabled in config");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Provide feedback
|
|
243
|
+
if (failedValidations.length > 0) {
|
|
244
|
+
const details = failedValidations.map(v => `### ${v.name}\n${(v.error || '').slice(0, 2000)}`).join('\n\n');
|
|
245
|
+
blockOrNotify(input, `⚠️ Validation failed: ${failedValidations.map(v => v.name).join(', ')}. Fix these before finishing (full report: ${reportPath}).\n\n${details}`);
|
|
246
|
+
} else {
|
|
247
|
+
console.error("[Auto-Validate] ✓ All validations passed");
|
|
248
|
+
done();
|
|
249
|
+
}
|