ai-first-cli 1.3.6 → 1.3.8
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/CHANGELOG.md +123 -0
- package/README.es.md +14 -1
- package/README.md +14 -1
- package/ai/graph/knowledge-graph.json +1 -1
- package/ai-context/index-state.json +86 -2
- package/dist/analyzers/techStack.d.ts.map +1 -1
- package/dist/analyzers/techStack.js +43 -0
- package/dist/analyzers/techStack.js.map +1 -1
- package/dist/commands/ai-first.d.ts.map +1 -1
- package/dist/commands/ai-first.js +78 -4
- package/dist/commands/ai-first.js.map +1 -1
- package/dist/config/configLoader.d.ts +6 -0
- package/dist/config/configLoader.d.ts.map +1 -0
- package/dist/config/configLoader.js +232 -0
- package/dist/config/configLoader.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +101 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/core/content/contentProcessor.d.ts +4 -0
- package/dist/core/content/contentProcessor.d.ts.map +1 -0
- package/dist/core/content/contentProcessor.js +235 -0
- package/dist/core/content/contentProcessor.js.map +1 -0
- package/dist/core/content/index.d.ts +3 -0
- package/dist/core/content/index.d.ts.map +1 -0
- package/dist/core/content/index.js +2 -0
- package/dist/core/content/index.js.map +1 -0
- package/dist/core/content/types.d.ts +32 -0
- package/dist/core/content/types.d.ts.map +1 -0
- package/dist/core/content/types.js +2 -0
- package/dist/core/content/types.js.map +1 -0
- package/dist/core/gitAnalyzer.d.ts +14 -0
- package/dist/core/gitAnalyzer.d.ts.map +1 -1
- package/dist/core/gitAnalyzer.js +98 -0
- package/dist/core/gitAnalyzer.js.map +1 -1
- package/dist/core/multiRepo/index.d.ts +3 -0
- package/dist/core/multiRepo/index.d.ts.map +1 -0
- package/dist/core/multiRepo/index.js +2 -0
- package/dist/core/multiRepo/index.js.map +1 -0
- package/dist/core/multiRepo/multiRepoScanner.d.ts +18 -0
- package/dist/core/multiRepo/multiRepoScanner.d.ts.map +1 -0
- package/dist/core/multiRepo/multiRepoScanner.js +131 -0
- package/dist/core/multiRepo/multiRepoScanner.js.map +1 -0
- package/dist/core/rag/index.d.ts +3 -0
- package/dist/core/rag/index.d.ts.map +1 -0
- package/dist/core/rag/index.js +2 -0
- package/dist/core/rag/index.js.map +1 -0
- package/dist/core/rag/vectorIndex.d.ts +28 -0
- package/dist/core/rag/vectorIndex.d.ts.map +1 -0
- package/dist/core/rag/vectorIndex.js +71 -0
- package/dist/core/rag/vectorIndex.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +154 -0
- package/dist/mcp/server.js.map +1 -0
- package/docs/planning/evaluator-v1.0.0/README.md +112 -0
- package/docs/planning/evaluator-v1.0.0/improvements_plan_2026-03-28.md +237 -0
- package/package.json +13 -3
- package/src/analyzers/techStack.ts +47 -1
- package/src/commands/ai-first.ts +83 -4
- package/src/config/configLoader.ts +274 -0
- package/src/config/index.ts +27 -0
- package/src/config/types.ts +117 -0
- package/src/core/content/contentProcessor.ts +292 -0
- package/src/core/content/index.ts +9 -0
- package/src/core/content/types.ts +35 -0
- package/src/core/gitAnalyzer.ts +130 -0
- package/src/core/multiRepo/index.ts +2 -0
- package/src/core/multiRepo/multiRepoScanner.ts +177 -0
- package/src/core/rag/index.ts +2 -0
- package/src/core/rag/vectorIndex.ts +105 -0
- package/src/mcp/index.ts +1 -0
- package/src/mcp/server.ts +179 -0
- package/tests/v1.3.8-integration.test.ts +361 -0
- package/ai-context-evaluation-report-1774223059505.md +0 -206
- package/scripts/ai-context-evaluator.ts +0 -440
|
@@ -1,440 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ai-context-evaluator.ts
|
|
5
|
-
*
|
|
6
|
-
* Evalúa la calidad de ai-context/ usando 3 modelos de AI (Kimi, GLM, MiniMax)
|
|
7
|
-
* en paralelo por proyecto, procesando secuencialmente para evitar timeouts.
|
|
8
|
-
*
|
|
9
|
-
* Proyectos evaluados:
|
|
10
|
-
* - PRIORIDAD: salesforce-cli (especial interés)
|
|
11
|
-
* - Soportados: ai-first, express-api, nestjs-backend, python-cli, spring-boot-app
|
|
12
|
-
* - No soportados: android-kotlin-app, ios-swift-app, go-microservice, rust-cli, php-vanilla
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { readFileSync, readdirSync, statSync, existsSync, writeFileSync } from 'fs';
|
|
16
|
-
import { join, basename } from 'path';
|
|
17
|
-
import { exec } from 'child_process';
|
|
18
|
-
import { promisify } from 'util';
|
|
19
|
-
|
|
20
|
-
const execAsync = promisify(exec);
|
|
21
|
-
|
|
22
|
-
// Configuration
|
|
23
|
-
const CONFIG = {
|
|
24
|
-
opencodeApiKey: process.env.OPENCODE_API_KEY || '',
|
|
25
|
-
minimaxApiKey: process.env.MINIMAX_API_KEY || '',
|
|
26
|
-
timeout: 120000, // 2 minutes per call
|
|
27
|
-
maxRetries: 3,
|
|
28
|
-
retryDelay: 2000,
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// Projects to evaluate (mix of supported and unsupported)
|
|
32
|
-
const PROJECTS = [
|
|
33
|
-
// PRIORITY: Salesforce (special interest)
|
|
34
|
-
{ path: 'test-projects/salesforce-cli', name: 'salesforce-cli', type: 'priority' },
|
|
35
|
-
// Supported
|
|
36
|
-
{ path: '.', name: 'ai-first-cli', type: 'supported' },
|
|
37
|
-
{ path: 'test-projects/express-api', name: 'express-api', type: 'supported' },
|
|
38
|
-
{ path: 'test-projects/nestjs-backend', name: 'nestjs-backend', type: 'supported' },
|
|
39
|
-
{ path: 'test-projects/python-cli', name: 'python-cli', type: 'supported' },
|
|
40
|
-
{ path: 'test-projects/spring-boot-app', name: 'spring-boot-app', type: 'supported' },
|
|
41
|
-
// Unsupported
|
|
42
|
-
{ path: 'test-projects/android-kotlin-app', name: 'android-kotlin-app', type: 'unsupported' },
|
|
43
|
-
{ path: 'test-projects/ios-swift-app', name: 'ios-swift-app', type: 'unsupported' },
|
|
44
|
-
{ path: 'test-projects/go-microservice', name: 'go-microservice', type: 'unsupported' },
|
|
45
|
-
{ path: 'test-projects/rust-cli', name: 'rust-cli', type: 'unsupported' },
|
|
46
|
-
{ path: 'test-projects/php-vanilla', name: 'php-vanilla', type: 'unsupported' },
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
interface EvaluationResult {
|
|
50
|
-
model: string;
|
|
51
|
-
perspective: string;
|
|
52
|
-
score: number;
|
|
53
|
-
feedback: string;
|
|
54
|
-
improvements: string[];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface ProjectEvaluation {
|
|
58
|
-
projectName: string;
|
|
59
|
-
projectType: 'priority' | 'supported' | 'unsupported';
|
|
60
|
-
aiContextPath: string;
|
|
61
|
-
hasIndexDb: boolean;
|
|
62
|
-
results: EvaluationResult[];
|
|
63
|
-
summary: string;
|
|
64
|
-
synthesizedImprovements: string[];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Read ai-context files and create evaluation prompt
|
|
69
|
-
*/
|
|
70
|
-
function createEvaluationPrompt(projectPath: string, projectName: string): string {
|
|
71
|
-
const aiContextPath = join(projectPath, 'ai-context');
|
|
72
|
-
|
|
73
|
-
if (!existsSync(aiContextPath)) {
|
|
74
|
-
return `ERROR: No ai-context directory found for ${projectName}`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Read key files
|
|
78
|
-
const files: Record<string, string> = {};
|
|
79
|
-
const keyFiles = [
|
|
80
|
-
'summary.md',
|
|
81
|
-
'architecture.md',
|
|
82
|
-
'tech_stack.md',
|
|
83
|
-
'repo_map.md',
|
|
84
|
-
'ai_context.md',
|
|
85
|
-
'entrypoints.md',
|
|
86
|
-
];
|
|
87
|
-
|
|
88
|
-
for (const file of keyFiles) {
|
|
89
|
-
const filePath = join(aiContextPath, file);
|
|
90
|
-
if (existsSync(filePath)) {
|
|
91
|
-
try {
|
|
92
|
-
files[file] = readFileSync(filePath, 'utf8').substring(0, 5000); // Limit size
|
|
93
|
-
} catch (e) {
|
|
94
|
-
files[file] = `[Error reading ${file}: ${e}]`;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Check for index.db
|
|
100
|
-
const hasIndexDb = existsSync(join(aiContextPath, 'index.db'));
|
|
101
|
-
const hasFeatures = existsSync(join(aiContextPath, 'context', 'features'));
|
|
102
|
-
const hasFlows = existsSync(join(aiContextPath, 'context', 'flows'));
|
|
103
|
-
|
|
104
|
-
return `
|
|
105
|
-
EVALUATE THIS AI-CONTEXT DIRECTORY:
|
|
106
|
-
|
|
107
|
-
PROJECT: ${projectName}
|
|
108
|
-
INDEX DB: ${hasIndexDb ? 'YES' : 'NO'}
|
|
109
|
-
FEATURES: ${hasFeatures ? 'YES' : 'NO'}
|
|
110
|
-
FLOWS: ${hasFlows ? 'YES' : 'NO'}
|
|
111
|
-
|
|
112
|
-
KEY FILES:
|
|
113
|
-
${Object.entries(files).map(([name, content]) => `=== ${name} ===\n${content}`).join('\n')}
|
|
114
|
-
|
|
115
|
-
TASK: Evaluate from 1-5 (5=excellent) on:
|
|
116
|
-
1. CLARITY - Clear and non-redundant?
|
|
117
|
-
2. COMPLETENESS - Has everything needed?
|
|
118
|
-
3. STRUCTURE - Easy for LLM to parse?
|
|
119
|
-
4. ACTIONABILITY - Can AI use it to code/decide?
|
|
120
|
-
5. IMPROVEMENTS - What changes would help?
|
|
121
|
-
|
|
122
|
-
REQUIRED RESPONSE:
|
|
123
|
-
- ONLY return valid JSON, no other text
|
|
124
|
-
- Start with { and end with }
|
|
125
|
-
- Include all fields
|
|
126
|
-
|
|
127
|
-
EXAMPLE (follow this exact format):
|
|
128
|
-
{"clarity":{"score":4,"feedback":"clear","improvements":["item1"]},"completeness":{"score":3,"feedback":"missing X","improvements":["item2"]},"structure":{"score":4,"feedback":"good","improvements":[]},"actionability":{"score":3,"feedback":"needs Y","improvements":["item3"]},"overall_score":3.5,"overall_feedback":"overall good","top_3_improvements":["fix A","add B","remove C"]}
|
|
129
|
-
`.trim();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Call OpenCode API (Kimi or GLM)
|
|
134
|
-
*/
|
|
135
|
-
async function callOpenCode(model: string, prompt: string): Promise<any> {
|
|
136
|
-
const data = {
|
|
137
|
-
model: model,
|
|
138
|
-
messages: [{ role: 'user', content: prompt }],
|
|
139
|
-
temperature: 0.3,
|
|
140
|
-
max_tokens: 8000,
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const curlCommand = `curl -s --max-time ${CONFIG.timeout / 1000} -X POST "https://opencode.ai/zen/go/v1/chat/completions" \
|
|
144
|
-
-H "Content-Type: application/json" \
|
|
145
|
-
-H "Authorization: Bearer ${CONFIG.opencodeApiKey}" \
|
|
146
|
-
--data-raw '${JSON.stringify(data).replace(/'/g, "'\\''")}'`;
|
|
147
|
-
|
|
148
|
-
for (let attempt = 1; attempt <= CONFIG.maxRetries; attempt++) {
|
|
149
|
-
try {
|
|
150
|
-
const { stdout } = await execAsync(curlCommand, { timeout: CONFIG.timeout });
|
|
151
|
-
const response = JSON.parse(stdout);
|
|
152
|
-
|
|
153
|
-
if (response.choices && response.choices[0]?.message?.content) {
|
|
154
|
-
const content = response.choices[0].message.content;
|
|
155
|
-
// Try to parse JSON from response
|
|
156
|
-
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
157
|
-
if (jsonMatch) {
|
|
158
|
-
return JSON.parse(jsonMatch[0]);
|
|
159
|
-
}
|
|
160
|
-
return { raw_response: content };
|
|
161
|
-
}
|
|
162
|
-
throw new Error('Invalid response format');
|
|
163
|
-
} catch (error) {
|
|
164
|
-
if (attempt === CONFIG.maxRetries) {
|
|
165
|
-
console.error(`OpenCode ${model} failed after ${CONFIG.maxRetries} attempts:`, error);
|
|
166
|
-
return { error: error.message };
|
|
167
|
-
}
|
|
168
|
-
await new Promise(resolve => setTimeout(resolve, CONFIG.retryDelay * attempt));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Call MiniMax API
|
|
175
|
-
*/
|
|
176
|
-
async function callMiniMax(prompt: string): Promise<any> {
|
|
177
|
-
const data = {
|
|
178
|
-
model: 'minimax-coding-plan/MiniMax-M2.7',
|
|
179
|
-
max_tokens: 8000,
|
|
180
|
-
temperature: 0.3,
|
|
181
|
-
messages: [{ role: 'user', content: prompt }],
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const curlCommand = `curl -s --max-time ${CONFIG.timeout / 1000} -X POST "https://api.minimax.io/anthropic/v1/messages" \
|
|
185
|
-
-H "Content-Type: application/json" \
|
|
186
|
-
-H "Authorization: Bearer ${CONFIG.minimaxApiKey}" \
|
|
187
|
-
--data-raw '${JSON.stringify(data).replace(/'/g, "'\\''")}'`;
|
|
188
|
-
|
|
189
|
-
for (let attempt = 1; attempt <= CONFIG.maxRetries; attempt++) {
|
|
190
|
-
try {
|
|
191
|
-
const { stdout } = await execAsync(curlCommand, { timeout: CONFIG.timeout });
|
|
192
|
-
const response = JSON.parse(stdout);
|
|
193
|
-
|
|
194
|
-
if (response.content && Array.isArray(response.content)) {
|
|
195
|
-
const textObj = response.content.find((c: any) => c.type === 'text');
|
|
196
|
-
const content = textObj?.text || response.content[0]?.text || '';
|
|
197
|
-
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
198
|
-
if (jsonMatch) {
|
|
199
|
-
return JSON.parse(jsonMatch[0]);
|
|
200
|
-
}
|
|
201
|
-
return { raw_response: content };
|
|
202
|
-
}
|
|
203
|
-
throw new Error('Invalid response format');
|
|
204
|
-
} catch (error) {
|
|
205
|
-
if (attempt === CONFIG.maxRetries) {
|
|
206
|
-
console.error('MiniMax failed after retries:', error);
|
|
207
|
-
return { error: error.message };
|
|
208
|
-
}
|
|
209
|
-
await new Promise(resolve => setTimeout(resolve, CONFIG.retryDelay * attempt));
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Evaluate single project with all 3 models in parallel
|
|
216
|
-
*/
|
|
217
|
-
async function evaluateProject(project: typeof PROJECTS[0]): Promise<ProjectEvaluation> {
|
|
218
|
-
console.log(`\n📁 Evaluating: ${project.name} (${project.type})`);
|
|
219
|
-
|
|
220
|
-
const aiContextPath = join(project.path, 'ai-context');
|
|
221
|
-
const hasIndexDb = existsSync(join(aiContextPath, 'index.db'));
|
|
222
|
-
|
|
223
|
-
// Skip if no ai-context
|
|
224
|
-
if (!existsSync(aiContextPath)) {
|
|
225
|
-
console.log(` ⚠️ No ai-context directory found`);
|
|
226
|
-
return {
|
|
227
|
-
projectName: project.name,
|
|
228
|
-
projectType: project.type as 'priority' | 'supported' | 'unsupported',
|
|
229
|
-
aiContextPath,
|
|
230
|
-
hasIndexDb,
|
|
231
|
-
results: [],
|
|
232
|
-
summary: 'No ai-context directory found',
|
|
233
|
-
synthesizedImprovements: [],
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const prompt = createEvaluationPrompt(project.path, project.name);
|
|
238
|
-
|
|
239
|
-
// Call all 3 models in parallel
|
|
240
|
-
console.log(' 🔄 Calling models in parallel...');
|
|
241
|
-
const startTime = Date.now();
|
|
242
|
-
|
|
243
|
-
const [kimiResult, glmResult, minimaxResult] = await Promise.all([
|
|
244
|
-
callOpenCode('kimi-k2.5', prompt),
|
|
245
|
-
callOpenCode('glm-5', prompt),
|
|
246
|
-
callMiniMax(prompt),
|
|
247
|
-
]);
|
|
248
|
-
|
|
249
|
-
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
250
|
-
console.log(` ✅ Completed in ${duration}s`);
|
|
251
|
-
|
|
252
|
-
// Process results
|
|
253
|
-
const results: EvaluationResult[] = [
|
|
254
|
-
{ model: 'Kimi K2.5', perspective: 'overall', score: kimiResult.overall_score || 0, feedback: kimiResult.overall_feedback || '', improvements: kimiResult.top_3_improvements || [] },
|
|
255
|
-
{ model: 'GLM 5', perspective: 'overall', score: glmResult.overall_score || 0, feedback: glmResult.overall_feedback || '', improvements: glmResult.top_3_improvements || [] },
|
|
256
|
-
{ model: 'MiniMax 2.7', perspective: 'overall', score: minimaxResult.overall_score || 0, feedback: minimaxResult.overall_feedback || '', improvements: minimaxResult.top_3_improvements || [] },
|
|
257
|
-
];
|
|
258
|
-
|
|
259
|
-
// Synthesize improvements
|
|
260
|
-
const allImprovements = [
|
|
261
|
-
...(kimiResult.top_3_improvements || []),
|
|
262
|
-
...(glmResult.top_3_improvements || []),
|
|
263
|
-
...(minimaxResult.top_3_improvements || []),
|
|
264
|
-
];
|
|
265
|
-
|
|
266
|
-
// Simple deduplication (in real implementation, use LLM to synthesize)
|
|
267
|
-
const synthesizedImprovements = [...new Set(allImprovements)].slice(0, 5);
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
projectName: project.name,
|
|
271
|
-
projectType: project.type as 'priority' | 'supported' | 'unsupported',
|
|
272
|
-
aiContextPath,
|
|
273
|
-
hasIndexDb,
|
|
274
|
-
results,
|
|
275
|
-
summary: `Average score: ${(results.reduce((a, b) => a + b.score, 0) / results.length).toFixed(1)}/5`,
|
|
276
|
-
synthesizedImprovements,
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Generate final report
|
|
282
|
-
*/
|
|
283
|
-
function generateReport(evaluations: ProjectEvaluation[]): string {
|
|
284
|
-
let report = `# AI-Context Evaluation Report\n\n`;
|
|
285
|
-
report += `Generated: ${new Date().toISOString()}\n\n`;
|
|
286
|
-
|
|
287
|
-
// Summary table
|
|
288
|
-
report += `## Summary\n\n`;
|
|
289
|
-
report += `| Project | Type | Index DB | Avg Score | Key Issue |\n`;
|
|
290
|
-
report += `|---------|------|----------|-----------|-----------|\n`;
|
|
291
|
-
|
|
292
|
-
for (const eval_ of evaluations) {
|
|
293
|
-
const avgScore = eval_.results.length > 0
|
|
294
|
-
? (eval_.results.reduce((a, b) => a + b.score, 0) / eval_.results.length).toFixed(1)
|
|
295
|
-
: 'N/A';
|
|
296
|
-
const keyIssue = eval_.synthesizedImprovements[0] || 'None identified';
|
|
297
|
-
report += `| ${eval_.projectName} | ${eval_.projectType} | ${eval_.hasIndexDb ? '✅' : '❌'} | ${avgScore} | ${keyIssue.substring(0, 40)}... |\n`;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Detailed findings
|
|
301
|
-
report += `\n## Detailed Findings\n\n`;
|
|
302
|
-
|
|
303
|
-
for (const eval_ of evaluations) {
|
|
304
|
-
report += `### ${eval_.projectName}\n\n`;
|
|
305
|
-
report += `- **Type:** ${eval_.projectType}\n`;
|
|
306
|
-
report += `- **Index DB:** ${eval_.hasIndexDb ? 'Yes' : 'No'}\n`;
|
|
307
|
-
report += `- **Model Scores:**\n`;
|
|
308
|
-
|
|
309
|
-
for (const result of eval_.results) {
|
|
310
|
-
report += ` - ${result.model}: ${result.score}/5\n`;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (eval_.synthesizedImprovements.length > 0) {
|
|
314
|
-
report += `- **Top Improvements:**\n`;
|
|
315
|
-
for (const improvement of eval_.synthesizedImprovements) {
|
|
316
|
-
report += ` - ${improvement}\n`;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
report += `\n`;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Cross-project patterns
|
|
324
|
-
const priority = evaluations.filter(e => e.projectType === 'priority');
|
|
325
|
-
const unsupported = evaluations.filter(e => e.projectType === 'unsupported');
|
|
326
|
-
const supported = evaluations.filter(e => e.projectType === 'supported');
|
|
327
|
-
|
|
328
|
-
report += `## Cross-Project Analysis\n\n`;
|
|
329
|
-
|
|
330
|
-
report += `### 🎯 Priority Projects (Salesforce)\n`;
|
|
331
|
-
report += `- Average score: ${calculateAverageScore(priority)}\n`;
|
|
332
|
-
report += `- Key findings: ${extractCommonIssues(priority).join(', ') || 'See detailed section above'}\n`;
|
|
333
|
-
report += `- Salesforce-specific insights: ${priority.length > 0 ? 'Apex classes, triggers, and SObject metadata handling' : 'N/A'}\n\n`;
|
|
334
|
-
|
|
335
|
-
report += `### Supported Projects\n`;
|
|
336
|
-
report += `- Average score: ${calculateAverageScore(supported)}\n`;
|
|
337
|
-
report += `- Common issues: ${extractCommonIssues(supported).join(', ') || 'None'}\n\n`;
|
|
338
|
-
|
|
339
|
-
report += `### Unsupported Projects\n`;
|
|
340
|
-
report += `- Average score: ${calculateAverageScore(unsupported)}\n`;
|
|
341
|
-
report += `- Common issues: ${extractCommonIssues(unsupported).join(', ') || 'None'}\n\n`;
|
|
342
|
-
|
|
343
|
-
report += `### Key Insights\n`;
|
|
344
|
-
report += `1. **Salesforce Priority**: Detailed analysis of Apex, triggers, and metadata\n`;
|
|
345
|
-
report += `2. Index DB impact on quality: ${evaluations.filter(e => e.hasIndexDb).length > 0 ? 'Projects with Index DB show...' : 'Mixed results'}\n`;
|
|
346
|
-
report += `3. Unsupported projects: ${unsupported.length > 0 ? 'Generic analysis provides value but lacks framework-specific insights' : 'N/A'}\n`;
|
|
347
|
-
|
|
348
|
-
return report;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function calculateAverageScore(evaluations: ProjectEvaluation[]): string {
|
|
352
|
-
const scores = evaluations
|
|
353
|
-
.flatMap(e => e.results)
|
|
354
|
-
.map(r => r.score)
|
|
355
|
-
.filter(s => s > 0);
|
|
356
|
-
|
|
357
|
-
if (scores.length === 0) return 'N/A';
|
|
358
|
-
return (scores.reduce((a, b) => a + b, 0) / scores.length).toFixed(1);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function extractCommonIssues(evaluations: ProjectEvaluation[]): string[] {
|
|
362
|
-
const allIssues = evaluations.flatMap(e => e.synthesizedImprovements);
|
|
363
|
-
const issueCounts: Record<string, number> = {};
|
|
364
|
-
|
|
365
|
-
for (const issue of allIssues) {
|
|
366
|
-
const normalized = issue.toLowerCase().replace(/[^a-z0-9\s]/g, '');
|
|
367
|
-
issueCounts[normalized] = (issueCounts[normalized] || 0) + 1;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return Object.entries(issueCounts)
|
|
371
|
-
.filter(([_, count]) => count > 1)
|
|
372
|
-
.map(([issue, _]) => issue)
|
|
373
|
-
.slice(0, 3);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Main execution
|
|
378
|
-
*/
|
|
379
|
-
async function main() {
|
|
380
|
-
console.log('🚀 AI-Context Evaluator\n');
|
|
381
|
-
console.log('Configuration:');
|
|
382
|
-
console.log(` - Timeout: ${CONFIG.timeout}ms per call`);
|
|
383
|
-
console.log(` - Max retries: ${CONFIG.maxRetries}`);
|
|
384
|
-
console.log(` - Projects: ${PROJECTS.length}`);
|
|
385
|
-
console.log(` - Models: Kimi K2.5, GLM 5, MiniMax 2.7\n`);
|
|
386
|
-
|
|
387
|
-
// Validate API keys
|
|
388
|
-
if (!CONFIG.opencodeApiKey) {
|
|
389
|
-
console.error('❌ Error: OPENCODE_API_KEY not set');
|
|
390
|
-
process.exit(1);
|
|
391
|
-
}
|
|
392
|
-
if (!CONFIG.minimaxApiKey) {
|
|
393
|
-
console.error('❌ Error: MINIMAX_API_KEY not set');
|
|
394
|
-
process.exit(1);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const evaluations: ProjectEvaluation[] = [];
|
|
398
|
-
|
|
399
|
-
// Evaluate each project sequentially
|
|
400
|
-
for (const project of PROJECTS) {
|
|
401
|
-
try {
|
|
402
|
-
const evaluation = await evaluateProject(project);
|
|
403
|
-
evaluations.push(evaluation);
|
|
404
|
-
|
|
405
|
-
// Save intermediate results
|
|
406
|
-
const intermediateFile = `evaluation-${project.name}.json`;
|
|
407
|
-
// In real implementation, write to file
|
|
408
|
-
console.log(` 💾 Saved intermediate results\n`);
|
|
409
|
-
|
|
410
|
-
} catch (error) {
|
|
411
|
-
console.error(` ❌ Failed to evaluate ${project.name}:`, error);
|
|
412
|
-
evaluations.push({
|
|
413
|
-
projectName: project.name,
|
|
414
|
-
projectType: project.type as 'priority' | 'supported' | 'unsupported',
|
|
415
|
-
aiContextPath: '',
|
|
416
|
-
hasIndexDb: false,
|
|
417
|
-
results: [],
|
|
418
|
-
summary: `Error: ${error}`,
|
|
419
|
-
synthesizedImprovements: [],
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Generate final report
|
|
425
|
-
console.log('\n📊 Generating final report...\n');
|
|
426
|
-
const report = generateReport(evaluations);
|
|
427
|
-
|
|
428
|
-
// Save report
|
|
429
|
-
const reportPath = `ai-context-evaluation-report-${Date.now()}.md`;
|
|
430
|
-
writeFileSync(reportPath, report);
|
|
431
|
-
console.log(`✅ Report saved to: ${reportPath}`);
|
|
432
|
-
console.log('\n📋 Summary:');
|
|
433
|
-
console.log(report.split('\n').slice(0, 20).join('\n'));
|
|
434
|
-
console.log('\n... (truncated)');
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// Run if called directly
|
|
438
|
-
main().catch(console.error);
|
|
439
|
-
|
|
440
|
-
export { evaluateProject, createEvaluationPrompt, generateReport };
|