substrate-ai 0.6.2 → 0.6.4

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,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { AdapterTelemetryPersistence, AppError, DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DoltClient, DoltNotInstalled, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, FileStateStore, GitClient, GrammarLoader, IngestionServer, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SUBSTRATE_OWNED_SETTINGS_KEYS, SymbolParser, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, checkDoltInstalled, createConfigSystem, createContextCompiler, createDatabaseAdapter, createDispatcher, createDoltClient, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStateStore, createStopAfterGate, createTelemetryAdvisor, detectCycles, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, initSchema, initializeDolt, isSyncAdapter, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-DCmne2q6.js";
2
+ import { AdapterTelemetryPersistence, AppError, DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DoltClient, DoltNotInstalled, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, FileStateStore, GitClient, GrammarLoader, IngestionServer, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SUBSTRATE_OWNED_SETTINGS_KEYS, SymbolParser, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, checkDoltInstalled, createConfigSystem, createContextCompiler, createDatabaseAdapter, createDispatcher, createDoltClient, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStateStore, createStopAfterGate, createTelemetryAdvisor, detectCycles, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, initSchema, initializeDolt, isSyncAdapter, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-CJKsY214.js";
3
3
  import { createLogger } from "../logger-D2fS2ccL.js";
4
4
  import { AdapterRegistry } from "../adapter-registry-D2zdMwVu.js";
5
5
  import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema } from "../config-migrator-DtZW1maj.js";
6
6
  import { ConfigError, createEventBus } from "../helpers-BihqWgVe.js";
7
- import { RoutingRecommender } from "../routing-BUE9pIxW.js";
7
+ import { RoutingRecommender } from "../routing-CobBiKeV.js";
8
8
  import { addTokenUsage, createDecision, createPipelineRun, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-C6MF2Cax.js";
9
9
  import { ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-BRpT8MYF.js";
10
10
  import { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, verifyGitVersion } from "../git-utils-C-fdrHF_.js";
@@ -432,8 +432,8 @@ async function detectMonorepoProfile(rootDir) {
432
432
  return { project: {
433
433
  type: "monorepo",
434
434
  tool: "turborepo",
435
- buildCommand: "turbo build",
436
- testCommand: "turbo test",
435
+ buildCommand: "npx turbo build",
436
+ testCommand: "npx turbo test",
437
437
  packages
438
438
  } };
439
439
  }
@@ -1557,7 +1557,7 @@ function mapInternalPhaseToEventPhase(internalPhase) {
1557
1557
  }
1558
1558
  }
1559
1559
  async function runResumeAction(options) {
1560
- const { runId: specifiedRunId, stopAfter, outputFormat, projectRoot, concurrency, pack: packName, events: eventsFlag, registry } = options;
1560
+ const { runId: specifiedRunId, stopAfter, outputFormat, projectRoot, concurrency, pack: packName, events: eventsFlag, registry, maxReviewCycles = 2 } = options;
1561
1561
  if (stopAfter !== void 0 && !VALID_PHASES.includes(stopAfter)) {
1562
1562
  const errorMsg = `Invalid phase: "${stopAfter}". Valid phases: ${VALID_PHASES.join(", ")}`;
1563
1563
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
@@ -1654,7 +1654,7 @@ async function runResumeAction(options) {
1654
1654
  }
1655
1655
  }
1656
1656
  async function runFullPipelineFromPhase(options) {
1657
- const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, events: eventsFlag, existingRunId, projectRoot, registry: injectedRegistry, stories: explicitStories } = options;
1657
+ const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, events: eventsFlag, existingRunId, projectRoot, registry: injectedRegistry, stories: explicitStories, maxReviewCycles = 2 } = options;
1658
1658
  if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });
