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.
- package/CHANGELOG.md +142 -0
- package/core/__tests__/agentic/memory-system.test.js +263 -0
- package/core/__tests__/agentic/plan-mode.test.js +336 -0
- package/core/agentic/agent-router.js +253 -186
- package/core/agentic/chain-of-thought.js +578 -0
- package/core/agentic/command-executor.js +299 -17
- package/core/agentic/context-builder.js +208 -8
- package/core/agentic/context-filter.js +83 -83
- package/core/agentic/ground-truth.js +591 -0
- package/core/agentic/loop-detector.js +406 -0
- package/core/agentic/memory-system.js +850 -0
- package/core/agentic/parallel-tools.js +366 -0
- package/core/agentic/plan-mode.js +572 -0
- package/core/agentic/prompt-builder.js +127 -2
- package/core/agentic/response-templates.js +290 -0
- package/core/agentic/semantic-compression.js +517 -0
- package/core/agentic/think-blocks.js +657 -0
- package/core/agentic/tool-registry.js +32 -0
- package/core/agentic/validation-rules.js +380 -0
- package/core/command-registry.js +48 -0
- package/core/commands.js +128 -60
- package/core/context-sync.js +183 -0
- package/core/domain/agent-generator.js +77 -46
- package/core/domain/agent-loader.js +183 -0
- package/core/domain/agent-matcher.js +217 -0
- package/core/domain/agent-validator.js +217 -0
- package/core/domain/context-estimator.js +175 -0
- package/core/domain/product-standards.js +92 -0
- package/core/domain/smart-cache.js +157 -0
- package/core/domain/task-analyzer.js +353 -0
- package/core/domain/tech-detector.js +365 -0
- package/package.json +8 -16
- package/templates/commands/done.md +7 -0
- package/templates/commands/feature.md +8 -0
- package/templates/commands/ship.md +8 -0
- package/templates/commands/spec.md +128 -0
- package/templates/global/CLAUDE.md +17 -0
- package/core/__tests__/agentic/agent-router.test.js +0 -398
- package/core/__tests__/agentic/command-executor.test.js +0 -223
- package/core/__tests__/agentic/context-builder.test.js +0 -160
- package/core/__tests__/agentic/context-filter.test.js +0 -494
- package/core/__tests__/agentic/prompt-builder.test.js +0 -212
- package/core/__tests__/agentic/template-loader.test.js +0 -164
- package/core/__tests__/agentic/tool-registry.test.js +0 -243
- package/core/__tests__/domain/agent-generator.test.js +0 -296
- package/core/__tests__/domain/analyzer.test.js +0 -324
- package/core/__tests__/infrastructure/author-detector.test.js +0 -103
- package/core/__tests__/infrastructure/config-manager.test.js +0 -454
- package/core/__tests__/infrastructure/path-manager.test.js +0 -412
- package/core/__tests__/setup.test.js +0 -15
- package/core/__tests__/utils/date-helper.test.js +0 -169
- package/core/__tests__/utils/file-helper.test.js +0 -258
- 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
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
*/
|