substrate-ai 0.2.16 → 0.2.17

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,21 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { createLogger, deepMask } from "../logger-C6n1g8uP.js";
3
- import { AdapterRegistry, createEventBus } from "../event-bus-J-bw-pkp.js";
4
- import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "../version-manager-impl-BpVx2DkY.js";
5
- import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getSubstrateDefaultSettings, parseDbTimestampAsUtc, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-BrI2xzk7.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-CLpXJNQ8.js";
3
+ import { createLogger, deepMask } from "../logger-D2fS2ccL.js";
4
+ import { AdapterRegistry, createEventBus } from "../event-bus-BMxhfxfT.js";
5
+ import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "../version-manager-impl-CZ6KF1Ds.js";
6
6
  import { ConfigError, ConfigIncompatibleFormatError } from "../errors-BPqtzQ4U.js";
7
- import { addTokenUsage, createDecision, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-SyswIRKz.js";
7
+ import { addTokenUsage, createDecision, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-Dq4cAA2L.js";
8
8
  import { EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-CobuCGbM.js";
9
- import { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, verifyGitVersion } from "../git-utils-BtI5eNoN.js";
10
- import { registerUpgradeCommand } from "../upgrade-rV26kdh3.js";
11
- import { createRequire } from "module";
9
+ import { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, verifyGitVersion } from "../git-utils-CtmrZrHS.js";
10
+ import { registerUpgradeCommand } from "../upgrade-CjjAx5kD.js";
12
11
  import { Command } from "commander";
13
12
  import { fileURLToPath } from "url";
14
13
  import { dirname, join, resolve } from "path";
15
14
  import { access, mkdir, readFile, writeFile } from "fs/promises";
16
15
  import { chmodSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, unlinkSync, writeFileSync } from "fs";
17
16
  import yaml from "js-yaml";
18
- import { createRequire as createRequire$1 } from "node:module";
17
+ import { createRequire } from "node:module";
19
18
  import * as path$1 from "node:path";
20
19
  import { isAbsolute, join as join$1 } from "node:path";
21
20
  import BetterSqlite3 from "better-sqlite3";
@@ -26,10 +25,6 @@ import { access as access$1 } from "node:fs/promises";
26
25
  import { randomUUID } from "crypto";
27
26
  import { createInterface as createInterface$1 } from "readline";
28
27
 
29
- //#region rolldown:runtime
30
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
31
-
32
- //#endregion
33
28
  //#region src/cli/utils/formatting.ts
34
29
  /**
35
30
  * Build adapter list rows from discovery results.
@@ -329,7 +324,7 @@ const DEFAULT_CONFIG = {
329
324
 
330
325
  //#endregion
331
326
  //#region src/cli/commands/init.ts
332
- const logger$17 = createLogger("init");
327
+ const logger$16 = createLogger("init");
333
328
  const __dirname = dirname(new URL(import.meta.url).pathname);
334
329
  const INIT_EXIT_SUCCESS = 0;
335
330
  const INIT_EXIT_ERROR = 1;
@@ -350,7 +345,7 @@ async function scaffoldBmadFramework(projectRoot, force, outputFormat) {
350
345
  const version = resolveBmadMethodVersion();
351
346
  if (force && bmadExists) process.stderr.write(`Warning: Replacing existing _bmad/ framework with bmad-method@${version}\n`);
352
347
  process.stdout.write(`Scaffolding BMAD framework from bmad-method@${version}\n`);
353
- logger$17.info({
348
+ logger$16.info({
354
349
  version,
355
350
  dest: bmadDest
356
351
  }, "Scaffolding BMAD framework");
@@ -360,7 +355,7 @@ async function scaffoldBmadFramework(projectRoot, force, outputFormat) {
360
355
  const destDir = join(bmadDest, dir);
361
356
  mkdirSync(destDir, { recursive: true });
362
357
  cpSync(srcDir, destDir, { recursive: true });
363
- logger$17.info({
358
+ logger$16.info({
364
359
  dir,
365
360
  dest: destDir
366
361
  }, "Scaffolded BMAD framework directory");
@@ -379,7 +374,7 @@ async function scaffoldBmadFramework(projectRoot, force, outputFormat) {
379
374
  "document_output_language: English"
380
375
  ].join("\n") + "\n";
381
376
  await writeFile(configFile, configStub, "utf8");
382
- logger$17.info({ configFile }, "Generated _bmad/_config/config.yaml stub");
377
+ logger$16.info({ configFile }, "Generated _bmad/_config/config.yaml stub");
383
378
  }
384
379
  }
385
380
  const CLAUDE_MD_START_MARKER = "<!-- substrate:start -->";
@@ -394,7 +389,7 @@ async function scaffoldClaudeMd(projectRoot) {
394
389
  try {
395
390
  sectionContent = await readFile(templatePath, "utf8");
396
391
  } catch {
397
- logger$17.warn({ templatePath }, "CLAUDE.md substrate section template not found; skipping");
392
+ logger$16.warn({ templatePath }, "CLAUDE.md substrate section template not found; skipping");
398
393
  return;
399
394
  }
400
395
  if (!sectionContent.endsWith("\n")) sectionContent += "\n";
@@ -412,7 +407,7 @@ async function scaffoldClaudeMd(projectRoot) {
412
407
  newContent = existingContent + separator + sectionContent;
413
408
  }
414
409
  await writeFile(claudeMdPath, newContent, "utf8");
415
- logger$17.info({ claudeMdPath }, "Wrote substrate section to CLAUDE.md");
410
+ logger$16.info({ claudeMdPath }, "Wrote substrate section to CLAUDE.md");
416
411
  }
417
412
  async function scaffoldStatuslineScript(projectRoot) {
418
413
  const pkgRoot = findPackageRoot(__dirname);
@@ -423,7 +418,7 @@ async function scaffoldStatuslineScript(projectRoot) {
423
418
  try {
424
419
  content = await readFile(templatePath, "utf8");
425
420
  } catch {
426
- logger$17.warn({ templatePath }, "statusline.sh template not found; skipping");
421
+ logger$16.warn({ templatePath }, "statusline.sh template not found; skipping");
427
422
  return;
428
423
  }
429
424
  const claudeDir = join(projectRoot, ".claude");
@@ -431,7 +426,7 @@ async function scaffoldStatuslineScript(projectRoot) {
431
426
  mkdirSync(claudeDir, { recursive: true });
432
427
  await writeFile(statuslinePath, content, "utf8");
433
428
  chmodSync(statuslinePath, 493);
434
- logger$17.info({ statuslinePath }, "Wrote .claude/statusline.sh");
429
+ logger$16.info({ statuslinePath }, "Wrote .claude/statusline.sh");
435
430
  }
436
431
  async function scaffoldClaudeSettings(projectRoot) {
437
432
  const claudeDir = join(projectRoot, ".claude");
@@ -447,11 +442,11 @@ async function scaffoldClaudeSettings(projectRoot) {
447
442
  if (!merged["$schema"]) merged["$schema"] = "https://json.schemastore.org/claude-code-settings.json";
448
443
  mkdirSync(claudeDir, { recursive: true });
449
444
  await writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
450
- logger$17.info({ settingsPath }, "Wrote substrate settings to .claude/settings.json");
445
+ logger$16.info({ settingsPath }, "Wrote substrate settings to .claude/settings.json");
451
446
  }
452
447
  function resolveBmadMethodInstallerLibPath(fromDir = __dirname) {
453
448
  try {
454
- const _require = createRequire$1(join(fromDir, "synthetic.js"));
449
+ const _require = createRequire(join(fromDir, "synthetic.js"));
455
450
  const pkgJsonPath = _require.resolve("bmad-method/package.json");
456
451
  return join(dirname(pkgJsonPath), "tools", "cli", "installers", "lib");
457
452
  } catch {
@@ -482,7 +477,7 @@ function clearBmadCommandFiles(commandsDir) {
482
477
  } catch {}
483
478
  }
484
479
  async function compileBmadAgents(bmadDir) {
485
- const _require = createRequire$1(join(__dirname, "synthetic.js"));
480
+ const _require = createRequire(join(__dirname, "synthetic.js"));
486
481
  let compilerPath;
487
482
  try {
488
483
  const pkgJsonPath = _require.resolve("bmad-method/package.json");
@@ -517,7 +512,7 @@ async function compileBmadAgents(bmadDir) {
517
512
  writeFileSync(mdPath, result.xml, "utf-8");
518
513
  compiled++;
519
514
  } catch (compileErr) {
520
- logger$17.debug({
515
+ logger$16.debug({
521
516
  err: compileErr,
522
517
  file
523
518
  }, "Failed to compile agent YAML");
@@ -535,12 +530,12 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
535
530
  return;
536
531
  }
537
532
  try {
538
- const _require = createRequire$1(join(__dirname, "synthetic.js"));
533
+ const _require = createRequire(join(__dirname, "synthetic.js"));
539
534
  try {
540
535
  const compiledCount = await compileBmadAgents(bmadDir);
541
- if (compiledCount > 0) logger$17.info({ compiledCount }, "Compiled agent YAML files to MD");
536
+ if (compiledCount > 0) logger$16.info({ compiledCount }, "Compiled agent YAML files to MD");
542
537
  } catch (compileErr) {
543
- logger$17.warn({ err: compileErr }, "Agent compilation failed; agent commands may be incomplete");
538
+ logger$16.warn({ err: compileErr }, "Agent compilation failed; agent commands may be incomplete");
544
539
  }
545
540
  const { AgentCommandGenerator } = _require(join(installerLibPath, "ide", "shared", "agent-command-generator.js"));
546
541
  const { WorkflowCommandGenerator } = _require(join(installerLibPath, "ide", "shared", "workflow-command-generator.js"));
@@ -552,7 +547,7 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
552
547
  const manifestGen = new ManifestGenerator();
553
548
  await manifestGen.generateManifests(bmadDir, allModules, [], { ides: ["claude-code"] });
554
549
  } catch (manifestErr) {
555
- logger$17.warn({ err: manifestErr }, "ManifestGenerator failed; workflow/task commands may be incomplete");
550
+ logger$16.warn({ err: manifestErr }, "ManifestGenerator failed; workflow/task commands may be incomplete");
556
551
  }
557
552
  const commandsDir = join(projectRoot, ".claude", "commands");
558
553
  mkdirSync(commandsDir, { recursive: true });
@@ -568,7 +563,7 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
568
563
  const taskToolCount = await taskToolGen.writeDashArtifacts(commandsDir, taskToolArtifacts);
569
564
  const total = agentCount + workflowCount + taskToolCount;
570
565
  if (outputFormat !== "json") process.stdout.write(`Generated ${String(total)} Claude Code commands (${String(agentCount)} agents, ${String(workflowCount)} workflows, ${String(taskToolCount)} tasks/tools)\n`);
571
- logger$17.info({
566
+ logger$16.info({
572
567
  agentCount,
573
568
  workflowCount,
574
569
  taskToolCount,
@@ -578,7 +573,7 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
578
573
  } catch (err) {
579
574
  const msg = err instanceof Error ? err.message : String(err);
580
575
  if (outputFormat !== "json") process.stderr.write(`Warning: .claude/commands/ generation failed: ${msg}\n`);
581
- logger$17.warn({ err }, "scaffoldClaudeCommands failed; init continues");
576
+ logger$16.warn({ err }, "scaffoldClaudeCommands failed; init continues");
582
577
  }
583
578
  }
584
579
  const PROVIDER_DEFAULTS = DEFAULT_CONFIG.providers;
@@ -652,7 +647,7 @@ async function runInitAction(options) {
652
647
  discoveryReport = await registry.discoverAndRegister();
653
648
  } catch (err) {
654
649
  const message = err instanceof Error ? err.message : String(err);
655
- logger$17.error({ err }, "Adapter discovery failed");
650
+ logger$16.error({ err }, "Adapter discovery failed");
656
651
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, `Adapter discovery failed: ${message}`) + "\n");
657
652
  else process.stderr.write(` Error: adapter discovery failed — ${message}\n`);
658
653
  return INIT_EXIT_ERROR;
@@ -701,12 +696,12 @@ async function runInitAction(options) {
701
696
  return INIT_EXIT_ERROR;
702
697
  }
703
698
  if (force && existsSync(localManifest)) {
704
- logger$17.info({ pack: packName }, "Replacing existing pack with bundled version");
699
+ logger$16.info({ pack: packName }, "Replacing existing pack with bundled version");
705
700
  process.stderr.write(`Warning: Replacing existing pack '${packName}' with bundled version\n`);
706
701
  }
707
702
  mkdirSync(dirname(packPath), { recursive: true });
708
703
  cpSync(bundledPackPath, packPath, { recursive: true });
709
- logger$17.info({
704
+ logger$16.info({
710
705
  pack: packName,
711
706
  dest: packPath
712
707
  }, "Scaffolded methodology pack");
@@ -759,7 +754,7 @@ async function runInitAction(options) {
759
754
  const msg = err instanceof Error ? err.message : String(err);
760
755
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
761
756
  else process.stderr.write(`Error: ${msg}\n`);
762
- logger$17.error({ err }, "init failed");
757
+ logger$16.error({ err }, "init failed");
763
758
  return INIT_EXIT_ERROR;
764
759
  }
765
760
  }
@@ -805,7 +800,7 @@ function formatUnsupportedVersionError(formatType, version, supported) {
805
800
 
806
801
  //#endregion
807
802
  //#region src/modules/config/config-system-impl.ts
808
- const logger$16 = createLogger("config");
803
+ const logger$15 = createLogger("config");
809
804
  function deepMerge(base, override) {
810
805
  const result = { ...base };
811
806
  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);
@@ -850,7 +845,7 @@ function readEnvOverrides() {
850
845
  }
851
846
  const parsed = PartialSubstrateConfigSchema.safeParse(overrides);
852
847
  if (!parsed.success) {
853
- logger$16.warn({ errors: parsed.error.issues }, "Invalid environment variable overrides ignored");
848
+ logger$15.warn({ errors: parsed.error.issues }, "Invalid environment variable overrides ignored");
854
849
  return {};
855
850
  }
856
851
  return parsed.data;
@@ -914,7 +909,7 @@ var ConfigSystemImpl = class {
914
909
  throw new ConfigError(`Configuration validation failed:\n${issues}`, { issues: result.error.issues });
915
910
  }
916
911
  this._config = result.data;
917
- logger$16.debug("Configuration loaded successfully");
912
+ logger$15.debug("Configuration loaded successfully");
918
913
  }
919
914
  getConfig() {
920
915
  if (this._config === null) throw new ConfigError("Configuration has not been loaded. Call load() before getConfig().", {});
@@ -977,7 +972,7 @@ var ConfigSystemImpl = class {
977
972
  if (version !== void 0 && typeof version === "string" && !isVersionSupported(version, SUPPORTED_CONFIG_FORMAT_VERSIONS)) if (defaultConfigMigrator.canMigrate(version, CURRENT_CONFIG_FORMAT_VERSION)) {
978
973
  const migrationOutput = defaultConfigMigrator.migrate(rawObj, version, CURRENT_CONFIG_FORMAT_VERSION, filePath);
979
974
  if (migrationOutput.result.success) {
980
- logger$16.info({
975
+ logger$15.info({
981
976
  from: version,
982
977
  to: CURRENT_CONFIG_FORMAT_VERSION,
983
978
  backup: migrationOutput.result.backupPath
@@ -1020,7 +1015,7 @@ function createConfigSystem(options = {}) {
1020
1015
 
1021
1016
  //#endregion
1022
1017
  //#region src/cli/commands/config.ts
1023
- const logger$15 = createLogger("config-cmd");
1018
+ const logger$14 = createLogger("config-cmd");
1024
1019
  const CONFIG_EXIT_SUCCESS = 0;
1025
1020
  const CONFIG_EXIT_ERROR = 1;
1026
1021
  const CONFIG_EXIT_INVALID = 2;
@@ -1046,7 +1041,7 @@ async function runConfigShow(opts = {}) {
1046
1041
  return CONFIG_EXIT_INVALID;
1047
1042
  }
1048
1043
  const message = err instanceof Error ? err.message : String(err);
1049
- logger$15.error({ err }, "Failed to load configuration");
1044
+ logger$14.error({ err }, "Failed to load configuration");
1050
1045
  process.stderr.write(` Error loading configuration: ${message}\n`);
1051
1046
  return CONFIG_EXIT_ERROR;
1052
1047
  }
@@ -1120,7 +1115,7 @@ async function runConfigExport(opts = {}) {
1120
1115
  return CONFIG_EXIT_INVALID;
1121
1116
  }
1122
1117
  const message = err instanceof Error ? err.message : String(err);
1123
- logger$15.error({ err }, "Failed to load configuration");
1118
+ logger$14.error({ err }, "Failed to load configuration");
1124
1119
  process.stderr.write(`Error loading configuration: ${message}\n`);
1125
1120
  return CONFIG_EXIT_ERROR;
1126
1121
  }
@@ -1274,7 +1269,7 @@ function registerConfigCommand(program, _version) {
1274
1269
 
1275
1270
  //#endregion
1276
1271
  //#region src/cli/commands/resume.ts
1277
- const logger$14 = createLogger("resume-cmd");
1272
+ const logger$13 = createLogger("resume-cmd");
1278
1273
  async function runResumeAction(options) {
1279
1274
  const { runId: specifiedRunId, stopAfter, outputFormat, projectRoot, concurrency, pack: packName } = options;
1280
1275
  if (stopAfter !== void 0 && !VALID_PHASES.includes(stopAfter)) {
@@ -1356,7 +1351,7 @@ async function runResumeAction(options) {
1356
1351
  const msg = err instanceof Error ? err.message : String(err);
1357
1352
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
1358
1353
  else process.stderr.write(`Error: ${msg}\n`);
1359
- logger$14.error({ err }, "auto resume failed");
1354
+ logger$13.error({ err }, "auto resume failed");
1360
1355
  return 1;
1361
1356
  } finally {
1362
1357
  try {
@@ -1507,7 +1502,7 @@ async function runFullPipelineFromPhase(options) {
1507
1502
  });
1508
1503
  }
1509
1504
  } catch (err) {
1510
- logger$14.warn({ err }, "Failed to record token usage");
1505
+ logger$13.warn({ err }, "Failed to record token usage");
1511
1506
  }
1512
1507
  });
1513
1508
  const storyDecisions = db.prepare(`SELECT description FROM requirements WHERE pipeline_run_id = ? AND source = 'solutioning-phase'`).all(runId);
@@ -1566,7 +1561,7 @@ async function runFullPipelineFromPhase(options) {
1566
1561
  const msg = err instanceof Error ? err.message : String(err);
1567
1562
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
1568
1563
  else process.stderr.write(`Error: ${msg}\n`);
1569
- logger$14.error({ err }, "pipeline from phase failed");
1564
+ logger$13.error({ err }, "pipeline from phase failed");
1570
1565
  return 1;
1571
1566
  } finally {
1572
1567
  try {
@@ -1591,7 +1586,7 @@ function registerResumeCommand(program, _version = "0.0.0", projectRoot = proces
1591
1586
 
1592
1587
  //#endregion
1593
1588
  //#region src/cli/commands/status.ts
1594
- const logger$13 = createLogger("status-cmd");
1589
+ const logger$12 = createLogger("status-cmd");
1595
1590
  async function runStatusAction(options) {
1596
1591
  const { outputFormat, runId, projectRoot } = options;
1597
1592
  const dbRoot = await resolveMainRepoRoot(projectRoot);
@@ -1668,7 +1663,7 @@ async function runStatusAction(options) {
1668
1663
  const msg = err instanceof Error ? err.message : String(err);
1669
1664
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
1670
1665
  else process.stderr.write(`Error: ${msg}\n`);
1671
- logger$13.error({ err }, "status action failed");
1666
+ logger$12.error({ err }, "status action failed");
1672
1667
  return 1;
1673
1668
  } finally {
1674
1669
  try {
@@ -2092,7 +2087,7 @@ Analyze thoroughly and return ONLY the JSON array with no additional text.`;
2092
2087
 
2093
2088
  //#endregion
2094
2089
  //#region src/cli/commands/amend.ts
2095
- const logger$12 = createLogger("amend-cmd");
2090
+ const logger$11 = createLogger("amend-cmd");
2096
2091
  /**
2097
2092
  * Detect and apply supersessions after a phase completes in an amendment run.
2098
2093
  *
@@ -2123,7 +2118,7 @@ function runPostPhaseSupersessionDetection(db, amendmentRunId, currentPhase, han
2123
2118
  });
2124
2119
  } catch (err) {
2125
2120
  const msg = err instanceof Error ? err.message : String(err);
2126
- logger$12.warn({
2121
+ logger$11.warn({
2127
2122
  err,
2128
2123
  originalId: parentMatch.id,
2129
2124
  supersedingId: newDec.id
@@ -2258,7 +2253,7 @@ async function runAmendAction(options) {
2258
2253
  for (let i = startIdx; i < phaseOrder.length; i++) {
2259
2254
  const currentPhase = phaseOrder[i];
2260
2255
  const amendmentContext = handler.loadContextForPhase(currentPhase);
2261
- logger$12.info({
2256
+ logger$11.info({
2262
2257
  phase: currentPhase,
2263
2258
  amendmentContextLen: amendmentContext.length
2264
2259
  }, "Amendment context loaded for phase");
@@ -2378,7 +2373,7 @@ async function runAmendAction(options) {
2378
2373
  } catch (err) {
2379
2374
  const msg = err instanceof Error ? err.message : String(err);
2380
2375
  process.stderr.write(`Error: ${msg}\n`);
2381
- logger$12.error({ err }, "amend failed");
2376
+ logger$11.error({ err }, "amend failed");
2382
2377
  return 1;
2383
2378
  } finally {
2384
2379
  try {
@@ -2401,268 +2396,6 @@ function registerAmendCommand(program, _version = "0.0.0", projectRoot = process
2401
2396
  });
2402
2397
  }
2403
2398
 
2404
- //#endregion
2405
- //#region src/cli/commands/health.ts
2406
- const logger$11 = createLogger("health-cmd");
2407
- /** Default stall threshold in seconds — also used by supervisor default */
2408
- const DEFAULT_STALL_THRESHOLD_SECONDS = 600;
2409
- /**
2410
- * Determine whether a ps output line represents the substrate pipeline orchestrator.
2411
- * Handles invocation via:
2412
- * - `substrate run` (globally installed)
2413
- * - `substrate-ai run`
2414
- * - `node dist/cli/index.js run` (npm run substrate:dev)
2415
- * - `npx substrate run`
2416
- * - any node process whose command contains `run` with `--events` or `--stories`
2417
- *
2418
- * When `projectRoot` is provided, additionally checks that the command line
2419
- * contains that path (via `--project-root` flag or as part of the binary/CWD path).
2420
- * This ensures multi-project environments match the correct orchestrator.
2421
- */
2422
- function isOrchestratorProcessLine(line, projectRoot) {
2423
- if (line.includes("grep")) return false;
2424
- let isOrchestrator = false;
2425
- if (line.includes("substrate run")) isOrchestrator = true;
2426
- else if (line.includes("substrate-ai run")) isOrchestrator = true;
2427
- else if (line.includes("index.js run")) isOrchestrator = true;
2428
- else if (line.includes("node") && /\srun(\s|$)/.test(line) && (line.includes("--events") || line.includes("--stories"))) isOrchestrator = true;
2429
- if (!isOrchestrator) return false;
2430
- if (projectRoot !== void 0) return line.includes(projectRoot);
2431
- return true;
2432
- }
2433
- function inspectProcessTree(opts) {
2434
- const { projectRoot, execFileSync: execFileSyncOverride } = opts ?? {};
2435
- const result = {
2436
- orchestrator_pid: null,
2437
- child_pids: [],
2438
- zombies: []
2439
- };
2440
- try {
2441
- let psOutput;
2442
- if (execFileSyncOverride !== void 0) psOutput = execFileSyncOverride("ps", ["-eo", "pid,ppid,stat,command"], {
2443
- encoding: "utf-8",
2444
- timeout: 5e3
2445
- });
2446
- else {
2447
- const { execFileSync } = __require("node:child_process");
2448
- psOutput = execFileSync("ps", ["-eo", "pid,ppid,stat,command"], {
2449
- encoding: "utf-8",
2450
- timeout: 5e3
2451
- });
2452
- }
2453
- const lines = psOutput.split("\n");
2454
- for (const line of lines) if (isOrchestratorProcessLine(line, projectRoot)) {
2455
- const match = line.trim().match(/^(\d+)/);
2456
- if (match) {
2457
- result.orchestrator_pid = parseInt(match[1], 10);
2458
- break;
2459
- }
2460
- }
2461
- if (result.orchestrator_pid !== null) for (const line of lines) {
2462
- const parts = line.trim().split(/\s+/);
2463
- if (parts.length >= 3) {
2464
- const pid = parseInt(parts[0], 10);
2465
- const ppid = parseInt(parts[1], 10);
2466
- const stat$2 = parts[2];
2467
- if (ppid === result.orchestrator_pid && pid !== result.orchestrator_pid) {
2468
- result.child_pids.push(pid);
2469
- if (stat$2.includes("Z")) result.zombies.push(pid);
2470
- }
2471
- }
2472
- }
2473
- } catch {}
2474
- return result;
2475
- }
2476
- /**
2477
- * Collect all descendant PIDs of the given root PIDs by walking the process
2478
- * tree recursively. This ensures that grandchildren of the orchestrator
2479
- * (e.g. node subprocesses spawned by `claude -p`) are also killed during
2480
- * stall recovery, leaving no orphan processes.
2481
- *
2482
- * Returns only the descendants — the root PIDs themselves are NOT included.
2483
- */
2484
- function getAllDescendantPids(rootPids, execFileSyncOverride) {
2485
- if (rootPids.length === 0) return [];
2486
- try {
2487
- let psOutput;
2488
- if (execFileSyncOverride !== void 0) psOutput = execFileSyncOverride("ps", ["-eo", "pid,ppid"], {
2489
- encoding: "utf-8",
2490
- timeout: 5e3
2491
- });
2492
- else {
2493
- const { execFileSync } = __require("node:child_process");
2494
- psOutput = execFileSync("ps", ["-eo", "pid,ppid"], {
2495
- encoding: "utf-8",
2496
- timeout: 5e3
2497
- });
2498
- }
2499
- const childrenOf = new Map();
2500
- for (const line of psOutput.split("\n")) {
2501
- const parts = line.trim().split(/\s+/);
2502
- if (parts.length >= 2) {
2503
- const pid = parseInt(parts[0], 10);
2504
- const ppid = parseInt(parts[1], 10);
2505
- if (!isNaN(pid) && !isNaN(ppid) && pid > 0) {
2506
- if (!childrenOf.has(ppid)) childrenOf.set(ppid, []);
2507
- childrenOf.get(ppid).push(pid);
2508
- }
2509
- }
2510
- }
2511
- const descendants = [];
2512
- const seen = new Set(rootPids);
2513
- const queue = [...rootPids];
2514
- while (queue.length > 0) {
2515
- const current = queue.shift();
2516
- const children = childrenOf.get(current) ?? [];
2517
- for (const child of children) if (!seen.has(child)) {
2518
- seen.add(child);
2519
- descendants.push(child);
2520
- queue.push(child);
2521
- }
2522
- }
2523
- return descendants;
2524
- } catch {
2525
- return [];
2526
- }
2527
- }
2528
- /**
2529
- * Fetch pipeline health data as a structured object without any stdout side-effects.
2530
- * Used by runSupervisorAction to poll health without formatting overhead.
2531
- *
2532
- * Returns a NO_PIPELINE_RUNNING health object for all graceful "no data" cases
2533
- * (missing DB, missing run, terminal run status). Throws only on unexpected errors.
2534
- */
2535
- async function getAutoHealthData(options) {
2536
- const { runId, projectRoot } = options;
2537
- const dbRoot = await resolveMainRepoRoot(projectRoot);
2538
- const dbPath = join(dbRoot, ".substrate", "substrate.db");
2539
- const NO_PIPELINE = {
2540
- verdict: "NO_PIPELINE_RUNNING",
2541
- run_id: null,
2542
- status: null,
2543
- current_phase: null,
2544
- staleness_seconds: 0,
2545
- last_activity: "",
2546
- process: {
2547
- orchestrator_pid: null,
2548
- child_pids: [],
2549
- zombies: []
2550
- },
2551
- stories: {
2552
- active: 0,
2553
- completed: 0,
2554
- escalated: 0,
2555
- details: {}
2556
- }
2557
- };
2558
- if (!existsSync(dbPath)) return NO_PIPELINE;
2559
- const dbWrapper = new DatabaseWrapper(dbPath);
2560
- try {
2561
- dbWrapper.open();
2562
- const db = dbWrapper.db;
2563
- let run;
2564
- if (runId !== void 0) run = getPipelineRunById(db, runId);
2565
- else run = getLatestRun(db);
2566
- if (run === void 0) return NO_PIPELINE;
2567
- const updatedAt = parseDbTimestampAsUtc(run.updated_at);
2568
- const stalenessSeconds = Math.round((Date.now() - updatedAt.getTime()) / 1e3);
2569
- let storyDetails = {};
2570
- let active = 0;
2571
- let completed = 0;
2572
- let escalated = 0;
2573
- try {
2574
- if (run.token_usage_json) {
2575
- const state = JSON.parse(run.token_usage_json);
2576
- if (state.stories) for (const [key, s] of Object.entries(state.stories)) {
2577
- storyDetails[key] = {
2578
- phase: s.phase,
2579
- review_cycles: s.reviewCycles
2580
- };
2581
- if (s.phase === "COMPLETE") completed++;
2582
- else if (s.phase === "ESCALATED") escalated++;
2583
- else if (s.phase !== "PENDING") active++;
2584
- }
2585
- }
2586
- } catch {}
2587
- const processInfo = inspectProcessTree({ projectRoot });
2588
- let verdict = "NO_PIPELINE_RUNNING";
2589
- if (run.status === "running") if (processInfo.orchestrator_pid === null && active === 0 && completed > 0) verdict = "NO_PIPELINE_RUNNING";
2590
- else if (processInfo.zombies.length > 0) verdict = "STALLED";
2591
- else if (processInfo.orchestrator_pid !== null && processInfo.child_pids.length > 0 && stalenessSeconds > DEFAULT_STALL_THRESHOLD_SECONDS) verdict = "HEALTHY";
2592
- else if (stalenessSeconds > DEFAULT_STALL_THRESHOLD_SECONDS) verdict = "STALLED";
2593
- else if (processInfo.orchestrator_pid !== null && processInfo.child_pids.length === 0 && active > 0) verdict = "STALLED";
2594
- else verdict = "HEALTHY";
2595
- else if (run.status === "completed" || run.status === "failed" || run.status === "stopped") verdict = "NO_PIPELINE_RUNNING";
2596
- return {
2597
- verdict,
2598
- run_id: run.id,
2599
- status: run.status,
2600
- current_phase: run.current_phase,
2601
- staleness_seconds: stalenessSeconds,
2602
- last_activity: run.updated_at,
2603
- process: processInfo,
2604
- stories: {
2605
- active,
2606
- completed,
2607
- escalated,
2608
- details: storyDetails
2609
- }
2610
- };
2611
- } finally {
2612
- try {
2613
- dbWrapper.close();
2614
- } catch {}
2615
- }
2616
- }
2617
- async function runHealthAction(options) {
2618
- const { outputFormat } = options;
2619
- try {
2620
- const health = await getAutoHealthData(options);
2621
- if (outputFormat === "json") process.stdout.write(formatOutput(health, "json", true) + "\n");
2622
- else {
2623
- const verdictLabel = health.verdict === "HEALTHY" ? "HEALTHY" : health.verdict === "STALLED" ? "STALLED" : "NO PIPELINE RUNNING";
2624
- process.stdout.write(`\nPipeline Health: ${verdictLabel}\n`);
2625
- if (health.run_id !== null) {
2626
- process.stdout.write(` Run: ${health.run_id}\n`);
2627
- process.stdout.write(` Status: ${health.status}\n`);
2628
- process.stdout.write(` Phase: ${health.current_phase ?? "N/A"}\n`);
2629
- process.stdout.write(` Last Active: ${health.last_activity} (${health.staleness_seconds}s ago)\n`);
2630
- const processInfo = health.process;
2631
- if (processInfo.orchestrator_pid !== null) {
2632
- process.stdout.write(` Orchestrator: PID ${processInfo.orchestrator_pid}\n`);
2633
- process.stdout.write(` Children: ${processInfo.child_pids.length} active`);
2634
- if (processInfo.zombies.length > 0) process.stdout.write(` (${processInfo.zombies.length} ZOMBIE)`);
2635
- process.stdout.write("\n");
2636
- } else process.stdout.write(" Orchestrator: not running\n");
2637
- const storyDetails = health.stories.details;
2638
- if (Object.keys(storyDetails).length > 0) {
2639
- process.stdout.write("\n Stories:\n");
2640
- for (const [key, s] of Object.entries(storyDetails)) process.stdout.write(` ${key}: ${s.phase} (${s.review_cycles} review cycles)\n`);
2641
- process.stdout.write(`\n Summary: ${health.stories.active} active, ${health.stories.completed} completed, ${health.stories.escalated} escalated\n`);
2642
- }
2643
- }
2644
- }
2645
- return 0;
2646
- } catch (err) {
2647
- const msg = err instanceof Error ? err.message : String(err);
2648
- if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
2649
- else process.stderr.write(`Error: ${msg}\n`);
2650
- logger$11.error({ err }, "health action failed");
2651
- return 1;
2652
- }
2653
- }
2654
- function registerHealthCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
2655
- program.command("health").description("Check pipeline health: process status, stall detection, and verdict").option("--run-id <id>", "Pipeline run ID to query (defaults to latest)").option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").action(async (opts) => {
2656
- const outputFormat = opts.outputFormat === "json" ? "json" : "human";
2657
- const exitCode = await runHealthAction({
2658
- outputFormat,
2659
- runId: opts.runId,
2660
- projectRoot: opts.projectRoot
2661
- });
2662
- process.exitCode = exitCode;
2663
- });
2664
- }
2665
-
2666
2399
  //#endregion
