prjct-cli 0.9.2 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/core/__tests__/agentic/memory-system.test.js +263 -0
  3. package/core/__tests__/agentic/plan-mode.test.js +336 -0
  4. package/core/agentic/agent-router.js +253 -186
  5. package/core/agentic/chain-of-thought.js +578 -0
  6. package/core/agentic/command-executor.js +299 -17
  7. package/core/agentic/context-builder.js +208 -8
  8. package/core/agentic/context-filter.js +83 -83
  9. package/core/agentic/ground-truth.js +591 -0
  10. package/core/agentic/loop-detector.js +406 -0
  11. package/core/agentic/memory-system.js +850 -0
  12. package/core/agentic/parallel-tools.js +366 -0
  13. package/core/agentic/plan-mode.js +572 -0
  14. package/core/agentic/prompt-builder.js +127 -2
  15. package/core/agentic/response-templates.js +290 -0
  16. package/core/agentic/semantic-compression.js +517 -0
  17. package/core/agentic/think-blocks.js +657 -0
  18. package/core/agentic/tool-registry.js +32 -0
  19. package/core/agentic/validation-rules.js +380 -0
  20. package/core/command-registry.js +48 -0
  21. package/core/commands.js +128 -60
  22. package/core/context-sync.js +183 -0
  23. package/core/domain/agent-generator.js +77 -46
  24. package/core/domain/agent-loader.js +183 -0
  25. package/core/domain/agent-matcher.js +217 -0
  26. package/core/domain/agent-validator.js +217 -0
  27. package/core/domain/context-estimator.js +175 -0
  28. package/core/domain/product-standards.js +92 -0
  29. package/core/domain/smart-cache.js +157 -0
  30. package/core/domain/task-analyzer.js +353 -0
  31. package/core/domain/tech-detector.js +365 -0
  32. package/package.json +8 -16
  33. package/templates/commands/done.md +7 -0
  34. package/templates/commands/feature.md +8 -0
  35. package/templates/commands/ship.md +8 -0
  36. package/templates/commands/spec.md +128 -0
  37. package/templates/global/CLAUDE.md +17 -0
  38. package/core/__tests__/agentic/agent-router.test.js +0 -398
  39. package/core/__tests__/agentic/command-executor.test.js +0 -223
  40. package/core/__tests__/agentic/context-builder.test.js +0 -160
  41. package/core/__tests__/agentic/context-filter.test.js +0 -494
  42. package/core/__tests__/agentic/prompt-builder.test.js +0 -212
  43. package/core/__tests__/agentic/template-loader.test.js +0 -164
  44. package/core/__tests__/agentic/tool-registry.test.js +0 -243
  45. package/core/__tests__/domain/agent-generator.test.js +0 -296
  46. package/core/__tests__/domain/analyzer.test.js +0 -324
  47. package/core/__tests__/infrastructure/author-detector.test.js +0 -103
  48. package/core/__tests__/infrastructure/config-manager.test.js +0 -454
  49. package/core/__tests__/infrastructure/path-manager.test.js +0 -412
  50. package/core/__tests__/setup.test.js +0 -15
  51. package/core/__tests__/utils/date-helper.test.js +0 -169
  52. package/core/__tests__/utils/file-helper.test.js +0 -258
  53. package/core/__tests__/utils/jsonl-helper.test.js +0 -387
