agentic-qe 3.7.20 → 3.7.22

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 (115) hide show
  1. package/.claude/agents/v3/qe-deployment-advisor.md +14 -0
  2. package/.claude/agents/v3/qe-gap-detector.md +8 -0
  3. package/.claude/agents/v3/qe-impact-analyzer.md +11 -0
  4. package/.claude/agents/v3/qe-queen-coordinator.md +45 -0
  5. package/.claude/agents/v3/qe-root-cause-analyzer.md +11 -0
  6. package/.claude/agents/v3/qe-security-scanner.md +25 -16
  7. package/.claude/helpers/brain-checkpoint.cjs +7 -4
  8. package/.claude/helpers/statusline-v3.cjs +7 -4
  9. package/.claude/skills/skills-manifest.json +1 -1
  10. package/CHANGELOG.md +34 -0
  11. package/README.md +0 -12
  12. package/assets/agents/v3/qe-deployment-advisor.md +14 -0
  13. package/assets/agents/v3/qe-gap-detector.md +8 -0
  14. package/assets/agents/v3/qe-impact-analyzer.md +11 -0
  15. package/assets/agents/v3/qe-queen-coordinator.md +45 -0
  16. package/assets/agents/v3/qe-root-cause-analyzer.md +11 -0
  17. package/assets/agents/v3/qe-security-scanner.md +25 -16
  18. package/assets/helpers/statusline-v3.cjs +7 -4
  19. package/dist/adapters/claude-flow/model-router-bridge.d.ts +0 -6
  20. package/dist/adapters/claude-flow/model-router-bridge.js +4 -17
  21. package/dist/adapters/claude-flow/pretrain-bridge.d.ts +0 -6
  22. package/dist/adapters/claude-flow/pretrain-bridge.js +6 -19
  23. package/dist/adapters/claude-flow/trajectory-bridge.d.ts +0 -6
  24. package/dist/adapters/claude-flow/trajectory-bridge.js +21 -23
  25. package/dist/cli/brain-commands.js +6 -10
  26. package/dist/cli/bundle.js +3124 -3622
  27. package/dist/cli/commands/hooks.js +29 -6
  28. package/dist/cli/commands/init.js +1 -73
  29. package/dist/cli/commands/learning.js +164 -12
  30. package/dist/cli/handlers/init-handler.d.ts +0 -1
  31. package/dist/cli/handlers/init-handler.js +0 -6
  32. package/dist/cli/index.js +0 -2
  33. package/dist/context/sources/defect-source.js +2 -2
  34. package/dist/context/sources/memory-source.js +2 -2
  35. package/dist/context/sources/requirements-source.js +2 -2
  36. package/dist/coordination/protocols/security-audit.d.ts +3 -6
  37. package/dist/coordination/protocols/security-audit.js +8 -88
  38. package/dist/coordination/queen-coordinator.d.ts +13 -0
  39. package/dist/coordination/queen-coordinator.js +76 -0
  40. package/dist/coordination/queen-task-management.d.ts +2 -0
  41. package/dist/coordination/queen-task-management.js +10 -0
  42. package/dist/coordination/queen-types.d.ts +3 -0
  43. package/dist/coordination/task-executor.js +7 -5
  44. package/dist/domains/security-compliance/services/scanners/sast-scanner.d.ts +25 -1
  45. package/dist/domains/security-compliance/services/scanners/sast-scanner.js +140 -11
  46. package/dist/domains/security-compliance/services/scanners/scanner-types.d.ts +2 -0
  47. package/dist/domains/security-compliance/services/scanners/scanner-types.js +1 -0
  48. package/dist/domains/test-execution/services/mincut-test-optimizer.js +2 -0
  49. package/dist/init/agents-installer.d.ts +2 -0
  50. package/dist/init/agents-installer.js +13 -0
  51. package/dist/init/enhancements/claude-flow-adapter.js +51 -24
  52. package/dist/init/index.d.ts +0 -2
  53. package/dist/init/index.js +0 -1
  54. package/dist/init/init-wizard-steps.d.ts +10 -0
  55. package/dist/init/init-wizard-steps.js +87 -1
  56. package/dist/init/init-wizard.d.ts +1 -9
  57. package/dist/init/init-wizard.js +3 -69
  58. package/dist/init/orchestrator.js +0 -1
  59. package/dist/init/phases/01-detection.js +0 -27
  60. package/dist/init/phases/07-hooks.js +12 -10
  61. package/dist/init/phases/phase-interface.d.ts +0 -1
  62. package/dist/init/settings-merge.js +1 -1
  63. package/dist/integrations/ruvector/brain-rvf-exporter.js +14 -2
  64. package/dist/kernel/unified-memory.js +5 -6
  65. package/dist/learning/experience-capture-middleware.js +23 -1
  66. package/dist/learning/index.d.ts +0 -2
  67. package/dist/learning/index.js +0 -4
  68. package/dist/learning/metrics-tracker.js +15 -13
  69. package/dist/learning/pattern-lifecycle.d.ts +1 -1
  70. package/dist/learning/pattern-lifecycle.js +18 -20
  71. package/dist/learning/qe-reasoning-bank.js +3 -3
  72. package/dist/learning/qe-unified-memory.js +1 -28
  73. package/dist/learning/sqlite-persistence.js +16 -0
  74. package/dist/learning/token-tracker.js +4 -2
  75. package/dist/mcp/bundle.js +1162 -478
  76. package/dist/routing/agent-dependency-graph.d.ts +77 -0
  77. package/dist/routing/agent-dependency-graph.js +359 -0
  78. package/dist/routing/co-execution-repository.d.ts +68 -0
  79. package/dist/routing/co-execution-repository.js +184 -0
  80. package/dist/routing/index.d.ts +6 -0
  81. package/dist/routing/index.js +6 -0
  82. package/dist/routing/qe-task-router.d.ts +7 -0
  83. package/dist/routing/qe-task-router.js +63 -1
  84. package/dist/routing/signal-merger.d.ts +81 -0
  85. package/dist/routing/signal-merger.js +136 -0
  86. package/dist/routing/types.d.ts +1 -0
  87. package/dist/shared/llm/providers/azure-openai.js +3 -2
  88. package/dist/shared/llm/providers/bedrock.js +3 -2
  89. package/dist/shared/llm/providers/claude.js +3 -2
  90. package/dist/shared/llm/providers/gemini.js +3 -2
  91. package/dist/shared/llm/providers/openai.js +3 -2
  92. package/dist/shared/llm/providers/openrouter.js +3 -2
  93. package/dist/shared/llm/retry.d.ts +10 -0
  94. package/dist/shared/llm/retry.js +16 -0
  95. package/dist/shared/llm/router/agent-router-config.d.ts +2 -1
  96. package/dist/shared/llm/router/agent-router-config.js +38 -88
  97. package/dist/validation/index.d.ts +2 -0
  98. package/dist/validation/index.js +4 -0
  99. package/dist/validation/steps/agent-mcp-validator.d.ts +88 -0
  100. package/dist/validation/steps/agent-mcp-validator.js +254 -0
  101. package/package.json +1 -1
  102. package/dist/cli/commands/migrate.d.ts +0 -9
  103. package/dist/cli/commands/migrate.js +0 -566
  104. package/dist/init/init-wizard-migration.d.ts +0 -52
  105. package/dist/init/init-wizard-migration.js +0 -345
  106. package/dist/init/migration/config-migrator.d.ts +0 -31
  107. package/dist/init/migration/config-migrator.js +0 -149
  108. package/dist/init/migration/data-migrator.d.ts +0 -72
  109. package/dist/init/migration/data-migrator.js +0 -232
  110. package/dist/init/migration/detector.d.ts +0 -44
  111. package/dist/init/migration/detector.js +0 -105
  112. package/dist/init/migration/index.d.ts +0 -8
  113. package/dist/init/migration/index.js +0 -8
  114. package/dist/learning/v2-to-v3-migration.d.ts +0 -86
  115. package/dist/learning/v2-to-v3-migration.js +0 -529
