claudify-agent 1.0.0 → 1.0.2

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 (47) hide show
  1. package/.claude/QUICK-REFERENCE.md +213 -213
  2. package/.claude/cost-dashboard.md +162 -162
  3. package/.claude/docs/COST-OPTIMIZATION.md +402 -403
  4. package/.claude/docs/MCP-INTEGRATION-GUIDE.md +348 -348
  5. package/.claude/docs/PLAN-MODE-OPTIMIZATION.md +835 -836
  6. package/.claude/hooks/auto-learning-review.ts +200 -195
  7. package/.claude/hooks/auto-lint-fix.ts +60 -104
  8. package/.claude/hooks/auto-security-review.ts +314 -354
  9. package/.claude/hooks/auto-validate.ts +249 -348
  10. package/.claude/hooks/lib/claude-hook.ts +168 -0
  11. package/.claude/hooks/plan-quality-tracker.ts +598 -719
  12. package/.claude/rules/anti-patterns.md +22 -22
  13. package/.claude/rules/context-tier-1.md +41 -41
  14. package/.claude/rules/context-tier-2.md +285 -285
  15. package/.claude/rules/general.md +25 -25
  16. package/.claude/rules/mcp-auto-use.md +46 -46
  17. package/.claude/rules/parallel-exploration-auto.md +30 -31
  18. package/.claude/rules/plan-mode-execution.md +289 -289
  19. package/.claude/rules/plan-mode-research.md +34 -34
  20. package/.claude/rules/plan-mode-structure.md +166 -166
  21. package/.claude/rules/plan-mode-validation.md +129 -130
  22. package/.claude/rules/security-critical.md +19 -19
  23. package/.claude/rules/tool-auto-selection.md +45 -45
  24. package/.claude/rules/tool-strategy.md +22 -22
  25. package/.claude/scripts/security-scan.js +1 -1
  26. package/.claude/security-dashboard.md +142 -142
  27. package/.claude/settings.json +50 -0
  28. package/.claude/skills/debug-optimizer/SKILL.md +420 -399
  29. package/.claude/skills/learning-loop/SKILL.md +313 -308
  30. package/.claude/skills/mcp-mastery/SKILL.md +218 -214
  31. package/.claude/skills/nextjs-guide/SKILL.md +687 -687
  32. package/.claude/skills/parallel-exploration/SKILL.md +197 -193
  33. package/.claude/skills/plan-mode-mastery/SKILL.md +268 -263
  34. package/.claude/skills/python-guide/SKILL.md +822 -822
  35. package/.claude/skills/react-guide/SKILL.md +762 -762
  36. package/.claude/skills/{security.md → security-guide/SKILL.md} +10 -0
  37. package/.claude/skills/solidity-guide/SKILL.md +5 -0
  38. package/.claude/skills/tool-selection/SKILL.md +433 -428
  39. package/.claude/skills/vite-guide/SKILL.md +445 -445
  40. package/CLAUDE.md +1 -0
  41. package/README.md +74 -35
  42. package/README.vi.md +106 -0
  43. package/bin/cli.js +13 -4
  44. package/package.json +9 -1
  45. package/.claude/skills/debug-optimizer.md +0 -12
  46. package/.claude/skills/nextjs.md +0 -13
  47. package/.claude/skills/react.md +0 -12
