claudify-agent 0.0.0-stage → 1.0.1

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 (48) hide show
  1. package/.claude/QUICK-REFERENCE.md +213 -0
  2. package/.claude/cost-dashboard.md +162 -0
  3. package/.claude/docs/COST-OPTIMIZATION.md +403 -0
  4. package/.claude/docs/MCP-INTEGRATION-GUIDE.md +348 -0
  5. package/.claude/docs/PLAN-MODE-OPTIMIZATION.md +836 -0
  6. package/.claude/hooks/auto-learning-review.ts +195 -0
  7. package/.claude/hooks/auto-lint-fix.ts +104 -0
  8. package/.claude/hooks/auto-security-review.ts +354 -0
  9. package/.claude/hooks/auto-validate.ts +348 -0
  10. package/.claude/hooks/plan-quality-tracker.ts +719 -0
  11. package/.claude/rules/anti-patterns.md +22 -0
  12. package/.claude/rules/context-tier-1.md +41 -0
  13. package/.claude/rules/context-tier-2.md +285 -0
  14. package/.claude/rules/general.md +25 -0
  15. package/.claude/rules/mcp-auto-use.md +46 -0
  16. package/.claude/rules/parallel-exploration-auto.md +31 -0
  17. package/.claude/rules/plan-mode-execution.md +289 -0
  18. package/.claude/rules/plan-mode-research.md +34 -0
  19. package/.claude/rules/plan-mode-structure.md +166 -0
  20. package/.claude/rules/plan-mode-validation.md +130 -0
  21. package/.claude/rules/security-critical.md +19 -0
  22. package/.claude/rules/tool-auto-selection.md +45 -0
  23. package/.claude/rules/tool-strategy.md +22 -0
  24. package/.claude/scripts/auto-validate.js +69 -0
  25. package/.claude/scripts/security-scan.js +45 -0
  26. package/.claude/security-dashboard.md +142 -0
  27. package/.claude/skills/backend-concurrency/SKILL.md +203 -0
  28. package/.claude/skills/debug-optimizer/SKILL.md +399 -0
  29. package/.claude/skills/debug-optimizer.md +12 -0
  30. package/.claude/skills/golang-guide/SKILL.md +177 -0
  31. package/.claude/skills/learning-loop/SKILL.md +308 -0
  32. package/.claude/skills/mcp-mastery/SKILL.md +214 -0
  33. package/.claude/skills/nestjs-guide/SKILL.md +197 -0
  34. package/.claude/skills/nextjs-guide/SKILL.md +687 -0
  35. package/.claude/skills/nextjs.md +13 -0
  36. package/.claude/skills/parallel-exploration/SKILL.md +193 -0
  37. package/.claude/skills/plan-mode-mastery/SKILL.md +263 -0
  38. package/.claude/skills/python-guide/SKILL.md +822 -0
  39. package/.claude/skills/react-guide/SKILL.md +762 -0
  40. package/.claude/skills/react.md +12 -0
  41. package/.claude/skills/security.md +18 -0
  42. package/.claude/skills/solidity-guide/SKILL.md +32 -0
  43. package/.claude/skills/tool-selection/SKILL.md +428 -0
  44. package/.claude/skills/vite-guide/SKILL.md +445 -0
  45. package/CLAUDE.md +49 -0
  46. package/README.md +65 -2
  47. package/bin/cli.js +50 -0
  48. package/package.json +44 -4