1659
1659
  const adapter = createDatabaseAdapter({
1660
1660
  backend: "auto",
@@ -1791,7 +1791,7 @@ async function runFullPipelineFromPhase(options) {
1791
1791
  eventBus,
1792
1792
  config: {
1793
1793
  maxConcurrency: concurrency,
1794
- maxReviewCycles: 2,
1794
+ maxReviewCycles,
1795
1795
  pipelineRunId: runId,
1796
1796
  enableHeartbeat: eventsFlag === true
1797
1797
  },
@@ -1971,7 +1971,7 @@ async function runFullPipelineFromPhase(options) {
1971
1971
  }
1972
1972
  }
1973
1973
  function registerResumeCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
1974
- 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").option("--events", "Emit structured NDJSON events on stdout for programmatic consumption").action(async (opts) => {
1974
+ 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").option("--events", "Emit structured NDJSON events on stdout for programmatic consumption").option("--max-review-cycles <n>", "Maximum review cycles per story (default: 2)", (v) => parseInt(v, 10), 2).action(async (opts) => {
1975
1975
  const outputFormat = opts.outputFormat === "json" ? "json" : "human";
1976
1976
  const exitCode = await runResumeAction({
1977
1977
  runId: opts.runId,
@@ -1981,6 +1981,7 @@ function registerResumeCommand(program, _version = "0.0.0", projectRoot = proces
1981
1981
  concurrency: opts.concurrency,
1982
1982
  pack: opts.pack,
1983
1983
  events: opts.events,
1984
+ maxReviewCycles: opts.maxReviewCycles,
1984
1985
  registry
1985
1986
  });
1986
1987
  process.exitCode = exitCode;
@@ -2074,7 +2075,7 @@ async function runStatusAction(options) {
2074
2075
  if (runId !== void 0 && runId !== "") run = await getPipelineRunById(adapter, runId);
2075
2076
  else run = await getLatestRun(adapter);
2076
2077
  if (run === void 0) {
2077
- const errorMsg = runId !== void 0 ? `Pipeline run '${runId}' not found.` : "No pipeline runs found. Run `substrate run` first.";
2078
+ const errorMsg = runId !== void 0 ? `Pipeline run '${runId}' not found.` : "No pipeline runs found. Run `substrate run --events` to start a pipeline first.";
2078
2079
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
2079
2080
  else process.stderr.write(`Error: ${errorMsg}\n`);
2080
2081
  return 1;
@@ -3485,7 +3486,7 @@ async function runSupervisorAction(options, deps = {}) {
3485
3486
  await initSchema(expAdapter);
3486
3487
  const { runRunAction: runPipeline } = await import(
3487
3488
  /* @vite-ignore */
3488
- "../run-CcUT8-DF.js"
3489
+ "../run-XI1lMRHg.js"
3489
3490
  );
3490
3491
  const runStoryFn = async (opts) => {
3491
3492
  const exitCode = await runPipeline({
@@ -4001,7 +4002,7 @@ async function runMetricsAction(options) {
4001
4002
  const routingConfigPath = join(dbDir, "routing.yml");
4002
4003
  let routingConfig = null;
4003
4004
  if (existsSync(routingConfigPath)) try {
4004
- const { loadModelRoutingConfig } = await import("../routing-DbR9FPmj.js");
4005
+ const { loadModelRoutingConfig } = await import("../routing-CpsRPjLE.js");
4005
4006
  routingConfig = loadModelRoutingConfig(routingConfigPath);
4006
4007
  } catch {}
4007
4008
  if (routingConfig === null) routingConfig = {
@@ -263,7 +263,7 @@ var RoutingResolver = class RoutingResolver {
263
263
  return new RoutingResolver(config, logger$1);
264
264
  } catch (err) {
265
265
  if (err instanceof RoutingConfigError && err.code === "CONFIG_NOT_FOUND") {
266
- logger$1.warn({
266
+ logger$1.debug({
267
267
  configPath: filePath,
268
268
  component: "routing",
269
269
  reason: "config not found"
@@ -829,4 +829,4 @@ var RoutingTuner = class {
829
829
 
830
830
  //#endregion
831
831
  export { ModelRoutingConfigSchema, ProviderPolicySchema, RoutingConfigError, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, TASK_TYPE_PHASE_MAP, getModelTier, loadModelRoutingConfig };
832
- //# sourceMappingURL=routing-BUE9pIxW.js.map
832
+ //# sourceMappingURL=routing-CobBiKeV.js.map
@@ -1,4 +1,4 @@
1
1
  import "./logger-D2fS2ccL.js";
2
- import { ModelRoutingConfigSchema, ProviderPolicySchema, RoutingConfigError, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, TASK_TYPE_PHASE_MAP, getModelTier, loadModelRoutingConfig } from "./routing-BUE9pIxW.js";
2
+ import { ModelRoutingConfigSchema, ProviderPolicySchema, RoutingConfigError, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, TASK_TYPE_PHASE_MAP, getModelTier, loadModelRoutingConfig } from "./routing-CobBiKeV.js";
3
3
 
4
4
  export { loadModelRoutingConfig };
@@ -1,7 +1,7 @@
1
1
  import { createLogger, deepMask } from "./logger-D2fS2ccL.js";
2
2
  import { CURRENT_CONFIG_FORMAT_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "./config-migrator-DtZW1maj.js";
3
3
  import { ConfigError, ConfigIncompatibleFormatError, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-BihqWgVe.js";
4
- import { RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, loadModelRoutingConfig } from "./routing-BUE9pIxW.js";
4
+ import { RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, loadModelRoutingConfig } from "./routing-CobBiKeV.js";
5
5
  import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-C6MF2Cax.js";
6
6
  import { ADVISORY_NOTES, ESCALATION_DIAGNOSIS, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, TEST_EXPANSION_FINDING, TEST_PLAN, aggregateTokenUsageForRun, aggregateTokenUsageForStory, getStoryMetricsForRun, writeRunMetrics, writeStoryMetrics } from "./operational-BRpT8MYF.js";
7
7
  import { createRequire } from "module";
@@ -6638,6 +6638,7 @@ const MIN_FREE_MEMORY_BYTES = (() => {
6638
6638
  return 256 * 1024 * 1024;
6639
6639
  })();
6640
6640
  const MEMORY_PRESSURE_POLL_MS = 1e4;
6641
+ const MEMORY_PRESSURE_MAX_HOLD_MS = 3e5;
6641
6642
  let _lastKnownPressureLevel = 0;
6642
6643
  /**
6643
6644
  * Get available system memory in bytes, accounting for platform differences.
@@ -6721,6 +6722,7 @@ var DispatcherImpl = class {
6721
6722
  _queue = [];
6722
6723
  _shuttingDown = false;
6723
6724
  _memoryPressureTimer = null;
6725
+ _memoryPressureHoldStart = null;
6724
6726
  constructor(eventBus, adapterRegistry, config) {
6725
6727
  this._eventBus = eventBus;
6726
6728
  this._adapterRegistry = adapterRegistry;
@@ -7134,13 +7136,28 @@ var DispatcherImpl = class {
7134
7136
  _isMemoryPressured() {
7135
7137
  const free = getAvailableMemory();
7136
7138
  if (free < MIN_FREE_MEMORY_BYTES) {
7139
+ const now = Date.now();
7140
+ if (this._memoryPressureHoldStart === null) this._memoryPressureHoldStart = now;
7141
+ const holdDurationMs = now - this._memoryPressureHoldStart;
7142
+ if (holdDurationMs >= MEMORY_PRESSURE_MAX_HOLD_MS) {
7143
+ logger$23.warn({
7144
+ freeMB: Math.round(free / 1024 / 1024),
7145
+ thresholdMB: Math.round(MIN_FREE_MEMORY_BYTES / 1024 / 1024),
7146
+ pressureLevel: _lastKnownPressureLevel,
7147
+ holdDurationMs
7148
+ }, "Memory pressure hold exceeded max duration — forcing dispatch");
7149
+ this._memoryPressureHoldStart = null;
7150
+ return false;
7151
+ }
7137
7152
  logger$23.warn({
7138
7153
  freeMB: Math.round(free / 1024 / 1024),
7139
7154
  thresholdMB: Math.round(MIN_FREE_MEMORY_BYTES / 1024 / 1024),
7140
- pressureLevel: _lastKnownPressureLevel
7155
+ pressureLevel: _lastKnownPressureLevel,
7156
+ holdDurationMs
7141
7157
  }, "Memory pressure detected — holding dispatch queue");
7142
7158
  return true;
7143
7159
  }
7160
+ this._memoryPressureHoldStart = null;
7144
7161
  return false;
7145
7162
  }
7146
7163
  /**
@@ -7202,7 +7219,7 @@ function detectPackageManager(projectRoot) {
7202
7219
  if (existsSync$1(join$1(projectRoot, "turbo.json"))) return {
7203
7220
  packageManager: "none",
7204
7221
  lockfile: "turbo.json",
7205
- command: "turbo build"
7222
+ command: "npx turbo build"
7206
7223
  };
7207
7224
  const nodeCandidates = [
7208
7225
  {
@@ -12511,7 +12528,7 @@ function detectInterfaceChanges(options) {
12511
12528
  for (const name of allNames) {
12512
12529
  let grepOutput = "";
12513
12530
  try {
12514
- grepOutput = execSync(`grep -r --include="*.test.ts" --include="*.spec.ts" -l "${name}" .`, {
12531
+ grepOutput = execSync(`grep -r --include="*.test.ts" --include="*.spec.ts" --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist --exclude-dir=.next --exclude-dir=coverage -l "${name}" .`, {
12515
12532
  cwd: projectRoot,
12516
12533
  encoding: "utf-8",
12517
12534
  timeout: 1e4,
@@ -16004,6 +16021,9 @@ function createTelemetryAdvisor(deps) {
16004
16021
 
16005
16022
  //#endregion
16006
16023
  //#region src/modules/implementation-orchestrator/orchestrator-impl.ts
16024
+ function estimateDispatchCost(input, output) {
16025
+ return (input * 3 + output * 15) / 1e6;
16026
+ }
16007
16027
  function createPauseGate() {
16008
16028
  let resolve$2;
16009
16029
  const promise = new Promise((res) => {
@@ -16719,7 +16739,7 @@ function createImplementationOrchestrator(deps) {
16719
16739
  agent: "create-story",
16720
16740
  input_tokens: createResult.tokenUsage.input,
16721
16741
  output_tokens: createResult.tokenUsage.output,
16722
- cost_usd: 0,
16742
+ cost_usd: estimateDispatchCost(createResult.tokenUsage.input, createResult.tokenUsage.output),
16723
16743
  metadata: JSON.stringify({ storyKey })
16724
16744
  });
16725
16745
  } catch (tokenErr) {
@@ -16897,7 +16917,7 @@ function createImplementationOrchestrator(deps) {
16897
16917
  agent: "test-plan",
16898
16918
  input_tokens: testPlanTokenUsage.input,
16899
16919
  output_tokens: testPlanTokenUsage.output,
16900
- cost_usd: 0,
16920
+ cost_usd: estimateDispatchCost(testPlanTokenUsage.input, testPlanTokenUsage.output),
16901
16921
  metadata: JSON.stringify({ storyKey })
16902
16922
  });
16903
16923
  } catch (tokenErr) {
@@ -17012,7 +17032,7 @@ function createImplementationOrchestrator(deps) {
17012
17032
  agent: `batch-${batch.batchIndex}`,
17013
17033
  input_tokens: batchResult.tokenUsage.input,
17014
17034
  output_tokens: batchResult.tokenUsage.output,
17015
- cost_usd: 0,
17035
+ cost_usd: estimateDispatchCost(batchResult.tokenUsage.input, batchResult.tokenUsage.output),
17016
17036
  metadata: JSON.stringify({
17017
17037
  storyKey,
17018
17038
  batchIndex: batch.batchIndex,
@@ -17068,7 +17088,7 @@ function createImplementationOrchestrator(deps) {
17068
17088
  agent: "dev-story",
17069
17089
  input_tokens: devResult.tokenUsage.input,
17070
17090
  output_tokens: devResult.tokenUsage.output,
17071
- cost_usd: 0,
17091
+ cost_usd: estimateDispatchCost(devResult.tokenUsage.input, devResult.tokenUsage.output),
17072
17092
  metadata: JSON.stringify({ storyKey })
17073
17093
  });
17074
17094
  } catch (tokenErr) {
@@ -17304,7 +17324,7 @@ function createImplementationOrchestrator(deps) {
17304
17324
  agent: useBatchedReview ? "code-review-batched" : "code-review",
17305
17325
  input_tokens: reviewResult.tokenUsage.input,
17306
17326
  output_tokens: reviewResult.tokenUsage.output,
17307
- cost_usd: 0,
17327
+ cost_usd: estimateDispatchCost(reviewResult.tokenUsage.input, reviewResult.tokenUsage.output),
17308
17328
  metadata: JSON.stringify({
17309
17329
  storyKey,
17310
17330
  reviewCycle: reviewCycles
@@ -17694,6 +17714,20 @@ function createImplementationOrchestrator(deps) {
17694
17714
  const constraints = decisions.filter((d) => d.category === "architecture");
17695
17715
  archConstraints = constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
17696
17716
  } catch {}
17717
+ let gitDiffContent = "";
17718
+ try {
17719
+ const diffFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
17720
+ if (diffFiles.length > 0) gitDiffContent = execSync(`git diff HEAD -- ${diffFiles.map((f) => `"${f}"`).join(" ")}`, {
17721
+ cwd: projectRoot ?? process.cwd(),
17722
+ encoding: "utf-8",
17723
+ timeout: 1e4,
17724
+ stdio: [
17725
+ "ignore",
17726
+ "pipe",
17727
+ "pipe"
17728
+ ]
17729
+ }).trim();
17730
+ } catch {}
17697
17731
  const sections = isMajorRework ? [
17698
17732
  {
17699
17733
  name: "story_content",
@@ -17712,7 +17746,7 @@ function createImplementationOrchestrator(deps) {
17712
17746
  },
17713
17747
  {
17714
17748
  name: "git_diff",
17715
- content: "",
17749
+ content: gitDiffContent,
17716
17750
  priority: "optional"
17717
17751
  }
17718
17752
  ] : (() => {
@@ -22477,7 +22511,7 @@ function mapInternalPhaseToEventPhase(internalPhase) {
22477
22511
  }
22478
22512
  }
22479
22513
  async function runRunAction(options) {
22480
- const { pack: packName, from: startPhase, stopAfter, concept: conceptArg, conceptFile, stories: storiesArg, concurrency, outputFormat, projectRoot, events: eventsFlag, verbose: verboseFlag, tui: tuiFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, epic: epicNumber, dryRun, registry: injectedRegistry } = options;
22514
+ const { pack: packName, from: startPhase, stopAfter, concept: conceptArg, conceptFile, stories: storiesArg, concurrency, outputFormat, projectRoot, events: eventsFlag, verbose: verboseFlag, tui: tuiFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, epic: epicNumber, dryRun, maxReviewCycles = 2, registry: injectedRegistry } = options;
22481
22515
  if (startPhase !== void 0 && !VALID_PHASES.includes(startPhase)) {
22482
22516
  const errorMsg = `Invalid phase '${startPhase}'. Valid phases: ${VALID_PHASES.join(", ")}`;
22483
22517
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
@@ -22615,7 +22649,8 @@ async function runRunAction(options) {
22615
22649
  ...telemetryEnabled ? {
22616
22650
  telemetryEnabled: true,
22617
22651
  telemetryPort
22618
- } : {}
22652
+ } : {},
22653
+ maxReviewCycles
22619
22654
  });
22620
22655
  let storyKeys = [...parsedStoryKeys];
22621
22656
  if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });
@@ -23117,7 +23152,7 @@ async function runRunAction(options) {
23117
23152
  type: "pipeline:pre-flight-failure",
23118
23153
  ts: new Date().toISOString(),
23119
23154
  exitCode: payload.exitCode,
23120
- output: payload.output
23155
+ output: payload.output + "\nTip: Use --skip-preflight to bypass, or check your build command in .substrate/project-profile.yaml"
23121
23156
  });
23122
23157
  });
23123
23158
  eventBus.on("pipeline:contract-mismatch", (payload) => {
@@ -23180,7 +23215,7 @@ async function runRunAction(options) {
23180
23215
  eventBus,
23181
23216
  config: {
23182
23217
  maxConcurrency: concurrency,
23183
- maxReviewCycles: 2,
23218
+ maxReviewCycles,
23184
23219
  pipelineRunId: pipelineRun.id,
23185
23220
  enableHeartbeat: eventsFlag === true,
23186
23221
  skipPreflight: skipPreflight === true
@@ -23299,7 +23334,7 @@ async function runRunAction(options) {
23299
23334
  }
23300
23335
  }
23301
23336
  async function runFullPipeline(options) {
23302
- const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, registry: injectedRegistry, tokenCeilings, stories: explicitStories, telemetryEnabled: fullTelemetryEnabled, telemetryPort: fullTelemetryPort } = options;
23337
+ const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, maxReviewCycles = 2, registry: injectedRegistry, tokenCeilings, stories: explicitStories, telemetryEnabled: fullTelemetryEnabled, telemetryPort: fullTelemetryPort } = options;
23303
23338
  if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });
23304
23339
  const adapter = createDatabaseAdapter({
23305
23340
  backend: "auto",
@@ -23519,7 +23554,7 @@ async function runFullPipeline(options) {
23519
23554
  eventBus,
23520
23555
  config: {
23521
23556
  maxConcurrency: concurrency,
23522
- maxReviewCycles: 2,
23557
+ maxReviewCycles,
23523
23558
  pipelineRunId: runId,
23524
23559
  skipPreflight: skipPreflight === true
23525
23560
  },
@@ -23628,7 +23663,7 @@ async function runFullPipeline(options) {
23628
23663
  }
23629
23664
  }
23630
23665
  function registerRunCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
23631
- program.command("run").description("Run the autonomous pipeline (use --from to start from a specific phase)").option("--pack <name>", "Methodology pack name", "bmad").option("--from <phase>", "Start from this phase: analysis, planning, solutioning, implementation").option("--stop-after <phase>", "Stop pipeline after this phase completes").option("--concept <text>", "Inline concept text (required when --from analysis)").option("--concept-file <path>", "Path to a file containing the concept text").option("--stories <keys>", "Comma-separated story keys (e.g., 10-1,10-2)").option("--epic <n>", "Scope story discovery to a single epic number (e.g., 27)", (v) => parseInt(v, 10)).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").option("--events", "Emit structured NDJSON events on stdout for programmatic consumption").option("--verbose", "Show detailed pino log output").option("--help-agent", "Print a machine-optimized prompt fragment for AI agents and exit").option("--tui", "Show TUI dashboard").option("--skip-ux", "Skip the UX design phase even if enabled in the pack manifest").option("--research", "Enable the research phase even if not set in the pack manifest").option("--skip-research", "Skip the research phase even if enabled in the pack manifest").option("--skip-preflight", "Skip the pre-flight build check (escape hatch for known-broken projects)").option("--dry-run", "Preview routing and repo-map injection without dispatching (Story 28-9)").action(async (opts) => {
23666
+ program.command("run").description("Run the autonomous pipeline (use --from to start from a specific phase)").option("--pack <name>", "Methodology pack name", "bmad").option("--from <phase>", "Start from this phase: analysis, planning, solutioning, implementation").option("--stop-after <phase>", "Stop pipeline after this phase completes").option("--concept <text>", "Inline concept text (required when --from analysis)").option("--concept-file <path>", "Path to a file containing the concept text").option("--stories <keys>", "Comma-separated story keys (e.g., 10-1,10-2)").option("--epic <n>", "Scope story discovery to a single epic number (e.g., 27)", (v) => parseInt(v, 10)).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").option("--events", "Emit structured NDJSON events on stdout for programmatic consumption").option("--verbose", "Show detailed pino log output").option("--help-agent", "Print a machine-optimized prompt fragment for AI agents and exit").option("--tui", "Show TUI dashboard").option("--skip-ux", "Skip the UX design phase even if enabled in the pack manifest").option("--research", "Enable the research phase even if not set in the pack manifest").option("--skip-research", "Skip the research phase even if enabled in the pack manifest").option("--skip-preflight", "Skip the pre-flight build check (escape hatch for known-broken projects)").option("--max-review-cycles <n>", "Maximum review cycles per story (default: 2)", (v) => parseInt(v, 10), 2).option("--dry-run", "Preview routing and repo-map injection without dispatching (Story 28-9)").action(async (opts) => {
23632
23667
  if (opts.helpAgent) {
23633
23668
  process.exitCode = await runHelpAgent();
23634
23669
  return;
@@ -23663,6 +23698,7 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
23663
23698
  research: opts.research,
23664
23699
  skipResearch: opts.skipResearch,
23665
23700
  skipPreflight: opts.skipPreflight,
23701
+ maxReviewCycles: opts.maxReviewCycles,
23666
23702
  dryRun: opts.dryRun,
23667
23703
  registry
23668
23704
  });
@@ -23672,4 +23708,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
23672
23708
 
23673
23709
  //#endregion
23674
23710
  export { AdapterTelemetryPersistence, AppError, DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DoltClient, DoltNotInstalled, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, FileStateStore, GitClient, GrammarLoader, IngestionServer, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SUBSTRATE_OWNED_SETTINGS_KEYS, SymbolParser, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, checkDoltInstalled, createConfigSystem, createContextCompiler, createDatabaseAdapter, createDispatcher, createDoltClient, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStateStore, createStopAfterGate, createTelemetryAdvisor, detectCycles, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, initSchema, initializeDolt, isSyncAdapter, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
23675
- //# sourceMappingURL=run-DCmne2q6.js.map
23711
+ //# sourceMappingURL=run-CJKsY214.js.map
@@ -1,8 +1,8 @@
1
- import { registerRunCommand, runRunAction } from "./run-DCmne2q6.js";
1
+ import { registerRunCommand, runRunAction } from "./run-CJKsY214.js";
2
2
  import "./logger-D2fS2ccL.js";
3
3
  import "./config-migrator-DtZW1maj.js";
4
4
  import "./helpers-BihqWgVe.js";
5
- import "./routing-BUE9pIxW.js";
5
+ import "./routing-CobBiKeV.js";
6
6
  import "./decisions-C6MF2Cax.js";
7
7
  import "./operational-BRpT8MYF.js";
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",