codeep 1.0.126 → 1.0.128
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/dist/api/index.js +24 -0
- package/dist/app.js +127 -2
- package/dist/components/AgentProgress.d.ts +5 -5
- package/dist/components/AgentProgress.js +110 -54
- package/dist/components/Help.js +1 -1
- package/dist/components/Input.js +3 -0
- package/dist/components/MessageList.d.ts +1 -0
- package/dist/components/MessageList.js +2 -2
- package/dist/components/ProjectPermission.js +17 -4
- package/dist/config/index.d.ts +13 -0
- package/dist/config/index.js +53 -0
- package/dist/utils/projectIntelligence.d.ts +67 -0
- package/dist/utils/projectIntelligence.js +601 -0
- package/package.json +1 -1
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Intelligence - Deep project analysis and caching
|
|
3
|
+
* Scans project once and caches important information for faster AI context
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync, readdirSync } from 'fs';
|
|
6
|
+
import { join, basename, extname } from 'path';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Constants
|
|
9
|
+
// ============================================================================
|
|
10
|
+
const INTELLIGENCE_VERSION = '1.0';
|
|
11
|
+
const INTELLIGENCE_FILE = 'intelligence.json';
|
|
12
|
+
const IGNORE_DIRS = new Set([
|
|
13
|
+
'node_modules', '.git', 'dist', 'build', '.next', 'coverage',
|
|
14
|
+
'.cache', '.vscode', '.idea', '__pycache__', 'venv', '.env',
|
|
15
|
+
'vendor', 'target', 'out', 'bin', 'obj', '.nuxt', '.output',
|
|
16
|
+
]);
|
|
17
|
+
const LANGUAGE_MAP = {
|
|
18
|
+
'.ts': 'TypeScript', '.tsx': 'TypeScript React',
|
|
19
|
+
'.js': 'JavaScript', '.jsx': 'JavaScript React',
|
|
20
|
+
'.py': 'Python', '.rb': 'Ruby', '.go': 'Go', '.rs': 'Rust',
|
|
21
|
+
'.java': 'Java', '.kt': 'Kotlin', '.swift': 'Swift',
|
|
22
|
+
'.php': 'PHP', '.cs': 'C#', '.cpp': 'C++', '.c': 'C',
|
|
23
|
+
'.vue': 'Vue', '.svelte': 'Svelte',
|
|
24
|
+
'.css': 'CSS', '.scss': 'SCSS', '.less': 'Less',
|
|
25
|
+
'.html': 'HTML', '.json': 'JSON', '.yaml': 'YAML', '.yml': 'YAML',
|
|
26
|
+
'.md': 'Markdown', '.sql': 'SQL', '.sh': 'Shell',
|
|
27
|
+
};
|
|
28
|
+
const FRAMEWORK_INDICATORS = {
|
|
29
|
+
'React': ['react', 'react-dom'],
|
|
30
|
+
'Next.js': ['next'],
|
|
31
|
+
'Vue': ['vue'],
|
|
32
|
+
'Nuxt': ['nuxt'],
|
|
33
|
+
'Angular': ['@angular/core'],
|
|
34
|
+
'Svelte': ['svelte'],
|
|
35
|
+
'Express': ['express'],
|
|
36
|
+
'Fastify': ['fastify'],
|
|
37
|
+
'NestJS': ['@nestjs/core'],
|
|
38
|
+
'Django': ['django'],
|
|
39
|
+
'Flask': ['flask'],
|
|
40
|
+
'FastAPI': ['fastapi'],
|
|
41
|
+
'Rails': ['rails'],
|
|
42
|
+
'Laravel': ['laravel'],
|
|
43
|
+
'Spring': ['spring-boot'],
|
|
44
|
+
};
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Main Functions
|
|
47
|
+
// ============================================================================
|
|
48
|
+
/**
|
|
49
|
+
* Scan project and generate intelligence
|
|
50
|
+
*/
|
|
51
|
+
export async function scanProject(projectPath) {
|
|
52
|
+
const intelligence = {
|
|
53
|
+
version: INTELLIGENCE_VERSION,
|
|
54
|
+
scannedAt: new Date().toISOString(),
|
|
55
|
+
projectPath,
|
|
56
|
+
name: basename(projectPath),
|
|
57
|
+
type: 'Unknown',
|
|
58
|
+
description: '',
|
|
59
|
+
structure: {
|
|
60
|
+
totalFiles: 0,
|
|
61
|
+
totalDirectories: 0,
|
|
62
|
+
languages: {},
|
|
63
|
+
topDirectories: [],
|
|
64
|
+
},
|
|
65
|
+
dependencies: {
|
|
66
|
+
runtime: [],
|
|
67
|
+
dev: [],
|
|
68
|
+
frameworks: [],
|
|
69
|
+
},
|
|
70
|
+
keyFiles: [],
|
|
71
|
+
entryPoints: [],
|
|
72
|
+
scripts: {},
|
|
73
|
+
architecture: {
|
|
74
|
+
patterns: [],
|
|
75
|
+
mainModules: [],
|
|
76
|
+
},
|
|
77
|
+
conventions: {
|
|
78
|
+
indentation: 'spaces',
|
|
79
|
+
quotes: 'single',
|
|
80
|
+
semicolons: true,
|
|
81
|
+
namingStyle: 'camelCase',
|
|
82
|
+
},
|
|
83
|
+
testing: {
|
|
84
|
+
framework: null,
|
|
85
|
+
testDirectory: null,
|
|
86
|
+
hasTests: false,
|
|
87
|
+
},
|
|
88
|
+
notes: [],
|
|
89
|
+
};
|
|
90
|
+
// Scan directory structure
|
|
91
|
+
scanDirectoryStructure(projectPath, intelligence);
|
|
92
|
+
// Detect project type and dependencies
|
|
93
|
+
detectProjectType(projectPath, intelligence);
|
|
94
|
+
// Analyze key files
|
|
95
|
+
analyzeKeyFiles(projectPath, intelligence);
|
|
96
|
+
// Detect architecture patterns
|
|
97
|
+
detectArchitecture(projectPath, intelligence);
|
|
98
|
+
// Analyze code conventions
|
|
99
|
+
analyzeConventions(projectPath, intelligence);
|
|
100
|
+
// Detect testing setup
|
|
101
|
+
detectTesting(projectPath, intelligence);
|
|
102
|
+
return intelligence;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Save intelligence to .codeep/intelligence.json
|
|
106
|
+
*/
|
|
107
|
+
export function saveProjectIntelligence(projectPath, intelligence) {
|
|
108
|
+
try {
|
|
109
|
+
const codeepDir = join(projectPath, '.codeep');
|
|
110
|
+
if (!existsSync(codeepDir)) {
|
|
111
|
+
mkdirSync(codeepDir, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
const filePath = join(codeepDir, INTELLIGENCE_FILE);
|
|
114
|
+
writeFileSync(filePath, JSON.stringify(intelligence, null, 2));
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Load intelligence from .codeep/intelligence.json
|
|
123
|
+
*/
|
|
124
|
+
export function loadProjectIntelligence(projectPath) {
|
|
125
|
+
try {
|
|
126
|
+
const filePath = join(projectPath, '.codeep', INTELLIGENCE_FILE);
|
|
127
|
+
if (!existsSync(filePath))
|
|
128
|
+
return null;
|
|
129
|
+
const data = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
130
|
+
return data;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Check if intelligence exists and is fresh (less than 24 hours old)
|
|
138
|
+
*/
|
|
139
|
+
export function isIntelligenceFresh(projectPath, maxAgeHours = 24) {
|
|
140
|
+
const intelligence = loadProjectIntelligence(projectPath);
|
|
141
|
+
if (!intelligence)
|
|
142
|
+
return false;
|
|
143
|
+
const scannedAt = new Date(intelligence.scannedAt).getTime();
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
const ageHours = (now - scannedAt) / (1000 * 60 * 60);
|
|
146
|
+
return ageHours < maxAgeHours;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Generate AI-friendly context from intelligence
|
|
150
|
+
*/
|
|
151
|
+
export function generateContextFromIntelligence(intelligence) {
|
|
152
|
+
const lines = [];
|
|
153
|
+
lines.push(`# Project: ${intelligence.name}`);
|
|
154
|
+
lines.push(`Type: ${intelligence.type}`);
|
|
155
|
+
if (intelligence.description) {
|
|
156
|
+
lines.push(`Description: ${intelligence.description}`);
|
|
157
|
+
}
|
|
158
|
+
lines.push('');
|
|
159
|
+
// Structure
|
|
160
|
+
lines.push('## Structure');
|
|
161
|
+
lines.push(`- ${intelligence.structure.totalFiles} files, ${intelligence.structure.totalDirectories} directories`);
|
|
162
|
+
const topLangs = Object.entries(intelligence.structure.languages)
|
|
163
|
+
.sort((a, b) => b[1] - a[1])
|
|
164
|
+
.slice(0, 5)
|
|
165
|
+
.map(([ext, count]) => `${LANGUAGE_MAP[ext] || ext} (${count})`)
|
|
166
|
+
.join(', ');
|
|
167
|
+
if (topLangs) {
|
|
168
|
+
lines.push(`- Languages: ${topLangs}`);
|
|
169
|
+
}
|
|
170
|
+
if (intelligence.structure.topDirectories.length > 0) {
|
|
171
|
+
lines.push(`- Main directories: ${intelligence.structure.topDirectories.join(', ')}`);
|
|
172
|
+
}
|
|
173
|
+
lines.push('');
|
|
174
|
+
// Frameworks
|
|
175
|
+
if (intelligence.dependencies.frameworks.length > 0) {
|
|
176
|
+
lines.push('## Frameworks');
|
|
177
|
+
lines.push(intelligence.dependencies.frameworks.join(', '));
|
|
178
|
+
lines.push('');
|
|
179
|
+
}
|
|
180
|
+
// Architecture
|
|
181
|
+
if (intelligence.architecture.patterns.length > 0 || intelligence.architecture.mainModules.length > 0) {
|
|
182
|
+
lines.push('## Architecture');
|
|
183
|
+
if (intelligence.architecture.patterns.length > 0) {
|
|
184
|
+
lines.push(`Patterns: ${intelligence.architecture.patterns.join(', ')}`);
|
|
185
|
+
}
|
|
186
|
+
if (intelligence.architecture.mainModules.length > 0) {
|
|
187
|
+
lines.push(`Main modules: ${intelligence.architecture.mainModules.join(', ')}`);
|
|
188
|
+
}
|
|
189
|
+
lines.push('');
|
|
190
|
+
}
|
|
191
|
+
// Entry points
|
|
192
|
+
if (intelligence.entryPoints.length > 0) {
|
|
193
|
+
lines.push('## Entry Points');
|
|
194
|
+
intelligence.entryPoints.forEach(ep => lines.push(`- ${ep}`));
|
|
195
|
+
lines.push('');
|
|
196
|
+
}
|
|
197
|
+
// Scripts
|
|
198
|
+
if (Object.keys(intelligence.scripts).length > 0) {
|
|
199
|
+
lines.push('## Available Scripts');
|
|
200
|
+
Object.entries(intelligence.scripts).slice(0, 10).forEach(([name, cmd]) => {
|
|
201
|
+
lines.push(`- ${name}: ${cmd}`);
|
|
202
|
+
});
|
|
203
|
+
lines.push('');
|
|
204
|
+
}
|
|
205
|
+
// Key files
|
|
206
|
+
if (intelligence.keyFiles.length > 0) {
|
|
207
|
+
lines.push('## Key Files');
|
|
208
|
+
intelligence.keyFiles.forEach(kf => {
|
|
209
|
+
lines.push(`- ${kf.path}: ${kf.summary}`);
|
|
210
|
+
});
|
|
211
|
+
lines.push('');
|
|
212
|
+
}
|
|
213
|
+
// Testing
|
|
214
|
+
if (intelligence.testing.hasTests) {
|
|
215
|
+
lines.push('## Testing');
|
|
216
|
+
lines.push(`Framework: ${intelligence.testing.framework || 'Unknown'}`);
|
|
217
|
+
if (intelligence.testing.testDirectory) {
|
|
218
|
+
lines.push(`Test directory: ${intelligence.testing.testDirectory}`);
|
|
219
|
+
}
|
|
220
|
+
lines.push('');
|
|
221
|
+
}
|
|
222
|
+
// Conventions
|
|
223
|
+
lines.push('## Code Conventions');
|
|
224
|
+
lines.push(`- Indentation: ${intelligence.conventions.indentation}`);
|
|
225
|
+
lines.push(`- Quotes: ${intelligence.conventions.quotes}`);
|
|
226
|
+
lines.push(`- Semicolons: ${intelligence.conventions.semicolons ? 'yes' : 'no'}`);
|
|
227
|
+
lines.push(`- Naming: ${intelligence.conventions.namingStyle}`);
|
|
228
|
+
// Notes
|
|
229
|
+
if (intelligence.notes.length > 0) {
|
|
230
|
+
lines.push('');
|
|
231
|
+
lines.push('## Notes');
|
|
232
|
+
intelligence.notes.forEach(note => lines.push(`- ${note}`));
|
|
233
|
+
}
|
|
234
|
+
return lines.join('\n');
|
|
235
|
+
}
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// Helper Functions
|
|
238
|
+
// ============================================================================
|
|
239
|
+
function scanDirectoryStructure(dir, intelligence, depth = 0) {
|
|
240
|
+
if (depth > 5)
|
|
241
|
+
return; // Max depth
|
|
242
|
+
try {
|
|
243
|
+
const entries = readdirSync(dir);
|
|
244
|
+
for (const entry of entries) {
|
|
245
|
+
if (IGNORE_DIRS.has(entry))
|
|
246
|
+
continue;
|
|
247
|
+
if (entry.startsWith('.') && entry !== '.env.example')
|
|
248
|
+
continue;
|
|
249
|
+
const fullPath = join(dir, entry);
|
|
250
|
+
try {
|
|
251
|
+
const stat = statSync(fullPath);
|
|
252
|
+
if (stat.isDirectory()) {
|
|
253
|
+
intelligence.structure.totalDirectories++;
|
|
254
|
+
// Track top-level directories
|
|
255
|
+
if (depth === 0) {
|
|
256
|
+
intelligence.structure.topDirectories.push(entry);
|
|
257
|
+
}
|
|
258
|
+
scanDirectoryStructure(fullPath, intelligence, depth + 1);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
intelligence.structure.totalFiles++;
|
|
262
|
+
const ext = extname(entry).toLowerCase();
|
|
263
|
+
if (ext) {
|
|
264
|
+
intelligence.structure.languages[ext] = (intelligence.structure.languages[ext] || 0) + 1;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
// Skip inaccessible files
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// Skip inaccessible directories
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function detectProjectType(projectPath, intelligence) {
|
|
278
|
+
// Node.js / JavaScript
|
|
279
|
+
const packageJsonPath = join(projectPath, 'package.json');
|
|
280
|
+
if (existsSync(packageJsonPath)) {
|
|
281
|
+
try {
|
|
282
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
283
|
+
intelligence.name = pkg.name || intelligence.name;
|
|
284
|
+
intelligence.description = pkg.description || '';
|
|
285
|
+
// Detect type
|
|
286
|
+
if (existsSync(join(projectPath, 'tsconfig.json'))) {
|
|
287
|
+
intelligence.type = 'TypeScript/Node.js';
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
intelligence.type = 'JavaScript/Node.js';
|
|
291
|
+
}
|
|
292
|
+
// Dependencies
|
|
293
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
294
|
+
intelligence.dependencies.runtime = Object.keys(pkg.dependencies || {});
|
|
295
|
+
intelligence.dependencies.dev = Object.keys(pkg.devDependencies || {});
|
|
296
|
+
// Detect frameworks
|
|
297
|
+
for (const [framework, indicators] of Object.entries(FRAMEWORK_INDICATORS)) {
|
|
298
|
+
if (indicators.some(ind => allDeps[ind])) {
|
|
299
|
+
intelligence.dependencies.frameworks.push(framework);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Scripts
|
|
303
|
+
intelligence.scripts = pkg.scripts || {};
|
|
304
|
+
// Entry points
|
|
305
|
+
if (pkg.main)
|
|
306
|
+
intelligence.entryPoints.push(pkg.main);
|
|
307
|
+
if (pkg.module)
|
|
308
|
+
intelligence.entryPoints.push(pkg.module);
|
|
309
|
+
if (pkg.bin) {
|
|
310
|
+
if (typeof pkg.bin === 'string') {
|
|
311
|
+
intelligence.entryPoints.push(pkg.bin);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
intelligence.entryPoints.push(...Object.values(pkg.bin));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
// Invalid package.json
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// Python
|
|
324
|
+
if (existsSync(join(projectPath, 'requirements.txt')) ||
|
|
325
|
+
existsSync(join(projectPath, 'pyproject.toml')) ||
|
|
326
|
+
existsSync(join(projectPath, 'setup.py'))) {
|
|
327
|
+
intelligence.type = 'Python';
|
|
328
|
+
// Try to read requirements
|
|
329
|
+
const reqPath = join(projectPath, 'requirements.txt');
|
|
330
|
+
if (existsSync(reqPath)) {
|
|
331
|
+
const content = readFileSync(reqPath, 'utf-8');
|
|
332
|
+
intelligence.dependencies.runtime = content
|
|
333
|
+
.split('\n')
|
|
334
|
+
.map(l => l.trim().split('==')[0].split('>=')[0])
|
|
335
|
+
.filter(l => l && !l.startsWith('#'));
|
|
336
|
+
// Detect frameworks
|
|
337
|
+
if (intelligence.dependencies.runtime.includes('django')) {
|
|
338
|
+
intelligence.dependencies.frameworks.push('Django');
|
|
339
|
+
}
|
|
340
|
+
if (intelligence.dependencies.runtime.includes('flask')) {
|
|
341
|
+
intelligence.dependencies.frameworks.push('Flask');
|
|
342
|
+
}
|
|
343
|
+
if (intelligence.dependencies.runtime.includes('fastapi')) {
|
|
344
|
+
intelligence.dependencies.frameworks.push('FastAPI');
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
// Go
|
|
350
|
+
if (existsSync(join(projectPath, 'go.mod'))) {
|
|
351
|
+
intelligence.type = 'Go';
|
|
352
|
+
try {
|
|
353
|
+
const content = readFileSync(join(projectPath, 'go.mod'), 'utf-8');
|
|
354
|
+
const moduleMatch = content.match(/module\s+(\S+)/);
|
|
355
|
+
if (moduleMatch) {
|
|
356
|
+
intelligence.name = moduleMatch[1].split('/').pop() || intelligence.name;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
catch { }
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
// Rust
|
|
363
|
+
if (existsSync(join(projectPath, 'Cargo.toml'))) {
|
|
364
|
+
intelligence.type = 'Rust';
|
|
365
|
+
try {
|
|
366
|
+
const content = readFileSync(join(projectPath, 'Cargo.toml'), 'utf-8');
|
|
367
|
+
const nameMatch = content.match(/name\s*=\s*"([^"]+)"/);
|
|
368
|
+
if (nameMatch) {
|
|
369
|
+
intelligence.name = nameMatch[1];
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
catch { }
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
// PHP
|
|
376
|
+
if (existsSync(join(projectPath, 'composer.json'))) {
|
|
377
|
+
intelligence.type = 'PHP';
|
|
378
|
+
try {
|
|
379
|
+
const composer = JSON.parse(readFileSync(join(projectPath, 'composer.json'), 'utf-8'));
|
|
380
|
+
intelligence.name = composer.name?.split('/').pop() || intelligence.name;
|
|
381
|
+
intelligence.description = composer.description || '';
|
|
382
|
+
if (composer.require?.['laravel/framework']) {
|
|
383
|
+
intelligence.dependencies.frameworks.push('Laravel');
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
catch { }
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function analyzeKeyFiles(projectPath, intelligence) {
|
|
391
|
+
const keyFilesToAnalyze = [
|
|
392
|
+
{ path: 'README.md', summarize: summarizeReadme },
|
|
393
|
+
{ path: 'readme.md', summarize: summarizeReadme },
|
|
394
|
+
{ path: 'package.json', summarize: summarizePackageJson },
|
|
395
|
+
{ path: 'tsconfig.json', summarize: summarizeTsConfig },
|
|
396
|
+
{ path: 'Dockerfile', summarize: summarizeDockerfile },
|
|
397
|
+
{ path: '.env.example', summarize: summarizeEnvExample },
|
|
398
|
+
];
|
|
399
|
+
for (const { path, summarize } of keyFilesToAnalyze) {
|
|
400
|
+
const fullPath = join(projectPath, path);
|
|
401
|
+
if (existsSync(fullPath)) {
|
|
402
|
+
try {
|
|
403
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
404
|
+
const summary = summarize(content);
|
|
405
|
+
if (summary) {
|
|
406
|
+
intelligence.keyFiles.push({ path, summary });
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
catch { }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function summarizeReadme(content) {
|
|
414
|
+
// Extract first meaningful paragraph
|
|
415
|
+
const lines = content.split('\n');
|
|
416
|
+
let inHeader = false;
|
|
417
|
+
for (const line of lines) {
|
|
418
|
+
if (line.startsWith('#')) {
|
|
419
|
+
inHeader = true;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (inHeader && line.trim() && !line.startsWith('#') && !line.startsWith('-') && !line.startsWith('*')) {
|
|
423
|
+
return line.trim().slice(0, 150) + (line.length > 150 ? '...' : '');
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return 'Project documentation';
|
|
427
|
+
}
|
|
428
|
+
function summarizePackageJson(content) {
|
|
429
|
+
try {
|
|
430
|
+
const pkg = JSON.parse(content);
|
|
431
|
+
const parts = [];
|
|
432
|
+
if (pkg.description)
|
|
433
|
+
parts.push(pkg.description);
|
|
434
|
+
if (pkg.version)
|
|
435
|
+
parts.push(`v${pkg.version}`);
|
|
436
|
+
return parts.join(' - ') || 'Node.js project configuration';
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
return 'Node.js project configuration';
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function summarizeTsConfig(content) {
|
|
443
|
+
try {
|
|
444
|
+
const config = JSON.parse(content);
|
|
445
|
+
const target = config.compilerOptions?.target || 'unknown';
|
|
446
|
+
const module = config.compilerOptions?.module || 'unknown';
|
|
447
|
+
return `TypeScript config (target: ${target}, module: ${module})`;
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
return 'TypeScript configuration';
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function summarizeDockerfile(_content) {
|
|
454
|
+
return 'Container configuration';
|
|
455
|
+
}
|
|
456
|
+
function summarizeEnvExample(content) {
|
|
457
|
+
const vars = content.split('\n').filter(l => l.includes('=') && !l.startsWith('#')).length;
|
|
458
|
+
return `${vars} environment variables`;
|
|
459
|
+
}
|
|
460
|
+
function detectArchitecture(projectPath, intelligence) {
|
|
461
|
+
const topDirs = intelligence.structure.topDirectories;
|
|
462
|
+
// Detect patterns based on directory structure
|
|
463
|
+
if (topDirs.includes('src')) {
|
|
464
|
+
intelligence.architecture.mainModules.push('src');
|
|
465
|
+
}
|
|
466
|
+
// MVC pattern
|
|
467
|
+
if (topDirs.includes('models') || topDirs.includes('views') || topDirs.includes('controllers')) {
|
|
468
|
+
intelligence.architecture.patterns.push('MVC');
|
|
469
|
+
}
|
|
470
|
+
// Component-based (React/Vue/etc)
|
|
471
|
+
if (topDirs.includes('components') || existsSync(join(projectPath, 'src', 'components'))) {
|
|
472
|
+
intelligence.architecture.patterns.push('Component-based');
|
|
473
|
+
intelligence.architecture.mainModules.push('components');
|
|
474
|
+
}
|
|
475
|
+
// API/Services pattern
|
|
476
|
+
if (topDirs.includes('api') || topDirs.includes('services') ||
|
|
477
|
+
existsSync(join(projectPath, 'src', 'api')) || existsSync(join(projectPath, 'src', 'services'))) {
|
|
478
|
+
intelligence.architecture.patterns.push('Service-oriented');
|
|
479
|
+
}
|
|
480
|
+
// Hooks (React)
|
|
481
|
+
if (topDirs.includes('hooks') || existsSync(join(projectPath, 'src', 'hooks'))) {
|
|
482
|
+
intelligence.architecture.mainModules.push('hooks');
|
|
483
|
+
}
|
|
484
|
+
// Utils/Helpers
|
|
485
|
+
if (topDirs.includes('utils') || topDirs.includes('helpers') || topDirs.includes('lib')) {
|
|
486
|
+
intelligence.architecture.mainModules.push('utils');
|
|
487
|
+
}
|
|
488
|
+
// Pages (Next.js, Nuxt, etc)
|
|
489
|
+
if (topDirs.includes('pages') || topDirs.includes('app')) {
|
|
490
|
+
intelligence.architecture.patterns.push('File-based routing');
|
|
491
|
+
}
|
|
492
|
+
// Detect API endpoints
|
|
493
|
+
const apiDir = join(projectPath, 'src', 'api');
|
|
494
|
+
const pagesApiDir = join(projectPath, 'pages', 'api');
|
|
495
|
+
const appApiDir = join(projectPath, 'app', 'api');
|
|
496
|
+
if (existsSync(apiDir) || existsSync(pagesApiDir) || existsSync(appApiDir)) {
|
|
497
|
+
intelligence.architecture.apiEndpoints = [];
|
|
498
|
+
// Could scan for route files here
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function analyzeConventions(projectPath, intelligence) {
|
|
502
|
+
// Find a sample code file to analyze
|
|
503
|
+
const sampleExtensions = ['.ts', '.tsx', '.js', '.jsx'];
|
|
504
|
+
let sampleContent = null;
|
|
505
|
+
const srcDir = join(projectPath, 'src');
|
|
506
|
+
const searchDir = existsSync(srcDir) ? srcDir : projectPath;
|
|
507
|
+
try {
|
|
508
|
+
const files = readdirSync(searchDir);
|
|
509
|
+
for (const file of files) {
|
|
510
|
+
const ext = extname(file).toLowerCase();
|
|
511
|
+
if (sampleExtensions.includes(ext)) {
|
|
512
|
+
const content = readFileSync(join(searchDir, file), 'utf-8');
|
|
513
|
+
if (content.length > 100) {
|
|
514
|
+
sampleContent = content;
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
catch { }
|
|
521
|
+
if (!sampleContent)
|
|
522
|
+
return;
|
|
523
|
+
// Analyze indentation
|
|
524
|
+
const tabCount = (sampleContent.match(/^\t/gm) || []).length;
|
|
525
|
+
const spaceCount = (sampleContent.match(/^ /gm) || []).length;
|
|
526
|
+
if (tabCount > spaceCount * 2) {
|
|
527
|
+
intelligence.conventions.indentation = 'tabs';
|
|
528
|
+
}
|
|
529
|
+
else if (spaceCount > tabCount * 2) {
|
|
530
|
+
intelligence.conventions.indentation = 'spaces';
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
intelligence.conventions.indentation = 'mixed';
|
|
534
|
+
}
|
|
535
|
+
// Analyze quotes
|
|
536
|
+
const singleQuotes = (sampleContent.match(/'/g) || []).length;
|
|
537
|
+
const doubleQuotes = (sampleContent.match(/"/g) || []).length;
|
|
538
|
+
if (singleQuotes > doubleQuotes * 1.5) {
|
|
539
|
+
intelligence.conventions.quotes = 'single';
|
|
540
|
+
}
|
|
541
|
+
else if (doubleQuotes > singleQuotes * 1.5) {
|
|
542
|
+
intelligence.conventions.quotes = 'double';
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
intelligence.conventions.quotes = 'mixed';
|
|
546
|
+
}
|
|
547
|
+
// Analyze semicolons
|
|
548
|
+
const semicolons = (sampleContent.match(/;\s*$/gm) || []).length;
|
|
549
|
+
const statements = (sampleContent.match(/\n/g) || []).length;
|
|
550
|
+
intelligence.conventions.semicolons = semicolons > statements * 0.3;
|
|
551
|
+
// Analyze naming
|
|
552
|
+
const camelCase = (sampleContent.match(/[a-z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*/g) || []).length;
|
|
553
|
+
const snakeCase = (sampleContent.match(/[a-z]+_[a-z]+/g) || []).length;
|
|
554
|
+
if (camelCase > snakeCase * 2) {
|
|
555
|
+
intelligence.conventions.namingStyle = 'camelCase';
|
|
556
|
+
}
|
|
557
|
+
else if (snakeCase > camelCase * 2) {
|
|
558
|
+
intelligence.conventions.namingStyle = 'snake_case';
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
intelligence.conventions.namingStyle = 'mixed';
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function detectTesting(projectPath, intelligence) {
|
|
565
|
+
// Check for test directories
|
|
566
|
+
const testDirs = ['test', 'tests', '__tests__', 'spec', 'specs'];
|
|
567
|
+
for (const dir of testDirs) {
|
|
568
|
+
if (existsSync(join(projectPath, dir))) {
|
|
569
|
+
intelligence.testing.hasTests = true;
|
|
570
|
+
intelligence.testing.testDirectory = dir;
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
// Also check src/__tests__
|
|
575
|
+
if (existsSync(join(projectPath, 'src', '__tests__'))) {
|
|
576
|
+
intelligence.testing.hasTests = true;
|
|
577
|
+
intelligence.testing.testDirectory = 'src/__tests__';
|
|
578
|
+
}
|
|
579
|
+
// Detect framework from config files or dependencies
|
|
580
|
+
if (existsSync(join(projectPath, 'jest.config.js')) ||
|
|
581
|
+
existsSync(join(projectPath, 'jest.config.ts')) ||
|
|
582
|
+
intelligence.dependencies.dev.includes('jest')) {
|
|
583
|
+
intelligence.testing.framework = 'Jest';
|
|
584
|
+
intelligence.testing.hasTests = true;
|
|
585
|
+
}
|
|
586
|
+
else if (existsSync(join(projectPath, 'vitest.config.ts')) ||
|
|
587
|
+
existsSync(join(projectPath, 'vitest.config.js')) ||
|
|
588
|
+
intelligence.dependencies.dev.includes('vitest')) {
|
|
589
|
+
intelligence.testing.framework = 'Vitest';
|
|
590
|
+
intelligence.testing.hasTests = true;
|
|
591
|
+
}
|
|
592
|
+
else if (existsSync(join(projectPath, 'pytest.ini')) ||
|
|
593
|
+
existsSync(join(projectPath, 'pyproject.toml'))) {
|
|
594
|
+
intelligence.testing.framework = 'Pytest';
|
|
595
|
+
intelligence.testing.hasTests = true;
|
|
596
|
+
}
|
|
597
|
+
else if (intelligence.dependencies.dev.includes('mocha')) {
|
|
598
|
+
intelligence.testing.framework = 'Mocha';
|
|
599
|
+
intelligence.testing.hasTests = true;
|
|
600
|
+
}
|
|
601
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.128",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|