@@ -1,348 +1,249 @@
1
- // .cursor/hooks/auto-validate.ts
2
- // Unified validation hook: 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
-
8
- export {};
9
-
10
- interface StopHookInput {
11
- conversation_id: string;
12
- status: "completed" | "aborted" | "error";
13
- loop_count: number;
14
- }
15
-
16
- interface StopHookOutput {
17
- followup_message?: string;
18
- }
19
-
20
- interface ValidationResult {
21
- name: string;
22
- passed: boolean;
23
- error?: string;
24
- skipped?: boolean;
25
- }
26
-
27
- interface MCPToolResult {
28
- tool: string;
29
- success: boolean;
30
- error?: string;
31
- }
32
-
33
- interface ValidationConfig {
34
- enableLint?: boolean;
35
- enableTypecheck?: boolean;
36
- enableTests?: boolean;
37
- enableMCPValidation?: boolean;
38
- testCommand?: string;
39
- lintCommand?: string;
40
- typecheckCommand?: string;
41
- }
42
-
43
- interface HooksConfig {
44
- validation?: ValidationConfig;
45
- reporting?: {
46
- generateReports?: boolean;
47
- reportDirectory?: string;
48
- appendReports?: boolean;
49
- };
50
- }
51
-
52
- // Read input from stdin
53
- const stdin = await new Promise<string>((resolve) => {
54
- let data = "";
55
- process.stdin.on("data", (chunk) => (data += chunk));
56
- process.stdin.on("end", () => resolve(data));
57
- });
58
-
59
- const input: StopHookInput = JSON.parse(stdin);
60
-
61
- // Load optional configuration
62
- const configPath = join(process.cwd(), '.cursor/hooks.config.json');
63
- const config: HooksConfig = existsSync(configPath)
64
- ? JSON.parse(readFileSync(configPath, 'utf-8'))
65
- : {};
66
-
67
- const validationConfig = {
68
- enableLint: config.validation?.enableLint !== false,
69
- enableTypecheck: config.validation?.enableTypecheck !== false,
70
- enableTests: config.validation?.enableTests !== false,
71
- enableMCPValidation: config.validation?.enableMCPValidation !== false,
72
- lintCommand: config.validation?.lintCommand,
73
- typecheckCommand: config.validation?.typecheckCommand,
74
- testCommand: config.validation?.testCommand
75
- };
76
-
77
- const reportingConfig = {
78
- generateReports: config.reporting?.generateReports !== false,
79
- reportDirectory: config.reporting?.reportDirectory || '.cursor',
80
- appendReports: config.reporting?.appendReports !== false
81
- };
82
-
83
- // Only validate on completion
84
- if (input.status !== "completed") {
85
- console.log(JSON.stringify({}));
86
- process.exit(0);
87
- }
88
-
89
- // Helper functions
90
- function hasScript(scriptName: string): boolean {
91
- try {
92
- const pkgPath = join(process.cwd(), 'package.json');
93
- if (!existsSync(pkgPath)) return false;
94
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
95
- return !!pkg.scripts?.[scriptName];
96
- } catch {
97
- return false;
98
- }
99
- }
100
-
101
- function detectCodeChanges(conversation: any): boolean {
102
- if (!conversation?.messages) return false;
103
-
104
- return conversation.messages.some((m: any) =>
105
- m.content?.includes('StrReplace') ||
106
- m.content?.includes('Write') ||
107
- m.content?.includes('EditNotebook') ||
108
- m.tool_calls?.some((t: any) =>
109
- ['StrReplace', 'Write', 'EditNotebook'].includes(t.function?.name)
110
- )
111
- );
112
- }
113
-
114
- function validateMCPTools(conversation: any): MCPToolResult[] {
115
- const results: MCPToolResult[] = [];
116
-
117
- if (!conversation?.messages) return results;
118
-
119
- const mcpToolPatterns = [
120
- 'browser_', 'github_', 'eslint', 'context7',
121
- 'chrome', 'playwright', 'selenium', 'dart', 'stitch', 'shadcn'
122
- ];
123
-
124
- for (const message of conversation.messages) {
125
- if (message.tool_calls) {
126
- for (const toolCall of message.tool_calls) {
127
- const toolName = toolCall.function?.name;
128
- if (!toolName) continue;
129
-
130
- const isMCPTool = mcpToolPatterns.some(pattern =>
131
- toolName.startsWith(pattern) || toolName.includes(pattern)
132
- );
133
-
134
- if (isMCPTool) {
135
- // Track MCP tool usage (simplified - assumes success)
136
- // In production, would check actual tool execution results
137
- results.push({
138
- tool: toolName,
139
- success: true
140
- });
141
- }
142
- }
143
- }
144
- }
145
-
146
- return results;
147
- }
148
-
149
- function detectTestFiles(conversation: any): boolean {
150
- if (!conversation?.messages) return false;
151
-
152
- return conversation.messages.some((m: any) =>
153
- m.content?.includes('.test.ts') ||
154
- m.content?.includes('.test.tsx') ||
155
- m.content?.includes('.spec.ts') ||
156
- m.content?.includes('.spec.tsx') ||
157
- m.content?.includes('__tests__') ||
158
- m.content?.includes('.test.js') ||
159
- m.content?.includes('.spec.js')
160
- );
161
- }
162
-
163
- // Try to detect code changes from conversation file
164
- const conversationFile = join(process.cwd(), `.cursor/conversations/${input.conversation_id}.json`);
165
- let conversation;
166
- try {
167
- if (existsSync(conversationFile)) {
168
- conversation = JSON.parse(readFileSync(conversationFile, 'utf-8'));
169
- }
170
- } catch {
171
- console.error("[Auto-Validate] Could not read conversation file, proceeding with validation");
172
- conversation = null;
173
- }
174
-
175
- // Smart change detection
176
- const hasCodeChanges = conversation ? detectCodeChanges(conversation) : true;
177
-
178
- if (!hasCodeChanges) {
179
- console.error("[Auto-Validate] No code changes detected, skipping validation");
180
- console.log(JSON.stringify({}));
181
- process.exit(0);
182
- }
183
-
184
- console.error("[Auto-Validate] Running automatic validation...");
185
-
186
- // Automatic validation sequence
187
- const validations: ValidationResult[] = [];
188
-
189
- // 1. Run ESLint (with graceful degradation)
190
- if (validationConfig.enableLint) {
191
- const lintCommand = validationConfig.lintCommand || (hasScript('lint') ? 'npm run lint' : null);
192
-
193
- if (lintCommand) {
194
- try {
195
- execSync(lintCommand, { stdio: 'pipe', encoding: 'utf-8', timeout: 60000 });
196
- validations.push({ name: 'ESLint', passed: true });
197
- console.error("[Auto-Validate] ✓ ESLint passed");
198
- } catch (error: any) {
199
- validations.push({
200
- name: 'ESLint',
201
- passed: false,
202
- error: error.stdout?.toString() || error.stderr?.toString() || error.message
203
- });
204
- console.error("[Auto-Validate] ✗ ESLint failed");
205
- }
206
- } else {
207
- validations.push({ name: 'ESLint', passed: true, skipped: true });
208
- console.error("[Auto-Validate] ⚠ ESLint skipped (no lint script found)");
209
- }
210
- } else {
211
- validations.push({ name: 'ESLint', passed: true, skipped: true });
212
- console.error("[Auto-Validate] ⚠ ESLint disabled in config");
213
- }
214
-
215
- // 2. Run typecheck (with graceful degradation)
216
- if (validationConfig.enableTypecheck) {
217
- const typecheckCommand = validationConfig.typecheckCommand || (hasScript('typecheck') ? 'npm run typecheck' : null);
218
-
219
- if (typecheckCommand) {
220
- try {
221
- execSync(typecheckCommand, { stdio: 'pipe', encoding: 'utf-8', timeout: 60000 });
222
- validations.push({ name: 'Typecheck', passed: true });
223
- console.error("[Auto-Validate] ✓ Typecheck passed");
224
- } catch (error: any) {
225
- validations.push({
226
- name: 'Typecheck',
227
- passed: false,
228
- error: error.stdout?.toString() || error.stderr?.toString() || error.message
229
- });
230
- console.error("[Auto-Validate] ✗ Typecheck failed");
231
- }
232
- } else {
233
- validations.push({ name: 'Typecheck', passed: true, skipped: true });
234
- console.error("[Auto-Validate] ⚠ Typecheck skipped (no typecheck script found)");
235
- }
236
- } else {
237
- validations.push({ name: 'Typecheck', passed: true, skipped: true });
238
- console.error("[Auto-Validate] ⚠ Typecheck disabled in config");
239
- }
240
-
241
- // 3. Run tests (if test files detected or configured)
242
- const hasTestFiles = conversation ? detectTestFiles(conversation) : false;
243
- const shouldRunTests = validationConfig.enableTests && (hasTestFiles || hasScript('test'));
244
-
245
- if (shouldRunTests) {
246
- const testCommand = validationConfig.testCommand || 'npm run test';
247
-
248
- if (hasScript('test') || validationConfig.testCommand) {
249
- try {
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
+ }