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.
@@ -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.126",
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",