agentic-qe 3.6.10 → 3.6.12

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 (159) hide show
  1. package/.claude/helpers/learning-service.mjs +2 -0
  2. package/.claude/helpers/statusline-v3.cjs +2 -0
  3. package/.claude/skills/skills-manifest.json +1 -1
  4. package/package.json +1 -1
  5. package/scripts/migrate-v2-to-v3-memory.js +2 -0
  6. package/scripts/sync-claude-flow.cjs +1 -0
  7. package/v3/CHANGELOG.md +39 -0
  8. package/v3/dist/adapters/claude-flow/trajectory-bridge.js +2 -2
  9. package/v3/dist/adapters/claude-flow/trajectory-bridge.js.map +1 -1
  10. package/v3/dist/benchmarks/performance-benchmarks.js +1 -1
  11. package/v3/dist/cli/bundle.js +659 -427
  12. package/v3/dist/cli/commands/code.d.ts.map +1 -1
  13. package/v3/dist/cli/commands/code.js +9 -85
  14. package/v3/dist/cli/commands/code.js.map +1 -1
  15. package/v3/dist/cli/commands/coverage.d.ts.map +1 -1
  16. package/v3/dist/cli/commands/coverage.js +3 -28
  17. package/v3/dist/cli/commands/coverage.js.map +1 -1
  18. package/v3/dist/cli/commands/learning-helpers.d.ts.map +1 -1
  19. package/v3/dist/cli/commands/learning-helpers.js +3 -4
  20. package/v3/dist/cli/commands/learning-helpers.js.map +1 -1
  21. package/v3/dist/cli/commands/learning.d.ts.map +1 -1
  22. package/v3/dist/cli/commands/learning.js +5 -8
  23. package/v3/dist/cli/commands/learning.js.map +1 -1
  24. package/v3/dist/cli/commands/migrate.js +2 -2
  25. package/v3/dist/cli/commands/security.d.ts.map +1 -1
  26. package/v3/dist/cli/commands/security.js +3 -29
  27. package/v3/dist/cli/commands/security.js.map +1 -1
  28. package/v3/dist/cli/commands/test.d.ts.map +1 -1
  29. package/v3/dist/cli/commands/test.js +5 -58
  30. package/v3/dist/cli/commands/test.js.map +1 -1
  31. package/v3/dist/cli/utils/file-discovery.d.ts +27 -0
  32. package/v3/dist/cli/utils/file-discovery.d.ts.map +1 -0
  33. package/v3/dist/cli/utils/file-discovery.js +105 -0
  34. package/v3/dist/cli/utils/file-discovery.js.map +1 -0
  35. package/v3/dist/coordination/constants.d.ts +1 -1
  36. package/v3/dist/coordination/constants.js +1 -1
  37. package/v3/dist/coordination/task-executor.d.ts.map +1 -1
  38. package/v3/dist/coordination/task-executor.js +612 -104
  39. package/v3/dist/coordination/task-executor.js.map +1 -1
  40. package/v3/dist/domains/code-intelligence/coordinator-hypergraph.js +2 -2
  41. package/v3/dist/domains/code-intelligence/coordinator-hypergraph.js.map +1 -1
  42. package/v3/dist/domains/code-intelligence/coordinator.d.ts.map +1 -1
  43. package/v3/dist/domains/code-intelligence/coordinator.js +10 -3
  44. package/v3/dist/domains/code-intelligence/coordinator.js.map +1 -1
  45. package/v3/dist/domains/code-intelligence/services/metric-collector/index.d.ts.map +1 -1
  46. package/v3/dist/domains/code-intelligence/services/metric-collector/index.js +10 -0
  47. package/v3/dist/domains/code-intelligence/services/metric-collector/index.js.map +1 -1
  48. package/v3/dist/domains/code-intelligence/services/metric-collector/interfaces.d.ts +7 -1
  49. package/v3/dist/domains/code-intelligence/services/metric-collector/interfaces.d.ts.map +1 -1
  50. package/v3/dist/domains/code-intelligence/services/metric-collector/interfaces.js +10 -1
  51. package/v3/dist/domains/code-intelligence/services/metric-collector/interfaces.js.map +1 -1
  52. package/v3/dist/domains/code-intelligence/services/metric-collector/loc-counter.js +34 -10
  53. package/v3/dist/domains/code-intelligence/services/metric-collector/loc-counter.js.map +1 -1
  54. package/v3/dist/domains/coverage-analysis/coordinator.js +1 -1
  55. package/v3/dist/domains/coverage-analysis/services/coverage-analyzer.js +1 -1
  56. package/v3/dist/domains/coverage-analysis/services/coverage-embedder.d.ts +1 -1
  57. package/v3/dist/domains/coverage-analysis/services/coverage-embedder.js +1 -1
  58. package/v3/dist/domains/coverage-analysis/services/gap-detector.js +1 -1
  59. package/v3/dist/domains/coverage-analysis/services/ghost-coverage-analyzer.js +1 -1
  60. package/v3/dist/domains/coverage-analysis/services/hnsw-index.d.ts +10 -1
  61. package/v3/dist/domains/coverage-analysis/services/hnsw-index.d.ts.map +1 -1
  62. package/v3/dist/domains/coverage-analysis/services/hnsw-index.js +40 -5
  63. package/v3/dist/domains/coverage-analysis/services/hnsw-index.js.map +1 -1
  64. package/v3/dist/domains/coverage-analysis/services/sublinear-analyzer.d.ts +1 -1
  65. package/v3/dist/domains/coverage-analysis/services/sublinear-analyzer.js +1 -1
  66. package/v3/dist/init/fleet-integration.d.ts.map +1 -1
  67. package/v3/dist/init/fleet-integration.js +3 -4
  68. package/v3/dist/init/fleet-integration.js.map +1 -1
  69. package/v3/dist/init/init-wizard-hooks.d.ts +8 -1
  70. package/v3/dist/init/init-wizard-hooks.d.ts.map +1 -1
  71. package/v3/dist/init/init-wizard-hooks.js +47 -39
  72. package/v3/dist/init/init-wizard-hooks.js.map +1 -1
  73. package/v3/dist/init/init-wizard-migration.d.ts.map +1 -1
  74. package/v3/dist/init/init-wizard-migration.js +3 -7
  75. package/v3/dist/init/init-wizard-migration.js.map +1 -1
  76. package/v3/dist/init/init-wizard-steps.d.ts.map +1 -1
  77. package/v3/dist/init/init-wizard-steps.js +3 -4
  78. package/v3/dist/init/init-wizard-steps.js.map +1 -1
  79. package/v3/dist/init/migration/data-migrator.d.ts.map +1 -1
  80. package/v3/dist/init/migration/data-migrator.js +6 -10
  81. package/v3/dist/init/migration/data-migrator.js.map +1 -1
  82. package/v3/dist/init/migration/detector.d.ts.map +1 -1
  83. package/v3/dist/init/migration/detector.js +2 -4
  84. package/v3/dist/init/migration/detector.js.map +1 -1
  85. package/v3/dist/init/phases/01-detection.d.ts.map +1 -1
  86. package/v3/dist/init/phases/01-detection.js +2 -4
  87. package/v3/dist/init/phases/01-detection.js.map +1 -1
  88. package/v3/dist/init/phases/06-code-intelligence.d.ts.map +1 -1
  89. package/v3/dist/init/phases/06-code-intelligence.js +4 -6
  90. package/v3/dist/init/phases/06-code-intelligence.js.map +1 -1
  91. package/v3/dist/init/phases/07-hooks.d.ts +22 -1
  92. package/v3/dist/init/phases/07-hooks.d.ts.map +1 -1
  93. package/v3/dist/init/phases/07-hooks.js +137 -51
  94. package/v3/dist/init/phases/07-hooks.js.map +1 -1
  95. package/v3/dist/init/phases/12-verification.d.ts.map +1 -1
  96. package/v3/dist/init/phases/12-verification.js +2 -4
  97. package/v3/dist/init/phases/12-verification.js.map +1 -1
  98. package/v3/dist/init/settings-merge.d.ts +35 -0
  99. package/v3/dist/init/settings-merge.d.ts.map +1 -0
  100. package/v3/dist/init/settings-merge.js +140 -0
  101. package/v3/dist/init/settings-merge.js.map +1 -0
  102. package/v3/dist/integrations/agentic-flow/model-router/router.js +1 -1
  103. package/v3/dist/integrations/agentic-flow/model-router/router.js.map +1 -1
  104. package/v3/dist/integrations/agentic-flow/model-router/score-calculator.d.ts.map +1 -1
  105. package/v3/dist/integrations/agentic-flow/model-router/score-calculator.js +18 -3
  106. package/v3/dist/integrations/agentic-flow/model-router/score-calculator.js.map +1 -1
  107. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.d.ts +3 -3
  108. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.d.ts.map +1 -1
  109. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.js +18 -0
  110. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.js.map +1 -1
  111. package/v3/dist/integrations/embeddings/cache/EmbeddingCache.d.ts.map +1 -1
  112. package/v3/dist/integrations/embeddings/cache/EmbeddingCache.js +2 -2
  113. package/v3/dist/integrations/embeddings/cache/EmbeddingCache.js.map +1 -1
  114. package/v3/dist/kernel/constants.d.ts +1 -1
  115. package/v3/dist/kernel/constants.js +1 -1
  116. package/v3/dist/kernel/unified-memory-hnsw.d.ts.map +1 -1
  117. package/v3/dist/kernel/unified-memory-hnsw.js +25 -6
  118. package/v3/dist/kernel/unified-memory-hnsw.js.map +1 -1
  119. package/v3/dist/kernel/unified-memory-migration.d.ts.map +1 -1
  120. package/v3/dist/kernel/unified-memory-migration.js +3 -3
  121. package/v3/dist/kernel/unified-memory-migration.js.map +1 -1
  122. package/v3/dist/learning/metrics-tracker.d.ts.map +1 -1
  123. package/v3/dist/learning/metrics-tracker.js +2 -2
  124. package/v3/dist/learning/metrics-tracker.js.map +1 -1
  125. package/v3/dist/learning/sqlite-persistence.d.ts.map +1 -1
  126. package/v3/dist/learning/sqlite-persistence.js +3 -6
  127. package/v3/dist/learning/sqlite-persistence.js.map +1 -1
  128. package/v3/dist/learning/v2-to-v3-migration.d.ts.map +1 -1
  129. package/v3/dist/learning/v2-to-v3-migration.js +4 -5
  130. package/v3/dist/learning/v2-to-v3-migration.js.map +1 -1
  131. package/v3/dist/mcp/bundle.js +1266 -245
  132. package/v3/dist/mcp/handlers/domain-handler-configs.d.ts.map +1 -1
  133. package/v3/dist/mcp/handlers/domain-handler-configs.js +40 -31
  134. package/v3/dist/mcp/handlers/domain-handler-configs.js.map +1 -1
  135. package/v3/dist/mcp/handlers/task-handlers.d.ts.map +1 -1
  136. package/v3/dist/mcp/handlers/task-handlers.js +68 -5
  137. package/v3/dist/mcp/handlers/task-handlers.js.map +1 -1
  138. package/v3/dist/mcp/protocol-server.d.ts.map +1 -1
  139. package/v3/dist/mcp/protocol-server.js +16 -2
  140. package/v3/dist/mcp/protocol-server.js.map +1 -1
  141. package/v3/dist/mcp/tools/security-compliance/scan.d.ts +3 -1
  142. package/v3/dist/mcp/tools/security-compliance/scan.d.ts.map +1 -1
  143. package/v3/dist/mcp/tools/security-compliance/scan.js +417 -72
  144. package/v3/dist/mcp/tools/security-compliance/scan.js.map +1 -1
  145. package/v3/dist/planning/plan-executor.d.ts.map +1 -1
  146. package/v3/dist/planning/plan-executor.js +2 -2
  147. package/v3/dist/planning/plan-executor.js.map +1 -1
  148. package/v3/dist/shared/safe-db.d.ts +32 -0
  149. package/v3/dist/shared/safe-db.d.ts.map +1 -0
  150. package/v3/dist/shared/safe-db.js +41 -0
  151. package/v3/dist/shared/safe-db.js.map +1 -0
  152. package/v3/dist/sync/claude-flow-bridge.js +5 -5
  153. package/v3/dist/sync/claude-flow-bridge.js.map +1 -1
  154. package/v3/dist/sync/embeddings/sync-embedding-generator.js +3 -3
  155. package/v3/dist/sync/embeddings/sync-embedding-generator.js.map +1 -1
  156. package/v3/dist/sync/readers/sqlite-reader.d.ts.map +1 -1
  157. package/v3/dist/sync/readers/sqlite-reader.js +2 -2
  158. package/v3/dist/sync/readers/sqlite-reader.js.map +1 -1
  159. package/v3/package.json +1 -1
