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.
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 +13 -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,195 +1,200 @@
1
- // .cursor/hooks/auto-learning-review.ts
2
- // Automated weekly learning loop review - runs every 10 plans or weekly
3
-
4
- import { writeFileSync, readFileSync, existsSync } from "fs";
5
- import { join } from "path";
6
-
7
- const METRICS_FILE = join(process.cwd(), ".cursor/plan-metrics.json");
8
- const WEEKLY_REPORT_FILE = join(process.cwd(), ".cursor/weekly-learning-report.md");
9
- const ANTI_PATTERNS_FILE = join(process.cwd(), ".cursor/rules/anti-patterns.md");
10
-
11
- interface PlanMetrics {
12
- plan_accuracy: number;
13
- iteration_count: number;
14
- tool_efficiency: number;
15
- cost_estimate?: number;
16
- anti_patterns_detected?: string[];
17
- timestamp: string;
18
- }
19
-
20
- interface AntiPattern {
21
- id: string;
22
- name: string;
23
- count: number;
24
- lastOccurrence: string;
25
- suggestion: string;
26
- }
27
-
28
- // Read input from stdin (hook trigger)
29
- const stdin = await new Promise<string>((resolve) => {
30
- let data = "";
31
- process.stdin.on("data", (chunk) => (data += chunk));
32
- process.stdin.on("end", () => resolve(data));
33
- });
34
-
35
- console.error("[Auto Learning Review] Starting automated review...");
36
-
37
- // Load metrics
38
- if (!existsSync(METRICS_FILE)) {
39
- console.error("[Auto Learning Review] No metrics file found. Run some plans first.");
40
- process.exit(0);
41
- }
42
-
43
- const allMetrics: PlanMetrics[] = JSON.parse(readFileSync(METRICS_FILE, "utf-8"));
44
- const recentMetrics = allMetrics.slice(-10); // Last 10 plans
45
-
46
- if (recentMetrics.length < 3) {
47
- console.error("[Auto Learning Review] Not enough data (need 3+ plans). Current:", recentMetrics.length);
48
- process.exit(0);
49
- }
50
-
51
- // Calculate averages
52
- const avgAccuracy = recentMetrics.reduce((sum, m) => sum + m.plan_accuracy, 0) / recentMetrics.length;
53
- const avgIterations = recentMetrics.reduce((sum, m) => sum + m.iteration_count, 0) / recentMetrics.length;
54
- const avgEfficiency = recentMetrics.reduce((sum, m) => sum + m.tool_efficiency, 0) / recentMetrics.length;
55
- const totalCost = recentMetrics.reduce((sum, m) => sum + (m.cost_estimate || 0), 0);
56
-
57
- // Detect patterns
58
- const patternCounts: Record<string, number> = {};
59
- recentMetrics.forEach(m => {
60
- if (m.anti_patterns_detected) {
61
- m.anti_patterns_detected.forEach(pattern => {
62
- patternCounts[pattern] = (patternCounts[pattern] || 0) + 1;
63
- });
64
- }
65
- });
66
-
67
- // Identify repeated patterns (3+ occurrences)
68
- const repeatedPatterns: AntiPattern[] = Object.entries(patternCounts)
69
- .filter(([_, count]) => count >= 3)
70
- .map(([name, count]) => ({
71
- id: name.toLowerCase().replace(/\s+/g, '-'),
72
- name,
73
- count,
74
- lastOccurrence: recentMetrics.find(m => m.anti_patterns_detected?.includes(name))?.timestamp || '',
75
- suggestion: getSuggestionForPattern(name)
76
- }));
77
-
78
- // Generate report
79
- const report = generateReport({
80
- planCount: recentMetrics.length,
81
- avgAccuracy,
82
- avgIterations,
83
- avgEfficiency,
84
- totalCost,
85
- repeatedPatterns,
86
- trend: calculateTrend(recentMetrics)
87
- });
88
-
89
- // Write report
90
- writeFileSync(WEEKLY_REPORT_FILE, report, 'utf-8');
91
- console.error(`[Auto Learning Review] Report generated: ${WEEKLY_REPORT_FILE}`);
92
-
93
- // Auto-update rules if patterns detected
94
- if (repeatedPatterns.length > 0) {
95
- console.error(`[Auto Learning Review] Detected ${repeatedPatterns.length} repeated patterns (3+ occurrences)`);
96
- console.error("[Auto Learning Review] Review .cursor/weekly-learning-report.md for details");
97
- } else {
98
- console.error("[Auto Learning Review] No repeated patterns detected. Great job!");
99
- }
100
-
101
- function getSuggestionForPattern(patternName: string): string {
102
- const suggestions: Record<string, string> = {
103
- "Over-Engineering": "Follow tool escalation protocol: Tier 1 (Read/Glob/Grep) → Tier 2 → Tier 3 → Tier 4",
104
- "Planning Without Research": "Always run SemanticSearch before creating plans. Spend 2-3 min on research.",
105
- "Repeated Execution Failures": "Add validation subagent before plan execution. Create checkpoints.",
106
- "MCP Overuse": "Use MCP tools only when essential. Prefer built-in tools for simple tasks.",
107
- "Skipping Validation": "Enable auto-validate hook. Run ReadLints after EVERY code change.",
108
- "Context Bloat": "Keep rules under 20 lines. Use references over content."
109
- };
110
- return suggestions[patternName] || "Review pattern in learning-loop skill documentation.";
111
- }
112
-
113
- function calculateTrend(metrics: PlanMetrics[]): "improving" | "stable" | "declining" {
114
- if (metrics.length < 5) return "stable";
115
-
116
- const firstHalf = metrics.slice(0, Math.floor(metrics.length / 2));
117
- const secondHalf = metrics.slice(Math.floor(metrics.length / 2));
118
-
119
- const firstAvg = firstHalf.reduce((sum, m) => sum + m.plan_accuracy, 0) / firstHalf.length;
120
- const secondAvg = secondHalf.reduce((sum, m) => sum + m.plan_accuracy, 0) / secondHalf.length;
121
-
122
- if (secondAvg > firstAvg + 5) return "improving";
123
- if (secondAvg < firstAvg - 5) return "declining";
124
- return "stable";
125
- }
126
-
127
- function generateReport(data: any): string {
128
- const date = new Date().toISOString().split('T')[0];
129
-
130
- return `# Weekly Learning Report
131
-
132
- **Generated**: ${date}
133
- **Analysis Period**: Last ${data.planCount} plans
134
- **Trend**: ${data.trend.toUpperCase()}
135
-
136
- ---
137
-
138
- ## Performance Summary
139
-
140
- | Metric | Current | Target | Status |
141
- |--------|---------|--------|--------|
142
- | Avg Accuracy | ${data.avgAccuracy.toFixed(1)}% | >80% | ${data.avgAccuracy >= 80 ? '✅' : '⚠️'} |
143
- | Avg Iterations | ${data.avgIterations.toFixed(1)} | <2.0 | ${data.avgIterations < 2 ? '✅' : '⚠️'} |
144
- | Tool Efficiency | ${data.avgEfficiency.toFixed(1)}% | >70% | ${data.avgEfficiency >= 70 ? '✅' : '⚠️'} |
145
- | Total Cost | ${data.totalCost.toLocaleString()} tokens | Optimize | ${data.totalCost < 100000 ? '✅' : '⚠️'} |
146
-
147
- ---
148
-
149
- ## Repeated Patterns Detected (3+ Occurrences)
150
-
151
- ${data.repeatedPatterns.length > 0 ? data.repeatedPatterns.map((p: AntiPattern) => `### ${p.name}
152
-
153
- **Occurrences**: ${p.count} times
154
- **Last Seen**: ${p.lastOccurrence || 'Unknown'}
155
- **Suggestion**: ${p.suggestion}
156
-
157
- ---
158
- `).join('\n') : '✅ No repeated patterns detected! Great job maintaining best practices.'}
159
-
160
- ## Action Items
161
-
162
- ${data.repeatedPatterns.length > 0 ? `
163
- ### Critical (Fix This Week)
164
- ${data.repeatedPatterns.filter((p: AntiPattern) => p.count >= 5).map((p: AntiPattern) => `- [ ] ${p.name}: ${p.suggestion}`).join('\n') || '- [ ] None'}
165
-
166
- ### Important (Fix Next Week)
167
- ${data.repeatedPatterns.filter((p: AntiPattern) => p.count >= 3 && p.count < 5).map((p: AntiPattern) => `- [ ] ${p.name}: ${p.suggestion}`).join('\n') || '- [ ] None'}
168
- ` : `
169
- ### Continue Current Practices
170
- - [ ] Maintain research-first approach
171
- - [ ] Keep following tool escalation protocol
172
- - [ ] Continue automatic validation
173
- `}
174
-
175
- ---
176
-
177
- ## Trend Analysis
178
-
179
- ${data.trend === 'improving' ? '📈 **Performance is IMPROVING** - Keep doing what works!' :
180
- data.trend === 'declining' ? '📉 **Performance is DECLINING** - Review recent changes and patterns.' :
181
- '➡️ **Performance is STABLE** - Look for optimization opportunities.'}
182
-
183
- ---
184
-
185
- ## Next Review
186
-
187
- **Scheduled**: In 10 plans or 7 days (whichever comes first)
188
- **Focus Areas**:
189
- ${data.repeatedPatterns.length > 0 ? data.repeatedPatterns.slice(0, 3).map((p: AntiPattern) => `- ${p.name}`) : '- Continue monitoring current practices'}
190
-
191
- ---
192
-
193
- *This report was automatically generated by the auto-learning-review hook*
194
- `;
195
- }
1
+ // .claude/hooks/auto-learning-review.ts
2
+ // SessionStart hook — learning loop review over the last 10 tracked plans
3
+
4
+ import { writeFileSync, readFileSync, existsSync } from "fs";
5
+ import { join } from "path";
6
+ import { readHookInput, stateDir } from "./lib/claude-hook.ts";
7
+
8
+ const input = await readHookInput();
9
+
10
+ const METRICS_FILE = join(stateDir(input), "plan-metrics.json");
11
+ const WEEKLY_REPORT_FILE = join(stateDir(input), "weekly-learning-report.md");
12
+
13
+ interface PlanMetrics {
14
+ plan_accuracy: number;
15
+ iteration_count: number;
16
+ tool_efficiency: number;
17
+ cost_estimate?: number;
18
+ anti_patterns_detected?: string[];
19
+ timestamp: string;
20
+ }
21
+
22
+ interface AntiPattern {
23
+ id: string;
24
+ name: string;
25
+ count: number;
26
+ lastOccurrence: string;
27
+ suggestion: string;
28
+ }
29
+
30
+ console.error("[Auto Learning Review] Starting automated review...");
31
+
32
+ // Load metrics
33
+ if (!existsSync(METRICS_FILE)) {
34
+ console.error("[Auto Learning Review] No metrics file found. Run some plans first.");
35
+ process.exit(0);
36
+ }
37
+
38
+ const allMetrics: PlanMetrics[] = JSON.parse(readFileSync(METRICS_FILE, "utf-8"));
39
+ const recentMetrics = allMetrics.slice(-10); // Last 10 plans
40
+
41
+ if (recentMetrics.length < 3) {
42
+ console.error("[Auto Learning Review] Not enough data (need 3+ plans). Current:", recentMetrics.length);
43
+ process.exit(0);
44
+ }
45
+
46
+ // Calculate averages
47
+ const avgAccuracy = recentMetrics.reduce((sum, m) => sum + m.plan_accuracy, 0) / recentMetrics.length;
48
+ const avgIterations = recentMetrics.reduce((sum, m) => sum + m.iteration_count, 0) / recentMetrics.length;
49
+ const avgEfficiency = recentMetrics.reduce((sum, m) => sum + m.tool_efficiency, 0) / recentMetrics.length;
50
+ const totalCost = recentMetrics.reduce((sum, m) => sum + (m.cost_estimate || 0), 0);
51
+
52
+ // Detect patterns
53
+ const patternCounts: Record<string, number> = {};
54
+ recentMetrics.forEach(m => {
55
+ if (m.anti_patterns_detected) {
56
+ m.anti_patterns_detected.forEach(pattern => {
57
+ patternCounts[pattern] = (patternCounts[pattern] || 0) + 1;
58
+ });
59
+ }
60
+ });
61
+
62
+ // Identify repeated patterns (3+ occurrences)
63
+ const repeatedPatterns: AntiPattern[] = Object.entries(patternCounts)
64
+ .filter(([, count]) => count >= 3)
65
+ .map(([name, count]) => ({
66
+ id: name.toLowerCase().replace(/\s+/g, '-'),
67
+ name,
68
+ count,
69
+ lastOccurrence: recentMetrics.find(m => m.anti_patterns_detected?.includes(name))?.timestamp || '',
70
+ suggestion: getSuggestionForPattern(name)
71
+ }));
72
+
73
+ // Generate report
74
+ const report = generateReport({
75
+ planCount: recentMetrics.length,
76
+ avgAccuracy,
77
+ avgIterations,
78
+ avgEfficiency,
79
+ totalCost,
80
+ repeatedPatterns,
81
+ trend: calculateTrend(recentMetrics)
82
+ });
83
+
84
+ // Write report
85
+ writeFileSync(WEEKLY_REPORT_FILE, report, 'utf-8');
86
+ console.error(`[Auto Learning Review] Report generated: ${WEEKLY_REPORT_FILE}`);
87
+
88
+ // Auto-update rules if patterns detected
89
+ if (repeatedPatterns.length > 0) {
90
+ console.error(`[Auto Learning Review] Detected ${repeatedPatterns.length} repeated patterns (3+ occurrences)`);
91
+ console.error(`[Auto Learning Review] Review ${WEEKLY_REPORT_FILE} for details`);
92
+ } else {
93
+ console.error("[Auto Learning Review] No repeated patterns detected. Great job!");
94
+ }
95
+
96
+ function getSuggestionForPattern(patternName: string): string {
97
+ const suggestions: Record<string, string> = {
98
+ "Over-Engineering": "Follow tool escalation protocol: Tier 1 (Read/Glob/Grep) → Tier 2 → Tier 3 → Tier 4",
99
+ "Planning Without Research": "Always research with Grep/Glob and Explore subagents before creating plans. Spend 2-3 min on research.",
100
+ "Repeated Execution Failures": "Add validation subagent before plan execution. Create checkpoints.",
101
+ "MCP Overuse": "Use MCP tools only when essential. Prefer built-in tools for simple tasks.",
102
+ "Skipping Validation": "Keep the auto-validate Stop hook enabled. Run lint/typecheck after EVERY code change.",
103
+ "Context Bloat": "Keep rules under 20 lines. Use references over content."
104
+ };
105
+ return suggestions[patternName] || "Review pattern in learning-loop skill documentation.";
106
+ }
107
+
108
+ function calculateTrend(metrics: PlanMetrics[]): "improving" | "stable" | "declining" {
109
+ if (metrics.length < 5) return "stable";
110
+
111
+ const firstHalf = metrics.slice(0, Math.floor(metrics.length / 2));
112
+ const secondHalf = metrics.slice(Math.floor(metrics.length / 2));
113
+
114
+ const firstAvg = firstHalf.reduce((sum, m) => sum + m.plan_accuracy, 0) / firstHalf.length;
115
+ const secondAvg = secondHalf.reduce((sum, m) => sum + m.plan_accuracy, 0) / secondHalf.length;
116
+
117
+ if (secondAvg > firstAvg + 5) return "improving";
118
+ if (secondAvg < firstAvg - 5) return "declining";
119
+ return "stable";
120
+ }
121
+
122
+ interface ReportData {
123
+ planCount: number;
124
+ avgAccuracy: number;
125
+ avgIterations: number;
126
+ avgEfficiency: number;
127
+ totalCost: number;
128
+ repeatedPatterns: AntiPattern[];
129
+ trend: "improving" | "stable" | "declining";
130
+ }
131
+
132
+ function generateReport(data: ReportData): string {
133
+ const date = new Date().toISOString().split('T')[0];
134
+
135
+ return `# Weekly Learning Report
136
+
137
+ **Generated**: ${date}
138
+ **Analysis Period**: Last ${data.planCount} plans
139
+ **Trend**: ${data.trend.toUpperCase()}
140
+
141
+ ---
142
+
143
+ ## Performance Summary
144
+
145
+ | Metric | Current | Target | Status |
146
+ |--------|---------|--------|--------|
147
+ | Avg Accuracy | ${data.avgAccuracy.toFixed(1)}% | >80% | ${data.avgAccuracy >= 80 ? '✅' : '⚠️'} |
148
+ | Avg Iterations | ${data.avgIterations.toFixed(1)} | <2.0 | ${data.avgIterations < 2 ? '✅' : '⚠️'} |
149
+ | Tool Efficiency | ${data.avgEfficiency.toFixed(1)}% | >70% | ${data.avgEfficiency >= 70 ? '✅' : '⚠️'} |
150
+ | Total Cost | ${data.totalCost.toLocaleString()} tokens | Optimize | ${data.totalCost < 100000 ? '✅' : '⚠️'} |
151
+
152
+ ---
153
+
154
+ ## Repeated Patterns Detected (3+ Occurrences)
155
+
156
+ ${data.repeatedPatterns.length > 0 ? data.repeatedPatterns.map((p: AntiPattern) => `### ${p.name}
157
+
158
+ **Occurrences**: ${p.count} times
159
+ **Last Seen**: ${p.lastOccurrence || 'Unknown'}
160
+ **Suggestion**: ${p.suggestion}
161
+
162
+ ---
163
+ `).join('\n') : '✅ No repeated patterns detected! Great job maintaining best practices.'}
164
+
165
+ ## Action Items
166
+
167
+ ${data.repeatedPatterns.length > 0 ? `
168
+ ### Critical (Fix This Week)
169
+ ${data.repeatedPatterns.filter((p: AntiPattern) => p.count >= 5).map((p: AntiPattern) => `- [ ] ${p.name}: ${p.suggestion}`).join('\n') || '- [ ] None'}
170
+
171
+ ### Important (Fix Next Week)
172
+ ${data.repeatedPatterns.filter((p: AntiPattern) => p.count >= 3 && p.count < 5).map((p: AntiPattern) => `- [ ] ${p.name}: ${p.suggestion}`).join('\n') || '- [ ] None'}
173
+ ` : `
174
+ ### Continue Current Practices
175
+ - [ ] Maintain research-first approach
176
+ - [ ] Keep following tool escalation protocol
177
+ - [ ] Continue automatic validation
178
+ `}
179
+
180
+ ---
181
+
182
+ ## Trend Analysis
183
+
184
+ ${data.trend === 'improving' ? '📈 **Performance is IMPROVING** - Keep doing what works!' :
185
+ data.trend === 'declining' ? '📉 **Performance is DECLINING** - Review recent changes and patterns.' :
186
+ '➡️ **Performance is STABLE** - Look for optimization opportunities.'}
187
+
188
+ ---
189
+
190
+ ## Next Review
191
+
192
+ **Scheduled**: In 10 plans or 7 days (whichever comes first)
193
+ **Focus Areas**:
194
+ ${data.repeatedPatterns.length > 0 ? data.repeatedPatterns.slice(0, 3).map((p: AntiPattern) => `- ${p.name}`) : '- Continue monitoring current practices'}
195
+
196
+ ---
197
+
198
+ *This report was automatically generated by the auto-learning-review hook*
199
+ `;
200
+ }
@@ -1,104 +1,60 @@
1
- import { execSync } from "child_process";
2
- import { readFileSync, writeFileSync, existsSync } from "fs";
3
- import { join } from "path";
4
-
5
- interface StopHookInput {
6
- conversation_id: string;
7
- status: "completed" | "aborted" | "error";
8
- loop_count: number;
9
- }
10
-
11
- interface StopHookOutput {
12
- followup_message?: string;
13
- }
14
-
15
- const LINT_REPORT_PATH = join(process.cwd(), ".cursor/lint-report.md");
16
-
17
- // Read input from stdin
18
- const stdin = await new Promise<string>((resolve) => {
19
- let data = "";
20
- process.stdin.on("data", (chunk) => (data += chunk));
21
- process.stdin.on("end", () => resolve(data));
22
- });
23
-
24
- const input: StopHookInput = JSON.parse(stdin);
25
-
26
- // Only lint on completion
27
- if (input.status !== "completed") {
28
- console.log(JSON.stringify({}));
29
- process.exit(0);
30
- }
31
-
32
- // Check if any code changes were made (simplified check)
33
- // In real implementation, would track file changes in conversation
34
- const codeChanged = true; // Assume code changed for now
35
-
36
- if (!codeChanged) {
37
- console.log(JSON.stringify({}));
38
- process.exit(0);
39
- }
40
-
41
- // Run ESLint on changed files
42
- try {
43
- // Try to run ESLint with auto-fix
44
- console.log("Running ESLint with auto-fix...");
45
-
46
- let lintOutput = "";
47
- let hasErrors = false;
48
- let hasWarnings = false;
49
-
50
- try {
51
- // First try auto-fix
52
- lintOutput = execSync("npm run lint -- --fix", {
53
- encoding: "utf-8",
54
- stdio: "pipe",
55
- timeout: 60000 // 60s timeout
56
- }).toString();
57
- } catch (error: any) {
58
- lintOutput = error.stdout?.toString() || error.stderr?.toString() || error.message;
59
- hasErrors = true;
60
- }
61
-
62
- // Check for remaining issues
63
- if (lintOutput.includes("error")) {
64
- hasErrors = true;
65
- }
66
- if (lintOutput.includes("warning")) {
67
- hasWarnings = true;
68
- }
69
-
70
- // Generate lint report
71
- const timestamp = new Date().toISOString();
72
- const report = `## ${timestamp}\n\n### ESLint Results\n\n${lintOutput}\n\n`;
73
-
74
- if (existsSync(LINT_REPORT_PATH)) {
75
- writeFileSync(LINT_REPORT_PATH, report, { flag: 'a' });
76
- } else {
77
- writeFileSync(LINT_REPORT_PATH, `# ESLint Report\n\n${report}`);
78
- }
79
-
80
- // Return appropriate message
81
- if (hasErrors) {
82
- const output: StopHookOutput = {
83
- followup_message: `⚠️ ESLint found errors that couldn't be auto-fixed. Please review and fix manually.`,
84
- };
85
- console.log(JSON.stringify(output));
86
- } else if (hasWarnings) {
87
- const output: StopHookOutput = {
88
- followup_message: `✅ ESLint auto-fixed issues. Some warnings remain (check ${LINT_REPORT_PATH}).`,
89
- };
90
- console.log(JSON.stringify(output));
91
- } else {
92
- const output: StopHookOutput = {
93
- followup_message: `✅ ESLint passed with no issues.`,
94
- };
95
- console.log(JSON.stringify(output));
96
- }
97
-
98
- } catch (error: any) {
99
- // ESLint not available or failed
100
- const output: StopHookOutput = {
101
- followup_message: `⚠️ ESLint check failed: ${error.message}. Continuing without linting.`,
102
- };
103
- console.log(JSON.stringify(output));
104
- }
1
+ // .claude/hooks/auto-lint-fix.ts
2
+ // PostToolUse (Edit|Write|MultiEdit): auto-fix ESLint issues in the edited file
3
+
4
+ import { execFileSync } from "child_process";
5
+ import { writeFileSync, existsSync } from "fs";
6
+ import { join } from "path";
7
+ import { readHookInput, stateDir, done, type ExecError } from "./lib/claude-hook.ts";
8
+
9
+ const LINTABLE = /\.(c|m)?(j|t)sx?$/;
10
+
11
+ const input = await readHookInput();
12
+ const file = input.tool_input?.file_path as string | undefined;
13
+
14
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || input.cwd || process.cwd();
15
+ // Only use the project's own ESLint, never download one
16
+ const eslintBin = join(projectDir, "node_modules", ".bin", process.platform === "win32" ? "eslint.cmd" : "eslint");
17
+
18
+ if (!file || !LINTABLE.test(file) || !existsSync(file) || !existsSync(eslintBin)) done();
19
+
20
+ const LINT_REPORT_PATH = join(stateDir(input), "lint-report.md");
21
+
22
+ let lintOutput: string;
23
+ let hasErrors = false;
24
+
25
+ try {
26
+ lintOutput = execFileSync(eslintBin, ["--fix", file], {
27
+ cwd: projectDir,
28
+ encoding: "utf-8",
29
+ stdio: "pipe",
30
+ timeout: 60000,
31
+ });
32
+ } catch (err) {
33
+ const error = err as ExecError;
34
+ lintOutput = error.stdout?.toString() || error.stderr?.toString() || error.message;
35
+ // ESLint exits 1 on remaining errors; 2 = config error/crash → skip silently
36
+ if (error.status !== 1) done();
37
+ hasErrors = true;
38
+ }
39
+
40
+ if (!hasErrors) {
41
+ if (lintOutput.includes("warning")) {
42
+ console.error(`[Auto Lint Fix] ✓ ${file} auto-fixed, warnings remain`);
43
+ }
44
+ done();
45
+ }
46
+
47
+ const report = `## ${new Date().toISOString()}\n\n### ESLint Results: ${file}\n\n${lintOutput}\n\n`;
48
+ writeFileSync(LINT_REPORT_PATH, existsSync(LINT_REPORT_PATH) ? report : `# ESLint Report\n\n${report}`, {
49
+ flag: existsSync(LINT_REPORT_PATH) ? "a" : "w",
50
+ });
51
+
52
+ // Feed remaining errors back to Claude so it fixes them right away
53
+ console.log(
54
+ JSON.stringify({
55
+ hookSpecificOutput: {
56
+ hookEventName: "PostToolUse",
57
+ additionalContext: `⚠️ ESLint found errors in ${file} that couldn't be auto-fixed:\n${lintOutput.slice(0, 4000)}`,
58
+ },
59
+ }),
60
+ );