agentic-qe 3.6.11 → 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 (105) 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 +14 -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 +198 -91
  12. package/v3/dist/cli/commands/learning-helpers.d.ts.map +1 -1
  13. package/v3/dist/cli/commands/learning-helpers.js +3 -4
  14. package/v3/dist/cli/commands/learning-helpers.js.map +1 -1
  15. package/v3/dist/cli/commands/learning.d.ts.map +1 -1
  16. package/v3/dist/cli/commands/learning.js +5 -8
  17. package/v3/dist/cli/commands/learning.js.map +1 -1
  18. package/v3/dist/cli/commands/migrate.js +2 -2
  19. package/v3/dist/coordination/constants.d.ts +1 -1
  20. package/v3/dist/coordination/constants.js +1 -1
  21. package/v3/dist/coordination/task-executor.d.ts.map +1 -1
  22. package/v3/dist/coordination/task-executor.js +322 -74
  23. package/v3/dist/coordination/task-executor.js.map +1 -1
  24. package/v3/dist/domains/code-intelligence/coordinator-hypergraph.js +2 -2
  25. package/v3/dist/domains/code-intelligence/coordinator-hypergraph.js.map +1 -1
  26. package/v3/dist/domains/code-intelligence/coordinator.js +2 -2
  27. package/v3/dist/domains/code-intelligence/coordinator.js.map +1 -1
  28. package/v3/dist/domains/coverage-analysis/coordinator.js +1 -1
  29. package/v3/dist/domains/coverage-analysis/services/coverage-analyzer.js +1 -1
  30. package/v3/dist/domains/coverage-analysis/services/coverage-embedder.d.ts +1 -1
  31. package/v3/dist/domains/coverage-analysis/services/coverage-embedder.js +1 -1
  32. package/v3/dist/domains/coverage-analysis/services/gap-detector.js +1 -1
  33. package/v3/dist/domains/coverage-analysis/services/ghost-coverage-analyzer.js +1 -1
  34. package/v3/dist/domains/coverage-analysis/services/hnsw-index.d.ts +2 -2
  35. package/v3/dist/domains/coverage-analysis/services/hnsw-index.js +3 -3
  36. package/v3/dist/domains/coverage-analysis/services/sublinear-analyzer.d.ts +1 -1
  37. package/v3/dist/domains/coverage-analysis/services/sublinear-analyzer.js +1 -1
  38. package/v3/dist/init/fleet-integration.d.ts.map +1 -1
  39. package/v3/dist/init/fleet-integration.js +3 -4
  40. package/v3/dist/init/fleet-integration.js.map +1 -1
  41. package/v3/dist/init/init-wizard-migration.d.ts.map +1 -1
  42. package/v3/dist/init/init-wizard-migration.js +3 -7
  43. package/v3/dist/init/init-wizard-migration.js.map +1 -1
  44. package/v3/dist/init/init-wizard-steps.d.ts.map +1 -1
  45. package/v3/dist/init/init-wizard-steps.js +3 -4
  46. package/v3/dist/init/init-wizard-steps.js.map +1 -1
  47. package/v3/dist/init/migration/data-migrator.d.ts.map +1 -1
  48. package/v3/dist/init/migration/data-migrator.js +6 -10
  49. package/v3/dist/init/migration/data-migrator.js.map +1 -1
  50. package/v3/dist/init/migration/detector.d.ts.map +1 -1
  51. package/v3/dist/init/migration/detector.js +2 -4
  52. package/v3/dist/init/migration/detector.js.map +1 -1
  53. package/v3/dist/init/phases/01-detection.d.ts.map +1 -1
  54. package/v3/dist/init/phases/01-detection.js +2 -4
  55. package/v3/dist/init/phases/01-detection.js.map +1 -1
  56. package/v3/dist/init/phases/06-code-intelligence.d.ts.map +1 -1
  57. package/v3/dist/init/phases/06-code-intelligence.js +4 -6
  58. package/v3/dist/init/phases/06-code-intelligence.js.map +1 -1
  59. package/v3/dist/init/phases/07-hooks.d.ts +11 -0
  60. package/v3/dist/init/phases/07-hooks.d.ts.map +1 -1
  61. package/v3/dist/init/phases/07-hooks.js +91 -1
  62. package/v3/dist/init/phases/07-hooks.js.map +1 -1
  63. package/v3/dist/init/phases/12-verification.d.ts.map +1 -1
  64. package/v3/dist/init/phases/12-verification.js +2 -4
  65. package/v3/dist/init/phases/12-verification.js.map +1 -1
  66. package/v3/dist/integrations/embeddings/cache/EmbeddingCache.d.ts.map +1 -1
  67. package/v3/dist/integrations/embeddings/cache/EmbeddingCache.js +2 -2
  68. package/v3/dist/integrations/embeddings/cache/EmbeddingCache.js.map +1 -1
  69. package/v3/dist/kernel/constants.d.ts +1 -1
  70. package/v3/dist/kernel/constants.js +1 -1
  71. package/v3/dist/kernel/unified-memory-hnsw.d.ts.map +1 -1
  72. package/v3/dist/kernel/unified-memory-hnsw.js +25 -6
  73. package/v3/dist/kernel/unified-memory-hnsw.js.map +1 -1
  74. package/v3/dist/kernel/unified-memory-migration.d.ts.map +1 -1
  75. package/v3/dist/kernel/unified-memory-migration.js +3 -3
  76. package/v3/dist/kernel/unified-memory-migration.js.map +1 -1
  77. package/v3/dist/learning/metrics-tracker.d.ts.map +1 -1
  78. package/v3/dist/learning/metrics-tracker.js +2 -2
  79. package/v3/dist/learning/metrics-tracker.js.map +1 -1
  80. package/v3/dist/learning/sqlite-persistence.d.ts.map +1 -1
  81. package/v3/dist/learning/sqlite-persistence.js +3 -6
  82. package/v3/dist/learning/sqlite-persistence.js.map +1 -1
  83. package/v3/dist/learning/v2-to-v3-migration.d.ts.map +1 -1
  84. package/v3/dist/learning/v2-to-v3-migration.js +4 -5
  85. package/v3/dist/learning/v2-to-v3-migration.js.map +1 -1
  86. package/v3/dist/mcp/bundle.js +733 -162
  87. package/v3/dist/mcp/tools/security-compliance/scan.d.ts +3 -1
  88. package/v3/dist/mcp/tools/security-compliance/scan.d.ts.map +1 -1
  89. package/v3/dist/mcp/tools/security-compliance/scan.js +417 -72
  90. package/v3/dist/mcp/tools/security-compliance/scan.js.map +1 -1
  91. package/v3/dist/planning/plan-executor.d.ts.map +1 -1
  92. package/v3/dist/planning/plan-executor.js +2 -2
  93. package/v3/dist/planning/plan-executor.js.map +1 -1
  94. package/v3/dist/shared/safe-db.d.ts +32 -0
  95. package/v3/dist/shared/safe-db.d.ts.map +1 -0
  96. package/v3/dist/shared/safe-db.js +41 -0
  97. package/v3/dist/shared/safe-db.js.map +1 -0
  98. package/v3/dist/sync/claude-flow-bridge.js +5 -5
  99. package/v3/dist/sync/claude-flow-bridge.js.map +1 -1
  100. package/v3/dist/sync/embeddings/sync-embedding-generator.js +3 -3
  101. package/v3/dist/sync/embeddings/sync-embedding-generator.js.map +1 -1
  102. package/v3/dist/sync/readers/sqlite-reader.d.ts.map +1 -1
  103. package/v3/dist/sync/readers/sqlite-reader.js +2 -2
  104. package/v3/dist/sync/readers/sqlite-reader.js.map +1 -1
  105. 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
  */
