substrate-ai 0.2.20 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-CcWb6Kb-.js";
2
+ import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-BaAws8IQ.js";
3
3
  import { createLogger, deepMask } from "../logger-D2fS2ccL.js";
4
- import { AdapterRegistry, createEventBus } from "../event-bus-BMxhfxfT.js";
4
+ import { AdapterRegistry, ConfigError, ConfigIncompatibleFormatError } from "../errors-CswS7Mzg.js";
5
5
  import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "../version-manager-impl-CZ6KF1Ds.js";
6
- import { ConfigError, ConfigIncompatibleFormatError } from "../errors-BPqtzQ4U.js";
7
- import { addTokenUsage, createDecision, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-Dq4cAA2L.js";
8
- import { EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-Dq4IfJzE.js";
6
+ import { createEventBus } from "../event-bus-CAvDMst7.js";
7
+ import { addTokenUsage, createDecision, createPipelineRun, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-Dq4cAA2L.js";
8
+ import { ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-CnMlvWqc.js";
9
9
  import { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, verifyGitVersion } from "../git-utils-CtmrZrHS.js";
10
10
  import { registerUpgradeCommand } from "../upgrade-CjjAx5kD.js";
11
11
  import { Command } from "commander";
@@ -207,8 +207,7 @@ function registerAdaptersCommand(program, version, registry) {
207
207
  const adaptersCmd = program.command("adapters").description("Manage and inspect CLI agent adapters");
208
208
  adaptersCmd.command("list").description("List all known adapters with availability status").option("--output-format <format>", "Output format: table (default) or json", "table").option("--verbose", "Show additional detail in output", false).action(async (opts) => {
209
209
  const outputFormat = opts.outputFormat;
210
- const reg = registry ?? new AdapterRegistry();
211
- const report = await reg.discoverAndRegister();
210
+ const report = await registry.discoverAndRegister();
212
211
  if (outputFormat === "json") {
213
212
  const jsonData = report.results.map((r) => healthResultToJson(r.adapterId, r.displayName, r.healthResult));
214
213
  const output = buildJsonOutput("substrate adapters list", jsonData, version);
@@ -231,8 +230,7 @@ function registerAdaptersCommand(program, version, registry) {
231
230
  });
232
231
  adaptersCmd.command("check").description("Run health checks on all adapters and verify headless mode").option("--output-format <format>", "Output format: table (default) or json", "table").option("--verbose", "Show additional detail including error messages", false).action(async (opts) => {
233
232
  const outputFormat = opts.outputFormat;
234
- const reg = registry ?? new AdapterRegistry();
235
- const report = await reg.discoverAndRegister();
233
+ const report = await registry.discoverAndRegister();
236
234
  const noneInstalled = report.results.length > 0 && report.results.every((r) => isNotInstalled(r.healthResult));
237
235
  if (outputFormat === "json") {
238
236
  const jsonData = report.results.map((r) => healthResultToJson(r.adapterId, r.displayName, r.healthResult));
@@ -324,7 +322,7 @@ const DEFAULT_CONFIG = {
324
322
 
325
323
  //#endregion
326
324
  //#region src/cli/commands/init.ts
327
- const logger$16 = createLogger("init");
325
+ const logger$17 = createLogger("init");
328
326
  const __dirname = dirname(new URL(import.meta.url).pathname);
329
327
  const INIT_EXIT_SUCCESS = 0;
330
328
  const INIT_EXIT_ERROR = 1;
@@ -345,7 +343,7 @@ async function scaffoldBmadFramework(projectRoot, force, outputFormat) {
345
343
  const version = resolveBmadMethodVersion();
346
344
  if (force && bmadExists) process.stderr.write(`Warning: Replacing existing _bmad/ framework with bmad-method@${version}\n`);
347
345
  process.stdout.write(`Scaffolding BMAD framework from bmad-method@${version}\n`);
348
- logger$16.info({
346
+ logger$17.info({
349
347
  version,
350
348
  dest: bmadDest
351
349
  }, "Scaffolding BMAD framework");
@@ -355,7 +353,7 @@ async function scaffoldBmadFramework(projectRoot, force, outputFormat) {
355
353
  const destDir = join(bmadDest, dir);
356
354
  mkdirSync(destDir, { recursive: true });
357
355
  cpSync(srcDir, destDir, { recursive: true });
358
- logger$16.info({
356
+ logger$17.info({
359
357
  dir,
360
358
  dest: destDir
361
359
  }, "Scaffolded BMAD framework directory");
@@ -374,7 +372,7 @@ async function scaffoldBmadFramework(projectRoot, force, outputFormat) {
374
372
  "document_output_language: English"
375
373
  ].join("\n") + "\n";
376
374
  await writeFile(configFile, configStub, "utf8");
377
- logger$16.info({ configFile }, "Generated _bmad/_config/config.yaml stub");
375
+ logger$17.info({ configFile }, "Generated _bmad/_config/config.yaml stub");
378
376
  }
379
377
  }
380
378
  const CLAUDE_MD_START_MARKER = "<!-- substrate:start -->";
@@ -389,7 +387,7 @@ async function scaffoldClaudeMd(projectRoot) {
389
387
  try {
390
388
  sectionContent = await readFile(templatePath, "utf8");
391
389
  } catch {
392
- logger$16.warn({ templatePath }, "CLAUDE.md substrate section template not found; skipping");
390
+ logger$17.warn({ templatePath }, "CLAUDE.md substrate section template not found; skipping");
393
391
  return;
394
392
  }
395
393
  if (!sectionContent.endsWith("\n")) sectionContent += "\n";
@@ -407,7 +405,7 @@ async function scaffoldClaudeMd(projectRoot) {
407
405
  newContent = existingContent + separator + sectionContent;
408
406
  }
409
407
  await writeFile(claudeMdPath, newContent, "utf8");
410
- logger$16.info({ claudeMdPath }, "Wrote substrate section to CLAUDE.md");
408
+ logger$17.info({ claudeMdPath }, "Wrote substrate section to CLAUDE.md");
411
409
  }
412
410
  async function scaffoldStatuslineScript(projectRoot) {
413
411
  const pkgRoot = findPackageRoot(__dirname);
@@ -418,7 +416,7 @@ async function scaffoldStatuslineScript(projectRoot) {
418
416
  try {
419
417
  content = await readFile(templatePath, "utf8");
420
418
  } catch {
421
- logger$16.warn({ templatePath }, "statusline.sh template not found; skipping");
419
+ logger$17.warn({ templatePath }, "statusline.sh template not found; skipping");
422
420
  return;
423
421
  }
424
422
  const claudeDir = join(projectRoot, ".claude");
@@ -426,7 +424,7 @@ async function scaffoldStatuslineScript(projectRoot) {
426
424
  mkdirSync(claudeDir, { recursive: true });
427
425
  await writeFile(statuslinePath, content, "utf8");
428
426
  chmodSync(statuslinePath, 493);
429
- logger$16.info({ statuslinePath }, "Wrote .claude/statusline.sh");
427
+ logger$17.info({ statuslinePath }, "Wrote .claude/statusline.sh");
430
428
  }
431
429
  async function scaffoldClaudeSettings(projectRoot) {
432
430
  const claudeDir = join(projectRoot, ".claude");
@@ -442,7 +440,7 @@ async function scaffoldClaudeSettings(projectRoot) {
442
440
  if (!merged["$schema"]) merged["$schema"] = "https://json.schemastore.org/claude-code-settings.json";
443
441
  mkdirSync(claudeDir, { recursive: true });
444
442
  await writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
445
- logger$16.info({ settingsPath }, "Wrote substrate settings to .claude/settings.json");
443
+ logger$17.info({ settingsPath }, "Wrote substrate settings to .claude/settings.json");
446
444
  }
447
445
  function resolveBmadMethodInstallerLibPath(fromDir = __dirname) {
448
446
  try {
@@ -512,7 +510,7 @@ async function compileBmadAgents(bmadDir) {
512
510
  writeFileSync(mdPath, result.xml, "utf-8");
513
511
  compiled++;
514
512
  } catch (compileErr) {
515
- logger$16.debug({
513
+ logger$17.debug({
516
514
  err: compileErr,
517
515
  file
518
516
  }, "Failed to compile agent YAML");
@@ -533,9 +531,9 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
533
531
  const _require = createRequire(join(__dirname, "synthetic.js"));
534
532
  try {
535
533
  const compiledCount = await compileBmadAgents(bmadDir);
536
- if (compiledCount > 0) logger$16.info({ compiledCount }, "Compiled agent YAML files to MD");
534
+ if (compiledCount > 0) logger$17.info({ compiledCount }, "Compiled agent YAML files to MD");
537
535
  } catch (compileErr) {
538
- logger$16.warn({ err: compileErr }, "Agent compilation failed; agent commands may be incomplete");
536
+ logger$17.warn({ err: compileErr }, "Agent compilation failed; agent commands may be incomplete");
539
537
  }
540
538
  const { AgentCommandGenerator } = _require(join(installerLibPath, "ide", "shared", "agent-command-generator.js"));
541
539
  const { WorkflowCommandGenerator } = _require(join(installerLibPath, "ide", "shared", "workflow-command-generator.js"));
@@ -547,7 +545,7 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
547
545
  const manifestGen = new ManifestGenerator();
548
546
  await manifestGen.generateManifests(bmadDir, allModules, [], { ides: ["claude-code"] });
549
547
  } catch (manifestErr) {
550
- logger$16.warn({ err: manifestErr }, "ManifestGenerator failed; workflow/task commands may be incomplete");
548
+ logger$17.warn({ err: manifestErr }, "ManifestGenerator failed; workflow/task commands may be incomplete");
551
549
  }
552
550
  const commandsDir = join(projectRoot, ".claude", "commands");
553
551
  mkdirSync(commandsDir, { recursive: true });
@@ -563,7 +561,7 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
563
561
  const taskToolCount = await taskToolGen.writeDashArtifacts(commandsDir, taskToolArtifacts);
564
562
  const total = agentCount + workflowCount + taskToolCount;
565
563
  if (outputFormat !== "json") process.stdout.write(`Generated ${String(total)} Claude Code commands (${String(agentCount)} agents, ${String(workflowCount)} workflows, ${String(taskToolCount)} tasks/tools)\n`);
566
- logger$16.info({
564
+ logger$17.info({
567
565
  agentCount,
568
566
  workflowCount,
569
567
  taskToolCount,
@@ -573,7 +571,7 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
573
571
  } catch (err) {
574
572
  const msg = err instanceof Error ? err.message : String(err);
575
573
  if (outputFormat !== "json") process.stderr.write(`Warning: .claude/commands/ generation failed: ${msg}\n`);
576
- logger$16.warn({ err }, "scaffoldClaudeCommands failed; init continues");
574
+ logger$17.warn({ err }, "scaffoldClaudeCommands failed; init continues");
577
575
  }
578
576
  }
579
577
  const PROVIDER_DEFAULTS = DEFAULT_CONFIG.providers;
@@ -641,13 +639,14 @@ async function runInitAction(options) {
641
639
  if (outputFormat !== "json") process.stdout.write(` .substrate/ directory already exists at ${substrateDir}\n`);
642
640
  }
643
641
  if (outputFormat !== "json") process.stdout.write("\n Discovering installed AI agents...\n");
644
- const registry = options.registry ?? new AdapterRegistry();
642
+ const registry = options.registry;
643
+ if (!registry) throw new Error("AdapterRegistry is required — must be initialized at CLI startup");
645
644
  let discoveryReport;
646
645
  try {
647
646
  discoveryReport = await registry.discoverAndRegister();
648
647
  } catch (err) {
649
648
  const message = err instanceof Error ? err.message : String(err);
650
- logger$16.error({ err }, "Adapter discovery failed");
649
+ logger$17.error({ err }, "Adapter discovery failed");
651
650
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, `Adapter discovery failed: ${message}`) + "\n");
652
651
  else process.stderr.write(` Error: adapter discovery failed — ${message}\n`);
653
652
  return INIT_EXIT_ERROR;
@@ -696,12 +695,12 @@ async function runInitAction(options) {
696
695
  return INIT_EXIT_ERROR;
697
696
  }
698
697
  if (force && existsSync(localManifest)) {
699
- logger$16.info({ pack: packName }, "Replacing existing pack with bundled version");
698
+ logger$17.info({ pack: packName }, "Replacing existing pack with bundled version");
700
699
  process.stderr.write(`Warning: Replacing existing pack '${packName}' with bundled version\n`);
701
700
  }
702
701
  mkdirSync(dirname(packPath), { recursive: true });
703
702
  cpSync(bundledPackPath, packPath, { recursive: true });
704
- logger$16.info({
703
+ logger$17.info({
705
704
  pack: packName,
706
705
  dest: packPath
707
706
  }, "Scaffolded methodology pack");
@@ -754,7 +753,7 @@ async function runInitAction(options) {
754
753
  const msg = err instanceof Error ? err.message : String(err);
755
754
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
756
755
  else process.stderr.write(`Error: ${msg}\n`);
757
- logger$16.error({ err }, "init failed");
756
+ logger$17.error({ err }, "init failed");
758
757
  return INIT_EXIT_ERROR;
759
758
  }
760
759
  }
@@ -800,7 +799,7 @@ function formatUnsupportedVersionError(formatType, version, supported) {
800
799
 
801
800
  //#endregion
802
801
  //#region src/modules/config/config-system-impl.ts
803
- const logger$15 = createLogger("config");
802
+ const logger$16 = createLogger("config");
804
803
  function deepMerge(base, override) {
805
804
  const result = { ...base };
806
805
  for (const [key, val] of Object.entries(override)) if (val !== null && val !== void 0 && typeof val === "object" && !Array.isArray(val) && typeof result[key] === "object" && result[key] !== null && !Array.isArray(result[key])) result[key] = deepMerge(result[key], val);
@@ -845,7 +844,7 @@ function readEnvOverrides() {
845
844
  }
846
845
  const parsed = PartialSubstrateConfigSchema.safeParse(overrides);
847
846
  if (!parsed.success) {
848
- logger$15.warn({ errors: parsed.error.issues }, "Invalid environment variable overrides ignored");
847
+ logger$16.warn({ errors: parsed.error.issues }, "Invalid environment variable overrides ignored");
849
848
  return {};
850
849
  }
851
850
  return parsed.data;
@@ -909,7 +908,7 @@ var ConfigSystemImpl = class {
909
908
  throw new ConfigError(`Configuration validation failed:\n${issues}`, { issues: result.error.issues });
910
909
  }
911
910
  this._config = result.data;
912
- logger$15.debug("Configuration loaded successfully");
911
+ logger$16.debug("Configuration loaded successfully");
913
912
  }
914
913
  getConfig() {
915
914
  if (this._config === null) throw new ConfigError("Configuration has not been loaded. Call load() before getConfig().", {});
@@ -972,7 +971,7 @@ var ConfigSystemImpl = class {
972
971
  if (version !== void 0 && typeof version === "string" && !isVersionSupported(version, SUPPORTED_CONFIG_FORMAT_VERSIONS)) if (defaultConfigMigrator.canMigrate(version, CURRENT_CONFIG_FORMAT_VERSION)) {
973
972
  const migrationOutput = defaultConfigMigrator.migrate(rawObj, version, CURRENT_CONFIG_FORMAT_VERSION, filePath);
974
973
  if (migrationOutput.result.success) {
975
- logger$15.info({
974
+ logger$16.info({
976
975
  from: version,
977
976
  to: CURRENT_CONFIG_FORMAT_VERSION,
978
977
  backup: migrationOutput.result.backupPath
@@ -1015,7 +1014,7 @@ function createConfigSystem(options = {}) {
1015
1014
 
1016
1015
  //#endregion
1017
1016
  //#region src/cli/commands/config.ts
1018
- const logger$14 = createLogger("config-cmd");
1017
+ const logger$15 = createLogger("config-cmd");
1019
1018
  const CONFIG_EXIT_SUCCESS = 0;
1020
1019
  const CONFIG_EXIT_ERROR = 1;
1021
1020
  const CONFIG_EXIT_INVALID = 2;
@@ -1041,7 +1040,7 @@ async function runConfigShow(opts = {}) {
1041
1040
  return CONFIG_EXIT_INVALID;
1042
1041
  }
1043
1042
  const message = err instanceof Error ? err.message : String(err);
1044
- logger$14.error({ err }, "Failed to load configuration");
1043
+ logger$15.error({ err }, "Failed to load configuration");
1045
1044
  process.stderr.write(` Error loading configuration: ${message}\n`);
1046
1045
  return CONFIG_EXIT_ERROR;
1047
1046
  }
@@ -1115,7 +1114,7 @@ async function runConfigExport(opts = {}) {
1115
1114
  return CONFIG_EXIT_INVALID;
1116
1115
  }
1117
1116
  const message = err instanceof Error ? err.message : String(err);
1118
- logger$14.error({ err }, "Failed to load configuration");
1117
+ logger$15.error({ err }, "Failed to load configuration");
1119
1118
  process.stderr.write(`Error loading configuration: ${message}\n`);
1120
1119
  return CONFIG_EXIT_ERROR;
1121
1120
  }
@@ -1269,9 +1268,9 @@ function registerConfigCommand(program, _version) {
1269
1268
 
1270
1269
  //#endregion
1271
1270
  //#region src/cli/commands/resume.ts
1272
- const logger$13 = createLogger("resume-cmd");
1271
+ const logger$14 = createLogger("resume-cmd");
1273
1272
  async function runResumeAction(options) {
1274
- const { runId: specifiedRunId, stopAfter, outputFormat, projectRoot, concurrency, pack: packName } = options;
1273
+ const { runId: specifiedRunId, stopAfter, outputFormat, projectRoot, concurrency, pack: packName, registry } = options;
1275
1274
  if (stopAfter !== void 0 && !VALID_PHASES.includes(stopAfter)) {
1276
1275
  const errorMsg = `Invalid phase: "${stopAfter}". Valid phases: ${VALID_PHASES.join(", ")}`;
1277
1276
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
@@ -1345,13 +1344,14 @@ async function runResumeAction(options) {
1345
1344
  concurrency,
1346
1345
  outputFormat,
1347
1346
  existingRunId: runId,
1348
- projectRoot
1347
+ projectRoot,
1348
+ registry
1349
1349
  });
1350
1350
  } catch (err) {
1351
1351
  const msg = err instanceof Error ? err.message : String(err);
1352
1352
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
1353
1353
  else process.stderr.write(`Error: ${msg}\n`);
1354
- logger$13.error({ err }, "auto resume failed");
1354
+ logger$14.error({ err }, "auto resume failed");
1355
1355
  return 1;
1356
1356
  } finally {
1357
1357
  try {
@@ -1360,7 +1360,7 @@ async function runResumeAction(options) {
1360
1360
  }
1361
1361
  }
1362
1362
  async function runFullPipelineFromPhase(options) {
1363
- const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, existingRunId, projectRoot } = options;
1363
+ const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, existingRunId, projectRoot, registry: injectedRegistry } = options;
1364
1364
  if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });
1365
1365
  const dbWrapper = new DatabaseWrapper(dbPath);
1366
1366
  try {
@@ -1380,11 +1380,10 @@ async function runFullPipelineFromPhase(options) {
1380
1380
  }
1381
1381
  const eventBus = createEventBus();
1382
1382
  const contextCompiler = createContextCompiler({ db });
1383
- const adapterRegistry = new AdapterRegistry();
1384
- await adapterRegistry.discoverAndRegister();
1383
+ if (!injectedRegistry) throw new Error("AdapterRegistry is required — must be initialized at CLI startup");
1385
1384
  const dispatcher = createDispatcher({
1386
1385
  eventBus,
1387
- adapterRegistry
1386
+ adapterRegistry: injectedRegistry
1388
1387
  });
1389
1388
  const phaseDeps = {
1390
1389
  db,
@@ -1502,7 +1501,7 @@ async function runFullPipelineFromPhase(options) {
1502
1501
  });
1503
1502
  }
1504
1503
  } catch (err) {
1505
- logger$13.warn({ err }, "Failed to record token usage");
1504
+ logger$14.warn({ err }, "Failed to record token usage");
1506
1505
  }
1507
1506
  });
1508
1507
  const storyDecisions = db.prepare(`SELECT description FROM requirements WHERE pipeline_run_id = ? AND source = 'solutioning-phase'`).all(runId);
@@ -1561,7 +1560,7 @@ async function runFullPipelineFromPhase(options) {
1561
1560
  const msg = err instanceof Error ? err.message : String(err);
1562
1561
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
1563
1562
  else process.stderr.write(`Error: ${msg}\n`);
1564
- logger$13.error({ err }, "pipeline from phase failed");
1563
+ logger$14.error({ err }, "pipeline from phase failed");
1565
1564
  return 1;
1566
1565
  } finally {
1567
1566
  try {
@@ -1569,7 +1568,7 @@ async function runFullPipelineFromPhase(options) {
1569
1568
  } catch {}
1570
1569
  }
1571
1570
  }
1572
- function registerResumeCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
1571
+ function registerResumeCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
1573
1572
  program.command("resume").description("Resume a previously interrupted pipeline run").option("--run-id <id>", "Pipeline run ID to resume (defaults to latest)").option("--pack <name>", "Methodology pack name", "bmad").option("--stop-after <phase>", "Stop pipeline after this phase completes (overrides saved state)").option("--concurrency <n>", "Maximum parallel conflict groups", (v) => parseInt(v, 10), 3).option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").action(async (opts) => {
1574
1573
  const outputFormat = opts.outputFormat === "json" ? "json" : "human";
1575
1574
  const exitCode = await runResumeAction({
@@ -1578,7 +1577,8 @@ function registerResumeCommand(program, _version = "0.0.0", projectRoot = proces
1578
1577
  outputFormat,
1579
1578
  projectRoot: opts.projectRoot,
1580
1579
  concurrency: opts.concurrency,
1581
- pack: opts.pack
1580
+ pack: opts.pack,
1581
+ registry
1582
1582
  });
1583
1583
  process.exitCode = exitCode;
1584
1584
  });
@@ -1586,7 +1586,7 @@ function registerResumeCommand(program, _version = "0.0.0", projectRoot = proces
1586
1586
 
1587
1587
  //#endregion
1588
1588
  //#region src/cli/commands/status.ts
1589
- const logger$12 = createLogger("status-cmd");
1589
+ const logger$13 = createLogger("status-cmd");
1590
1590
  async function runStatusAction(options) {
1591
1591
  const { outputFormat, runId, projectRoot } = options;
1592
1592
  const dbRoot = await resolveMainRepoRoot(projectRoot);
@@ -1663,7 +1663,7 @@ async function runStatusAction(options) {
1663
1663
  const msg = err instanceof Error ? err.message : String(err);
1664
1664
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
1665
1665
  else process.stderr.write(`Error: ${msg}\n`);
1666
- logger$12.error({ err }, "status action failed");
1666
+ logger$13.error({ err }, "status action failed");
1667
1667
  return 1;
1668
1668
  } finally {
1669
1669
  try {
@@ -2087,7 +2087,7 @@ Analyze thoroughly and return ONLY the JSON array with no additional text.`;
2087
2087
 
2088
2088
  //#endregion
2089
2089
  //#region src/cli/commands/amend.ts
2090
- const logger$11 = createLogger("amend-cmd");
2090
+ const logger$12 = createLogger("amend-cmd");
2091
2091
  /**
2092
2092
  * Detect and apply supersessions after a phase completes in an amendment run.
2093
2093
  *
@@ -2118,7 +2118,7 @@ function runPostPhaseSupersessionDetection(db, amendmentRunId, currentPhase, han
2118
2118
  });
2119
2119
  } catch (err) {
2120
2120
  const msg = err instanceof Error ? err.message : String(err);
2121
- logger$11.warn({
2121
+ logger$12.warn({
2122
2122
  err,
2123
2123
  originalId: parentMatch.id,
2124
2124
  supersedingId: newDec.id
@@ -2127,7 +2127,7 @@ function runPostPhaseSupersessionDetection(db, amendmentRunId, currentPhase, han
2127
2127
  }
2128
2128
  }
2129
2129
  async function runAmendAction(options) {
2130
- const { concept: conceptArg, conceptFile, runId: specifiedRunId, stopAfter, from: startPhase, projectRoot, pack: packName } = options;
2130
+ const { concept: conceptArg, conceptFile, runId: specifiedRunId, stopAfter, from: startPhase, projectRoot, pack: packName, registry: injectedRegistry } = options;
2131
2131
  let concept;
2132
2132
  if (conceptFile !== void 0 && conceptFile !== "") try {
2133
2133
  concept = await readFile(conceptFile, "utf-8");
@@ -2214,11 +2214,10 @@ async function runAmendAction(options) {
2214
2214
  }
2215
2215
  const eventBus = createEventBus();
2216
2216
  const contextCompiler = createContextCompiler({ db });
2217
- const adapterRegistry = new AdapterRegistry();
2218
- await adapterRegistry.discoverAndRegister();
2217
+ if (!injectedRegistry) throw new Error("AdapterRegistry is required — must be initialized at CLI startup");
2219
2218
  const dispatcher = createDispatcher({
2220
2219
  eventBus,
2221
- adapterRegistry
2220
+ adapterRegistry: injectedRegistry
2222
2221
  });
2223
2222
  const phaseDeps = {
2224
2223
  db,
@@ -2253,7 +2252,7 @@ async function runAmendAction(options) {
2253
2252
  for (let i = startIdx; i < phaseOrder.length; i++) {
2254
2253
  const currentPhase = phaseOrder[i];
2255
2254
  const amendmentContext = handler.loadContextForPhase(currentPhase);
2256
- logger$11.info({
2255
+ logger$12.info({
2257
2256
  phase: currentPhase,
2258
2257
  amendmentContextLen: amendmentContext.length
2259
2258
  }, "Amendment context loaded for phase");
@@ -2373,7 +2372,7 @@ async function runAmendAction(options) {
2373
2372
  } catch (err) {
2374
2373
  const msg = err instanceof Error ? err.message : String(err);
2375
2374
  process.stderr.write(`Error: ${msg}\n`);
2376
- logger$11.error({ err }, "amend failed");
2375
+ logger$12.error({ err }, "amend failed");
2377
2376
  return 1;
2378
2377
  } finally {
2379
2378
  try {
@@ -2381,7 +2380,7 @@ async function runAmendAction(options) {
2381
2380
  } catch {}
2382
2381
  }
2383
2382
  }
2384
- function registerAmendCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
2383
+ function registerAmendCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
2385
2384
  program.command("amend").description("Run an amendment pipeline against a completed run and an existing run").option("--concept <text>", "Amendment concept description (inline)").option("--concept-file <path>", "Path to concept file").option("--run-id <id>", "Parent run ID (defaults to latest completed run)").option("--stop-after <phase>", "Stop pipeline after this phase completes").option("--from <phase>", "Start pipeline from this phase").option("--pack <name>", "Methodology pack name", "bmad").option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").action(async (opts) => {
2386
2385
  const exitCode = await runAmendAction({
2387
2386
  concept: opts.concept,
@@ -2390,7 +2389,8 @@ function registerAmendCommand(program, _version = "0.0.0", projectRoot = process
2390
2389
  stopAfter: opts.stopAfter,
2391
2390
  from: opts.from,
2392
2391
  projectRoot: opts.projectRoot,
2393
- pack: opts.pack
2392
+ pack: opts.pack,
2393
+ registry
2394
2394
  });
2395
2395
  process.exitCode = exitCode;
2396
2396
  });
@@ -2828,7 +2828,7 @@ async function runSupervisorAction(options, deps = {}) {
2828
2828
  try {
2829
2829
  const { createExperimenter } = await import(
2830
2830
  /* @vite-ignore */
2831
- "../experimenter-prkFLFPw.js"
2831
+ "../experimenter-bc40oi8p.js"
2832
2832
  );
2833
2833
  const { getLatestRun: getLatest } = await import(
2834
2834
  /* @vite-ignore */
@@ -2842,7 +2842,7 @@ async function runSupervisorAction(options, deps = {}) {
2842
2842
  const expDb = expDbWrapper.db;
2843
2843
  const { runRunAction: runPipeline } = await import(
2844
2844
  /* @vite-ignore */
2845
- "../run-C5zfaWYN.js"
2845
+ "../run-BenC8JuM.js"
2846
2846
  );
2847
2847
  const runStoryFn = async (opts) => {
2848
2848
  const exitCode = await runPipeline({
@@ -3085,7 +3085,7 @@ function registerSupervisorCommand(program, _version = "0.0.0", projectRoot = pr
3085
3085
 
3086
3086
  //#endregion
3087
3087
  //#region src/cli/commands/metrics.ts
3088
- const logger$10 = createLogger("metrics-cmd");
3088
+ const logger$11 = createLogger("metrics-cmd");
3089
3089
  async function runMetricsAction(options) {
3090
3090
  const { outputFormat, projectRoot, limit = 10, compare, tagBaseline, analysis } = options;
3091
3091
  if (analysis !== void 0) {
@@ -3237,7 +3237,7 @@ async function runMetricsAction(options) {
3237
3237
  const msg = err instanceof Error ? err.message : String(err);
3238
3238
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
3239
3239
  else process.stderr.write(`Error: ${msg}\n`);
3240
- logger$10.error({ err }, "metrics action failed");
3240
+ logger$11.error({ err }, "metrics action failed");
3241
3241
  return 1;
3242
3242
  } finally {
3243
3243
  try {
@@ -3491,7 +3491,7 @@ function getPlanningCostTotal(db, sessionId) {
3491
3491
  function getLatestSessionId(_db) {
3492
3492
  return null;
3493
3493
  }
3494
- const logger$9 = createLogger("cost-cmd");
3494
+ const logger$10 = createLogger("cost-cmd");
3495
3495
  const COST_EXIT_SUCCESS = 0;
3496
3496
  const COST_EXIT_ERROR = 1;
3497
3497
  /**
@@ -3737,7 +3737,7 @@ async function runCostAction(options) {
3737
3737
  } catch (err) {
3738
3738
  const message = err instanceof Error ? err.message : String(err);
3739
3739
  process.stderr.write(`Error: ${message}\n`);
3740
- logger$9.error({ err }, "runCostAction failed");
3740
+ logger$10.error({ err }, "runCostAction failed");
3741
3741
  return COST_EXIT_ERROR;
3742
3742
  } finally {
3743
3743
  if (wrapper !== null) try {
@@ -3839,7 +3839,7 @@ function applyMonitorSchema(db) {
3839
3839
 
3840
3840
  //#endregion
3841
3841
  //#region src/persistence/monitor-database.ts
3842
- const logger$8 = createLogger("persistence:monitor-db");
3842
+ const logger$9 = createLogger("persistence:monitor-db");
3843
3843
  var MonitorDatabaseImpl = class {
3844
3844
  _db = null;
3845
3845
  _path;
@@ -3850,10 +3850,10 @@ var MonitorDatabaseImpl = class {
3850
3850
  this._open();
3851
3851
  }
3852
3852
  _open() {
3853
- logger$8.info({ path: this._path }, "Opening monitor database");
3853
+ logger$9.info({ path: this._path }, "Opening monitor database");
3854
3854
  this._db = new BetterSqlite3(this._path);
3855
3855
  const walResult = this._db.pragma("journal_mode = WAL");
3856
- if (walResult?.[0]?.journal_mode !== "wal") logger$8.warn({ result: walResult?.[0]?.journal_mode }, "Monitor DB: WAL pragma did not confirm wal mode");
3856
+ if (walResult?.[0]?.journal_mode !== "wal") logger$9.warn({ result: walResult?.[0]?.journal_mode }, "Monitor DB: WAL pragma did not confirm wal mode");
3857
3857
  this._db.pragma("synchronous = NORMAL");
3858
3858
  this._db.pragma("busy_timeout = 5000");
3859
3859
  this._db.pragma("foreign_keys = ON");
@@ -3888,7 +3888,7 @@ var MonitorDatabaseImpl = class {
3888
3888
  total_retries = total_retries + @retries,
3889
3889
  last_updated = @lastUpdated
3890
3890
  `);
3891
- logger$8.info({ path: this._path }, "Monitor database ready");
3891
+ logger$9.info({ path: this._path }, "Monitor database ready");
3892
3892
  }
3893
3893
  _assertOpen() {
3894
3894
  if (this._db === null) throw new Error("MonitorDatabase: connection is closed");
@@ -4037,7 +4037,7 @@ var MonitorDatabaseImpl = class {
4037
4037
  const db = this._assertOpen();
4038
4038
  const cutoff = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1e3).toISOString();
4039
4039
  const result = db.prepare("DELETE FROM task_metrics WHERE recorded_at < @cutoff").run({ cutoff });
4040
- logger$8.info({
4040
+ logger$9.info({
4041
4041
  cutoff,
4042
4042
  deleted: result.changes
4043
4043
  }, "Pruned old task_metrics rows");
@@ -4076,13 +4076,13 @@ var MonitorDatabaseImpl = class {
4076
4076
  db.exec("ROLLBACK");
4077
4077
  throw err;
4078
4078
  }
4079
- logger$8.info("Rebuilt performance_aggregates from task_metrics");
4079
+ logger$9.info("Rebuilt performance_aggregates from task_metrics");
4080
4080
  }
4081
4081
  resetAllData() {
4082
4082
  const db = this._assertOpen();
4083
4083
  db.exec("DELETE FROM task_metrics");
4084
4084
  db.exec("DELETE FROM performance_aggregates");
4085
- logger$8.info({ path: this._path }, "Monitor data reset — all rows deleted");
4085
+ logger$9.info({ path: this._path }, "Monitor data reset — all rows deleted");
4086
4086
  }
4087
4087
  getTaskMetricsDateRange() {
4088
4088
  const db = this._assertOpen();
@@ -4099,7 +4099,7 @@ var MonitorDatabaseImpl = class {
4099
4099
  if (this._db === null) return;
4100
4100
  this._db.close();
4101
4101
  this._db = null;
4102
- logger$8.info({ path: this._path }, "Monitor database closed");
4102
+ logger$9.info({ path: this._path }, "Monitor database closed");
4103
4103
  }
4104
4104
  /**
4105
4105
  * Access the raw underlying database for testing purposes only.
@@ -4112,7 +4112,7 @@ var MonitorDatabaseImpl = class {
4112
4112
 
4113
4113
  //#endregion
4114
4114
  //#region src/modules/monitor/recommendation-engine.ts
4115
- const logger$7 = createLogger("monitor:recommendations");
4115
+ const logger$8 = createLogger("monitor:recommendations");
4116
4116
  var RecommendationEngine = class {
4117
4117
  _monitorDb;
4118
4118
  _filters;
@@ -4145,7 +4145,7 @@ var RecommendationEngine = class {
4145
4145
  const sinceDate = new Date(Date.now() - this._historyDays * 24 * 60 * 60 * 1e3).toISOString();
4146
4146
  const aggregates = this._monitorDb.getAggregates({ sinceDate });
4147
4147
  if (aggregates.length === 0) {
4148
- logger$7.debug("No performance aggregates found — no recommendations to generate");
4148
+ logger$8.debug("No performance aggregates found — no recommendations to generate");
4149
4149
  return [];
4150
4150
  }
4151
4151
  const byTaskType = new Map();
@@ -4210,7 +4210,7 @@ var RecommendationEngine = class {
4210
4210
  if (confDiff !== 0) return confDiff;
4211
4211
  return b.improvement_percentage - a.improvement_percentage;
4212
4212
  });
4213
- logger$7.debug({ count: recommendations.length }, "Generated routing recommendations");
4213
+ logger$8.debug({ count: recommendations.length }, "Generated routing recommendations");
4214
4214
  return recommendations;
4215
4215
  }
4216
4216
  /**
@@ -4376,7 +4376,7 @@ function generateMonitorReport(monitorDb, options = {}) {
4376
4376
 
4377
4377
  //#endregion
4378
4378
  //#region src/cli/commands/monitor.ts
4379
- const logger$6 = createLogger("monitor-cmd");
4379
+ const logger$7 = createLogger("monitor-cmd");
4380
4380
  const MONITOR_EXIT_SUCCESS = 0;
4381
4381
  const MONITOR_EXIT_ERROR = 1;
4382
4382
  /**
@@ -4579,7 +4579,7 @@ async function runMonitorReportAction(options) {
4579
4579
  } catch (err) {
4580
4580
  const message = err instanceof Error ? err.message : String(err);
4581
4581
  process.stderr.write(`Error: ${message}\n`);
4582
- logger$6.error({ err }, "runMonitorReportAction failed");
4582
+ logger$7.error({ err }, "runMonitorReportAction failed");
4583
4583
  return MONITOR_EXIT_ERROR;
4584
4584
  } finally {
4585
4585
  if (monitorDb !== null) try {
@@ -4641,7 +4641,7 @@ async function runMonitorStatusAction(options) {
4641
4641
  } catch (err) {
4642
4642
  const message = err instanceof Error ? err.message : String(err);
4643
4643
  process.stderr.write(`Error: ${message}\n`);
4644
- logger$6.error({ err }, "runMonitorStatusAction failed");
4644
+ logger$7.error({ err }, "runMonitorStatusAction failed");
4645
4645
  return MONITOR_EXIT_ERROR;
4646
4646
  } finally {
4647
4647
  if (monitorDb !== null) try {
@@ -4676,7 +4676,7 @@ async function runMonitorResetAction(options) {
4676
4676
  } catch (err) {
4677
4677
  const message = err instanceof Error ? err.message : String(err);
4678
4678
  process.stderr.write(`Error: ${message}\n`);
4679
- logger$6.error({ err }, "runMonitorResetAction failed");
4679
+ logger$7.error({ err }, "runMonitorResetAction failed");
4680
4680
  return MONITOR_EXIT_ERROR;
4681
4681
  } finally {
4682
4682
  if (monitorDb !== null) try {
@@ -4724,7 +4724,7 @@ async function runMonitorRecommendationsAction(options) {
4724
4724
  } catch (err) {
4725
4725
  const message = err instanceof Error ? err.message : String(err);
4726
4726
  process.stderr.write(`Error: ${message}\n`);
4727
- logger$6.error({ err }, "runMonitorRecommendationsAction failed");
4727
+ logger$7.error({ err }, "runMonitorRecommendationsAction failed");
4728
4728
  return MONITOR_EXIT_ERROR;
4729
4729
  } finally {
4730
4730
  if (monitorDb !== null) try {
@@ -4802,7 +4802,7 @@ function registerMonitorCommand(program, version = "0.0.0", projectRoot = proces
4802
4802
 
4803
4803
  //#endregion
4804
4804
  //#region src/modules/git-worktree/git-worktree-manager-impl.ts
4805
- const logger$5 = createLogger("git-worktree");
4805
+ const logger$6 = createLogger("git-worktree");
4806
4806
  const BRANCH_PREFIX = "substrate/task-";
4807
4807
  const DEFAULT_WORKTREE_BASE = ".substrate-worktrees";
4808
4808
  var GitWorktreeManagerImpl = class {
@@ -4821,7 +4821,7 @@ var GitWorktreeManagerImpl = class {
4821
4821
  this._db = db;
4822
4822
  this._onTaskReady = ({ taskId }) => {
4823
4823
  this._handleTaskReady(taskId).catch((err) => {
4824
- logger$5.error({
4824
+ logger$6.error({
4825
4825
  taskId,
4826
4826
  err
4827
4827
  }, "Unhandled error in _handleTaskReady");
@@ -4835,40 +4835,40 @@ var GitWorktreeManagerImpl = class {
4835
4835
  };
4836
4836
  }
4837
4837
  async initialize() {
4838
- logger$5.info({ projectRoot: this._projectRoot }, "GitWorktreeManager.initialize()");
4838
+ logger$6.info({ projectRoot: this._projectRoot }, "GitWorktreeManager.initialize()");
4839
4839
  await this.verifyGitVersion();
4840
4840
  const cleaned = await this.cleanupAllWorktrees();
4841
- if (cleaned > 0) logger$5.info({ cleaned }, "Recovered orphaned worktrees on startup");
4841
+ if (cleaned > 0) logger$6.info({ cleaned }, "Recovered orphaned worktrees on startup");
4842
4842
  this._eventBus.on("task:ready", this._onTaskReady);
4843
4843
  this._eventBus.on("task:complete", this._onTaskComplete);
4844
4844
  this._eventBus.on("task:failed", this._onTaskFailed);
4845
- logger$5.info("GitWorktreeManager initialized");
4845
+ logger$6.info("GitWorktreeManager initialized");
4846
4846
  }
4847
4847
  async shutdown() {
4848
- logger$5.info("GitWorktreeManager.shutdown()");
4848
+ logger$6.info("GitWorktreeManager.shutdown()");
4849
4849
  this._eventBus.off("task:ready", this._onTaskReady);
4850
4850
  this._eventBus.off("task:complete", this._onTaskComplete);
4851
4851
  this._eventBus.off("task:failed", this._onTaskFailed);
4852
4852
  await this.cleanupAllWorktrees();
4853
- logger$5.info("GitWorktreeManager shutdown complete");
4853
+ logger$6.info("GitWorktreeManager shutdown complete");
4854
4854
  }
4855
4855
  async _handleTaskReady(taskId) {
4856
- logger$5.debug({ taskId }, "task:ready — creating worktree");
4856
+ logger$6.debug({ taskId }, "task:ready — creating worktree");
4857
4857
  try {
4858
4858
  await this.createWorktree(taskId);
4859
4859
  } catch (err) {
4860
- logger$5.error({
4860
+ logger$6.error({
4861
4861
  taskId,
4862
4862
  err
4863
4863
  }, "Failed to create worktree for task");
4864
4864
  }
4865
4865
  }
4866
4866
  async _handleTaskDone(taskId) {
4867
- logger$5.debug({ taskId }, "task done — cleaning up worktree");
4867
+ logger$6.debug({ taskId }, "task done — cleaning up worktree");
4868
4868
  try {
4869
4869
  await this.cleanupWorktree(taskId);
4870
4870
  } catch (err) {
4871
- logger$5.warn({
4871
+ logger$6.warn({
4872
4872
  taskId,
4873
4873
  err
4874
4874
  }, "Failed to cleanup worktree for task");
@@ -4878,7 +4878,7 @@ var GitWorktreeManagerImpl = class {
4878
4878
  if (!taskId || taskId.trim().length === 0) throw new Error("createWorktree: taskId must be a non-empty string");
4879
4879
  const branchName = BRANCH_PREFIX + taskId;
4880
4880
  const worktreePath = this.getWorktreePath(taskId);
4881
- logger$5.debug({
4881
+ logger$6.debug({
4882
4882
  taskId,
4883
4883
  branchName,
4884
4884
  worktreePath,
@@ -4898,7 +4898,7 @@ var GitWorktreeManagerImpl = class {
4898
4898
  worktreePath,
4899
4899
  createdAt
4900
4900
  };
4901
- logger$5.info({
4901
+ logger$6.info({
4902
4902
  taskId,
4903
4903
  branchName,
4904
4904
  worktreePath
@@ -4908,7 +4908,7 @@ var GitWorktreeManagerImpl = class {
4908
4908
  async cleanupWorktree(taskId) {
4909
4909
  const branchName = BRANCH_PREFIX + taskId;
4910
4910
  const worktreePath = this.getWorktreePath(taskId);
4911
- logger$5.debug({
4911
+ logger$6.debug({
4912
4912
  taskId,
4913
4913
  branchName,
4914
4914
  worktreePath
@@ -4918,7 +4918,7 @@ var GitWorktreeManagerImpl = class {
4918
4918
  await access$1(worktreePath);
4919
4919
  worktreeExists = true;
4920
4920
  } catch {
4921
- logger$5.debug({
4921
+ logger$6.debug({
4922
4922
  taskId,
4923
4923
  worktreePath
4924
4924
  }, "cleanupWorktree: worktree does not exist, skipping removal");
@@ -4926,7 +4926,7 @@ var GitWorktreeManagerImpl = class {
4926
4926
  if (worktreeExists) try {
4927
4927
  await removeWorktree(worktreePath, this._projectRoot);
4928
4928
  } catch (err) {
4929
- logger$5.warn({
4929
+ logger$6.warn({
4930
4930
  taskId,
4931
4931
  worktreePath,
4932
4932
  err
@@ -4935,7 +4935,7 @@ var GitWorktreeManagerImpl = class {
4935
4935
  try {
4936
4936
  await removeBranch(branchName, this._projectRoot);
4937
4937
  } catch (err) {
4938
- logger$5.warn({
4938
+ logger$6.warn({
4939
4939
  taskId,
4940
4940
  branchName,
4941
4941
  err
@@ -4945,13 +4945,13 @@ var GitWorktreeManagerImpl = class {
4945
4945
  taskId,
4946
4946
  branchName
4947
4947
  });
4948
- logger$5.info({
4948
+ logger$6.info({
4949
4949
  taskId,
4950
4950
  branchName
4951
4951
  }, "Worktree cleaned up");
4952
4952
  }
4953
4953
  async cleanupAllWorktrees() {
4954
- logger$5.debug({ projectRoot: this._projectRoot }, "cleanupAllWorktrees");
4954
+ logger$6.debug({ projectRoot: this._projectRoot }, "cleanupAllWorktrees");
4955
4955
  const orphanedPaths = await getOrphanedWorktrees(this._projectRoot, this._baseDirectory);
4956
4956
  let cleaned = 0;
4957
4957
  for (const worktreePath of orphanedPaths) {
@@ -4960,12 +4960,12 @@ var GitWorktreeManagerImpl = class {
4960
4960
  try {
4961
4961
  await removeWorktree(worktreePath, this._projectRoot);
4962
4962
  worktreeRemoved = true;
4963
- logger$5.debug({
4963
+ logger$6.debug({
4964
4964
  taskId,
4965
4965
  worktreePath
4966
4966
  }, "cleanupAllWorktrees: removed orphaned worktree");
4967
4967
  } catch (err) {
4968
- logger$5.warn({
4968
+ logger$6.warn({
4969
4969
  taskId,
4970
4970
  worktreePath,
4971
4971
  err
@@ -4975,12 +4975,12 @@ var GitWorktreeManagerImpl = class {
4975
4975
  let branchRemoved = false;
4976
4976
  try {
4977
4977
  branchRemoved = await removeBranch(branchName, this._projectRoot);
4978
- if (branchRemoved) logger$5.debug({
4978
+ if (branchRemoved) logger$6.debug({
4979
4979
  taskId,
4980
4980
  branchName
4981
4981
  }, "cleanupAllWorktrees: removed orphaned branch");
4982
4982
  } catch (err) {
4983
- logger$5.warn({
4983
+ logger$6.warn({
4984
4984
  taskId,
4985
4985
  branchName,
4986
4986
  err
@@ -4988,14 +4988,14 @@ var GitWorktreeManagerImpl = class {
4988
4988
  }
4989
4989
  if (worktreeRemoved) cleaned++;
4990
4990
  }
4991
- if (cleaned > 0) logger$5.info({ cleaned }, "cleanupAllWorktrees: recovered orphaned worktrees");
4991
+ if (cleaned > 0) logger$6.info({ cleaned }, "cleanupAllWorktrees: recovered orphaned worktrees");
4992
4992
  return cleaned;
4993
4993
  }
4994
4994
  async detectConflicts(taskId, targetBranch = "main") {
4995
4995
  if (!taskId || taskId.trim().length === 0) throw new Error("detectConflicts: taskId must be a non-empty string");
4996
4996
  const branchName = BRANCH_PREFIX + taskId;
4997
4997
  const worktreePath = this.getWorktreePath(taskId);
4998
- logger$5.debug({
4998
+ logger$6.debug({
4999
4999
  taskId,
5000
5000
  branchName,
5001
5001
  targetBranch
@@ -5023,7 +5023,7 @@ var GitWorktreeManagerImpl = class {
5023
5023
  branch: branchName,
5024
5024
  conflictingFiles: report.conflictingFiles
5025
5025
  });
5026
- logger$5.info({
5026
+ logger$6.info({
5027
5027
  taskId,
5028
5028
  hasConflicts: report.hasConflicts,
5029
5029
  conflictCount: conflictingFiles.length
@@ -5033,14 +5033,14 @@ var GitWorktreeManagerImpl = class {
5033
5033
  async mergeWorktree(taskId, targetBranch = "main") {
5034
5034
  if (!taskId || taskId.trim().length === 0) throw new Error("mergeWorktree: taskId must be a non-empty string");
5035
5035
  const branchName = BRANCH_PREFIX + taskId;
5036
- logger$5.debug({
5036
+ logger$6.debug({
5037
5037
  taskId,
5038
5038
  branchName,
5039
5039
  targetBranch
5040
5040
  }, "mergeWorktree");
5041
5041
  const conflictReport = await this.detectConflicts(taskId, targetBranch);
5042
5042
  if (conflictReport.hasConflicts) {
5043
- logger$5.info({
5043
+ logger$6.info({
5044
5044
  taskId,
5045
5045
  conflictCount: conflictReport.conflictingFiles.length
5046
5046
  }, "Merge skipped due to conflicts");
@@ -5062,7 +5062,7 @@ var GitWorktreeManagerImpl = class {
5062
5062
  success: true,
5063
5063
  mergedFiles
5064
5064
  };
5065
- logger$5.info({
5065
+ logger$6.info({
5066
5066
  taskId,
5067
5067
  branchName,
5068
5068
  mergedFileCount: mergedFiles.length
@@ -5070,7 +5070,7 @@ var GitWorktreeManagerImpl = class {
5070
5070
  return result;
5071
5071
  }
5072
5072
  async listWorktrees() {
5073
- logger$5.debug({
5073
+ logger$6.debug({
5074
5074
  projectRoot: this._projectRoot,
5075
5075
  baseDirectory: this._baseDirectory
5076
5076
  }, "listWorktrees");
@@ -5094,7 +5094,7 @@ var GitWorktreeManagerImpl = class {
5094
5094
  createdAt
5095
5095
  });
5096
5096
  }
5097
- logger$5.debug({ count: results.length }, "listWorktrees: found worktrees");
5097
+ logger$6.debug({ count: results.length }, "listWorktrees: found worktrees");
5098
5098
  return results;
5099
5099
  }
5100
5100
  getWorktreePath(taskId) {
@@ -5114,7 +5114,7 @@ function createGitWorktreeManager(options) {
5114
5114
 
5115
5115
  //#endregion
5116
5116
  //#region src/cli/commands/merge.ts
5117
- const logger$4 = createLogger("merge-cmd");
5117
+ const logger$5 = createLogger("merge-cmd");
5118
5118
  const MERGE_EXIT_SUCCESS = 0;
5119
5119
  const MERGE_EXIT_CONFLICT = 1;
5120
5120
  const MERGE_EXIT_ERROR = 2;
@@ -5152,7 +5152,7 @@ async function mergeTask(taskId, targetBranch, projectRoot) {
5152
5152
  projectRoot
5153
5153
  });
5154
5154
  try {
5155
- logger$4.info({
5155
+ logger$5.info({
5156
5156
  taskId,
5157
5157
  targetBranch
5158
5158
  }, "Running conflict detection...");
@@ -5174,7 +5174,7 @@ async function mergeTask(taskId, targetBranch, projectRoot) {
5174
5174
  } catch (err) {
5175
5175
  const message = err instanceof Error ? err.message : String(err);
5176
5176
  console.error(`Error merging task "${taskId}": ${message}`);
5177
- logger$4.error({
5177
+ logger$5.error({
5178
5178
  taskId,
5179
5179
  err
5180
5180
  }, "merge --task failed");
@@ -5228,7 +5228,7 @@ async function mergeAll(targetBranch, projectRoot, taskIds) {
5228
5228
  error: message
5229
5229
  });
5230
5230
  console.log(` Error for task "${taskId}": ${message}`);
5231
- logger$4.error({
5231
+ logger$5.error({
5232
5232
  taskId,
5233
5233
  err
5234
5234
  }, "merge --all: task failed");
@@ -5281,7 +5281,7 @@ function registerMergeCommand(program, projectRoot = process.cwd()) {
5281
5281
 
5282
5282
  //#endregion
5283
5283
  //#region src/cli/commands/worktrees.ts
5284
- const logger$3 = createLogger("worktrees-cmd");
5284
+ const logger$4 = createLogger("worktrees-cmd");
5285
5285
  const WORKTREES_EXIT_SUCCESS = 0;
5286
5286
  const WORKTREES_EXIT_ERROR = 1;
5287
5287
  /** Valid task statuses for filtering */
@@ -5408,7 +5408,7 @@ async function listWorktreesAction(options) {
5408
5408
  try {
5409
5409
  worktreeInfos = await manager.listWorktrees();
5410
5410
  } catch (err) {
5411
- logger$3.error({ err }, "Failed to list worktrees");
5411
+ logger$4.error({ err }, "Failed to list worktrees");
5412
5412
  const message = err instanceof Error ? err.message : String(err);
5413
5413
  process.stderr.write(`Error listing worktrees: ${message}\n`);
5414
5414
  return WORKTREES_EXIT_ERROR;
@@ -5435,7 +5435,7 @@ async function listWorktreesAction(options) {
5435
5435
  } catch (err) {
5436
5436
  const message = err instanceof Error ? err.message : String(err);
5437
5437
  process.stderr.write(`Error: ${message}\n`);
5438
- logger$3.error({ err }, "listWorktreesAction failed");
5438
+ logger$4.error({ err }, "listWorktreesAction failed");
5439
5439
  return WORKTREES_EXIT_ERROR;
5440
5440
  }
5441
5441
  }
@@ -5476,7 +5476,7 @@ function registerWorktreesCommand(program, version = "0.0.0", projectRoot = proc
5476
5476
 
5477
5477
  //#endregion
5478
5478
  //#region src/cli/commands/brainstorm.ts
5479
- const logger$2 = createLogger("brainstorm-cmd");
5479
+ const logger$3 = createLogger("brainstorm-cmd");
5480
5480
  /**
5481
5481
  * Detect whether the project has existing planning artifacts that indicate
5482
5482
  * this is an amendment session (vs. a brand-new project brainstorm).
@@ -5522,13 +5522,13 @@ async function loadAmendmentContextDocuments(projectRoot) {
5522
5522
  try {
5523
5523
  brief = await readFile(briefPath, "utf-8");
5524
5524
  } catch {
5525
- logger$2.warn({ briefPath }, "product-brief.md not found — continuing without brief context");
5525
+ logger$3.warn({ briefPath }, "product-brief.md not found — continuing without brief context");
5526
5526
  process.stderr.write(`Warning: product-brief.md not found at ${briefPath}\n`);
5527
5527
  }
5528
5528
  try {
5529
5529
  prd = await readFile(prdPath, "utf-8");
5530
5530
  } catch {
5531
- logger$2.warn({ prdPath }, "requirements.md not found — continuing without PRD context");
5531
+ logger$3.warn({ prdPath }, "requirements.md not found — continuing without PRD context");
5532
5532
  process.stderr.write(`Warning: requirements.md not found at ${prdPath}\n`);
5533
5533
  }
5534
5534
  return {
@@ -5737,7 +5737,7 @@ async function dispatchToPersonas(userPrompt, context, llmDispatch) {
5737
5737
  }
5738
5738
  ];
5739
5739
  const defaultDispatch = async (prompt, personaName) => {
5740
- logger$2.debug({
5740
+ logger$3.debug({
5741
5741
  personaName,
5742
5742
  promptLength: prompt.length
5743
5743
  }, "Dispatching to persona (stub mode)");
@@ -5754,7 +5754,7 @@ async function dispatchToPersonas(userPrompt, context, llmDispatch) {
5754
5754
  };
5755
5755
  } catch (err) {
5756
5756
  const msg = err instanceof Error ? err.message : String(err);
5757
- logger$2.error({
5757
+ logger$3.error({
5758
5758
  err,
5759
5759
  personaName: persona.name
5760
5760
  }, "Persona dispatch failed");
@@ -5906,7 +5906,7 @@ async function runBrainstormSession(options, llmDispatch, rlInterface) {
5906
5906
  }
5907
5907
  });
5908
5908
  rl.on("error", (err) => {
5909
- logger$2.error({ err }, "readline error");
5909
+ logger$3.error({ err }, "readline error");
5910
5910
  if (!sessionEnded) endSession(false);
5911
5911
  });
5912
5912
  });
@@ -6496,7 +6496,7 @@ function renderReadinessReport(decisions) {
6496
6496
 
6497
6497
  //#endregion
6498
6498
  //#region src/cli/commands/export.ts
6499
- const logger$1 = createLogger("export-cmd");
6499
+ const logger$2 = createLogger("export-cmd");
6500
6500
  /**
6501
6501
  * Execute the export action.
6502
6502
  * Returns an exit code (0 = success, 1 = error).
@@ -6623,7 +6623,7 @@ async function runExportAction(options) {
6623
6623
  const msg = err instanceof Error ? err.message : String(err);
6624
6624
  if (outputFormat === "json") process.stdout.write(JSON.stringify({ error: msg }) + "\n");
6625
6625
  else process.stderr.write(`Error: ${msg}\n`);
6626
- logger$1.error({ err }, "export action failed");
6626
+ logger$2.error({ err }, "export action failed");
6627
6627
  return 1;
6628
6628
  } finally {
6629
6629
  if (dbWrapper !== void 0) try {
@@ -6645,6 +6645,219 @@ function registerExportCommand(program, _version = "0.0.0", projectRoot = proces
6645
6645
  });
6646
6646
  }
6647
6647
 
6648
+ //#endregion
6649
+ //#region src/persistence/queries/retry-escalated.ts
6650
+ /**
6651
+ * Query the decision store for escalation-diagnosis decisions and classify
6652
+ * each story key as retryable or skipped.
6653
+ *
6654
+ * Key format in the DB: `{storyKey}:{runId}`
6655
+ *
6656
+ * - When `runId` is provided, only decisions whose key contains that runId
6657
+ * are considered (AC5 scoping).
6658
+ * - When `runId` is omitted, the runId of the last (most recently created)
6659
+ * escalation-diagnosis decision is used as the default (AC1 defaulting).
6660
+ *
6661
+ * @param db The SQLite database connection
6662
+ * @param runId Optional run ID to scope the query
6663
+ */
6664
+ function getRetryableEscalations(db, runId) {
6665
+ const decisions = getDecisionsByCategory(db, ESCALATION_DIAGNOSIS);
6666
+ const result = {
6667
+ retryable: [],
6668
+ skipped: []
6669
+ };
6670
+ if (decisions.length === 0) return result;
6671
+ const parsed = [];
6672
+ for (const decision of decisions) {
6673
+ const colonIdx = decision.key.indexOf(":");
6674
+ if (colonIdx === -1) continue;
6675
+ const storyKey = decision.key.slice(0, colonIdx);
6676
+ const decisionRunId = decision.key.slice(colonIdx + 1);
6677
+ let diagnosis;
6678
+ try {
6679
+ diagnosis = JSON.parse(decision.value);
6680
+ } catch {
6681
+ continue;
6682
+ }
6683
+ parsed.push({
6684
+ storyKey,
6685
+ decisionRunId,
6686
+ diagnosis
6687
+ });
6688
+ }
6689
+ if (parsed.length === 0) return result;
6690
+ const effectiveRunId = runId ?? parsed[parsed.length - 1].decisionRunId;
6691
+ const lastEntryByKey = new Map();
6692
+ for (const entry of parsed) {
6693
+ if (entry.decisionRunId !== effectiveRunId) continue;
6694
+ lastEntryByKey.set(entry.storyKey, entry);
6695
+ }
6696
+ for (const [storyKey, entry] of lastEntryByKey.entries()) {
6697
+ const { recommendedAction } = entry.diagnosis;
6698
+ if (recommendedAction === "retry-targeted") result.retryable.push(storyKey);
6699
+ else if (recommendedAction === "human-intervention") result.skipped.push({
6700
+ key: storyKey,
6701
+ reason: "needs human review"
6702
+ });
6703
+ else if (recommendedAction === "split-story") result.skipped.push({
6704
+ key: storyKey,
6705
+ reason: "story should be split"
6706
+ });
6707
+ }
6708
+ return result;
6709
+ }
6710
+
6711
+ //#endregion
6712
+ //#region src/cli/commands/retry-escalated.ts
6713
+ const logger$1 = createLogger("retry-escalated-cmd");
6714
+ async function runRetryEscalatedAction(options) {
6715
+ const { runId, dryRun, outputFormat, projectRoot, concurrency, pack: packName, registry: injectedRegistry } = options;
6716
+ const dbRoot = await resolveMainRepoRoot(projectRoot);
6717
+ const dbPath = join(dbRoot, ".substrate", "substrate.db");
6718
+ if (!existsSync(dbPath)) {
6719
+ const errorMsg = `Decision store not initialized. Run 'substrate init' first.`;
6720
+ if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
6721
+ else process.stderr.write(`Error: ${errorMsg}\n`);
6722
+ return 1;
6723
+ }
6724
+ const dbWrapper = new DatabaseWrapper(dbPath);
6725
+ try {
6726
+ dbWrapper.open();
6727
+ runMigrations(dbWrapper.db);
6728
+ const db = dbWrapper.db;
6729
+ const { retryable, skipped } = getRetryableEscalations(db, runId);
6730
+ if (retryable.length === 0) {
6731
+ if (outputFormat === "json") process.stdout.write(formatOutput({
6732
+ retryKeys: [],
6733
+ skippedKeys: skipped
6734
+ }, "json", true) + "\n");
6735
+ else process.stdout.write("No retry-targeted escalations found.\n");
6736
+ return 0;
6737
+ }
6738
+ if (dryRun) {
6739
+ if (outputFormat === "json") process.stdout.write(formatOutput({
6740
+ retryKeys: retryable,
6741
+ skippedKeys: skipped
6742
+ }, "json", true) + "\n");
6743
+ else {
6744
+ const count = retryable.length;
6745
+ process.stdout.write(`Retrying: ${count} ${count === 1 ? "story" : "stories"} — ${retryable.join(", ")}\n`);
6746
+ for (const s of skipped) process.stdout.write(`Skipping: ${s.key} (${s.reason})\n`);
6747
+ }
6748
+ return 0;
6749
+ }
6750
+ const packPath = join(projectRoot, "packs", packName);
6751
+ const packLoader = createPackLoader();
6752
+ let pack;
6753
+ try {
6754
+ pack = await packLoader.load(packPath);
6755
+ } catch (err) {
6756
+ const msg = err instanceof Error ? err.message : String(err);
6757
+ const errorMsg = `Methodology pack '${packName}' not found. Run 'substrate init' first.\n${msg}`;
6758
+ if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
6759
+ else process.stderr.write(`Error: ${errorMsg}\n`);
6760
+ return 1;
6761
+ }
6762
+ if (outputFormat === "human") {
6763
+ const count = retryable.length;
6764
+ process.stdout.write(`Retrying: ${count} ${count === 1 ? "story" : "stories"} — ${retryable.join(", ")}\n`);
6765
+ for (const s of skipped) process.stdout.write(`Skipping: ${s.key} (${s.reason})\n`);
6766
+ }
6767
+ const pipelineRun = createPipelineRun(db, {
6768
+ methodology: pack.manifest.name,
6769
+ start_phase: "implementation",
6770
+ config_json: JSON.stringify({
6771
+ storyKeys: retryable,
6772
+ concurrency,
6773
+ retryRun: true
6774
+ })
6775
+ });
6776
+ const eventBus = createEventBus();
6777
+ const contextCompiler = createContextCompiler({ db });
6778
+ if (!injectedRegistry) throw new Error("AdapterRegistry is required — must be initialized at CLI startup");
6779
+ const dispatcher = createDispatcher({
6780
+ eventBus,
6781
+ adapterRegistry: injectedRegistry
6782
+ });
6783
+ const orchestrator = createImplementationOrchestrator({
6784
+ db,
6785
+ pack,
6786
+ contextCompiler,
6787
+ dispatcher,
6788
+ eventBus,
6789
+ config: {
6790
+ maxConcurrency: concurrency,
6791
+ maxReviewCycles: 2,
6792
+ pipelineRunId: pipelineRun.id
6793
+ },
6794
+ projectRoot
6795
+ });
6796
+ eventBus.on("orchestrator:story-phase-complete", (payload) => {
6797
+ try {
6798
+ const result = payload.result;
6799
+ if (result?.tokenUsage !== void 0) {
6800
+ const { input, output } = result.tokenUsage;
6801
+ const costUsd = (input * 3 + output * 15) / 1e6;
6802
+ addTokenUsage(db, pipelineRun.id, {
6803
+ phase: payload.phase,
6804
+ agent: "claude-code",
6805
+ input_tokens: input,
6806
+ output_tokens: output,
6807
+ cost_usd: costUsd
6808
+ });
6809
+ }
6810
+ } catch (err) {
6811
+ logger$1.warn({ err }, "Failed to record token usage");
6812
+ }
6813
+ });
6814
+ if (outputFormat === "human") {
6815
+ eventBus.on("orchestrator:story-complete", (payload) => {
6816
+ process.stdout.write(` [COMPLETE] ${payload.storyKey} (${payload.reviewCycles} review cycle(s))\n`);
6817
+ });
6818
+ eventBus.on("orchestrator:story-escalated", (payload) => {
6819
+ process.stdout.write(` [ESCALATED] ${payload.storyKey}: ${payload.lastVerdict}\n`);
6820
+ });
6821
+ }
6822
+ await orchestrator.run(retryable);
6823
+ if (outputFormat === "json") process.stdout.write(formatOutput({
6824
+ retryKeys: retryable,
6825
+ skippedKeys: skipped
6826
+ }, "json", true) + "\n");
6827
+ else process.stdout.write("[RETRY] Complete\n");
6828
+ return 0;
6829
+ } catch (err) {
6830
+ const msg = err instanceof Error ? err.message : String(err);
6831
+ if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
6832
+ else process.stderr.write(`Error: ${msg}\n`);
6833
+ logger$1.error({ err }, "retry-escalated failed");
6834
+ return 1;
6835
+ } finally {
6836
+ try {
6837
+ dbWrapper.close();
6838
+ } catch {}
6839
+ }
6840
+ }
6841
+ function registerRetryEscalatedCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
6842
+ program.command("retry-escalated").description("Retry escalated stories flagged as retry-targeted by escalation diagnosis").option("--run-id <id>", "Scope to a specific pipeline run ID (defaults to latest run with escalations)").option("--dry-run", "Print retryable and skipped stories without invoking the orchestrator").option("--concurrency <n>", "Maximum parallel story executions", (v) => {
6843
+ const n = parseInt(v, 10);
6844
+ if (isNaN(n) || n < 1) throw new Error(`--concurrency must be a positive integer, got: ${v}`);
6845
+ return n;
6846
+ }, 3).option("--pack <name>", "Methodology pack name", "bmad").option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").action(async (opts) => {
6847
+ const outputFormat = opts.outputFormat === "json" ? "json" : "human";
6848
+ const exitCode = await runRetryEscalatedAction({
6849
+ runId: opts.runId,
6850
+ dryRun: opts.dryRun === true,
6851
+ outputFormat,
6852
+ projectRoot: opts.projectRoot,
6853
+ concurrency: opts.concurrency,
6854
+ pack: opts.pack,
6855
+ registry
6856
+ });
6857
+ process.exitCode = exitCode;
6858
+ });
6859
+ }
6860
+
6648
6861
  //#endregion
6649
6862
  //#region src/cli/index.ts
6650
6863
  process.setMaxListeners(20);
@@ -6680,16 +6893,19 @@ async function createProgram() {
6680
6893
  const version = await getPackageVersion();
6681
6894
  const program = new Command();
6682
6895
  program.name("substrate").description("Substrate - Autonomous implementation pipeline for AI coding agents").version(version, "-v, --version", "Output the current version");
6683
- registerAdaptersCommand(program, version);
6684
- registerInitCommand(program, version);
6896
+ const registry = new AdapterRegistry();
6897
+ await registry.discoverAndRegister();
6898
+ registerAdaptersCommand(program, version, registry);
6899
+ registerInitCommand(program, version, registry);
6685
6900
  registerConfigCommand(program, version);
6686
- registerRunCommand(program, version);
6687
- registerResumeCommand(program, version);
6901
+ registerRunCommand(program, version, process.cwd(), registry);
6902
+ registerResumeCommand(program, version, process.cwd(), registry);
6688
6903
  registerStatusCommand(program, version);
6689
- registerAmendCommand(program, version);
6904
+ registerAmendCommand(program, version, process.cwd(), registry);
6690
6905
  registerHealthCommand(program, version);
6691
6906
  registerSupervisorCommand(program, version);
6692
6907
  registerMetricsCommand(program, version);
6908
+ registerRetryEscalatedCommand(program, version, process.cwd(), registry);
6693
6909
  registerCostCommand(program, version);
6694
6910
  registerMonitorCommand(program, version);
6695
6911
  registerMergeCommand(program);