@@ -358,7 +358,24 @@ async function consolidateExperiencesToPatterns() {
358
358
  await um.initialize();
359
359
  }
360
360
  const db = um.getDatabase();
361
- // Aggregate unprocessed experiences by domain+agent with quality thresholds
361
+ // Ensure consolidation columns exist (may be missing on older DBs)
362
+ const existingCols = new Set(db.prepare('PRAGMA table_info(captured_experiences)').all().map(c => c.name));
363
+ const migrations = [
364
+ ['consolidated_into', 'TEXT DEFAULT NULL'],
365
+ ['consolidation_count', 'INTEGER DEFAULT 1'],
366
+ ['quality_updated_at', 'TEXT DEFAULT NULL'],
367
+ ['reuse_success_count', 'INTEGER DEFAULT 0'],
368
+ ['reuse_failure_count', 'INTEGER DEFAULT 0'],
369
+ ];
370
+ for (const [col, def] of migrations) {
371
+ if (!existingCols.has(col)) {
372
+ db.exec(`ALTER TABLE captured_experiences ADD COLUMN ${col} ${def}`);
373
+ }
374
+ }
375
+ // Aggregate unprocessed experiences by domain+agent with quality thresholds.
376
+ // Exclude 'cli-hook' agent — these are low-quality hook telemetry events
377
+ // (quality ~0.40, success_rate ~0.24) that flood the pipeline and block
378
+ // real pattern creation. See issue #348.
362
379
  const aggregates = db.prepare(`
363
380
  SELECT
364
381
  domain,
@@ -371,6 +388,7 @@ async function consolidateExperiencesToPatterns() {
371
388
  GROUP_CONCAT(DISTINCT source) as sources
372
389
  FROM captured_experiences
373
390
  WHERE application_count = 0
391
+ AND agent != 'cli-hook'
374
392
  GROUP BY domain, agent
375
393
  HAVING cnt >= 3 AND avg_quality >= 0.5 AND success_rate >= 0.6
376
394
  ORDER BY avg_quality DESC
@@ -382,20 +400,25 @@ async function consolidateExperiencesToPatterns() {
382
400
  let created = 0;
383
401
  for (const agg of aggregates) {
384
402
  try {
385
- // Check for existing pattern with same domain+agent to avoid duplicates
403
+ // Use date-bucketed names so new patterns emerge as usage evolves,
404
+ // instead of silently reinforcing one static pattern forever.
405
+ const dateBucket = new Date().toISOString().slice(0, 7); // YYYY-MM
406
+ const patternName = `${agg.agent}-${agg.domain}-${dateBucket}`;
407
+ // Check for existing pattern with same name this month
386
408
  const existing = db.prepare(`
387
409
  SELECT id FROM qe_patterns
388
410
  WHERE qe_domain = ? AND name = ?
389
411
  LIMIT 1
390
- `).get(agg.domain, `${agg.agent}-session-pattern`);
412
+ `).get(agg.domain, patternName);
391
413
  if (existing) {
392
- // Reinforce existing pattern instead of creating duplicate
414
+ // Reinforce existing monthly pattern
393
415
  db.prepare(`
394
416
  UPDATE qe_patterns
395
417
  SET usage_count = usage_count + ?,
396
418
  successful_uses = successful_uses + ?,
397
419
  confidence = MIN(0.99, confidence + 0.01),
398
- quality_score = MIN(0.99, quality_score + 0.005)
420
+ quality_score = MIN(0.99, quality_score + 0.005),
421
+ updated_at = datetime('now')
399
422
  WHERE id = ?
400
423
  `).run(agg.cnt, agg.successes, existing.id);
401
424
  }