@@ -25,10 +25,31 @@ class ContextFilter {
25
25
 
26
26
  /**
27
27
  * Main entry point - filters context based on agent and task
28
+ * IMPROVED: Supports pre-estimated files for lazy loading
28
29
  */
29
30
  async filterForAgent(agent, task, projectPath, fullContext = {}) {
30
31
  const startTime = Date.now();
31
32
 
33
+ // If files were pre-estimated (lazy loading), use them
34
+ if (fullContext.estimatedFiles && fullContext.estimatedFiles.length > 0) {
35
+ const filteredFiles = fullContext.estimatedFiles;
36
+
37
+ const metrics = this.calculateMetrics(
38
+ fullContext.fileCount || filteredFiles.length,
39
+ filteredFiles.length,
40
+ startTime
41
+ );
42
+
43
+ return {
44
+ files: filteredFiles,
45
+ patterns: { preEstimated: true },
46
+ metrics,
47
+ agent: agent.name,
48
+ filtered: true
49
+ };
50
+ }
51
+
52
+ // Fallback to traditional filtering if no pre-estimation
32
53
  // Determine what files this agent needs
33
54
  const relevantPatterns = await this.determineRelevantPatterns(
34
55
  agent,
@@ -268,93 +289,29 @@ class ContextFilter {
268
289
 
269
290
  /**
270
291
  * Detect technologies used in the project
292
+ * NOW USES TechDetector - NO HARDCODING
271
293
  */
272
294
  async detectProjectTechnologies(projectPath) {
273
- const detected = new Set();
274
-
275
295
  try {
276
- // Check package.json for JS/TS projects
277
- const packageJsonPath = path.join(projectPath, 'package.json');
278
- if (await this.fileExists(packageJsonPath)) {
279
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
280
-
281
- // Check dependencies
282
- const deps = {
283
- ...packageJson.dependencies,
284
- ...packageJson.devDependencies
285
- };
286
-
287
- // Detect frameworks
288
- if (deps.react) detected.add('react');
289
- if (deps.vue) detected.add('vue');
290
- if (deps['@angular/core']) detected.add('angular');
291
- if (deps.express) detected.add('express');
292
- if (deps.next) detected.add('nextjs');
293
- if (deps.fastify) detected.add('fastify');
294
-
295
- // Language
296
- if (deps.typescript) detected.add('typescript');
297
- else detected.add('javascript');
298
- }
299
-
300
- // Check Gemfile for Ruby projects
301
- const gemfilePath = path.join(projectPath, 'Gemfile');
302
- if (await this.fileExists(gemfilePath)) {
303
- detected.add('ruby');
304
- const gemfile = await fs.readFile(gemfilePath, 'utf8');
305
- if (gemfile.includes('rails')) detected.add('rails');
306
- }
307
-
308
- // Check requirements.txt or setup.py for Python
309
- const requirementsPath = path.join(projectPath, 'requirements.txt');
310
- const setupPyPath = path.join(projectPath, 'setup.py');
311
- if (await this.fileExists(requirementsPath) || await this.fileExists(setupPyPath)) {
312
- detected.add('python');
313
-
314
- if (await this.fileExists(requirementsPath)) {
315
- const requirements = await fs.readFile(requirementsPath, 'utf8');
316
- if (requirements.includes('django')) detected.add('django');
317
- if (requirements.includes('fastapi')) detected.add('fastapi');
318
- if (requirements.includes('flask')) detected.add('flask');
319
- }
320
- }
321
-
322
- // Check go.mod for Go projects
323
- const goModPath = path.join(projectPath, 'go.mod');
324
- if (await this.fileExists(goModPath)) {
325
- detected.add('go');
326
- }
327
-
328
- // Check Cargo.toml for Rust
329
- const cargoPath = path.join(projectPath, 'Cargo.toml');
330
- if (await this.fileExists(cargoPath)) {
331
- detected.add('rust');
332
- }
333
-
334
- // Check mix.exs for Elixir
335
- const mixPath = path.join(projectPath, 'mix.exs');
336
- if (await this.fileExists(mixPath)) {
337
- detected.add('elixir');
338
- }
339
-
340
- // Check for Java/Maven/Gradle
341
- const pomPath = path.join(projectPath, 'pom.xml');
342
- const gradlePath = path.join(projectPath, 'build.gradle');
343
- if (await this.fileExists(pomPath) || await this.fileExists(gradlePath)) {
344
- detected.add('java');
345
- }
346
-
347
- // Check composer.json for PHP
348
- const composerPath = path.join(projectPath, 'composer.json');
349
- if (await this.fileExists(composerPath)) {
350
- detected.add('php');
351
- }
352
-
296
+ const TechDetector = require('../domain/tech-detector');
297
+ const detector = new TechDetector(projectPath);
298
+ const tech = await detector.detectAll();
299
+
300
+ // Convert to array of all detected technologies
301
+ const all = [
302
+ ...tech.languages,
303
+ ...tech.frameworks,
304
+ ...tech.tools,
305
+ ...tech.databases,
306
+ ...tech.buildTools,
307
+ ...tech.testFrameworks
308
+ ];
309
+
310
+ return Array.from(new Set(all)); // Remove duplicates
353
311
  } catch (error) {
354
312
  console.error('Error detecting technologies:', error.message);
313
+ return [];
355
314
  }
356
-
357
- return Array.from(detected);
358
315
  }
359
316
 
360
317
  /**
@@ -463,13 +420,16 @@ class ContextFilter {
463
420
  const uniqueFiles = [...new Set(files)].sort();
464
421
 
465
422
  // Limit to reasonable number
466
- const maxFiles = 100;
423
+ const maxFiles = 300;
467
424
  if (uniqueFiles.length > maxFiles) {
468
425
  console.log(`Limiting context to ${maxFiles} most relevant files`);
469
426
  return uniqueFiles.slice(0, maxFiles);
470
427
  }
471
428
 
472
- return uniqueFiles;
429
+ // Expand context with related files
430
+ const expandedFiles = await this.expandContext(uniqueFiles);
431
+
432
+ return expandedFiles.slice(0, maxFiles);
473
433
 
474
434
  } catch (error) {
475
435
  console.error('Error loading files:', error.message);
@@ -534,6 +494,46 @@ class ContextFilter {
534
494
  }
535
495
  }
536
496
 
497
+ /**
498
+ * Expand context with related files (tests, styles, etc.)
499
+ */
500
+ async expandContext(files) {
501
+ const expanded = new Set(files);
502
+
503
+ for (const file of files) {
504
+ const ext = path.extname(file);
505
+ const basename = path.basename(file, ext);
506
+ const dirname = path.dirname(file);
507
+
508
+ // 1. Look for test files
509
+ const testPatterns = [
510
+ path.join(dirname, `${basename}.test${ext}`),
511
+ path.join(dirname, `${basename}.spec${ext}`),
512
+ path.join(dirname, '__tests__', `${basename}.test${ext}`),
513
+ path.join(dirname, 'tests', `${basename}.test${ext}`)
514
+ ];
515
+
516
+ // 2. Look for style files (for UI components)
517
+ const stylePatterns = [
518
+ path.join(dirname, `${basename}.css`),
519
+ path.join(dirname, `${basename}.scss`),
520
+ path.join(dirname, `${basename}.module.css`),
521
+ path.join(dirname, `${basename}.module.scss`)
522
+ ];
523
+
524
+ // Check if these related files exist
525
+ const potentialFiles = [...testPatterns, ...stylePatterns];
526
+
527
+ for (const potential of potentialFiles) {
528
+ if (!expanded.has(potential) && await this.fileExists(potential)) {
529
+ expanded.add(potential);
530
+ }
531
+ }
532
+ }
533
+
534
+ return Array.from(expanded).sort();
535
+ }
536
+
537
537
  /**
538
538
  * Get filter statistics
539
539
  */