@@ -701,13 +836,17 @@ export class DomainTaskExecutor {
701
836
  ? filePath.slice(targetPath.length).replace(/^\//, '')
702
837
  : filePath;
703
838
  // Pattern: Hardcoded secrets/keys
839
+ // Fix #287: Use \w* around keywords to match SECRET_KEY, JWT_SECRET, API_TOKEN, etc.
704
840
  const secretPatterns = [
705
- { regex: /(?:secret|password|api_key|apikey|token|jwt_secret|private_key)\s*[=:]\s*['"][^'"]{8,}['"]/gi, title: 'Hardcoded secret', severity: 'critical' },
706
- { regex: /(?:AWS_SECRET|GITHUB_TOKEN|SLACK_TOKEN)\s*[=:]\s*['"][^'"]+['"]/gi, title: 'Hardcoded cloud credential', severity: 'critical' },
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' },
707
844
  ];
708
845
  for (const pattern of secretPatterns) {
709
846
  for (let i = 0; i < lines.length; i++) {
710
- if (pattern.regex.test(lines[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) {
711
850
  crossLangVulns.push({
712
851
  title: pattern.title,
713
852
  severity: pattern.severity,
@@ -716,8 +855,6 @@ export class DomainTaskExecutor {
716
855
  category: 'sensitive-data',
717
856
  });
718
857
  }
719
- // Reset regex lastIndex for global regexes
720
- pattern.regex.lastIndex = 0;
721
858
  }
722
859
  }
723
860
  // Pattern: SQL injection risks
@@ -734,15 +871,25 @@ export class DomainTaskExecutor {
734
871
  }
735
872
  sqlPatterns.lastIndex = 0;
736
873
  }
737
- // Pattern: CORS wildcard
738
- if (/allow_origins\s*=\s*\[?\s*['"]?\*['"]?\s*\]?/i.test(content)) {
739
- crossLangVulns.push({
740
- title: 'CORS wildcard origin',
741
- severity: 'high',
742
- location: { file: relPath, line: lines.findIndex(l => /allow_origins/i.test(l)) + 1 },
743
- description: 'CORS configured with wildcard (*) origin — restrict to specific domains',
744
- category: 'security-misconfiguration',
745
- });
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
+ }
746
893
  }
747
894
  // Pattern: Debug/development mode enabled
748
895
  if (/(?:DEBUG|debug)\s*[=:]\s*(?:True|true|1)/i.test(content)) {
@@ -1010,22 +1157,57 @@ export class DomainTaskExecutor {
1010
1157
  return err(toError(error));
1011
1158
  }
1012
1159
  });
1013
- // Register test execution handler
1160
+ // Register test execution handler - runs real tests via child process
1014
1161
  this.taskHandlers.set('execute-tests', async (task) => {
1015
1162
  const payload = task.payload;
1016
- // In production, would actually run tests via test runner
1017
- const testCount = payload.testFiles?.length || 10;
1018
- const passed = Math.floor(testCount * 0.9);
1019
- const failed = testCount - passed;
1020
- return ok({
1021
- total: testCount,
1022
- passed,
1023
- failed,
1024
- skipped: 0,
1025
- duration: testCount * 50, // ~50ms per test
1026
- coverage: 82.5,
1027
- failedTests: failed > 0 ? ['example.test.ts:42'] : [],
1028
- });
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
+ }
1029
1211
  });
1030
1212
  // Register defect prediction handler - REAL IMPLEMENTATION
1031
1213
  this.taskHandlers.set('predict-defects', async (task) => {
@@ -1151,72 +1333,138 @@ export class DomainTaskExecutor {
1151
1333
  // Register requirements validation handler
1152
1334
  this.taskHandlers.set('validate-requirements', async (task) => {
1153
1335
  const payload = task.payload;
1154
- return ok({
1155
- requirementsAnalyzed: 15,
1156
- testable: 12,
1157
- ambiguous: 2,
1158
- untestable: 1,
1159
- coverage: 80,
1160
- bddScenarios: payload.generateBDD ? [
1161
- 'Given a user is logged in, When they view the dashboard, Then they see their metrics',
1162
- 'Given an API request fails, When the retry limit is exceeded, Then an error is returned',
1163
- ] : [],
1164
- });
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
+ }
1165
1367
  });
1166
1368
  // Register contract validation handler
1167
1369
  this.taskHandlers.set('validate-contracts', async (task) => {
1168
1370
  const payload = task.payload;
1169
- return ok({
1170
- contractPath: payload.contractPath,
1171
- valid: true,
1172
- breakingChanges: [],
1173
- warnings: [
1174
- 'Deprecated field "legacyId" should be removed in next major version',
1175
- ],
1176
- coverage: 95,
1177
- });
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
+ }
1178
1414
  });
1179
1415
  // Register accessibility test handler
1180
1416
  this.taskHandlers.set('test-accessibility', async (task) => {
1181
1417
  const payload = task.payload;
1418
+ // Accessibility testing requires a browser/DOM — return honest guidance
1182
1419
  return ok({
1183
- url: payload.url,
1420
+ url: payload.url || '',
1184
1421
  standard: payload.standard || 'wcag21-aa',
1185
- passed: true,
1422
+ passed: false,
1186
1423
  violations: [],
1187
- warnings: [
1188
- { rule: 'color-contrast', impact: 'minor', element: 'nav > a' },
1189
- ],
1190
- 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>'),
1191
1429
  });
1192
1430
  });
1193
1431
  // Register chaos test handler
1194
1432
  this.taskHandlers.set('run-chaos', async (task) => {
1195
1433
  const payload = task.payload;
1434
+ // Chaos testing requires infrastructure access — return honest guidance
1196
1435
  return ok({
1197
- faultType: payload.faultType,
1198
- target: payload.target,
1199
- dryRun: payload.dryRun,
1200
- duration: payload.duration,
1201
- systemBehavior: payload.dryRun ? 'simulated' : 'tested',
1202
- resilience: {
1203
- recovered: true,
1204
- recoveryTime: 2500,
1205
- dataLoss: false,
1206
- },
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).',
1207
1445
  });
1208
1446
  });
1209
1447
  // Register learning optimization handler
1210
1448
  this.taskHandlers.set('optimize-learning', async (_task) => {
1211
- return ok({
1212
- patternsLearned: 12,
1213
- modelsUpdated: 3,
1214
- memoryConsolidated: true,
1215
- recommendations: [
1216
- 'Pattern recognition improved for error handling',
1217
- 'Test generation templates optimized',
1218
- ],
1219
- });
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
+ }
1220
1468
  });
1221
1469
  }
1222
1470
  // ============================================================================