@@ -409,7 +432,7 @@ async function consolidateExperiencesToPatterns() {
409
432
  confidence, usage_count, success_rate, quality_score, tier,
410
433
  template_json, context_json, created_at, successful_uses
411
434
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), ?)
412
- `).run(patternId, 'workflow', agg.domain, agg.domain, `${agg.agent}-session-pattern`, `Auto-consolidated from ${agg.cnt} experiences. Agent: ${agg.agent}, success rate: ${(agg.success_rate * 100).toFixed(0)}%`, confidence, agg.cnt, agg.success_rate, qualityScore, 'short-term', JSON.stringify({ type: 'workflow', content: `${agg.agent} pattern for ${agg.domain}`, variables: [] }), JSON.stringify({ tags: (agg.sources || '').split(','), sourceType: 'session-consolidation', extractedAt: new Date().toISOString() }), agg.successes);
435
+ `).run(patternId, 'workflow', agg.domain, agg.domain, patternName, `Auto-consolidated from ${agg.cnt} experiences. Agent: ${agg.agent}, success rate: ${(agg.success_rate * 100).toFixed(0)}%`, confidence, agg.cnt, agg.success_rate, qualityScore, 'short-term', JSON.stringify({ type: 'workflow', content: `${agg.agent} pattern for ${agg.domain}`, variables: [] }), JSON.stringify({ tags: (agg.sources || '').split(','), sourceType: 'session-consolidation', extractedAt: new Date().toISOString() }), agg.successes);
413
436
  created++;
414
437
  }
415
438
  // Mark experiences as processed