@@ -0,0 +1,195 @@
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
+ }
@@ -0,0 +1,104 @@
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
+ }
@@ -0,0 +1,354 @@
1
+ // .cursor/hooks/auto-security-review.ts
2
+ // Automatic security vulnerability scanning for AI-generated code
3
+
4
+ import { readFileSync, writeFileSync, existsSync } from "fs";
5
+ import { join } from "path";
6
+
7
+ export {};
8
+
9
+ interface SecurityHookInput {
10
+ conversation_id: string;
11
+ status: "completed" | "aborted" | "error";
12
+ loop_count: number;
13
+ }
14
+
15
+ interface SecurityFinding {
16
+ severity: "critical" | "high" | "medium" | "low";
17
+ type: string;
18
+ description: string;
19
+ location?: string;
20
+ suggestion: string;
21
+ }
22
+
23
+ interface SecurityConfig {
24
+ enableSecurityReview?: boolean;
25
+ severityThreshold?: "critical" | "high" | "medium" | "low";
26
+ scanPatterns?: {
27
+ hardcodedCredentials?: boolean;
28
+ sqlInjection?: boolean;
29
+ xss?: boolean;
30
+ insecureCrypto?: boolean;
31
+ pathTraversal?: boolean;
32
+ commandInjection?: boolean;
33
+ };
34
+ }
35
+
36
+ // Security pattern definitions (OWASP Top 10 inspired)
37
+ const SECURITY_PATTERNS = {
38
+ hardcodedCredentials: [
39
+ {
40
+ pattern: /(password|passwd|pwd)\s*[=:]\s*["'][^"']+["']/gi,
41
+ type: "Hardcoded Password",
42
+ severity: "critical" as const,
43
+ suggestion: "Use environment variables or secure secret management (e.g., AWS Secrets Manager, Azure Key Vault)"
44
+ },
45
+ {
46
+ pattern: /(api[_-]?key|apikey)\s*[=:]\s*["'][^"']+["']/gi,
47
+ type: "Hardcoded API Key",
48
+ severity: "critical" as const,
49
+ suggestion: "Store API keys in environment variables or secure vault"
50
+ },
51
+ {
52
+ pattern: /(secret|token|auth)\s*[=:]\s*["'][^"']+["']/gi,
53
+ type: "Hardcoded Secret/Token",
54
+ severity: "critical" as const,
55
+ suggestion: "Use environment variables for secrets"
56
+ },
57
+ {
58
+ pattern: /AWS[_A-Z]*KEY|AWS[_A-Z]*SECRET/gi,
59
+ type: "AWS Credentials",
60
+ severity: "critical" as const,
61
+ suggestion: "Use IAM roles or AWS Secrets Manager"
62
+ }
63
+ ],
64
+ sqlInjection: [
65
+ {
66
+ pattern: /(execute|query|raw)\s*\(\s*`[^`]*\$\{[^}]+\}/gi,
67
+ type: "Potential SQL Injection",
68
+ severity: "critical" as const,
69
+ suggestion: "Use parameterized queries or ORM methods instead of string interpolation"
70
+ },
71
+ {
72
+ pattern: /execute\s*\(\s*["'][^"']*["']\s*\+/gi,
73
+ type: "SQL String Concatenation",
74
+ severity: "critical" as const,
75
+ suggestion: "Use parameterized queries"
76
+ }
77
+ ],
78
+ xss: [
79
+ {
80
+ pattern: /dangerouslySetInnerHTML\s*=/gi,
81
+ type: "Dangerous HTML Rendering",
82
+ severity: "high" as const,
83
+ suggestion: "Avoid dangerouslySetInnerHTML or sanitize input with DOMPurify"
84
+ },
85
+ {
86
+ pattern: /innerHTML\s*=/gi,
87
+ type: "Inner HTML Assignment",
88
+ severity: "high" as const,
89
+ suggestion: "Use textContent or sanitize HTML before assignment"
90
+ },
91
+ {
92
+ pattern: /eval\s*\(/gi,
93
+ type: "Eval Usage",
94
+ severity: "high" as const,
95
+ suggestion: "Avoid eval() - use safer alternatives"
96
+ }
97
+ ],
98
+ insecureCrypto: [
99
+ {
100
+ pattern: /crypto\s*\.\s*createHash\s*\(\s*["']md5["']/gi,
101
+ type: "Weak Hash Algorithm (MD5)",
102
+ severity: "high" as const,
103
+ suggestion: "Use SHA-256 or stronger (crypto.createHash('sha256'))"
104
+ },
105
+ {
106
+ pattern: /crypto\s*\.\s*createHash\s*\(\s*["']sha1["']/gi,
107
+ type: "Weak Hash Algorithm (SHA1)",
108
+ severity: "medium" as const,
109
+ suggestion: "Use SHA-256 or stronger for security-critical applications"
110
+ },
111
+ {
112
+ pattern: /Math\.random\s*\(/gi,
113
+ type: "Insecure Random Number Generator",
114
+ severity: "medium" as const,
115
+ suggestion: "Use crypto.randomBytes() for security-sensitive randomness"
116
+ }
117
+ ],
118
+ pathTraversal: [
119
+ {
120
+ pattern: /fs\s*\.\s*(readFile|writeFile|appendFile)\s*\([^)]*\+/gi,
121
+ type: "Potential Path Traversal",
122
+ severity: "high" as const,
123
+ suggestion: "Validate and sanitize file paths, use path.resolve() and path.normalize()"
124
+ },
125
+ {
126
+ pattern: /require\s*\(\s*[^)]*\+/gi,
127
+ type: "Dynamic Require with User Input",
128
+ severity: "high" as const,
129
+ suggestion: "Avoid dynamic require with user-controlled paths"
130
+ }
131
+ ],
132
+ commandInjection: [
133
+ {
134
+ pattern: /exec\s*\(\s*`[^`]*\$\{[^}]+\}/gi,
135
+ type: "Command Injection via Template Literal",
136
+ severity: "critical" as const,
137
+ suggestion: "Avoid shell commands with user input, use execFile with arguments array"
138
+ },
139
+ {
140
+ pattern: /spawn\s*\(\s*[^)]*\+/gi,
141
+ type: "Command Injection via String Concatenation",
142
+ severity: "critical" as const,
143
+ suggestion: "Use spawn with arguments array instead of string concatenation"
144
+ },
145
+ {
146
+ pattern: /child_process\s*\.\s*exec\s*\(/gi,
147
+ type: "Unsafe exec Usage",
148
+ severity: "medium" as const,
149
+ suggestion: "Prefer execFile or spawn with argument separation"
150
+ }
151
+ ]
152
+ };
153
+
154
+ // Read input from stdin
155
+ const stdin = await new Promise<string>((resolve) => {
156
+ let data = "";
157
+ process.stdin.on("data", (chunk) => (data += chunk));
158
+ process.stdin.on("end", () => resolve(data));
159
+ });
160
+
161
+ const input: SecurityHookInput = JSON.parse(stdin);
162
+
163
+ // Load configuration
164
+ const configPath = join(process.cwd(), '.cursor/hooks.config.json');
165
+ const config: SecurityConfig = existsSync(configPath)
166
+ ? JSON.parse(readFileSync(configPath, 'utf-8'))
167
+ : {};
168
+
169
+ const securityConfig = {
170
+ enableSecurityReview: (config as any).validation?.enableSecurityReview !== false,
171
+ severityThreshold: (config as any).validation?.securitySeverityThreshold || "high",
172
+ scanPatterns: (config as any).validation?.securityScanPatterns || {
173
+ hardcodedCredentials: true,
174
+ sqlInjection: true,
175
+ xss: true,
176
+ insecureCrypto: true,
177
+ pathTraversal: true,
178
+ commandInjection: true
179
+ }
180
+ };
181
+
182
+ // Only run on completion
183
+ if (input.status !== "completed") {
184
+ console.log(JSON.stringify({}));
185
+ process.exit(0);
186
+ }
187
+
188
+ if (!securityConfig.enableSecurityReview) {
189
+ console.error("[Security Review] ⚠ Security review disabled in config");
190
+ console.log(JSON.stringify({}));
191
+ process.exit(0);
192
+ }
193
+
194
+ console.error("[Security Review] Running automatic security scan...");
195
+
196
+ const findings: SecurityFinding[] = [];
197
+
198
+ // Try to scan changed files
199
+ const conversationFile = join(process.cwd(), `.cursor/conversations/${input.conversation_id}.json`);
200
+ let conversation;
201
+ try {
202
+ if (existsSync(conversationFile)) {
203
+ conversation = JSON.parse(readFileSync(conversationFile, 'utf-8'));
204
+ }
205
+ } catch {
206
+ console.error("[Security Review] Could not read conversation file, proceeding with scan");
207
+ conversation = null;
208
+ }
209
+
210
+ // Extract code from conversation messages
211
+ function extractCodeFromConversation(conv: any): string[] {
212
+ const codeBlocks: string[] = [];
213
+
214
+ if (!conv?.messages) return codeBlocks;
215
+
216
+ for (const message of conv.messages) {
217
+ if (message.content) {
218
+ // Extract code blocks
219
+ const codeBlockRegex = /```(?:typescript|javascript|tsx|ts|js)?\n([\s\S]*?)```/g;
220
+ let match;
221
+ while ((match = codeBlockRegex.exec(message.content)) !== null) {
222
+ codeBlocks.push(match[1]);
223
+ }
224
+ }
225
+ }
226
+
227
+ return codeBlocks;
228
+ }
229
+
230
+ // Scan code for security issues
231
+ function scanCodeForSecurity(code: string, patternCategory: string): SecurityFinding[] {
232
+ const categoryFindings: SecurityFinding[] = [];
233
+ const patterns = SECURITY_PATTERNS[patternCategory as keyof typeof SECURITY_PATTERNS];
234
+
235
+ if (!patterns || !securityConfig.scanPatterns[patternCategory as keyof typeof securityConfig.scanPatterns]) {
236
+ return categoryFindings;
237
+ }
238
+
239
+ for (const { pattern, type, severity, suggestion } of patterns) {
240
+ const matches = code.match(pattern);
241
+ if (matches) {
242
+ categoryFindings.push({
243
+ severity,
244
+ type,
245
+ description: `Found ${matches.length} occurrence(s) of potentially insecure pattern`,
246
+ suggestion
247
+ });
248
+ }
249
+ }
250
+
251
+ return categoryFindings;
252
+ }
253
+
254
+ // Scan all pattern categories
255
+ const codeToScan = conversation ? extractCodeFromConversation(conversation) : [];
256
+
257
+ // If no code extracted, skip scan
258
+ if (codeToScan.length === 0) {
259
+ console.error("[Security Review] ⚠ No code blocks found to scan");
260
+ console.log(JSON.stringify({}));
261
+ process.exit(0);
262
+ }
263
+
264
+ const combinedCode = codeToScan.join('\n');
265
+
266
+ // Run security scans
267
+ const patternCategories = Object.keys(SECURITY_PATTERNS);
268
+ for (const category of patternCategories) {
269
+ const categoryFindings = scanCodeForSecurity(combinedCode, category);
270
+ findings.push(...categoryFindings);
271
+ }
272
+
273
+ // Filter by severity threshold
274
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
275
+ const thresholdLevel = severityOrder[securityConfig.severityThreshold];
276
+
277
+ const filteredFindings = findings.filter(
278
+ finding => severityOrder[finding.severity] <= thresholdLevel
279
+ );
280
+
281
+ // Generate security report
282
+ const criticalCount = filteredFindings.filter(f => f.severity === "critical").length;
283
+ const highCount = filteredFindings.filter(f => f.severity === "high").length;
284
+ const mediumCount = filteredFindings.filter(f => f.severity === "medium").length;
285
+ const lowCount = filteredFindings.filter(f => f.severity === "low").length;
286
+
287
+ const reportPath = join(process.cwd(), '.cursor/security-report.md');
288
+
289
+ const securityReport = `## Security Scan Report
290
+
291
+ Generated: ${new Date().toISOString()}
292
+ Conversation: ${input.conversation_id}
293
+ Severity Threshold: ${securityConfig.severityThreshold}
294
+
295
+ ## Summary
296
+
297
+ - 🔴 Critical: ${criticalCount}
298
+ - 🟠 High: ${highCount}
299
+ - 🟡 Medium: ${mediumCount}
300
+ - 🟢 Low: ${lowCount}
301
+ - **Total Findings**: ${filteredFindings.length}
302
+
303
+ ## Findings
304
+
305
+ ${filteredFindings.length === 0 ? '✅ No security issues found!' : filteredFindings.map((finding, i) => `
306
+ ### ${i + 1}. ${finding.type}
307
+
308
+ **Severity**: ${finding.severity.toUpperCase()}
309
+
310
+ **Description**: ${finding.description}
311
+
312
+ **Recommendation**: ${finding.suggestion}
313
+
314
+ ---
315
+ `).join('\n')}
316
+
317
+ ## Security Checklist (OWASP Top 10)
318
+
319
+ - [ ] All user input validated and sanitized
320
+ - [ ] SQL injection prevented (parameterized queries)
321
+ - [ ] XSS prevented (escaping/sanitization)
322
+ - [ ] Authentication/authorization verified
323
+ - [ ] No hardcoded credentials or secrets
324
+ - [ ] Error handling doesn't leak sensitive info
325
+ - [ ] Rate limiting on auth endpoints
326
+ - [ ] Secure random number generation used
327
+ - [ ] Strong cryptographic algorithms used
328
+ - [ ] File paths validated and normalized
329
+
330
+ ## Next Steps
331
+
332
+ ${criticalCount > 0 ? '🚨 CRITICAL issues found! Address immediately before deployment.' :
333
+ highCount > 0 ? '⚠️ HIGH severity issues found. Review and fix before production.' :
334
+ mediumCount > 0 ? '📋 Medium severity issues found. Schedule for next sprint.' :
335
+ lowCount > 0 ? '💡 Low severity issues found. Consider fixing for best practices.' :
336
+ '✅ No security issues detected at current threshold.'}
337
+ `;
338
+
339
+ writeFileSync(reportPath, securityReport);
340
+ console.error(`[Security Review] Report saved to ${reportPath}`);
341
+
342
+ // Provide feedback to user
343
+ if (criticalCount > 0 || highCount > 0) {
344
+ const output = {
345
+ followup_message: `🚨 Security scan found ${criticalCount} critical and ${highCount} high severity issues. See \`.cursor/security-report.md\` for details.`,
346
+ };
347
+ console.log(JSON.stringify(output));
348
+ } else if (filteredFindings.length > 0) {
349
+ console.error(`[Security Review] ⚠️ Found ${filteredFindings.length} security finding(s). Report saved.`);
350
+ console.log(JSON.stringify({}));
351
+ } else {
352
+ console.error("[Security Review] ✓ No security issues found");
353
+ console.log(JSON.stringify({}));
354
+ }