2667
2400
  //#region src/cli/commands/supervisor.ts
2668
2401
  function defaultSupervisorDeps() {
@@ -3095,11 +2828,11 @@ async function runSupervisorAction(options, deps = {}) {
3095
2828
  try {
3096
2829
  const { createExperimenter } = await import(
3097
2830
  /* @vite-ignore */
3098
- "../experimenter-Cd04gAWQ.js"
2831
+ "../experimenter-CHRVkV3d.js"
3099
2832
  );
3100
2833
  const { getLatestRun: getLatest } = await import(
3101
2834
  /* @vite-ignore */
3102
- "../decisions-BmqXQ3Se.js"
2835
+ "../decisions-DxgMpQpz.js"
3103
2836
  );
3104
2837
  const dbPath = join(projectRoot, ".substrate", "substrate.db");
3105
2838
  const expDbWrapper = new DatabaseWrapper(dbPath);
@@ -3109,7 +2842,7 @@ async function runSupervisorAction(options, deps = {}) {
3109
2842
  const expDb = expDbWrapper.db;
3110
2843
  const { runRunAction: runPipeline } = await import(
3111
2844
  /* @vite-ignore */
3112
- "../run-fjuwOUib.js"
2845
+ "../run-DPrhfU2T.js"
3113
2846
  );
3114
2847
  const runStoryFn = async (opts) => {
3115
2848
  const exitCode = await runPipeline({
@@ -6969,8 +6702,8 @@ async function createProgram() {
6969
6702
  /** Fire-and-forget startup version check (story 8.3, AC3/AC5) */
6970
6703
  function checkForUpdatesInBackground(currentVersion) {
6971
6704
  if (process.env.SUBSTRATE_NO_UPDATE_CHECK === "1") return;
6972
- import("../upgrade-BjYVeC6G.js").then(async () => {
6973
- const { createVersionManager } = await import("../version-manager-impl-9N_519Ey.js");
6705
+ import("../upgrade-CF8EjNuO.js").then(async () => {
6706
+ const { createVersionManager } = await import("../version-manager-impl-C6jmvble.js");
6974
6707
  const vm = createVersionManager();
6975
6708
  const result = await vm.checkForUpdates();
6976
6709
  if (result.updateAvailable) {