@@ -28,7 +28,6 @@ export function createInitCommand() {
28
28
  .description('Initialize Agentic QE v3 in your project')
29
29
  .option('-a, --auto', 'Auto-configure without prompts')
30
30
  .option('-u, --upgrade', 'Upgrade existing installation (overwrites skills, agents, validation)')
31
- .option('--auto-migrate', 'Automatically migrate from v2 if detected')
32
31
  .option('--minimal', 'Minimal installation (no skills, patterns, or workers)')
33
32
  .option('--skip-patterns', 'Skip pattern loading')
34
33
  .option('--with-n8n', 'Include n8n workflow testing platform')
@@ -57,14 +56,6 @@ export function createInitCommand() {
57
56
  .action(async () => {
58
57
  await checkStatus();
59
58
  });
60
- initCmd
61
- .command('migrate')
62
- .description('Migrate from v2 to v3')
63
- .option('--dry-run', 'Show what would be migrated without making changes')
64
- .option('--force', 'Force migration even if v3 already exists')
65
- .action(async (migrateOptions) => {
66
- await runMigration(migrateOptions);
67
- });
68
59
  initCmd
69
60
  .command('reset')
70
61
  .description('Reset AQE configuration (keeps data)')
@@ -87,11 +78,10 @@ async function runInit(options) {
87
78
  // Check if already initialized
88
79
  const aqeDir = path.join(projectRoot, '.agentic-qe');
89
80
  const isExisting = existsSync(aqeDir);
90
- if (isExisting && !options.auto && !options.autoMigrate && !options.upgrade) {
81
+ if (isExisting && !options.auto && !options.upgrade) {
91
82
  console.log(chalk.yellow(' ⚠ AQE directory already exists at:'), aqeDir);
92
83
  console.log(chalk.gray(' Use --auto to update configuration (keeps existing skills)'));
93
84
  console.log(chalk.gray(' Use --upgrade to update all skills, agents, and validation'));
94
- console.log(chalk.gray(' Use --auto-migrate to migrate from v2'));
95
85
  console.log('');
96
86
  }
97
87
  // Expand --with-all-platforms into individual flags
@@ -110,7 +100,6 @@ async function runInit(options) {
110
100
  projectRoot,
111
101
  autoMode: options.auto,
112
102
  upgrade: options.upgrade,
113
- autoMigrate: options.autoMigrate,
114
103
  minimal: options.minimal,
115
104
  skipPatterns: options.skipPatterns,
116
105
  withN8n: options.withN8n,
@@ -223,67 +212,6 @@ async function checkStatus() {
223
212
  }
224
213
  console.log('');
225
214
  }
226
- /**
227
- * Run v2 to v3 migration
228
- */
229
- async function runMigration(options) {
230
- const projectRoot = process.cwd();
231
- const aqeDir = path.join(projectRoot, '.agentic-qe');
232
- console.log('');
233
- console.log(chalk.bold.blue(' AQE v2 to v3 Migration'));
234
- console.log(chalk.gray(' ──────────────────────────'));
235
- console.log('');
236
- // Import migration modules
237
- const { createV2Detector, createV2DataMigrator, createV2ConfigMigrator } = await import('../../init/migration/index.js');
238
- // Detect v2
239
- const detector = createV2Detector(projectRoot);
240
- const v2Info = await detector.detect();
241
- if (!v2Info.detected) {
242
- console.log(chalk.yellow(' ⚠ No v2 installation detected'));
243
- console.log(chalk.gray(' Run "aqe init" to create a new installation'));
244
- console.log('');
245
- return;
246
- }
247
- console.log(chalk.green(' ✓ Found v2 installation'));
248
- console.log(chalk.gray(` Database: ${v2Info.paths.memoryDb || 'not found'}`));
249
- console.log(chalk.gray(` Config: ${v2Info.paths.configDir || 'not found'}`));
250
- console.log(chalk.gray(` Version: ${v2Info.version || 'unknown'}`));
251
- console.log('');
252
- if (options.dryRun) {
253
- console.log(chalk.yellow(' Dry run - no changes will be made'));
254
- console.log('');
255
- return;
256
- }
257
- // Run migration
258
- console.log(chalk.blue(' Migrating data...'));
259
- if (v2Info.paths.memoryDb) {
260
- const dataMigrator = createV2DataMigrator({
261
- v2DbPath: v2Info.paths.memoryDb,
262
- v3PatternsDbPath: path.join(aqeDir, 'patterns.db'),
263
- onProgress: (p) => console.log(chalk.gray(` ${p.message}`)),
264
- });
265
- const dataResult = await dataMigrator.migrate();
266
- if (dataResult.success) {
267
- console.log(chalk.green(` ✓ Migrated ${dataResult.counts.patterns || 0} patterns`));
268
- console.log(chalk.green(` ✓ Migrated ${dataResult.counts.experiences || 0} experiences`));
269
- }
270
- else {
271
- console.log(chalk.red(` ✗ Data migration failed: ${dataResult.errors.join(', ')}`));
272
- }
273
- }
274
- const configMigrator = createV2ConfigMigrator(projectRoot);
275
- const configResult = await configMigrator.migrate();
276
- if (configResult.success) {
277
- console.log(chalk.green(' ✓ Config migrated'));
278
- }
279
- else {
280
- console.log(chalk.yellow(' ⚠ Config migration skipped (no v2 config found)'));
281
- }
282
- console.log('');
283
- console.log(chalk.green(' Migration complete!'));
284
- console.log(chalk.gray(' Run "aqe init" to complete setup'));
285
- console.log('');
286
- }
287
215
  /**
288
216
  * Reset AQE configuration
289
217
  */
@@ -13,7 +13,7 @@ import { Command } from 'commander';
13
13
  import chalk from 'chalk';
14
14
  import path from 'node:path';
15
15
  import { findProjectRoot } from '../../kernel/unified-memory.js';
16
- import { existsSync, writeFileSync, readFileSync, mkdirSync, copyFileSync } from 'node:fs';
16
+ import { existsSync, writeFileSync, readFileSync, mkdirSync, copyFileSync, renameSync } from 'node:fs';
17
17
  import { safeJsonParse } from '../../shared/safe-json.js';
18
18
  import { stat, unlink } from 'node:fs/promises';
19
19
  import { QE_DOMAIN_LIST } from '../../learning/qe-patterns.js';
@@ -62,6 +62,7 @@ Examples:
62
62
  registerExportFullCommand(learning);
63
63
  registerImportMergeCommand(learning);
64
64
  registerDreamCommand(learning);
65
+ registerRepairCommand(learning);
65
66
  return learning;
66
67
  }
67
68
  // ============================================================================
@@ -372,15 +373,19 @@ function registerExtractCommand(learning) {
372
373
  console.log(` Min occurrences: ${minCount}\n`);
373
374
  const db = openDatabase(dbPath, { readonly: true });
374
375
  const experiences = db.prepare(`
375
- SELECT task_type, COUNT(*) as count, AVG(reward) as avg_reward, MAX(reward) as max_reward,
376
- MIN(reward) as min_reward, GROUP_CONCAT(DISTINCT action) as actions
377
- FROM learning_experiences WHERE reward >= ? GROUP BY task_type HAVING COUNT(*) >= ? ORDER BY avg_reward DESC
376
+ SELECT domain as task_type, COUNT(*) as count, AVG(quality) as avg_reward, MAX(quality) as max_reward,
377
+ MIN(quality) as min_reward, GROUP_CONCAT(DISTINCT agent) as actions
378
+ FROM captured_experiences WHERE quality >= ? AND agent != 'cli-hook' GROUP BY domain HAVING COUNT(*) >= ? ORDER BY avg_reward DESC
378
379
  `).all(minReward, minCount);
379
- const memoryPatterns = db.prepare(`
380
- SELECT substr(key, 1, 40) as key_prefix, COUNT(*) as count
381
- FROM memory_entries WHERE key LIKE 'phase2/learning/%'
382
- GROUP BY substr(key, 1, 40) HAVING COUNT(*) >= ? ORDER BY COUNT(*) DESC LIMIT 20
383
- `).all(minCount);
380
+ let memoryPatterns = [];
381
+ try {
382
+ memoryPatterns = db.prepare(`
383
+ SELECT substr(key, 1, 40) as key_prefix, COUNT(*) as count
384
+ FROM memory_entries WHERE key LIKE 'phase2/learning/%'
385
+ GROUP BY substr(key, 1, 40) HAVING COUNT(*) >= ? ORDER BY COUNT(*) DESC LIMIT 20
386
+ `).all(minCount);
387
+ }
388
+ catch { /* memory_entries table may not exist */ }
384
389
  db.close();
385
390
  const extractedPatterns = [];
386
391
  for (const exp of experiences) {
@@ -689,7 +694,7 @@ function registerVerifyCommand(learning) {
689
694
  let tableCounts = {};
690
695
  try {
691
696
  const db = openDatabase(dbPath, { readonly: true });
692
- for (const table of ['qe_patterns', 'qe_trajectories', 'learning_experiences', 'kv_store', 'vectors']) {
697
+ for (const table of ['qe_patterns', 'qe_trajectories', 'captured_experiences', 'kv_store', 'vectors']) {
693
698
  try {
694
699
  const r = db.prepare(`SELECT COUNT(*) as count FROM ${table}`).get();
695
700
  tableCounts[table] = r.count;
@@ -770,9 +775,9 @@ function registerExportFullCommand(learning) {
770
775
  if (options.includeExperiences) {
771
776
  try {
772
777
  const db = openDatabase(dbPath, { readonly: true });
773
- const experiences = db.prepare(`SELECT task_type, action, AVG(reward) as avg_reward, COUNT(*) as count FROM learning_experiences GROUP BY task_type, action ORDER BY count DESC LIMIT 500`).all();
778
+ const experiences = db.prepare(`SELECT domain as task_type, agent as action, AVG(quality) as avg_reward, COUNT(*) as count FROM captured_experiences WHERE agent != 'cli-hook' GROUP BY domain, agent ORDER BY count DESC LIMIT 500`).all();
774
779
  exportData.experiences = experiences.map(e => ({ taskType: e.task_type, action: e.action, reward: e.avg_reward, count: e.count }));
775
- const metaRow = db.prepare(`SELECT COUNT(*) as total, AVG(reward) as avg_reward FROM learning_experiences`).get();
780
+ const metaRow = db.prepare(`SELECT COUNT(*) as total, AVG(quality) as avg_reward FROM captured_experiences WHERE agent != 'cli-hook'`).get();
776
781
  exportData.metadata = { totalExperiences: metaRow.total, avgReward: metaRow.avg_reward };
777
782
  db.close();
778
783
  }
@@ -1136,6 +1141,153 @@ function registerDreamCommand(learning) {
1136
1141
  });
1137
1142
  }
1138
1143
  // ============================================================================
1144
+ // Subcommand: repair
1145
+ // ============================================================================
1146
+ function registerRepairCommand(learning) {
1147
+ learning
1148
+ .command('repair')
1149
+ .description('Repair a corrupted learning database (dump + reimport to rebuild indexes)')
1150
+ .option('-f, --file <path>', 'Database file to repair (defaults to current project)')
1151
+ .option('--dry-run', 'Show what would be done without modifying files')
1152
+ .option('--json', 'Output as JSON')
1153
+ .action(async (options) => {
1154
+ try {
1155
+ const dbPath = options.file ? path.resolve(options.file) : getDbPath();
1156
+ if (!existsSync(dbPath))
1157
+ throw new Error(`Database file not found: ${dbPath}`);
1158
+ // Step 1: Check integrity first
1159
+ const integrityResult = await verifyDatabaseIntegrity(dbPath);
1160
+ if (integrityResult.valid) {
1161
+ if (options.json) {
1162
+ printJson({ status: 'healthy', message: 'Database is already healthy, no repair needed', path: dbPath });
1163
+ }
1164
+ else {
1165
+ printSuccess('Database integrity check passed — no repair needed.');
1166
+ }
1167
+ process.exit(0);
1168
+ }
1169
+ printInfo(`Corruption detected: ${integrityResult.message}`);
1170
+ // Step 2: Count rows before repair
1171
+ let rowCountsBefore = {};
1172
+ try {
1173
+ const db = openDatabase(dbPath, { readonly: true });
1174
+ const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
1175
+ for (const t of tables) {
1176
+ try {
1177
+ const r = db.prepare(`SELECT COUNT(*) as count FROM "${t.name}"`).get();
1178
+ rowCountsBefore[t.name] = r.count;
1179
+ }
1180
+ catch { /* corrupted table, count what we can */ }
1181
+ }
1182
+ db.close();
1183
+ }
1184
+ catch { /* ignore count failures */ }
1185
+ const totalBefore = Object.values(rowCountsBefore).reduce((a, b) => a + b, 0);
1186
+ printInfo(`Found ${Object.keys(rowCountsBefore).length} tables, ~${totalBefore} total rows`);
1187
+ if (options.dryRun) {
1188
+ if (options.json) {
1189
+ printJson({ status: 'dry-run', corruptionDetected: true, message: integrityResult.message, tables: rowCountsBefore, totalRows: totalBefore });
1190
+ }
1191
+ else {
1192
+ console.log(chalk.bold('\nDry run — would perform these steps:'));
1193
+ console.log(' 1. Backup corrupted DB');
1194
+ console.log(' 2. Dump all data via .dump');
1195
+ console.log(' 3. Reimport into fresh DB (rebuilds all indexes)');
1196
+ console.log(' 4. Verify integrity of repaired DB');
1197
+ console.log(' 5. Verify row counts match');
1198
+ console.log(' 6. Swap repaired DB into place');
1199
+ console.log('');
1200
+ }
1201
+ process.exit(0);
1202
+ }
1203
+ // Step 3: Backup
1204
+ const backupPath = `${dbPath}.bak-${Date.now()}`;
1205
+ copyFileSync(dbPath, backupPath);
1206
+ printInfo(`Backup created: ${backupPath}`);
1207
+ // Step 4: Remove stale WAL/SHM
1208
+ for (const suffix of ['-wal', '-shm']) {
1209
+ const walFile = dbPath + suffix;
1210
+ if (existsSync(walFile)) {
1211
+ await unlink(walFile);
1212
+ }
1213
+ }
1214
+ // Step 5: Dump and reimport using sqlite3 CLI
1215
+ const { execSync } = await import('node:child_process');
1216
+ const repairedPath = `${dbPath}.repaired`;
1217
+ const dumpPath = `${dbPath}.dump.sql`;
1218
+ try {
1219
+ // Dump all data
1220
+ execSync(`sqlite3 "${dbPath}" ".dump" > "${dumpPath}"`, { stdio: 'pipe', timeout: 120000 });
1221
+ // Reimport into fresh DB
1222
+ execSync(`sqlite3 "${repairedPath}" < "${dumpPath}"`, { stdio: 'pipe', timeout: 120000 });
1223
+ // Enable WAL on repaired DB
1224
+ execSync(`sqlite3 "${repairedPath}" "PRAGMA journal_mode=WAL;"`, { stdio: 'pipe' });
1225
+ }
1226
+ catch (dumpError) {
1227
+ // Clean up partial files
1228
+ if (existsSync(repairedPath))
1229
+ await unlink(repairedPath);
1230
+ if (existsSync(dumpPath))
1231
+ await unlink(dumpPath);
1232
+ throw new Error(`sqlite3 dump/reimport failed: ${dumpError instanceof Error ? dumpError.message : 'unknown'}. Is sqlite3 installed?`);
1233
+ }
1234
+ // Step 6: Verify repaired DB
1235
+ const repairedIntegrity = await verifyDatabaseIntegrity(repairedPath);
1236
+ if (!repairedIntegrity.valid) {
1237
+ if (existsSync(dumpPath))
1238
+ await unlink(dumpPath);
1239
+ throw new Error(`Repaired DB still has integrity issues: ${repairedIntegrity.message}. Backup preserved at ${backupPath}`);
1240
+ }
1241
+ // Step 7: Verify row counts
1242
+ let rowCountsAfter = {};
1243
+ const repairedDb = openDatabase(repairedPath, { readonly: true });
1244
+ const repairedTables = repairedDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
1245
+ for (const t of repairedTables) {
1246
+ try {
1247
+ const r = repairedDb.prepare(`SELECT COUNT(*) as count FROM "${t.name}"`).get();
1248
+ rowCountsAfter[t.name] = r.count;
1249
+ }
1250
+ catch { /* skip */ }
1251
+ }
1252
+ repairedDb.close();
1253
+ const totalAfter = Object.values(rowCountsAfter).reduce((a, b) => a + b, 0);
1254
+ // Step 8: Swap
1255
+ renameSync(dbPath, `${dbPath}.pre-repair`);
1256
+ renameSync(repairedPath, dbPath);
1257
+ // Clean up dump file
1258
+ if (existsSync(dumpPath))
1259
+ await unlink(dumpPath);
1260
+ if (options.json) {
1261
+ printJson({
1262
+ status: 'repaired',
1263
+ backupPath,
1264
+ rowsBefore: totalBefore,
1265
+ rowsAfter: totalAfter,
1266
+ tablesRepaired: Object.keys(rowCountsAfter).length,
1267
+ rowCounts: rowCountsAfter,
1268
+ });
1269
+ }
1270
+ else {
1271
+ printSuccess('Database repaired successfully!');
1272
+ console.log(` Backup: ${backupPath}`);
1273
+ console.log(` Rows before: ${totalBefore}`);
1274
+ console.log(` Rows after: ${totalAfter}`);
1275
+ if (totalAfter < totalBefore) {
1276
+ console.log(chalk.yellow(` Warning: ${totalBefore - totalAfter} rows may have been lost due to data page corruption`));
1277
+ }
1278
+ console.log(` Tables: ${Object.keys(rowCountsAfter).length}`);
1279
+ console.log(` Integrity: ${chalk.green('OK')}`);
1280
+ console.log('');
1281
+ }
1282
+ process.exit(0);
1283
+ }
1284
+ catch (error) {
1285
+ printError(`repair failed: ${error instanceof Error ? error.message : 'unknown'}`);
1286
+ process.exit(1);
1287
+ }
1288
+ });
1289
+ }
1290
+ // ============================================================================
1139
1291
  // Exports
1140
1292
  // ============================================================================
1141
1293
  export { initializeLearningSystem } from './learning-helpers.js';
@@ -40,7 +40,6 @@ interface InitOptions {
40
40
  withWindsurf?: boolean;
41
41
  withContinuedev?: boolean;
42
42
  withAllPlatforms?: boolean;
43
- autoMigrate?: boolean;
44
43
  withClaudeFlow?: boolean;
45
44
  skipClaudeFlow?: boolean;
46
45
  noGovernance?: boolean;
@@ -72,10 +72,6 @@ export class InitHandler {
72
72
  options.withWindsurf = true;
73
73
  options.withContinuedev = true;
74
74
  }
75
- // --auto-migrate implies --auto (must use orchestrator for migration)
76
- if (options.autoMigrate && !options.auto && !options.wizard) {
77
- options.auto = true;
78
- }
79
75
  // --upgrade implies --auto (must use modular orchestrator to overwrite files)
80
76
  if (options.upgrade && !options.auto && !options.wizard) {
81
77
  options.auto = true;
@@ -118,7 +114,6 @@ export class InitHandler {
118
114
  withCodex: options.withCodex,
119
115
  withWindsurf: options.withWindsurf,
120
116
  withContinueDev: options.withContinuedev,
121
- autoMigrate: options.autoMigrate,
122
117
  noGovernance: options.noGovernance,
123
118
  });
124
119
  console.log(chalk.white(' Analyzing project...\n'));
@@ -192,7 +187,6 @@ export class InitHandler {
192
187
  minimal: options.minimal,
193
188
  skipPatterns: options.skipPatterns,
194
189
  withN8n: options.withN8n,
195
- autoMigrate: options.autoMigrate,
196
190
  };
197
191
  const orchestrator = new InitOrchestrator(orchestratorOptions);
198
192
  if (options.wizard) {
package/dist/cli/index.js CHANGED
@@ -790,7 +790,6 @@ import { createCoverageCommand } from './commands/coverage.js';
790
790
  import { createQualityCommand } from './commands/quality.js';
791
791
  import { createSecurityCommand } from './commands/security.js';
792
792
  import { createCodeCommand } from './commands/code.js';
793
- import { createMigrateCommand } from './commands/migrate.js';
794
793
  import { createCompletionsCommand } from './commands/completions.js';
795
794
  import { createFleetCommand } from './commands/fleet.js';
796
795
  import { createValidateSwarmCommand } from './commands/validate-swarm.js';
@@ -803,7 +802,6 @@ program.addCommand(createCoverageCommand(context, cleanupAndExit, ensureInitiali
803
802
  program.addCommand(createQualityCommand(context, cleanupAndExit, ensureInitialized));
804
803
  program.addCommand(createSecurityCommand(context, cleanupAndExit, ensureInitialized));
805
804
  program.addCommand(createCodeCommand(context, cleanupAndExit, ensureInitialized));
806
- program.addCommand(createMigrateCommand(context, cleanupAndExit, ensureInitialized));
807
805
  program.addCommand(createCompletionsCommand(cleanupAndExit));
808
806
  program.addCommand(createFleetCommand(context, cleanupAndExit, ensureInitialized, registerDomainWorkflowActions));
809
807
  program.addCommand(createValidateSwarmCommand(context, cleanupAndExit, ensureInitialized));
@@ -19,8 +19,8 @@ export class DefectContextSource {
19
19
  if (!existsSync(dbPath)) {
20
20
  return [];
21
21
  }
22
- const Database = (await import('better-sqlite3')).default;
23
- const db = new Database(dbPath, { readonly: true });
22
+ const { openDatabase } = await import('../../shared/safe-db.js');
23
+ const db = openDatabase(dbPath, { readonly: true });
24
24
  try {
25
25
  // Check if patterns table exists
26
26
  const tableCheck = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='qe_patterns'").get();
@@ -20,8 +20,8 @@ export class MemoryContextSource {
20
20
  return this.fallbackGather(request);
21
21
  }
22
22
  // Dynamic import to avoid hard dependency
23
- const Database = (await import('better-sqlite3')).default;
24
- const db = new Database(dbPath, { readonly: true });
23
+ const { openDatabase } = await import('../../shared/safe-db.js');
24
+ const db = openDatabase(dbPath, { readonly: true });
25
25
  try {
26
26
  // Query patterns relevant to the task domain
27
27
  const domainKeywords = this.extractDomainKeywords(request.taskDescription);
@@ -18,8 +18,8 @@ export class RequirementsContextSource {
18
18
  if (!existsSync(dbPath)) {
19
19
  return this.fallbackGather(request);
20
20
  }
21
- const Database = (await import('better-sqlite3')).default;
22
- const db = new Database(dbPath, { readonly: true });
21
+ const { openDatabase } = await import('../../shared/safe-db.js');
22
+ const db = openDatabase(dbPath, { readonly: true });
23
23
  try {
24
24
  // Check if requirements table exists
25
25
  const tableCheck = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='qe_patterns'").get();
@@ -169,14 +169,11 @@ export declare class SecurityAuditProtocol {
169
169
  */
170
170
  execute(trigger: SecurityAuditTrigger): Promise<Result<SecurityAuditResult>>;
171
171
  /**
172
- * Scan for vulnerabilities using SAST
173
- * Delegates to real SecurityScannerService with semgrep integration when available
172
+ * Scan for vulnerabilities using SAST.
173
+ * SASTScanner now integrates semgrep internally (runs alongside pattern scanning
174
+ * when semgrep is installed), so no separate fallback is needed here.
174
175
  */
175
176
  scanVulnerabilities(options: SecurityAuditOptions): Promise<Result<SASTResult>>;
176
- /**
177
- * Map semgrep OWASP category to VulnerabilityCategory
178
- */
179
- private mapSemgrepCategory;
180
177
  /**
181
178
  * Scan dependencies for vulnerabilities
182
179
  * Delegates to real SecurityScannerService which uses OSV API for real vulnerability data
@@ -10,6 +10,7 @@ import { v4 as uuidv4 } from 'uuid';
10
10
  import { ok, err, } from '../../shared/types/index.js';
11
11
  import { FilePath, RiskScore } from '../../shared/value-objects/index.js';
12
12
  import { createEvent, } from '../../shared/events/domain-events.js';
13
+ // Semgrep is now integrated directly into SASTScanner (no coordination-layer fallback needed)
13
14
  import { toError } from '../../shared/error-utils.js';
14
15
  // CQ-005: Use DomainServiceRegistry instead of dynamic imports from domains/
15
16
  import { DomainServiceRegistry, ServiceKeys } from '../../shared/domain-service-registry.js';
@@ -22,18 +23,6 @@ function resolveSecurityScannerService(memory) {
22
23
  const factory = DomainServiceRegistry.resolve(ServiceKeys.SecurityScannerService);
23
24
  return factory(memory);
24
25
  }
25
- function resolveIsSemgrepAvailable() {
26
- const fn = DomainServiceRegistry.resolve(ServiceKeys.isSemgrepAvailable);
27
- return fn();
28
- }
29
- function resolveRunSemgrepWithRules(targetPath, ruleSetIds) {
30
- const fn = DomainServiceRegistry.resolve(ServiceKeys.runSemgrepWithRules);
31
- return fn(targetPath, ruleSetIds);
32
- }
33
- function resolveConvertSemgrepFindings(findings) {
34
- const fn = DomainServiceRegistry.resolve(ServiceKeys.convertSemgrepFindings);
35
- return fn(findings);
36
- }
37
26
  // ============================================================================
38
27
  // Protocol Events
39
28
  // ============================================================================
@@ -208,8 +197,9 @@ export class SecurityAuditProtocol {
208
197
  // Scanning Methods
209
198
  // ==========================================================================
210
199
  /**
211
- * Scan for vulnerabilities using SAST
212
- * Delegates to real SecurityScannerService with semgrep integration when available
200
+ * Scan for vulnerabilities using SAST.
201
+ * SASTScanner now integrates semgrep internally (runs alongside pattern scanning
202
+ * when semgrep is installed), so no separate fallback is needed here.
213
203
  */
214
204
  async scanVulnerabilities(options) {
215
205
  try {
@@ -219,96 +209,26 @@ export class SecurityAuditProtocol {
219
209
  return err(agentId.error);
220
210
  }
221
211
  const files = this.config.scanPaths.map(path => FilePath.create(path));
222
- // Try real SecurityScannerService first
223
212
  try {
224
213
  const scanner = this.getSecurityScanner();
225
214
  const ruleSetIds = options.ruleSetIds || ['owasp-top-10', 'cwe-sans-25'];
215
+ // SASTScanner.scanWithRules now runs pattern scanning + semgrep in parallel
226
216
  const scanResult = await scanner.scanWithRules(files, ruleSetIds);
227
217
  if (scanResult.success) {
228
218
  return ok(scanResult.value);
229
219
  }
230
- // If scanner fails, continue to fallback
220
+ return err(scanResult.error);
231
221
  }
232
222
  catch (scannerError) {
233
- // Scanner unavailable - log and continue to fallback
234
223
  await this.memory.set('security-audit:scanner-error', { error: String(scannerError), timestamp: new Date().toISOString() }, { namespace: 'security-compliance', ttl: 3600 });
224
+ return err(new Error(`SAST scanning failed: ${String(scannerError)}. ` +
225
+ 'Ensure SecurityScannerService is properly configured.'));
235
226
  }
236
- // Try semgrep if available as secondary option
237
- const semgrepAvailable = await resolveIsSemgrepAvailable();
238
- if (semgrepAvailable) {
239
- try {
240
- const semgrepResult = await resolveRunSemgrepWithRules(this.config.scanPaths[0] || '.', options.ruleSetIds || ['owasp-top-10']);
241
- if (semgrepResult.success && semgrepResult.findings.length > 0) {
242
- const convertedFindings = resolveConvertSemgrepFindings(semgrepResult.findings);
243
- const vulnerabilities = convertedFindings.map(f => ({
244
- id: uuidv4(),
245
- cveId: undefined,
246
- title: f.title,
247
- description: f.description,
248
- severity: f.severity,
249
- category: this.mapSemgrepCategory(f.owaspCategory || 'injection'),
250
- location: {
251
- file: f.file,
252
- line: f.line,
253
- column: f.column,
254
- snippet: f.snippet,
255
- },
256
- remediation: {
257
- description: f.remediation,
258
- estimatedEffort: 'moderate',
259
- automatable: false,
260
- },
261
- references: f.references,
262
- }));
263
- const summary = this.calculateSummary(vulnerabilities);
264
- return ok({
265
- scanId: uuidv4(),
266
- vulnerabilities,
267
- summary,
268
- coverage: {
269
- filesScanned: files.length,
270
- linesScanned: vulnerabilities.length * 50,
271
- rulesApplied: 45,
272
- },
273
- });
274
- }
275
- }
276
- catch (semgrepError) {
277
- // Semgrep failed - log error
278
- await this.memory.set('security-audit:semgrep-error', { error: String(semgrepError), timestamp: new Date().toISOString() }, { namespace: 'security-compliance', ttl: 3600 });
279
- }
280
- }
281
- // NO FALLBACK - Security scans must either succeed or fail explicitly
282
- // An empty vulnerability list would falsely indicate "scan succeeded, nothing found"
283
- // when in reality we couldn't scan at all
284
- return err(new Error('SAST scanning unavailable: neither SecurityScannerService nor semgrep could execute. ' +
285
- 'Install semgrep (pip install semgrep) or ensure SecurityScannerService is properly configured.'));
286
227
  }
287
228
  catch (error) {
288
229
  return err(toError(error));
289
230
  }
290
231
  }
291
- /**
292
- * Map semgrep OWASP category to VulnerabilityCategory
293
- */
294
- mapSemgrepCategory(owaspCategory) {
295
- const categoryMap = {
296
- 'A01': 'access-control',
297
- 'A02': 'sensitive-data',
298
- 'A03': 'injection',
299
- 'A04': 'insecure-deserialization',
300
- 'A05': 'security-misconfiguration',
301
- 'A06': 'vulnerable-components',
302
- 'A07': 'broken-auth',
303
- 'A08': 'insecure-deserialization',
304
- 'A09': 'insufficient-logging',
305
- 'A10': 'xxe',
306
- 'injection': 'injection',
307
- 'xss': 'xss',
308
- 'broken-auth': 'broken-auth',
309
- };
310
- return categoryMap[owaspCategory] || 'security-misconfiguration';
311
- }
312
232
  /**
313
233
  * Scan dependencies for vulnerabilities
314
234
  * Delegates to real SecurityScannerService which uses OSV API for real vulnerability data