@@ -102,12 +102,18 @@ function detectTransformType(task) {
102
102
  * Load coverage data from common coverage file formats
103
103
  */
104
104
  async function loadCoverageData(targetPath) {
105
- // Try various coverage file locations
105
+ // Try various coverage file locations (JS, Python, Java, etc.)
106
106
  const coverageLocations = [
107
+ // JavaScript/TypeScript (Istanbul/nyc/vitest)
107
108
  path.join(targetPath, 'coverage', 'coverage-final.json'),
108
109
  path.join(targetPath, 'coverage', 'lcov.info'),
109
110
  path.join(targetPath, '.nyc_output', 'coverage-final.json'),
110
111
  path.join(targetPath, 'coverage-final.json'),
112
+ // Python (pytest-cov, coverage.py)
113
+ path.join(targetPath, 'coverage.xml'),
114
+ path.join(targetPath, 'htmlcov', 'status.json'),
115
+ // Cobertura (generic)
116
+ path.join(targetPath, 'cobertura-coverage.xml'),
111
117
  ];
112
118
  for (const coveragePath of coverageLocations) {
113
119
  try {
@@ -118,6 +124,9 @@ async function loadCoverageData(targetPath) {
118
124
  else if (coveragePath.endsWith('.info')) {
119
125
  return parseLcovInfo(content);
120
126
  }
127
+ else if (coveragePath.endsWith('.xml')) {
128
+ return parseCoberturaXml(content);
129
+ }
121
130
  }
122
131
  catch {
123
132
  // File not found, try next
@@ -290,6 +299,132 @@ function parseLcovInfo(content) {
290
299
  },
291
300
  };
292
301
  }
302
+ /**
303
+ * Parse Cobertura XML format (Python coverage.py, Java JaCoCo, etc.)
304
+ * Handles both coverage.xml and cobertura-coverage.xml formats.
305
+ * Uses attribute-order-independent extraction to handle output from
306
+ * different tools (coverage.py, JaCoCo, Cobertura) that order attributes differently.
307
+ */
308
+ function parseCoberturaXml(content) {
309
+ const files = [];
310
+ // Helper: extract a named attribute from an XML element string, order-independent
311
+ function attr(element, name) {
312
+ const match = element.match(new RegExp(`${name}=["']([^"']*)["']`));
313
+ return match ? match[1] : null;
314
+ }
315
+ // Find all <class ...> elements (handles any attribute order)
316
+ const classRegex = /<class\s[^>]*?>/g;
317
+ let classMatch;
318
+ while ((classMatch = classRegex.exec(content)) !== null) {
319
+ const classTag = classMatch[0];
320
+ const filename = attr(classTag, 'filename');
321
+ if (!filename)
322
+ continue;
323
+ const lineRate = parseFloat(attr(classTag, 'line-rate') || 'NaN');
324
+ const branchRate = parseFloat(attr(classTag, 'branch-rate') || 'NaN');
325
+ // Find the </class> boundary for this element
326
+ const classStart = classMatch.index;
327
+ const classEnd = content.indexOf('</class>', classStart);
328
+ const classContent = classEnd > classStart
329
+ ? content.slice(classStart, classEnd)
330
+ : '';
331
+ let linesTotal = 0, linesCovered = 0;
332
+ let branchesTotal = 0, branchesCovered = 0;
333
+ let functionsTotal = 0, functionsCovered = 0;
334
+ const uncoveredLines = [];
335
+ const uncoveredBranches = [];
336
+ // Parse <line> elements (attribute-order-independent)
337
+ const lineRegex = /<line\s([^>]*?)\/>/g;
338
+ let lineMatch;
339
+ while ((lineMatch = lineRegex.exec(classContent)) !== null) {
340
+ const lineTag = lineMatch[1];
341
+ const lineNum = parseInt(attr(lineTag, 'number') || '0', 10);
342
+ const hits = parseInt(attr(lineTag, 'hits') || '0', 10);
343
+ const isBranch = attr(lineTag, 'branch') === 'true';
344
+ const condCoverage = attr(lineTag, 'condition-coverage');
345
+ linesTotal++;
346
+ if (hits > 0) {
347
+ linesCovered++;
348
+ }
349
+ else {
350
+ uncoveredLines.push(lineNum);
351
+ }
352
+ if (isBranch) {
353
+ branchesTotal++;
354
+ const condPct = condCoverage ? parseInt(condCoverage, 10) : (hits > 0 ? 100 : 0);
355
+ if (condPct === 100) {
356
+ branchesCovered++;
357
+ }
358
+ else {
359
+ uncoveredBranches.push(lineNum);
360
+ }
361
+ }
362
+ }
363
+ // Parse <method> elements for function coverage
364
+ const methodRegex = /<method\s([^>]*?)>/g;
365
+ let methodMatch;
366
+ while ((methodMatch = methodRegex.exec(classContent)) !== null) {
367
+ functionsTotal++;
368
+ // Check if method has any line hits (look for <line> within this method)
369
+ const methodStart = methodMatch.index;
370
+ const methodEnd = classContent.indexOf('</method>', methodStart);
371
+ if (methodEnd > methodStart) {
372
+ const methodContent = classContent.slice(methodStart, methodEnd);
373
+ const methodLineRegex = /<line\s([^>]*?)\/>/g;
374
+ let hasHits = false;
375
+ let mLineMatch;
376
+ while ((mLineMatch = methodLineRegex.exec(methodContent)) !== null) {
377
+ if (parseInt(attr(mLineMatch[1], 'hits') || '0', 10) > 0) {
378
+ hasHits = true;
379
+ break;
380
+ }
381
+ }
382
+ if (hasHits)
383
+ functionsCovered++;
384
+ }
385
+ }
386
+ // If no lines parsed, estimate from rates
387
+ if (linesTotal === 0 && !isNaN(lineRate)) {
388
+ linesTotal = 1;
389
+ linesCovered = lineRate >= 0.5 ? 1 : 0;
390
+ }
391
+ files.push({
392
+ path: filename,
393
+ lines: { covered: linesCovered, total: linesTotal },
394
+ branches: { covered: branchesCovered, total: branchesTotal },
395
+ functions: { covered: functionsCovered, total: functionsTotal },
396
+ statements: { covered: linesCovered, total: linesTotal },
397
+ uncoveredLines,
398
+ uncoveredBranches,
399
+ });
400
+ }
401
+ // Calculate summary
402
+ let totalLines = 0, totalCoveredLines = 0;
403
+ let totalBranches = 0, totalCoveredBranches = 0;
404
+ let totalFunctions = 0, totalCoveredFunctions = 0;
405
+ for (const file of files) {
406
+ totalLines += file.lines.total;
407
+ totalCoveredLines += file.lines.covered;
408
+ totalBranches += file.branches.total;
409
+ totalCoveredBranches += file.branches.covered;
410
+ totalFunctions += file.functions.total;
411
+ totalCoveredFunctions += file.functions.covered;
412
+ }
413
+ // Also try to extract top-level summary from <coverage> element (order-independent)
414
+ const coverageTag = content.match(/<coverage\s[^>]*?>/);
415
+ const summaryLineRate = coverageTag ? parseFloat(attr(coverageTag[0], 'line-rate') || 'NaN') * 100 : NaN;
416
+ const summaryBranchRate = coverageTag ? parseFloat(attr(coverageTag[0], 'branch-rate') || 'NaN') * 100 : NaN;
417
+ return {
418
+ files,
419
+ summary: {
420
+ line: !isNaN(summaryLineRate) ? summaryLineRate : (totalLines > 0 ? (totalCoveredLines / totalLines) * 100 : 0),
421
+ branch: !isNaN(summaryBranchRate) ? summaryBranchRate : (totalBranches > 0 ? (totalCoveredBranches / totalBranches) * 100 : 0),
422
+ function: totalFunctions > 0 ? (totalCoveredFunctions / totalFunctions) * 100 : 0,
423
+ statement: totalLines > 0 ? (totalCoveredLines / totalLines) * 100 : 0,
424
+ files: files.length,
425
+ },
426
+ };
427
+ }
293
428
  /**
294
429
  * Discover source files in a directory
295
430
  */
@@ -297,7 +432,20 @@ async function discoverSourceFiles(targetPath, options = {}) {
297
432
  const files = [];
298
433
  const { includeTests = true, languages } = options;
299
434
  // Determine file extensions to include
300
- let extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
435
+ // Default: scan ALL common source languages unless explicitly filtered
436
+ let extensions = [
437
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', // JavaScript/TypeScript
438
+ '.py', '.pyw', // Python
439
+ '.go', // Go
440
+ '.rs', // Rust
441
+ '.java', '.kt', '.kts', // Java/Kotlin
442
+ '.rb', // Ruby
443
+ '.cs', // C#
444
+ '.php', // PHP
445
+ '.swift', // Swift
446
+ '.c', '.h', '.cpp', '.hpp', '.cc', // C/C++
447
+ '.scala', // Scala
448
+ ];
301
449
  if (languages && languages.length > 0) {
302
450
  extensions = [];
303
451
  for (const lang of languages) {
@@ -306,7 +454,27 @@ async function discoverSourceFiles(targetPath, options = {}) {
306
454
  if (lang === 'javascript')
307
455
  extensions.push('.js', '.jsx', '.mjs', '.cjs');
308
456
  if (lang === 'python')
309
- extensions.push('.py');
457
+ extensions.push('.py', '.pyw');
458
+ if (lang === 'go')
459
+ extensions.push('.go');
460
+ if (lang === 'rust')
461
+ extensions.push('.rs');
462
+ if (lang === 'java')
463
+ extensions.push('.java');
464
+ if (lang === 'kotlin')
465
+ extensions.push('.kt', '.kts');
466
+ if (lang === 'ruby')
467
+ extensions.push('.rb');
468
+ if (lang === 'csharp' || lang === 'c#')
469
+ extensions.push('.cs');
470
+ if (lang === 'php')
471
+ extensions.push('.php');
472
+ if (lang === 'swift')
473
+ extensions.push('.swift');
474
+ if (lang === 'c' || lang === 'cpp' || lang === 'c++')
475
+ extensions.push('.c', '.h', '.cpp', '.hpp', '.cc');
476
+ if (lang === 'scala')
477
+ extensions.push('.scala');
310
478
  }
311
479
  }
312
480
  async function walkDir(dir) {
@@ -316,7 +484,9 @@ async function discoverSourceFiles(targetPath, options = {}) {
316
484
  const fullPath = path.join(dir, entry.name);
317
485
  // Skip common non-source directories
318
486
  if (entry.isDirectory()) {
319
- if (['node_modules', '.git', 'dist', 'build', 'coverage', '.nyc_output'].includes(entry.name)) {
487
+ if (['node_modules', '.git', 'dist', 'build', 'coverage', '.nyc_output',
488
+ '__pycache__', '.venv', 'venv', '.tox', '.mypy_cache', 'target',
489
+ '.gradle', 'vendor', '.bundle'].includes(entry.name)) {
320
490
  continue;
321
491
  }
322
492
  await walkDir(fullPath);
@@ -515,7 +685,15 @@ export class DomainTaskExecutor {
515
685
  }
516
686
  else if (payload.sourceCode) {
517
687
  // Write temporary file for analysis if only source code provided
518
- const tempPath = `/tmp/aqe-temp-${uuidv4()}.ts`;
688
+ // Use correct file extension based on language parameter
689
+ const langExtMap = {
690
+ python: '.py', typescript: '.ts', javascript: '.js',
691
+ go: '.go', rust: '.rs', java: '.java', ruby: '.rb',
692
+ kotlin: '.kt', csharp: '.cs', php: '.php', swift: '.swift',
693
+ cpp: '.cpp', c: '.c', scala: '.scala',
694
+ };
695
+ const ext = langExtMap[payload.language?.toLowerCase() || 'typescript'] || '.ts';
696
+ const tempPath = `/tmp/aqe-temp-${uuidv4()}${ext}`;
519
697
  await fs.writeFile(tempPath, payload.sourceCode, 'utf-8');
520
698
  sourceFiles = [tempPath];
521
699
  }
@@ -642,14 +820,125 @@ export class DomainTaskExecutor {
642
820
  sast: payload.sast !== false,
643
821
  dast: payload.dast || false,
644
822
  },
645
- warning: `No TypeScript/JavaScript files found in ${targetPath}`,
823
+ warning: `No source files found in ${targetPath}`,
646
824
  });
647
825
  }
648
- // Convert string paths to FilePath value objects
649
- const filePathObjects = filesToScan.map(filePath => FilePath.create(filePath));
650
- // Run SAST scan if requested
826
+ // Separate files by language capability
827
+ const jstsFiles = filesToScan.filter(f => /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(f));
828
+ const otherFiles = filesToScan.filter(f => !/\.(ts|tsx|js|jsx|mjs|cjs)$/.test(f));
829
+ // Run basic cross-language security patterns on non-JS/TS files
830
+ const crossLangVulns = [];
831
+ for (const filePath of otherFiles) {
832
+ try {
833
+ const content = await fs.readFile(filePath, 'utf-8');
834
+ const lines = content.split('\n');
835
+ const relPath = filePath.startsWith(targetPath)
836
+ ? filePath.slice(targetPath.length).replace(/^\//, '')
837
+ : filePath;
838
+ // Pattern: Hardcoded secrets/keys
839
+ // Fix #287: Use \w* around keywords to match SECRET_KEY, JWT_SECRET, API_TOKEN, etc.
840
+ const secretPatterns = [
841
+ { regex: /\w*(?:secret|password|passwd|api_key|apikey|private_key|jwt_secret)\w*\s*[=:]\s*['"][^'"]{4,}['"]/gi, title: 'Hardcoded secret', severity: 'critical' },
842
+ { regex: /\w*(?:token|auth_token|access_key|secret_key)\w*\s*[=:]\s*['"][^'"]{8,}['"]/gi, title: 'Hardcoded credential', severity: 'critical' },
843
+ { regex: /(?:AWS_SECRET|GITHUB_TOKEN|SLACK_TOKEN|OPENAI_API_KEY)\s*[=:]\s*['"][^'"]+['"]/gi, title: 'Hardcoded cloud credential', severity: 'critical' },
844
+ ];
845
+ for (const pattern of secretPatterns) {
846
+ for (let i = 0; i < lines.length; i++) {
847
+ // Use matchAll to find ALL secrets on a single line (not just first)
848
+ const matches = [...lines[i].matchAll(pattern.regex)];
849
+ for (const _m of matches) {
850
+ crossLangVulns.push({
851
+ title: pattern.title,
852
+ severity: pattern.severity,
853
+ location: { file: relPath, line: i + 1 },
854
+ description: `Potential hardcoded secret found at line ${i + 1}`,
855
+ category: 'sensitive-data',
856
+ });
857
+ }
858
+ }
859
+ }
860
+ // Pattern: SQL injection risks
861
+ const sqlPatterns = /(?:execute|query|cursor\.execute)\s*\(\s*(?:f['"]|['"].*%s|['"].*\+\s*\w)/gi;
862
+ for (let i = 0; i < lines.length; i++) {
863
+ if (sqlPatterns.test(lines[i])) {
864
+ crossLangVulns.push({
865
+ title: 'Potential SQL injection',
866
+ severity: 'high',
867
+ location: { file: relPath, line: i + 1 },
868
+ description: 'String interpolation in SQL query — use parameterized queries',
869
+ category: 'injection',
870
+ });
871
+ }
872
+ sqlPatterns.lastIndex = 0;
873
+ }
874
+ // Pattern: CORS wildcard (multi-framework)
875
+ const corsPatterns = [
876
+ /allow_origins\s*=\s*\[?\s*['"]?\*['"]?\s*\]?/i, // Python FastAPI/Flask
877
+ /cors\(\s*\{[^}]*origin:\s*['"]?\*['"]?/i, // Express.js cors()
878
+ /Access-Control-Allow-Origin['":\s]+\*/i, // Raw header / Nginx / .htaccess
879
+ /@CrossOrigin\(\s*origins?\s*=\s*["']\*["']/i, // Spring Boot
880
+ /\.Header\(\)\.Set\(["']Access-Control-Allow-Origin["'],\s*["']\*["']/i, // Go
881
+ ];
882
+ for (const corsPattern of corsPatterns) {
883
+ if (corsPattern.test(content)) {
884
+ crossLangVulns.push({
885
+ title: 'CORS wildcard origin',
886
+ severity: 'high',
887
+ location: { file: relPath, line: lines.findIndex(l => corsPattern.test(l)) + 1 },
888
+ description: 'CORS configured with wildcard (*) origin — restrict to specific domains',
889
+ category: 'security-misconfiguration',
890
+ });
891
+ break; // One CORS finding per file is enough
892
+ }
893
+ }
894
+ // Pattern: Debug/development mode enabled
895
+ if (/(?:DEBUG|debug)\s*[=:]\s*(?:True|true|1)/i.test(content)) {
896
+ crossLangVulns.push({
897
+ title: 'Debug mode enabled',
898
+ severity: 'medium',
899
+ location: { file: relPath, line: lines.findIndex(l => /DEBUG\s*[=:]\s*(?:True|true|1)/i.test(l)) + 1 },
900
+ description: 'Debug mode should be disabled in production',
901
+ category: 'security-misconfiguration',
902
+ });
903
+ }
904
+ // Pattern: Eval/exec usage
905
+ if (/\b(?:eval|exec)\s*\(/i.test(content)) {
906
+ crossLangVulns.push({
907
+ title: 'Dangerous eval/exec usage',
908
+ severity: 'high',
909
+ location: { file: relPath, line: lines.findIndex(l => /\b(?:eval|exec)\s*\(/.test(l)) + 1 },
910
+ description: 'eval/exec can lead to code injection — avoid using with user input',
911
+ category: 'injection',
912
+ });
913
+ }
914
+ }
915
+ catch {
916
+ // Skip unreadable files
917
+ }
918
+ }
919
+ // Also check dependency manifests for known vulnerable packages
920
+ const depManifests = ['requirements.txt', 'pyproject.toml', 'Gemfile', 'go.mod', 'Cargo.toml'];
921
+ for (const manifest of depManifests) {
922
+ const manifestPath = path.join(targetPath, manifest);
923
+ try {
924
+ await fs.access(manifestPath);
925
+ crossLangVulns.push({
926
+ title: 'Dependency audit recommended',
927
+ severity: 'informational',
928
+ location: { file: manifest, line: 1 },
929
+ description: `Found ${manifest} — run language-specific dependency audit (e.g., pip-audit, npm audit, cargo audit)`,
930
+ category: 'dependencies',
931
+ });
932
+ }
933
+ catch {
934
+ // Manifest doesn't exist
935
+ }
936
+ }
937
+ // Convert JS/TS file paths to FilePath value objects for the SAST scanner
938
+ const filePathObjects = jstsFiles.map(filePath => FilePath.create(filePath));
939
+ // Run SAST scan on JS/TS files if requested and files exist
651
940
  let sastResult = null;
652
- if (payload.sast !== false) {
941
+ if (payload.sast !== false && filePathObjects.length > 0) {
653
942
  const result = await scanner.scanFiles(filePathObjects);
654
943
  if (result.success) {
655
944
  sastResult = result.value;
@@ -667,18 +956,26 @@ export class DomainTaskExecutor {
667
956
  dastResult = result.value;
668
957
  }
669
958
  }
670
- // Combine results - create mutable copy
959
+ // Combine results from all scan sources - SAST, DAST, and cross-language patterns
960
+ const crossLangSeverityCounts = {
961
+ critical: crossLangVulns.filter(v => v.severity === 'critical').length,
962
+ high: crossLangVulns.filter(v => v.severity === 'high').length,
963
+ medium: crossLangVulns.filter(v => v.severity === 'medium').length,
964
+ low: crossLangVulns.filter(v => v.severity === 'low').length,
965
+ informational: crossLangVulns.filter(v => v.severity === 'informational').length,
966
+ };
671
967
  const summary = {
672
- critical: (sastResult?.summary?.critical || 0) + (dastResult?.summary?.critical || 0),
673
- high: (sastResult?.summary?.high || 0) + (dastResult?.summary?.high || 0),
674
- medium: (sastResult?.summary?.medium || 0) + (dastResult?.summary?.medium || 0),
675
- low: (sastResult?.summary?.low || 0) + (dastResult?.summary?.low || 0),
676
- informational: (sastResult?.summary?.informational || 0) + (dastResult?.summary?.informational || 0),
968
+ critical: (sastResult?.summary?.critical || 0) + (dastResult?.summary?.critical || 0) + crossLangSeverityCounts.critical,
969
+ high: (sastResult?.summary?.high || 0) + (dastResult?.summary?.high || 0) + crossLangSeverityCounts.high,
970
+ medium: (sastResult?.summary?.medium || 0) + (dastResult?.summary?.medium || 0) + crossLangSeverityCounts.medium,
971
+ low: (sastResult?.summary?.low || 0) + (dastResult?.summary?.low || 0) + crossLangSeverityCounts.low,
972
+ informational: (sastResult?.summary?.informational || 0) + (dastResult?.summary?.informational || 0) + crossLangSeverityCounts.informational,
677
973
  };
678
- // Extract top vulnerabilities
974
+ // Extract top vulnerabilities from all sources
679
975
  const allVulns = [
680
976
  ...(sastResult?.vulnerabilities || []),
681
977
  ...(dastResult?.vulnerabilities || []),
978
+ ...crossLangVulns,
682
979
  ];
683
980
  const topVulnerabilities = allVulns
684
981
  .sort((a, b) => {
@@ -709,7 +1006,12 @@ export class DomainTaskExecutor {
709
1006
  dast: payload.dast || false,
710
1007
  },
711
1008
  filesScanned: filesToScan.length,
1009
+ jstsFilesScanned: jstsFiles.length,
1010
+ otherFilesScanned: otherFiles.length,
712
1011
  coverage: sastResult?.coverage,
1012
+ ...(otherFiles.length > 0 && jstsFiles.length === 0 ? {
1013
+ note: 'Non-JS/TS files were scanned with cross-language pattern matching. For deeper analysis, use language-specific security tools.',
1014
+ } : {}),
713
1015
  });
714
1016
  }
715
1017
  catch (error) {
@@ -735,9 +1037,9 @@ export class DomainTaskExecutor {
735
1037
  edgesCreated: 0,
736
1038
  target: targetPath,
737
1039
  incremental: payload.incremental || false,
738
- languages: payload.languages || ['typescript', 'javascript'],
1040
+ languages: payload.languages || [],
739
1041
  duration: Date.now() - startTime,
740
- warning: `No source files found in ${targetPath}`,
1042
+ warning: `No source files found in ${targetPath}. Searched for: TypeScript, JavaScript, Python, Go, Rust, Java, Ruby, C/C++, and more.`,
741
1043
  });
742
1044
  }
743
1045
  // Use the real KnowledgeGraphService to index files
@@ -753,14 +1055,21 @@ export class DomainTaskExecutor {
753
1055
  const indexResult = result.value;
754
1056
  // Detect languages from files
755
1057
  const detectedLanguages = new Set();
1058
+ const extToLang = {
1059
+ ts: 'typescript', tsx: 'typescript',
1060
+ js: 'javascript', jsx: 'javascript', mjs: 'javascript', cjs: 'javascript',
1061
+ py: 'python', pyw: 'python',
1062
+ go: 'go', rs: 'rust',
1063
+ java: 'java', kt: 'kotlin', kts: 'kotlin',
1064
+ rb: 'ruby', cs: 'csharp', php: 'php', swift: 'swift',
1065
+ c: 'c', h: 'c', cpp: 'cpp', hpp: 'cpp', cc: 'cpp',
1066
+ scala: 'scala',
1067
+ };
756
1068
  for (const file of filesToIndex) {
757
1069
  const ext = path.extname(file).slice(1);
758
- if (['ts', 'tsx'].includes(ext))
759
- detectedLanguages.add('typescript');
760
- if (['js', 'jsx', 'mjs', 'cjs'].includes(ext))
761
- detectedLanguages.add('javascript');
762
- if (ext === 'py')
763
- detectedLanguages.add('python');
1070
+ const lang = extToLang[ext];
1071
+ if (lang)
1072
+ detectedLanguages.add(lang);
764
1073
  }
765
1074
  return ok({
766
1075
  filesIndexed: indexResult.filesIndexed,
@@ -848,115 +1157,314 @@ export class DomainTaskExecutor {
848
1157
  return err(toError(error));
849
1158
  }
850
1159
  });
851
- // Register test execution handler
1160
+ // Register test execution handler - runs real tests via child process
852
1161
  this.taskHandlers.set('execute-tests', async (task) => {
853
1162
  const payload = task.payload;
854
- // In production, would actually run tests via test runner
855
- const testCount = payload.testFiles?.length || 10;
856
- const passed = Math.floor(testCount * 0.9);
857
- const failed = testCount - passed;
858
- return ok({
859
- total: testCount,
860
- passed,
861
- failed,
862
- skipped: 0,
863
- duration: testCount * 50, // ~50ms per test
864
- coverage: 82.5,
865
- failedTests: failed > 0 ? ['example.test.ts:42'] : [],
866
- });
1163
+ try {
1164
+ const { execSync } = await import('child_process');
1165
+ const testFiles = payload.testFiles || [];
1166
+ if (testFiles.length === 0) {
1167
+ return ok({
1168
+ total: 0, passed: 0, failed: 0, skipped: 0,
1169
+ duration: 0, coverage: 0, failedTests: [],
1170
+ warning: 'No test files specified. Provide testFiles array with paths to test files.',
1171
+ });
1172
+ }
1173
+ // Attempt to run tests using common test runners
1174
+ const cwd = process.cwd();
1175
+ let output;
1176
+ try {
1177
+ // Try vitest first, then jest, then mocha
1178
+ output = execSync(`npx vitest run ${testFiles.join(' ')} --reporter=json 2>/dev/null || npx jest ${testFiles.join(' ')} --json 2>/dev/null`, { cwd, timeout: 120000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
1179
+ }
1180
+ catch (execError) {
1181
+ // Test runner may exit non-zero when tests fail — that's expected
1182
+ output = execError.stdout || '';
1183
+ }
1184
+ // Try to parse JSON output from test runner
1185
+ try {
1186
+ const jsonStart = output.indexOf('{');
1187
+ if (jsonStart >= 0) {
1188
+ const json = JSON.parse(output.slice(jsonStart));
1189
+ // vitest format
1190
+ if (json.testResults) {
1191
+ const total = json.numTotalTests || 0;
1192
+ const passed = json.numPassedTests || 0;
1193
+ const failed = json.numFailedTests || 0;
1194
+ return ok({ total, passed, failed, skipped: total - passed - failed, duration: 0, coverage: 0, failedTests: [] });
1195
+ }
1196
+ }
1197
+ }
1198
+ catch {
1199
+ // JSON parsing failed — return raw info
1200
+ }
1201
+ return ok({
1202
+ total: testFiles.length, passed: 0, failed: 0, skipped: 0,
1203
+ duration: 0, coverage: 0, failedTests: [],
1204
+ warning: 'Could not parse test runner output. Check that vitest or jest is installed.',
1205
+ rawOutput: output.slice(0, 500),
1206
+ });
1207
+ }
1208
+ catch (error) {
1209
+ return err(toError(error));
1210
+ }
867
1211
  });
868
- // Register defect prediction handler
1212
+ // Register defect prediction handler - REAL IMPLEMENTATION
869
1213
  this.taskHandlers.set('predict-defects', async (task) => {
870
1214
  const payload = task.payload;
871
- return ok({
872
- predictedDefects: [
873
- {
874
- file: `${payload.target}/complex-module.ts`,
875
- probability: 0.78,
876
- reason: 'High cyclomatic complexity combined with low test coverage',
877
- },
878
- {
879
- file: `${payload.target}/legacy-handler.ts`,
880
- probability: 0.65,
881
- reason: 'Frequent changes in recent commits with error-prone patterns',
882
- },
883
- ],
884
- riskScore: 42,
885
- recommendations: [
886
- 'Add integration tests for complex-module.ts',
887
- 'Refactor legacy-handler.ts to reduce complexity',
888
- ],
889
- });
1215
+ try {
1216
+ const targetPath = payload.target || process.cwd();
1217
+ const minConfidence = payload.minConfidence || 0.5;
1218
+ // Discover actual source files in the target directory
1219
+ const sourceFiles = await discoverSourceFiles(targetPath, { includeTests: false });
1220
+ if (sourceFiles.length === 0) {
1221
+ return ok({
1222
+ predictedDefects: [],
1223
+ riskScore: 0,
1224
+ recommendations: [
1225
+ `No source files found in ${targetPath}. Ensure the path contains source code files.`,
1226
+ ],
1227
+ warning: `No source files found in ${targetPath}`,
1228
+ filesAnalyzed: 0,
1229
+ });
1230
+ }
1231
+ // Analyze each file for defect indicators based on real metrics
1232
+ const predictedDefects = [];
1233
+ for (const filePath of sourceFiles) {
1234
+ try {
1235
+ const content = await fs.readFile(filePath, 'utf-8');
1236
+ const lines = content.split('\n');
1237
+ const lineCount = lines.length;
1238
+ // Calculate complexity indicators from real code
1239
+ let probability = 0;
1240
+ const reasons = [];
1241
+ // Factor 1: File size (large files are more defect-prone)
1242
+ if (lineCount > 500) {
1243
+ probability += 0.25;
1244
+ reasons.push(`Large file (${lineCount} lines)`);
1245
+ }
1246
+ else if (lineCount > 300) {
1247
+ probability += 0.15;
1248
+ reasons.push(`Medium-large file (${lineCount} lines)`);
1249
+ }
1250
+ // Factor 2: Cyclomatic complexity indicators
1251
+ const branchKeywords = content.match(/\b(if|else|switch|case|for|while|catch|&&|\|\|)\b/g) || [];
1252
+ const branchDensity = branchKeywords.length / Math.max(lineCount, 1);
1253
+ if (branchDensity > 0.15) {
1254
+ probability += 0.25;
1255
+ reasons.push(`High branch density (${branchKeywords.length} branches in ${lineCount} lines)`);
1256
+ }
1257
+ else if (branchDensity > 0.08) {
1258
+ probability += 0.10;
1259
+ reasons.push('Moderate branch complexity');
1260
+ }
1261
+ // Factor 3: Deeply nested code
1262
+ const maxIndent = Math.max(...lines.map(l => {
1263
+ const match = l.match(/^(\s*)/);
1264
+ return match ? match[1].length : 0;
1265
+ }));
1266
+ if (maxIndent > 20) {
1267
+ probability += 0.15;
1268
+ reasons.push('Deep nesting detected');
1269
+ }
1270
+ // Factor 4: TODO/FIXME/HACK comments
1271
+ const debtComments = (content.match(/\b(TODO|FIXME|HACK|XXX|WORKAROUND)\b/gi) || []).length;
1272
+ if (debtComments > 3) {
1273
+ probability += 0.15;
1274
+ reasons.push(`${debtComments} technical debt markers`);
1275
+ }
1276
+ // Factor 5: Long functions (heuristic)
1277
+ const functionStarts = (content.match(/\b(function|def|func|async)\b/g) || []).length;
1278
+ if (functionStarts > 0 && lineCount / functionStarts > 80) {
1279
+ probability += 0.10;
1280
+ reasons.push('Potentially long functions');
1281
+ }
1282
+ probability = Math.min(probability, 0.95);
1283
+ if (probability >= minConfidence) {
1284
+ // Use relative path for readability
1285
+ const relativePath = filePath.startsWith(targetPath)
1286
+ ? filePath.slice(targetPath.length).replace(/^\//, '')
1287
+ : filePath;
1288
+ predictedDefects.push({
1289
+ file: relativePath,
1290
+ probability: Math.round(probability * 100) / 100,
1291
+ reason: reasons.join('; '),
1292
+ });
1293
+ }
1294
+ }
1295
+ catch {
1296
+ // Skip files that can't be read
1297
+ }
1298
+ }
1299
+ // Sort by probability descending
1300
+ predictedDefects.sort((a, b) => b.probability - a.probability);
1301
+ // Calculate overall risk score
1302
+ const avgProb = predictedDefects.length > 0
1303
+ ? predictedDefects.reduce((sum, d) => sum + d.probability, 0) / predictedDefects.length
1304
+ : 0;
1305
+ const riskScore = Math.round(avgProb * 100);
1306
+ // Generate recommendations from actual findings
1307
+ const recommendations = [];
1308
+ if (predictedDefects.length > 0) {
1309
+ recommendations.push(`${predictedDefects.length} files flagged for potential defects out of ${sourceFiles.length} analyzed`);
1310
+ const topFile = predictedDefects[0];
1311
+ recommendations.push(`Highest risk: ${topFile.file} (${Math.round(topFile.probability * 100)}%) — ${topFile.reason}`);
1312
+ }
1313
+ if (predictedDefects.some(d => d.reason.includes('Large file'))) {
1314
+ recommendations.push('Consider splitting large files to reduce complexity');
1315
+ }
1316
+ if (predictedDefects.some(d => d.reason.includes('technical debt'))) {
1317
+ recommendations.push('Address TODO/FIXME comments to reduce technical debt');
1318
+ }
1319
+ if (predictedDefects.length === 0) {
1320
+ recommendations.push('No files exceeded the defect probability threshold — code looks healthy');
1321
+ }
1322
+ return ok({
1323
+ predictedDefects: predictedDefects.slice(0, 20), // Top 20
1324
+ riskScore,
1325
+ recommendations,
1326
+ filesAnalyzed: sourceFiles.length,
1327
+ });
1328
+ }
1329
+ catch (error) {
1330
+ return err(toError(error));
1331
+ }
890
1332
  });
891
1333
  // Register requirements validation handler
892
1334
  this.taskHandlers.set('validate-requirements', async (task) => {
893
1335
  const payload = task.payload;
894
- return ok({
895
- requirementsAnalyzed: 15,
896
- testable: 12,
897
- ambiguous: 2,
898
- untestable: 1,
899
- coverage: 80,
900
- bddScenarios: payload.generateBDD ? [
901
- 'Given a user is logged in, When they view the dashboard, Then they see their metrics',
902
- 'Given an API request fails, When the retry limit is exceeded, Then an error is returned',
903
- ] : [],
904
- });
1336
+ try {
1337
+ const targetPath = payload.requirementsPath || process.cwd();
1338
+ // Look for requirements files (markdown, feature files, etc.)
1339
+ const reqFiles = await discoverSourceFiles(targetPath, {
1340
+ includeTests: false,
1341
+ languages: [],
1342
+ });
1343
+ // Scan for requirement-like files
1344
+ const reqPatterns = ['.md', '.feature', '.gherkin', '.txt', '.rst'];
1345
+ const requirementFiles = [];
1346
+ for (const f of reqFiles) {
1347
+ if (reqPatterns.some(ext => f.endsWith(ext))) {
1348
+ requirementFiles.push(f);
1349
+ }
1350
+ }
1351
+ return ok({
1352
+ requirementsAnalyzed: requirementFiles.length,
1353
+ testable: 0,
1354
+ ambiguous: 0,
1355
+ untestable: 0,
1356
+ coverage: 0,
1357
+ bddScenarios: [],
1358
+ warning: requirementFiles.length === 0
1359
+ ? 'No requirement files (.md, .feature, .gherkin) found. Provide requirementsPath or add requirement docs.'
1360
+ : 'Requirements validation requires LLM analysis. File inventory returned — use task_orchestrate for deep analysis.',
1361
+ files: requirementFiles.map(f => f.startsWith(targetPath) ? f.slice(targetPath.length + 1) : f).slice(0, 20),
1362
+ });
1363
+ }
1364
+ catch (error) {
1365
+ return err(toError(error));
1366
+ }
905
1367
  });
906
1368
  // Register contract validation handler
907
1369
  this.taskHandlers.set('validate-contracts', async (task) => {
908
1370
  const payload = task.payload;
909
- return ok({
910
- contractPath: payload.contractPath,
911
- valid: true,
912
- breakingChanges: [],
913
- warnings: [
914
- 'Deprecated field "legacyId" should be removed in next major version',
915
- ],
916
- coverage: 95,
917
- });
1371
+ try {
1372
+ if (!payload.contractPath) {
1373
+ return ok({
1374
+ contractPath: '',
1375
+ valid: false,
1376
+ breakingChanges: [],
1377
+ warnings: [],
1378
+ coverage: 0,
1379
+ error: 'contractPath is required. Provide a path to an OpenAPI spec, JSON Schema, or Protocol Buffer file.',
1380
+ });
1381
+ }
1382
+ // Check if the contract file exists
1383
+ try {
1384
+ const content = await fs.readFile(payload.contractPath, 'utf-8');
1385
+ const isJson = payload.contractPath.endsWith('.json');
1386
+ const isYaml = payload.contractPath.endsWith('.yaml') || payload.contractPath.endsWith('.yml');
1387
+ // Basic structural validation
1388
+ if (isJson) {
1389
+ JSON.parse(content); // throws if invalid
1390
+ }
1391
+ return ok({
1392
+ contractPath: payload.contractPath,
1393
+ valid: true,
1394
+ format: isJson ? 'json' : isYaml ? 'yaml' : 'unknown',
1395
+ breakingChanges: [],
1396
+ warnings: [],
1397
+ linesAnalyzed: content.split('\n').length,
1398
+ note: 'Structural validation passed. For semantic contract testing, use consumer-driven contract tests.',
1399
+ });
1400
+ }
1401
+ catch (readErr) {
1402
+ return ok({
1403
+ contractPath: payload.contractPath,
1404
+ valid: false,
1405
+ breakingChanges: [],
1406
+ warnings: [],
1407
+ error: `Could not read or parse contract file: ${toErrorMessage(readErr)}`,
1408
+ });
1409
+ }
1410
+ }
1411
+ catch (error) {
1412
+ return err(toError(error));
1413
+ }
918
1414
  });
919
1415
  // Register accessibility test handler
920
1416
  this.taskHandlers.set('test-accessibility', async (task) => {
921
1417
  const payload = task.payload;
1418
+ // Accessibility testing requires a browser/DOM — return honest guidance
922
1419
  return ok({
923
- url: payload.url,
1420
+ url: payload.url || '',
924
1421
  standard: payload.standard || 'wcag21-aa',
925
- passed: true,
1422
+ passed: false,
926
1423
  violations: [],
927
- warnings: [
928
- { rule: 'color-contrast', impact: 'minor', element: 'nav > a' },
929
- ],
930
- score: 94,
1424
+ warnings: [],
1425
+ score: 0,
1426
+ note: 'Accessibility testing requires a browser environment (Puppeteer/Playwright). ' +
1427
+ 'Use tools like axe-core, pa11y, or Lighthouse CLI for WCAG compliance testing. ' +
1428
+ 'Example: npx pa11y ' + (payload.url || '<url>'),
931
1429
  });
932
1430
  });
933
1431
  // Register chaos test handler
934
1432
  this.taskHandlers.set('run-chaos', async (task) => {
935
1433
  const payload = task.payload;
1434
+ // Chaos testing requires infrastructure access — return honest guidance
936
1435
  return ok({
937
- faultType: payload.faultType,
938
- target: payload.target,
939
- dryRun: payload.dryRun,
940
- duration: payload.duration,
941
- systemBehavior: payload.dryRun ? 'simulated' : 'tested',
942
- resilience: {
943
- recovered: true,
944
- recoveryTime: 2500,
945
- dataLoss: false,
946
- },
1436
+ faultType: payload.faultType || 'unknown',
1437
+ target: payload.target || 'unknown',
1438
+ dryRun: payload.dryRun ?? true,
1439
+ duration: payload.duration || 0,
1440
+ systemBehavior: 'not-executed',
1441
+ resilience: null,
1442
+ note: 'Chaos engineering requires infrastructure-level fault injection. ' +
1443
+ 'Use tools like Chaos Monkey, Litmus, or toxiproxy for real resilience testing. ' +
1444
+ 'For Node.js apps, consider: nock (HTTP faults), testcontainers (dependency failures).',
947
1445
  });
948
1446
  });
949
1447
  // Register learning optimization handler
950
1448
  this.taskHandlers.set('optimize-learning', async (_task) => {
951
- return ok({
952
- patternsLearned: 12,
953
- modelsUpdated: 3,
954
- memoryConsolidated: true,
955
- recommendations: [
956
- 'Pattern recognition improved for error handling',
957
- 'Test generation templates optimized',
958
- ],
959
- });
1449
+ // Check actual pattern store state
1450
+ try {
1451
+ const memUsage = await import('../kernel/unified-memory-hnsw.js');
1452
+ return ok({
1453
+ patternsLearned: 0,
1454
+ modelsUpdated: 0,
1455
+ memoryConsolidated: false,
1456
+ note: 'Learning optimization runs during the dream cycle (SessionEnd hook). ' +
1457
+ 'Use "npx agentic-qe hooks session-end --save-state" to trigger pattern consolidation.',
1458
+ });
1459
+ }
1460
+ catch {
1461
+ return ok({
1462
+ patternsLearned: 0,
1463
+ modelsUpdated: 0,
1464
+ memoryConsolidated: false,
1465
+ note: 'Learning system not initialized. Run "aqe init --auto" first.',
1466
+ });
1467
+ }
960
1468
  });
961
1469
  }
962
1470
  // ============================================================================