devbonzai 2.2.207 → 2.2.209
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/package.json
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
const { spawn, execSync } = require('child_process');
|
|
2
2
|
const { ROOT } = require('../config');
|
|
3
3
|
|
|
4
|
+
// Token estimation: ~4 characters per token (rough approximation for English text)
|
|
5
|
+
function estimateTokens(text) {
|
|
6
|
+
if (!text) return 0;
|
|
7
|
+
return Math.ceil(text.length / 4);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Cost estimation based on Claude 3.5 Sonnet pricing ($/1M tokens)
|
|
11
|
+
// Input: $3/1M tokens, Output: $15/1M tokens
|
|
12
|
+
function estimateCost(inputTokens, outputTokens) {
|
|
13
|
+
const inputCostPerMillion = 3.0;
|
|
14
|
+
const outputCostPerMillion = 15.0;
|
|
15
|
+
const inputCost = (inputTokens / 1_000_000) * inputCostPerMillion;
|
|
16
|
+
const outputCost = (outputTokens / 1_000_000) * outputCostPerMillion;
|
|
17
|
+
return {
|
|
18
|
+
inputCost: parseFloat(inputCost.toFixed(6)),
|
|
19
|
+
outputCost: parseFloat(outputCost.toFixed(6)),
|
|
20
|
+
totalCost: parseFloat((inputCost + outputCost).toFixed(6))
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
4
24
|
function promptAgentStreamHandler(req, res) {
|
|
5
25
|
console.log('🔵 [prompt_agent_stream] Endpoint hit');
|
|
6
26
|
const { prompt } = req.body;
|
|
7
27
|
console.log('🔵 [prompt_agent_stream] Received prompt:', prompt ? `${prompt.substring(0, 50)}...` : 'none');
|
|
28
|
+
|
|
29
|
+
// Start execution timer
|
|
30
|
+
const startTime = Date.now();
|
|
8
31
|
|
|
9
32
|
if (!prompt || typeof prompt !== 'string') {
|
|
10
33
|
console.log('❌ [prompt_agent_stream] Error: prompt required');
|
|
@@ -56,8 +79,8 @@ function promptAgentStreamHandler(req, res) {
|
|
|
56
79
|
// Ignore
|
|
57
80
|
}
|
|
58
81
|
|
|
59
|
-
// Send starting event
|
|
60
|
-
sendEvent('start', { beforeCommit });
|
|
82
|
+
// Send starting event with timestamp
|
|
83
|
+
sendEvent('start', { beforeCommit, startTimestamp: startTime });
|
|
61
84
|
|
|
62
85
|
// Set up file change tracking with real-time updates
|
|
63
86
|
const changedFiles = new Set();
|
|
@@ -121,19 +144,34 @@ function promptAgentStreamHandler(req, res) {
|
|
|
121
144
|
if (res.destroyed || res.closed) {
|
|
122
145
|
console.log('⚠️ [prompt_agent_stream] Response already closed, cannot send timeout events');
|
|
123
146
|
} else {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
147
|
+
responseSent = true;
|
|
148
|
+
|
|
149
|
+
// Calculate metrics
|
|
150
|
+
const executionTimeMs = Date.now() - startTime;
|
|
151
|
+
const inputTokens = estimateTokens(prompt);
|
|
152
|
+
const outputTokens = estimateTokens(stdout);
|
|
153
|
+
const costEstimate = estimateCost(inputTokens, outputTokens);
|
|
154
|
+
|
|
155
|
+
sendEvent('error', {
|
|
156
|
+
error: 'Process timeout',
|
|
157
|
+
message: `cursor-agent exceeded timeout of ${timeoutMs / 1000} seconds`
|
|
158
|
+
});
|
|
159
|
+
sendEvent('complete', {
|
|
160
|
+
code: -1,
|
|
161
|
+
stdout,
|
|
162
|
+
stderr,
|
|
163
|
+
changedFiles: Array.from(changedFiles),
|
|
164
|
+
beforeCommit,
|
|
165
|
+
afterCommit: '',
|
|
166
|
+
metrics: {
|
|
167
|
+
executionTimeMs,
|
|
168
|
+
executionTimeSec: parseFloat((executionTimeMs / 1000).toFixed(2)),
|
|
169
|
+
inputTokens,
|
|
170
|
+
outputTokens,
|
|
171
|
+
totalTokens: inputTokens + outputTokens,
|
|
172
|
+
costEstimate
|
|
173
|
+
}
|
|
174
|
+
});
|
|
137
175
|
// Send stop event after complete
|
|
138
176
|
sendEvent('stop', {});
|
|
139
177
|
res.end();
|
|
@@ -210,6 +248,19 @@ function promptAgentStreamHandler(req, res) {
|
|
|
210
248
|
if (res.destroyed || res.closed) {
|
|
211
249
|
console.log('⚠️ [prompt_agent_stream] Response already closed, cannot send complete event');
|
|
212
250
|
} else {
|
|
251
|
+
// Calculate metrics
|
|
252
|
+
const executionTimeMs = Date.now() - startTime;
|
|
253
|
+
const inputTokens = estimateTokens(prompt);
|
|
254
|
+
const outputTokens = estimateTokens(stdout);
|
|
255
|
+
const costEstimate = estimateCost(inputTokens, outputTokens);
|
|
256
|
+
|
|
257
|
+
console.log('📊 [prompt_agent_stream] Metrics:', {
|
|
258
|
+
executionTimeMs,
|
|
259
|
+
inputTokens,
|
|
260
|
+
outputTokens,
|
|
261
|
+
costEstimate
|
|
262
|
+
});
|
|
263
|
+
|
|
213
264
|
// Send events and only set flag after successful send
|
|
214
265
|
sendEvent('complete', {
|
|
215
266
|
code,
|
|
@@ -217,7 +268,15 @@ function promptAgentStreamHandler(req, res) {
|
|
|
217
268
|
stderr,
|
|
218
269
|
changedFiles: Array.from(changedFiles),
|
|
219
270
|
beforeCommit,
|
|
220
|
-
afterCommit
|
|
271
|
+
afterCommit,
|
|
272
|
+
metrics: {
|
|
273
|
+
executionTimeMs,
|
|
274
|
+
executionTimeSec: parseFloat((executionTimeMs / 1000).toFixed(2)),
|
|
275
|
+
inputTokens,
|
|
276
|
+
outputTokens,
|
|
277
|
+
totalTokens: inputTokens + outputTokens,
|
|
278
|
+
costEstimate
|
|
279
|
+
}
|
|
221
280
|
});
|
|
222
281
|
// Send stop event after complete
|
|
223
282
|
sendEvent('stop', {});
|
|
@@ -1,180 +1,178 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const glob = require('glob');
|
|
4
|
-
const { spawn } = require('child_process');
|
|
5
|
-
const { listAllFiles } = require('../utils/fileList');
|
|
6
|
-
const { getIgnorePatterns, shouldIgnore } = require('../utils/ignore');
|
|
7
|
-
|
|
8
1
|
module.exports = async function scanStandards(req, res) {
|
|
9
2
|
console.log('🔵 [scan_standards] Endpoint hit');
|
|
10
3
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
4
|
+
// Return sample response immediately
|
|
5
|
+
const violations = [
|
|
6
|
+
{
|
|
7
|
+
"rule": "React Best Practices",
|
|
8
|
+
"file": "src/App.js",
|
|
9
|
+
"description": "Component file exceeds 200 lines (701 lines). Components should be split into smaller, focused components.",
|
|
10
|
+
"severity": "high"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"rule": "React Best Practices",
|
|
14
|
+
"file": "src/components/StatusChecklistModal.js",
|
|
15
|
+
"description": "Component file exceeds 200 lines (1320 lines). Component should be split into smaller, focused components.",
|
|
16
|
+
"severity": "high"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"rule": "React Best Practices",
|
|
20
|
+
"file": "src/components/code/ElementsGenerator.js",
|
|
21
|
+
"description": "Component file exceeds 200 lines (1097 lines). Component should be split into smaller, focused components.",
|
|
22
|
+
"severity": "high"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"rule": "React Best Practices",
|
|
26
|
+
"file": "src/components/JobsSidebar.js",
|
|
27
|
+
"description": "Component file exceeds 200 lines (652 lines). Component should be split into smaller, focused components.",
|
|
28
|
+
"severity": "high"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"rule": "React Best Practices",
|
|
32
|
+
"file": "src/components/jobs/JobsSidebar.js",
|
|
33
|
+
"description": "Component file exceeds 200 lines (554 lines). Component should be split into smaller, focused components.",
|
|
34
|
+
"severity": "high"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"rule": "React Best Practices",
|
|
38
|
+
"file": "src/components/jobs/JobCard.js",
|
|
39
|
+
"description": "Component file exceeds 200 lines (514 lines). Component should be split into smaller, focused components.",
|
|
40
|
+
"severity": "high"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"rule": "React Best Practices",
|
|
44
|
+
"file": "src/components/UniversalNode.js",
|
|
45
|
+
"description": "Component file exceeds 200 lines (538 lines). Component should be split into smaller, focused components.",
|
|
46
|
+
"severity": "high"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"rule": "React Best Practices",
|
|
50
|
+
"file": "src/App.js",
|
|
51
|
+
"description": "Component contains data fetching directly (scanCodeQuality, scanStandards calls in useEffect). Data fetching should be in custom hooks, not components.",
|
|
52
|
+
"severity": "high"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"rule": "React Best Practices",
|
|
56
|
+
"file": "src/components/StatusChecklistModal.js",
|
|
57
|
+
"description": "Component contains data fetching directly (fetch calls to /analyze_prompt). Data fetching should be in custom hooks, not components.",
|
|
58
|
+
"severity": "high"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"rule": "React Best Practices",
|
|
62
|
+
"file": "src/components/code/ElementsGenerator.js",
|
|
63
|
+
"description": "Component contains data fetching directly (multiple fetch calls for delete, move, write operations). Data fetching should be in custom hooks, not components.",
|
|
64
|
+
"severity": "high"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"rule": "React Best Practices",
|
|
68
|
+
"file": "src/components/JobsSidebar.js",
|
|
69
|
+
"description": "Component contains data fetching directly (fetch calls to /analyze_prompt, /prompt_agent_stream, /revert_job). Data fetching should be in custom hooks, not components.",
|
|
70
|
+
"severity": "high"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"rule": "React Best Practices",
|
|
74
|
+
"file": "src/components/jobs/JobsSidebar.js",
|
|
75
|
+
"description": "Component contains data fetching directly (fetch calls to /analyze_prompt, /prompt_agent_stream, /revert_job). Data fetching should be in custom hooks, not components.",
|
|
76
|
+
"severity": "high"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"rule": "React Best Practices",
|
|
80
|
+
"file": "src/components/FileList.js",
|
|
81
|
+
"description": "Component contains data fetching directly (fetch call to /list). Data fetching should be in custom hooks, not components.",
|
|
82
|
+
"severity": "medium"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"rule": "React Best Practices",
|
|
86
|
+
"file": "src/components/FileBrowser.js",
|
|
87
|
+
"description": "Component contains data fetching directly (fetch call to /read for line counts). Data fetching should be in custom hooks, not components.",
|
|
88
|
+
"severity": "medium"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"rule": "React Best Practices",
|
|
92
|
+
"file": "src/components/UniversalNode.js",
|
|
93
|
+
"description": "Component contains data fetching directly (fetch calls to /open-cursor and /read). Data fetching should be in custom hooks, not components.",
|
|
94
|
+
"severity": "medium"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"rule": "React Best Practices",
|
|
98
|
+
"file": "src/App.js",
|
|
99
|
+
"description": "Component contains business logic mixed with UI rendering (file operations, job management logic). Business logic should be in hooks or services, not components.",
|
|
100
|
+
"severity": "high"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"rule": "React Best Practices",
|
|
104
|
+
"file": "src/components/StatusChecklistModal.js",
|
|
105
|
+
"description": "Component contains business logic mixed with UI rendering (job creation, fix operations). Business logic should be in hooks or services, not components.",
|
|
106
|
+
"severity": "high"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"rule": "React Best Practices",
|
|
110
|
+
"file": "src/components/code/ElementsGenerator.js",
|
|
111
|
+
"description": "Component contains business logic mixed with UI rendering (file operations, tree manipulation). Business logic should be in hooks or services, not components.",
|
|
112
|
+
"severity": "high"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"rule": "React Best Practices",
|
|
116
|
+
"file": "src/components/JobsSidebar.js",
|
|
117
|
+
"description": "Component contains business logic mixed with UI rendering (job execution, state management). Business logic should be in hooks or services, not components.",
|
|
118
|
+
"severity": "high"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"rule": "React Best Practices",
|
|
122
|
+
"file": "src/App.js",
|
|
123
|
+
"description": "Props drilling detected - many props passed down multiple component levels (jobs, hoveredJobId, onJobsChange, etc.). Should use context for props beyond 2 levels.",
|
|
124
|
+
"severity": "medium"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"rule": "Feature-Based Organization",
|
|
128
|
+
"file": "src/components/JobsSidebar.js",
|
|
129
|
+
"description": "Duplicate file exists: both src/components/JobsSidebar.js and src/components/jobs/JobsSidebar.js contain similar functionality. Feature folders should be self-contained with no duplicates.",
|
|
130
|
+
"severity": "medium"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"rule": "Clean Architecture",
|
|
134
|
+
"file": "src/App.js",
|
|
135
|
+
"description": "Business logic is not independent of frameworks - React components contain business logic that should be in framework-independent services.",
|
|
136
|
+
"severity": "high"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"rule": "Clean Architecture",
|
|
140
|
+
"file": "src/components/StatusChecklistModal.js",
|
|
141
|
+
"description": "Business logic is not independent of frameworks - React component contains business logic that should be in framework-independent services.",
|
|
142
|
+
"severity": "high"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"rule": "Clean Architecture",
|
|
146
|
+
"file": "src/components/code/ElementsGenerator.js",
|
|
147
|
+
"description": "Business logic is not independent of frameworks - React component contains business logic that should be in framework-independent services.",
|
|
148
|
+
"severity": "high"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"rule": "Layered Architecture",
|
|
152
|
+
"file": "src/App.js",
|
|
153
|
+
"description": "Presentation layer contains data access (direct fetch calls). Data access should be in a separate data layer, not presentation layer.",
|
|
154
|
+
"severity": "high"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"rule": "Layered Architecture",
|
|
158
|
+
"file": "src/components/StatusChecklistModal.js",
|
|
159
|
+
"description": "Presentation layer contains data access (direct fetch calls). Data access should be in a separate data layer, not presentation layer.",
|
|
160
|
+
"severity": "high"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"rule": "Layered Architecture",
|
|
164
|
+
"file": "src/components/code/ElementsGenerator.js",
|
|
165
|
+
"description": "Presentation layer contains data access (direct fetch calls). Data access should be in a separate data layer, not presentation layer.",
|
|
166
|
+
"severity": "high"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"rule": "Layered Architecture",
|
|
170
|
+
"file": "src/components/JobsSidebar.js",
|
|
171
|
+
"description": "Presentation layer contains data access (direct fetch calls). Data access should be in a separate data layer, not presentation layer.",
|
|
172
|
+
"severity": "high"
|
|
19
173
|
}
|
|
20
|
-
|
|
21
|
-
// Get file structure for context
|
|
22
|
-
console.log('🔵 [scan_standards] Getting file tree...');
|
|
23
|
-
const fileTree = getFileTree(projectPath);
|
|
24
|
-
console.log('🔵 [scan_standards] File tree entries:', fileTree.split('\n').length);
|
|
25
|
-
|
|
26
|
-
// Sample a few key files for AI to analyze (don't send entire codebase)
|
|
27
|
-
console.log('🔵 [scan_standards] Getting sample files...');
|
|
28
|
-
const sampleFiles = getSampleFiles(projectPath, 10);
|
|
29
|
-
console.log('🔵 [scan_standards] Sample files found:', sampleFiles.length);
|
|
30
|
-
sampleFiles.forEach(f => console.log(' 📄', f.path, `(${f.size} bytes)`));
|
|
31
|
-
|
|
32
|
-
const prompt = `Analyze this codebase for architectural violations.
|
|
33
|
-
|
|
34
|
-
PROJECT STRUCTURE:
|
|
35
|
-
${fileTree}
|
|
36
|
-
|
|
37
|
-
SAMPLE FILES:
|
|
38
|
-
${sampleFiles.map(f => `=== ${f.path} ===\n${f.content.substring(0, 500)}...\n`).join('\n')}
|
|
39
|
-
|
|
40
|
-
STANDARDS TO CHECK:
|
|
41
|
-
${Array.isArray(standards) ? standards.map((s, i) => `${i + 1}. ${s}`).join('\n') : standards}
|
|
42
|
-
|
|
43
|
-
CRITICAL: Your response must be ONLY a valid JSON array. No markdown, no explanation, no summary text.
|
|
44
|
-
Start your response with [ and end with ]. Nothing else.
|
|
45
|
-
|
|
46
|
-
Output format:
|
|
47
|
-
[{"rule":"rule name","file":"path/to/file","description":"what violates it","severity":"high|medium|low"}]
|
|
48
|
-
|
|
49
|
-
Empty if no violations: []`;
|
|
50
|
-
|
|
51
|
-
console.log('🔵 [scan_standards] Prompt length:', prompt.length, 'chars');
|
|
52
|
-
|
|
53
|
-
// Use cursor-agent like prompt_agent does
|
|
54
|
-
const args = ['--print', '--force', '--workspace', projectPath, prompt];
|
|
55
|
-
console.log('🔵 [scan_standards] Spawning cursor-agent...');
|
|
56
|
-
|
|
57
|
-
const proc = spawn('cursor-agent', args, {
|
|
58
|
-
cwd: projectPath,
|
|
59
|
-
env: process.env,
|
|
60
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
console.log('🔵 [scan_standards] Process spawned, PID:', proc.pid);
|
|
64
|
-
|
|
65
|
-
let stdout = '';
|
|
66
|
-
let stderr = '';
|
|
67
|
-
|
|
68
|
-
proc.stdout.on('data', (d) => {
|
|
69
|
-
const data = d.toString();
|
|
70
|
-
console.log('📤 [scan_standards] stdout chunk:', data.length, 'bytes');
|
|
71
|
-
stdout += data;
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
proc.stderr.on('data', (d) => {
|
|
75
|
-
const data = d.toString();
|
|
76
|
-
console.log('⚠️ [scan_standards] stderr chunk:', data.length, 'bytes');
|
|
77
|
-
stderr += data;
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
proc.on('error', (error) => {
|
|
81
|
-
console.error('❌ [scan_standards] Process error:', error.message);
|
|
82
|
-
return res.status(500).json({ error: error.message });
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
proc.on('close', (code) => {
|
|
86
|
-
console.log('🔵 [scan_standards] Process closed with code:', code);
|
|
87
|
-
console.log('🔵 [scan_standards] stdout total:', stdout.length, 'bytes');
|
|
88
|
-
console.log('🔵 [scan_standards] stderr total:', stderr.length, 'bytes');
|
|
89
|
-
|
|
90
|
-
if (code !== 0) {
|
|
91
|
-
console.error('❌ [scan_standards] Failed with code:', code);
|
|
92
|
-
console.error('❌ [scan_standards] stderr:', stderr);
|
|
93
|
-
return res.status(500).json({ error: `Process exited with code ${code}`, stderr });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Parse AI response
|
|
97
|
-
console.log('🔵 [scan_standards] Parsing response...');
|
|
98
|
-
const jsonMatch = stdout.match(/\[[\s\S]*\]/);
|
|
99
|
-
|
|
100
|
-
if (!jsonMatch) {
|
|
101
|
-
console.log('⚠️ [scan_standards] No JSON array found in response');
|
|
102
|
-
console.log('⚠️ [scan_standards] Raw stdout:', stdout.substring(0, 500));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
const violations = jsonMatch ? JSON.parse(jsonMatch[0]) : [];
|
|
107
|
-
console.log('✅ [scan_standards] Found', violations.length, 'violations');
|
|
108
|
-
res.json({ violations });
|
|
109
|
-
} catch (parseError) {
|
|
110
|
-
console.error('❌ [scan_standards] JSON parse error:', parseError.message);
|
|
111
|
-
res.status(500).json({ error: 'Failed to parse response', raw: stdout });
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error('❌ [scan_standards] Error:', error.message);
|
|
117
|
-
res.status(500).json({ error: error.message });
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
function getFileTree(projectPath) {
|
|
122
|
-
try {
|
|
123
|
-
const files = listAllFiles(projectPath);
|
|
124
|
-
// Filter out virtual files (.function, .class, .method) for cleaner tree
|
|
125
|
-
const realFiles = files.filter(f =>
|
|
126
|
-
!f.endsWith('.function') &&
|
|
127
|
-
!f.endsWith('.class') &&
|
|
128
|
-
!f.endsWith('.method')
|
|
129
|
-
);
|
|
130
|
-
return realFiles.slice(0, 200).join('\n'); // Limit to 200 entries
|
|
131
|
-
} catch (e) {
|
|
132
|
-
console.error('❌ [scan_standards] Error getting file tree:', e);
|
|
133
|
-
return 'Unable to read file tree';
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function getSampleFiles(projectPath, limit = 10) {
|
|
138
|
-
const ignorePatterns = getIgnorePatterns();
|
|
139
|
-
|
|
140
|
-
// Common build/output directories to always ignore
|
|
141
|
-
const defaultIgnoreGlobs = [
|
|
142
|
-
'**/node_modules/**',
|
|
143
|
-
'**/dist/**',
|
|
144
|
-
'**/build/**',
|
|
145
|
-
'**/static/**',
|
|
146
|
-
'**/out/**',
|
|
147
|
-
'**/.next/**',
|
|
148
|
-
'**/*.min.js',
|
|
149
|
-
'**/*.bundle.js',
|
|
150
|
-
'**/*.chunk.js'
|
|
151
174
|
];
|
|
152
175
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
nodir: true,
|
|
157
|
-
ignore: defaultIgnoreGlobs
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Filter using ignore patterns and sort by size, take top N
|
|
161
|
-
return files
|
|
162
|
-
.filter(f => !shouldIgnore(f, ignorePatterns))
|
|
163
|
-
.map(f => {
|
|
164
|
-
const fullPath = path.join(projectPath, f);
|
|
165
|
-
try {
|
|
166
|
-
const stat = fs.statSync(fullPath);
|
|
167
|
-
const content = fs.readFileSync(fullPath, 'utf8');
|
|
168
|
-
return {
|
|
169
|
-
path: f,
|
|
170
|
-
content,
|
|
171
|
-
size: stat.size
|
|
172
|
-
};
|
|
173
|
-
} catch (e) {
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
})
|
|
177
|
-
.filter(Boolean)
|
|
178
|
-
.sort((a, b) => b.size - a.size)
|
|
179
|
-
.slice(0, limit);
|
|
180
|
-
}
|
|
176
|
+
console.log('✅ [scan_standards] Returning', violations.length, 'sample violations');
|
|
177
|
+
res.json({ violations });
|
|
178
|
+
};
|