substrate-ai 0.6.1 → 0.6.2

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,5 +1,5 @@
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-IDOmPys1.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-DCmne2q6.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";
@@ -912,6 +912,22 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
912
912
  const TaskToolCommandGenerator = resolveExport(taskToolMod, "TaskToolCommandGenerator");
913
913
  const manifestMod = _require(join(installerLibPath, "core", "manifest-generator.js"));
914
914
  const ManifestGenerator = resolveExport(manifestMod, "ManifestGenerator");
915
+ const pathUtilsMod = _require(join(installerLibPath, "ide", "shared", "path-utils.js"));
916
+ const pathUtils = { toDashPath: pathUtilsMod.toDashPath ?? pathUtilsMod.default?.toDashPath };
917
+ const writeDashFallback = async (baseDir, artifacts, acceptTypes) => {
918
+ let written = 0;
919
+ for (const artifact of artifacts) {
920
+ if (!acceptTypes.includes(artifact.type)) continue;
921
+ const content = artifact.content;
922
+ if (!content || !artifact.relativePath) continue;
923
+ const flatName = pathUtils.toDashPath(artifact.relativePath);
924
+ const dest = join(baseDir, flatName);
925
+ mkdirSync(dirname(dest), { recursive: true });
926
+ writeFileSync(dest, content, "utf-8");
927
+ written++;
928
+ }
929
+ return written;
930
+ };
915
931
  const nonCoreModules = scanBmadModules(bmadDir);
916
932
  const allModules = ["core", ...nonCoreModules];
917
933
  try {
@@ -925,13 +941,13 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
925
941
  clearBmadCommandFiles(commandsDir);
926
942
  const agentGen = new AgentCommandGenerator("_bmad");
927
943
  const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, nonCoreModules);
928
- const agentCount = await agentGen.writeDashArtifacts(commandsDir, agentArtifacts);
944
+ const agentCount = typeof agentGen.writeDashArtifacts === "function" ? await agentGen.writeDashArtifacts(commandsDir, agentArtifacts) : await writeDashFallback(commandsDir, agentArtifacts, ["agent-launcher"]);
929
945
  const workflowGen = new WorkflowCommandGenerator("_bmad");
930
946
  const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
931
- const workflowCount = await workflowGen.writeDashArtifacts(commandsDir, workflowArtifacts);
947
+ const workflowCount = typeof workflowGen.writeDashArtifacts === "function" ? await workflowGen.writeDashArtifacts(commandsDir, workflowArtifacts) : await writeDashFallback(commandsDir, workflowArtifacts, ["workflow-command", "workflow-launcher"]);
932
948
  const taskToolGen = new TaskToolCommandGenerator("_bmad");
933
949
  const { artifacts: taskToolArtifacts } = await taskToolGen.collectTaskToolArtifacts(bmadDir);
934
- const taskToolCount = await taskToolGen.writeDashArtifacts(commandsDir, taskToolArtifacts);
950
+ const taskToolCount = typeof taskToolGen.writeDashArtifacts === "function" ? await taskToolGen.writeDashArtifacts(commandsDir, taskToolArtifacts) : await writeDashFallback(commandsDir, taskToolArtifacts, ["task", "tool"]);
935
951
  const total = agentCount + workflowCount + taskToolCount;
936
952
  if (outputFormat !== "json") process.stdout.write(`Generated ${String(total)} Claude Code commands (${String(agentCount)} agents, ${String(workflowCount)} workflows, ${String(taskToolCount)} tasks/tools)\n`);
937
953
  logger$18.info({
@@ -3469,7 +3485,7 @@ async function runSupervisorAction(options, deps = {}) {
3469
3485
  await initSchema(expAdapter);
3470
3486
  const { runRunAction: runPipeline } = await import(
3471
3487
  /* @vite-ignore */
3472
- "../run-DTOsG7PJ.js"
3488
+ "../run-CcUT8-DF.js"
3473
3489
  );
3474
3490
  const runStoryFn = async (opts) => {
3475
3491
  const exitCode = await runPipeline({
package/dist/index.d.ts CHANGED
@@ -514,6 +514,23 @@ interface SupervisorExperimentErrorEvent {
514
514
  /** Error message describing why the experiment failed */
515
515
  error: string;
516
516
  }
517
+ /**
518
+ * Emitted after all stories complete when the `.substrate/project-profile.yaml`
519
+ * may be outdated relative to the actual project structure (e.g., profile says
520
+ * `type: single` but a `turbo.json` now exists, or new language markers appeared).
521
+ *
522
+ * Non-blocking warning — the pipeline has already finished. The user should
523
+ * re-run `substrate init --force` to regenerate the profile.
524
+ */
525
+ interface PipelineProfileStaleEvent {
526
+ type: 'pipeline:profile-stale';
527
+ /** ISO-8601 timestamp generated at emit time */
528
+ ts: string;
529
+ /** Human-readable message describing the staleness indicators found */
530
+ message: string;
531
+ /** List of staleness indicators detected (e.g., "turbo.json exists but profile says type: single") */
532
+ indicators: string[];
533
+ }
517
534
  /**
518
535
  * Emitted when post-sprint contract verification finds a mismatch between
519
536
  * declared export/import contracts (Story 25-6).
@@ -581,7 +598,7 @@ interface RoutingModelSelectedEvent {
581
598
  * }
582
599
  * ```
583
600
  */
584
- type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | PipelinePreFlightFailureEvent | PipelineContractMismatchEvent | PipelineContractVerificationSummaryEvent | StoryPhaseEvent | StoryDoneEvent | StoryEscalationEvent | StoryWarnEvent | StoryLogEvent | PipelineHeartbeatEvent | StoryStallEvent | StoryZeroDiffEscalationEvent | StoryBuildVerificationFailedEvent | StoryBuildVerificationPassedEvent | StoryInterfaceChangeWarningEvent | StoryMetricsEvent | SupervisorPollEvent | SupervisorKillEvent | SupervisorRestartEvent | SupervisorAbortEvent | SupervisorSummaryEvent | SupervisorAnalysisCompleteEvent | SupervisorAnalysisErrorEvent | SupervisorExperimentStartEvent | SupervisorExperimentSkipEvent | SupervisorExperimentRecommendationsEvent | SupervisorExperimentCompleteEvent | SupervisorExperimentErrorEvent | RoutingModelSelectedEvent; //#endregion
601
+ type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | PipelinePreFlightFailureEvent | PipelineProfileStaleEvent | PipelineContractMismatchEvent | PipelineContractVerificationSummaryEvent | StoryPhaseEvent | StoryDoneEvent | StoryEscalationEvent | StoryWarnEvent | StoryLogEvent | PipelineHeartbeatEvent | StoryStallEvent | StoryZeroDiffEscalationEvent | StoryBuildVerificationFailedEvent | StoryBuildVerificationPassedEvent | StoryInterfaceChangeWarningEvent | StoryMetricsEvent | SupervisorPollEvent | SupervisorKillEvent | SupervisorRestartEvent | SupervisorAbortEvent | SupervisorSummaryEvent | SupervisorAnalysisCompleteEvent | SupervisorAnalysisErrorEvent | SupervisorExperimentStartEvent | SupervisorExperimentSkipEvent | SupervisorExperimentRecommendationsEvent | SupervisorExperimentCompleteEvent | SupervisorExperimentErrorEvent | RoutingModelSelectedEvent; //#endregion
585
602
  //#region src/core/errors.d.ts
586
603
 
587
604
  /**
@@ -1264,6 +1281,13 @@ interface OrchestratorEvents {
1264
1281
  /** Build output (stdout+stderr), truncated to 2000 chars */
1265
1282
  output: string;
1266
1283
  };
1284
+ /** Project profile may be outdated relative to the actual project structure */
1285
+ 'pipeline:profile-stale': {
1286
+ /** Human-readable message describing the staleness indicators found */
1287
+ message: string;
1288
+ /** List of staleness indicators detected */
1289
+ indicators: string[];
1290
+ };
1267
1291
  /** Contract verification found a mismatch between declared export/import contracts */
1268
1292
  'pipeline:contract-mismatch': {
1269
1293
  /** Story key that declared the export for this contract */
@@ -1,4 +1,4 @@
1
- import { registerRunCommand, runRunAction } from "./run-IDOmPys1.js";
1
+ import { registerRunCommand, runRunAction } from "./run-DCmne2q6.js";
2
2
  import "./logger-D2fS2ccL.js";
3
3
  import "./config-migrator-DtZW1maj.js";
4
4
  import "./helpers-BihqWgVe.js";
@@ -325,7 +325,8 @@ var InMemoryDatabaseAdapter = class {
325
325
  if (!m$1) return [];
326
326
  return [this._evalSelectExprs(m$1[1].trim())];
327
327
  }
328
- const m = /SELECT\s+(.+?)\s+FROM\s+(\w+)(?:\s+WHERE\s+(.+))?$/is.exec(sql);
328
+ const stripped = sql.replace(/\s+ORDER\s+BY\s+.+?(?=\s+LIMIT\s|\s*$)/is, "").replace(/\s+LIMIT\s+\d+\s*$/is, "");
329
+ const m = /SELECT\s+(.+?)\s+FROM\s+(\w+)(?:\s+WHERE\s+(.+))?$/is.exec(stripped);
329
330
  if (!m) return [];
330
331
  const colsStr = m[1].trim();
331
332
  const tableName = m[2];
@@ -334,6 +335,7 @@ var InMemoryDatabaseAdapter = class {
334
335
  let rows = table.map((r) => ({ ...r }));
335
336
  if (whereStr) rows = rows.filter((row) => this._matchWhere(whereStr.trim(), row));
336
337
  if (colsStr === "*") return rows;
338
+ if (/\b(?:SUM|COALESCE|COUNT|AVG|MIN|MAX)\s*\(/i.test(colsStr)) return [this._evalAggregate(colsStr, rows)];
337
339
  return rows.map((row) => this._projectCols(colsStr, row));
338
340
  }
339
341
  _update(sql) {
@@ -393,12 +395,22 @@ var InMemoryDatabaseAdapter = class {
393
395
  if (row[notNullM[1]] === null || row[notNullM[1]] === void 0) return false;
394
396
  continue;
395
397
  }
398
+ const likeM = /^(\w+)\s+LIKE\s+'(.*)'$/is.exec(trimmed);
399
+ if (likeM) {
400
+ const colVal = row[likeM[1]];
401
+ if (colVal === null || colVal === void 0) return false;
402
+ const pattern = likeM[2].replace(/''/g, "'");
403
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, (ch) => ch === "%" || ch === "_" ? ch : "\\" + ch);
404
+ const regex = new RegExp("^" + escaped.replace(/%/g, ".*").replace(/_/g, ".") + "$", "s");
405
+ if (!regex.test(String(colVal))) return false;
406
+ continue;
407
+ }
396
408
  }
397
409
  return true;
398
410
  }
399
411
  _projectCols(colsStr, row) {
400
412
  const result = {};
401
- const cols = colsStr.split(",").map((c) => c.trim());
413
+ const cols = this._splitTopLevelCommas(colsStr);
402
414
  for (const col of cols) {
403
415
  const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(col);
404
416
  if (aliasM) result[aliasM[2]] = this._evalExprAgainstRow(aliasM[1].trim(), row);
@@ -408,7 +420,7 @@ var InMemoryDatabaseAdapter = class {
408
420
  }
409
421
  _evalSelectExprs(exprs) {
410
422
  const result = {};
411
- const parts = exprs.split(",").map((p) => p.trim());
423
+ const parts = this._splitTopLevelCommas(exprs);
412
424
  for (const part of parts) {
413
425
  const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(part);
414
426
  if (aliasM) result[aliasM[2]] = this._evalLiteral(aliasM[1].trim());
@@ -431,6 +443,87 @@ var InMemoryDatabaseAdapter = class {
431
443
  return literal;
432
444
  }
433
445
  /**
446
+ * Split a string by commas that are NOT inside parentheses.
447
+ * E.g. "COALESCE(SUM(x), 0) as a, y" → ["COALESCE(SUM(x), 0) as a", "y"]
448
+ */
449
+ _splitTopLevelCommas(str) {
450
+ const parts = [];
451
+ let current = "";
452
+ let depth = 0;
453
+ let inStr = false;
454
+ for (let i = 0; i < str.length; i++) {
455
+ const ch = str[i];
456
+ if (ch === "'" && !inStr) {
457
+ inStr = true;
458
+ current += ch;
459
+ } else if (ch === "'" && inStr) if (str[i + 1] === "'") {
460
+ current += "''";
461
+ i++;
462
+ } else {
463
+ inStr = false;
464
+ current += ch;
465
+ }
466
+ else if (!inStr && ch === "(") {
467
+ depth++;
468
+ current += ch;
469
+ } else if (!inStr && ch === ")") {
470
+ depth--;
471
+ current += ch;
472
+ } else if (!inStr && ch === "," && depth === 0) {
473
+ parts.push(current.trim());
474
+ current = "";
475
+ } else current += ch;
476
+ }
477
+ if (current.trim() !== "") parts.push(current.trim());
478
+ return parts;
479
+ }
480
+ /**
481
+ * Evaluate aggregate SELECT expressions (SUM, COALESCE, COUNT) across
482
+ * a set of filtered rows, returning a single result row.
483
+ */
484
+ _evalAggregate(colsStr, rows) {
485
+ const result = {};
486
+ const cols = this._splitTopLevelCommas(colsStr);
487
+ for (const col of cols) {
488
+ const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(col);
489
+ const expr = aliasM ? aliasM[1].trim() : col.trim();
490
+ const alias = aliasM ? aliasM[2] : col.trim();
491
+ result[alias] = this._evalAggregateExpr(expr, rows);
492
+ }
493
+ return result;
494
+ }
495
+ /**
496
+ * Evaluate a single aggregate expression against a set of rows.
497
+ * Supports: SUM(col), COALESCE(expr, default), COUNT(*).
498
+ */
499
+ _evalAggregateExpr(expr, rows) {
500
+ const trimmed = expr.trim();
501
+ const coalesceM = /^COALESCE\((.+)\)$/i.exec(trimmed);
502
+ if (coalesceM) {
503
+ const args = this._splitTopLevelCommas(coalesceM[1]);
504
+ for (const arg of args) {
505
+ const val = this._evalAggregateExpr(arg.trim(), rows);
506
+ if (val !== null && val !== void 0) return val;
507
+ }
508
+ return null;
509
+ }
510
+ const sumM = /^SUM\((\w+)\)$/i.exec(trimmed);
511
+ if (sumM) {
512
+ const col = sumM[1];
513
+ if (rows.length === 0) return null;
514
+ let total = 0;
515
+ for (const row of rows) total += Number(row[col] ?? 0);
516
+ return total;
517
+ }
518
+ if (/^COUNT\(\*\)$/i.test(trimmed)) return rows.length;
519
+ const countM = /^COUNT\((\w+)\)$/i.exec(trimmed);
520
+ if (countM) {
521
+ const col = countM[1];
522
+ return rows.filter((r) => r[col] !== null && r[col] !== void 0).length;
523
+ }
524
+ return this._evalLiteral(trimmed);
525
+ }
526
+ /**
434
527
  * Parse a comma-separated list of SQL literal values.
435
528
  * Handles: NULL, numbers, single-quoted strings.
436
529
  * Simple split by comma (assumes no commas inside string values).
@@ -1829,8 +1922,8 @@ function resolveBmadMethodVersion(fromDir = __dirname) {
1829
1922
  const BMAD_BASELINE_TOKENS_FULL = 56800;
1830
1923
  /** BMAD baseline token total for create+dev+review comparison */
1831
1924
  const BMAD_BASELINE_TOKENS = 23800;
1832
- /** Story key pattern: <epic>-<story> e.g. "10-1" */
1833
- const STORY_KEY_PATTERN$1 = /^\d+-\d+$/;
1925
+ /** Story key pattern: <epic>-<story> e.g. "10-1", "1-1a", "NEW-26" */
1926
+ const STORY_KEY_PATTERN$1 = /^[A-Za-z0-9]+-[A-Za-z0-9]+$/;
1834
1927
  /**
1835
1928
  * Top-level keys in .claude/settings.json that substrate owns.
1836
1929
  * On init, these are set/updated unconditionally.
@@ -3441,6 +3534,28 @@ const PIPELINE_EVENT_METADATA = [
3441
3534
  description: "Overall verification result."
3442
3535
  }
3443
3536
  ]
3537
+ },
3538
+ {
3539
+ type: "pipeline:profile-stale",
3540
+ description: "Project profile may be outdated. Non-blocking warning — run `substrate init --force` to re-detect.",
3541
+ when: "After all stories complete, before pipeline:complete. Emitted when staleness indicators are found.",
3542
+ fields: [
3543
+ {
3544
+ name: "ts",
3545
+ type: "string",
3546
+ description: "Timestamp."
3547
+ },
3548
+ {
3549
+ name: "message",
3550
+ type: "string",
3551
+ description: "Human-readable staleness warning message."
3552
+ },
3553
+ {
3554
+ name: "indicators",
3555
+ type: "string[]",
3556
+ description: "List of staleness indicators (e.g., \"turbo.json exists but profile says type: single\")."
3557
+ }
3558
+ ]
3444
3559
  }
3445
3560
  ];
3446
3561
  /**
@@ -6338,7 +6453,8 @@ var DispatcherShuttingDownError = class extends Error {
6338
6453
  const YAML_ANCHOR_KEYS = [
6339
6454
  "result:",
6340
6455
  "verdict:",
6341
- "story_file:"
6456
+ "story_file:",
6457
+ "expansion_priority:"
6342
6458
  ];
6343
6459
  /**
6344
6460
  * Extract the YAML result block from sub-agent output.
@@ -7188,6 +7304,16 @@ function runBuildVerification(options) {
7188
7304
  output: combinedOutput,
7189
7305
  reason: "build-verification-timeout"
7190
7306
  };
7307
+ const missingScriptPattern = /Missing script[:\s]|No script found|Command "build" not found/i;
7308
+ if (missingScriptPattern.test(combinedOutput)) {
7309
+ logger$23.warn("Build script not found — skipping pre-flight (greenfield repo)");
7310
+ return {
7311
+ status: "skipped",
7312
+ exitCode,
7313
+ output: combinedOutput,
7314
+ reason: "build-script-not-found"
7315
+ };
7316
+ }
7191
7317
  return {
7192
7318
  status: "failed",
7193
7319
  exitCode,
@@ -7665,12 +7791,12 @@ var FileStateStore = class {
7665
7791
  //#region src/modules/state/dolt-store.ts
7666
7792
  const log = createLogger("modules:state:dolt");
7667
7793
  /**
7668
- * Validate that a story key matches the expected pattern (e.g. "26-7").
7794
+ * Validate that a story key matches the expected pattern (e.g. "26-7", "1-1a", "NEW-26").
7669
7795
  * Prevents SQL injection via string-interpolated identifiers.
7670
7796
  */
7671
- const STORY_KEY_PATTERN = /^[0-9]+-[0-9]+$/;
7797
+ const STORY_KEY_PATTERN = /^[A-Za-z0-9]+-[A-Za-z0-9]+$/;
7672
7798
  function assertValidStoryKey(storyKey) {
7673
- if (!STORY_KEY_PATTERN.test(storyKey)) throw new DoltQueryError("assertValidStoryKey", `Invalid story key: '${storyKey}'. Must match pattern <number>-<number>.`);
7799
+ if (!STORY_KEY_PATTERN.test(storyKey)) throw new DoltQueryError("assertValidStoryKey", `Invalid story key: '${storyKey}'. Must match pattern <segment>-<segment> (e.g. "10-1", "1-1a", "NEW-26").`);
7674
7800
  }
7675
7801
  /**
7676
7802
  * Dolt-backed implementation of the StateStore interface.
@@ -9095,9 +9221,17 @@ function readEpicShardFromFile(projectRoot, epicId) {
9095
9221
  if (!epicsPath) return "";
9096
9222
  const content = readFileSync$1(epicsPath, "utf-8");
9097
9223
  const epicNum = epicId.replace(/^epic-/i, "");
9098
- const pattern = new RegExp(`^#{2,4}\\s+(?:Epic\\s+)?${epicNum}[.:\\s].*?(?=\\n#{2,4}\\s|$)`, "ms");
9099
- const match$1 = pattern.exec(content);
9100
- return match$1 ? match$1[0].trim() : "";
9224
+ const headingPattern = new RegExp(`^(#{2,4})\\s+(?:Epic\\s+)?${epicNum}[.:\\s]`, "m");
9225
+ const headingMatch = headingPattern.exec(content);
9226
+ if (!headingMatch) return "";
9227
+ const startIdx = headingMatch.index;
9228
+ const headingLevel = headingMatch[1].length;
9229
+ const hashes = "#".repeat(headingLevel);
9230
+ const endPattern = new RegExp(`\\n${hashes}\\s`, "g");
9231
+ endPattern.lastIndex = startIdx + headingMatch[0].length;
9232
+ const endMatch = endPattern.exec(content);
9233
+ const endIdx = endMatch ? endMatch.index : content.length;
9234
+ return content.slice(startIdx, endIdx).trim();
9101
9235
  } catch (err) {
9102
9236
  logger$20.warn({
9103
9237
  epicId,
@@ -9356,9 +9490,9 @@ async function getProjectFindings(db) {
9356
9490
  sections.push("**Prior escalations:**");
9357
9491
  for (const d of diagnoses.slice(-3)) try {
9358
9492
  const val = JSON.parse(d.value);
9359
- sections.push(`- ${d.key.split(":")[0]}: ${val.recommendedAction} — ${val.rationale}`);
9493
+ sections.push(`- ${(d.key ?? "").split(":")[0]}: ${val.recommendedAction} — ${val.rationale}`);
9360
9494
  } catch {
9361
- sections.push(`- ${d.key}: escalated`);
9495
+ sections.push(`- ${d.key ?? "unknown"}: escalated`);
9362
9496
  }
9363
9497
  }
9364
9498
  const highCycleStories = metrics.filter((m) => {
@@ -9373,16 +9507,16 @@ async function getProjectFindings(db) {
9373
9507
  sections.push("**Stories with high review cycles:**");
9374
9508
  for (const m of highCycleStories) try {
9375
9509
  const val = JSON.parse(m.value);
9376
- sections.push(`- ${m.key.split(":")[0]}: ${val.review_cycles} cycles`);
9510
+ sections.push(`- ${(m.key ?? "").split(":")[0]}: ${val.review_cycles} cycles`);
9377
9511
  } catch {}
9378
9512
  }
9379
- const stalls = operational.filter((o) => o.key.startsWith("stall:"));
9513
+ const stalls = operational.filter((o) => o.key?.startsWith("stall:"));
9380
9514
  if (stalls.length > 0) sections.push(`**Prior stalls:** ${stalls.length} stall event(s) recorded`);
9381
9515
  if (advisoryNotes.length > 0) {
9382
9516
  sections.push("**Advisory notes from prior reviews (LGTM_WITH_NOTES):**");
9383
9517
  for (const n of advisoryNotes.slice(-3)) try {
9384
9518
  const val = JSON.parse(n.value);
9385
- const storyId = n.key.split(":")[0];
9519
+ const storyId = (n.key ?? "").split(":")[0];
9386
9520
  if (typeof val.notes === "string" && val.notes.length > 0) sections.push(`- ${storyId}: ${val.notes}`);
9387
9521
  } catch {
9388
9522
  sections.push(`- ${n.key}: advisory notes available`);
@@ -15902,6 +16036,69 @@ function buildTargetedFilesContent(issueList) {
15902
16036
  return lines.join("\n");
15903
16037
  }
15904
16038
  /**
16039
+ * Normalize a title string into a set of meaningful words for comparison.
16040
+ * Strips punctuation, lowercases, and filters out very short words (<=2 chars)
16041
+ * and common stop words to focus on content-bearing terms.
16042
+ */
16043
+ function titleToWordSet(title) {
16044
+ const stopWords = new Set([
16045
+ "the",
16046
+ "and",
16047
+ "for",
16048
+ "with",
16049
+ "from",
16050
+ "into",
16051
+ "that",
16052
+ "this",
16053
+ "via"
16054
+ ]);
16055
+ return new Set(title.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/[\s-]+/).filter((w) => w.length > 2 && !stopWords.has(w)));
16056
+ }
16057
+ /**
16058
+ * Compute the word overlap ratio between two titles.
16059
+ * Returns a value between 0 and 1, where 1 means all words in the smaller set
16060
+ * are present in the larger set.
16061
+ *
16062
+ * Uses the smaller set as the denominator so that a generated title that is a
16063
+ * reasonable subset or superset of the expected title still scores well.
16064
+ */
16065
+ function computeTitleOverlap(titleA, titleB) {
16066
+ const wordsA = titleToWordSet(titleA);
16067
+ const wordsB = titleToWordSet(titleB);
16068
+ if (wordsA.size === 0 || wordsB.size === 0) return 0;
16069
+ let shared = 0;
16070
+ for (const w of wordsA) if (wordsB.has(w)) shared++;
16071
+ const denominator = Math.min(wordsA.size, wordsB.size);
16072
+ return shared / denominator;
16073
+ }
16074
+ /**
16075
+ * Extract the expected story title from the epic shard content.
16076
+ *
16077
+ * Looks for patterns like:
16078
+ * - "### Story 37-1: Turborepo monorepo scaffold"
16079
+ * - "Story 37-1: Turborepo monorepo scaffold"
16080
+ * - "**37-1**: Turborepo monorepo scaffold"
16081
+ * - "37-1: Turborepo monorepo scaffold"
16082
+ *
16083
+ * Returns the title portion after the story key, or null if no match.
16084
+ */
16085
+ function extractExpectedStoryTitle(shardContent, storyKey) {
16086
+ if (!shardContent || !storyKey) return null;
16087
+ const escaped = storyKey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16088
+ const patterns = [
16089
+ new RegExp(`^#{2,4}\\s+Story\\s+${escaped}[:\\s]+\\s*(.+)$`, "mi"),
16090
+ new RegExp(`^Story\\s+${escaped}[:\\s]+\\s*(.+)$`, "mi"),
16091
+ new RegExp(`^\\*\\*${escaped}\\*\\*[:\\s]+\\s*(.+)$`, "mi"),
16092
+ new RegExp(`^${escaped}[:\\s]+\\s*(.+)$`, "mi")
16093
+ ];
16094
+ for (const pattern of patterns) {
16095
+ const match$1 = pattern.exec(shardContent);
16096
+ if (match$1?.[1]) return match$1[1].replace(/\*+$/, "").trim();
16097
+ }
16098
+ return null;
16099
+ }
16100
+ const TITLE_OVERLAP_WARNING_THRESHOLD = .3;
16101
+ /**
15905
16102
  * Map a StoryPhase to the corresponding WgStoryStatus for wg_stories writes.
15906
16103
  * Returns null for PENDING (no write needed).
15907
16104
  */
@@ -15918,6 +16115,55 @@ function wgStatusForPhase(phase) {
15918
16115
  }
15919
16116
  }
15920
16117
  /**
16118
+ * Check whether `.substrate/project-profile.yaml` is stale relative to
16119
+ * the actual project structure.
16120
+ *
16121
+ * Returns an array of human-readable indicator strings. An empty array
16122
+ * means the profile appears current (or doesn't exist).
16123
+ *
16124
+ * Staleness indicators checked:
16125
+ * - Profile says `type: single` but `turbo.json` exists (should be monorepo)
16126
+ * - Profile has no Go language but `go.mod` exists
16127
+ * - Profile has no Python language but `pyproject.toml` exists
16128
+ * - Profile has no Rust language but `Cargo.toml` exists
16129
+ */
16130
+ function checkProfileStaleness(projectRoot) {
16131
+ const profilePath = join$1(projectRoot, ".substrate", "project-profile.yaml");
16132
+ if (!existsSync$1(profilePath)) return [];
16133
+ let profile;
16134
+ try {
16135
+ const raw = readFileSync$1(profilePath, "utf-8");
16136
+ profile = yaml.load(raw) ?? {};
16137
+ } catch {
16138
+ return [];
16139
+ }
16140
+ const project = profile.project;
16141
+ if (project === void 0) return [];
16142
+ const indicators = [];
16143
+ const declaredLanguages = new Set();
16144
+ if (typeof project.language === "string") declaredLanguages.add(project.language);
16145
+ if (Array.isArray(project.packages)) {
16146
+ for (const pkg of project.packages) if (typeof pkg.language === "string") declaredLanguages.add(pkg.language);
16147
+ }
16148
+ if (project.type === "single" && existsSync$1(join$1(projectRoot, "turbo.json"))) indicators.push("turbo.json exists but profile says type: single (should be monorepo)");
16149
+ const languageMarkers = [
16150
+ {
16151
+ file: "go.mod",
16152
+ language: "go"
16153
+ },
16154
+ {
16155
+ file: "pyproject.toml",
16156
+ language: "python"
16157
+ },
16158
+ {
16159
+ file: "Cargo.toml",
16160
+ language: "rust"
16161
+ }
16162
+ ];
16163
+ for (const marker of languageMarkers) if (existsSync$1(join$1(projectRoot, marker.file)) && !declaredLanguages.has(marker.language)) indicators.push(`${marker.file} exists but profile does not declare ${marker.language}`);
16164
+ return indicators;
16165
+ }
16166
+ /**
15921
16167
  * Factory function that creates an ImplementationOrchestrator instance.
15922
16168
  *
15923
16169
  * @param deps - Injected dependencies (db, pack, contextCompiler, dispatcher,
@@ -16274,8 +16520,7 @@ function createImplementationOrchestrator(deps) {
16274
16520
  for (const s of _stories.values()) if (s.phase === "COMPLETE" || s.phase === "ESCALATED") completed++;
16275
16521
  else if (s.phase === "PENDING") queued++;
16276
16522
  else active++;
16277
- const timeSinceProgress = Date.now() - _lastProgressTs;
16278
- if (timeSinceProgress >= HEARTBEAT_INTERVAL_MS) eventBus.emit("orchestrator:heartbeat", {
16523
+ eventBus.emit("orchestrator:heartbeat", {
16279
16524
  runId: config.pipelineRunId ?? "",
16280
16525
  activeDispatches: active,
16281
16526
  completedDispatches: completed,
@@ -16519,6 +16764,46 @@ function createImplementationOrchestrator(deps) {
16519
16764
  return;
16520
16765
  }
16521
16766
  storyFilePath = createResult.story_file;
16767
+ if (createResult.story_title) try {
16768
+ const epicId = storyKey.split("-")[0] ?? storyKey;
16769
+ const implDecisions = await getDecisionsByPhase(db, "implementation");
16770
+ let shardContent;
16771
+ const perStoryShard = implDecisions.find((d) => d.category === "epic-shard" && d.key === storyKey);
16772
+ if (perStoryShard?.value) shardContent = perStoryShard.value;
16773
+ else {
16774
+ const epicShard = implDecisions.find((d) => d.category === "epic-shard" && d.key === epicId);
16775
+ if (epicShard?.value) shardContent = extractStorySection(epicShard.value, storyKey) ?? epicShard.value;
16776
+ }
16777
+ if (shardContent) {
16778
+ const expectedTitle = extractExpectedStoryTitle(shardContent, storyKey);
16779
+ if (expectedTitle) {
16780
+ const overlap = computeTitleOverlap(expectedTitle, createResult.story_title);
16781
+ if (overlap < TITLE_OVERLAP_WARNING_THRESHOLD) {
16782
+ const msg = `Story title mismatch: expected "${expectedTitle}" but got "${createResult.story_title}" (word overlap: ${Math.round(overlap * 100)}%). This may indicate the create-story agent received truncated context.`;
16783
+ logger$27.warn({
16784
+ storyKey,
16785
+ expectedTitle,
16786
+ generatedTitle: createResult.story_title,
16787
+ overlap
16788
+ }, msg);
16789
+ eventBus.emit("orchestrator:story-warn", {
16790
+ storyKey,
16791
+ msg
16792
+ });
16793
+ } else logger$27.debug({
16794
+ storyKey,
16795
+ expectedTitle,
16796
+ generatedTitle: createResult.story_title,
16797
+ overlap
16798
+ }, "Story title validation passed");
16799
+ }
16800
+ }
16801
+ } catch (titleValidationErr) {
16802
+ logger$27.debug({
16803
+ storyKey,
16804
+ err: titleValidationErr
16805
+ }, "Story title validation skipped due to error");
16806
+ }
16522
16807
  } catch (err) {
16523
16808
  const errMsg = err instanceof Error ? err.message : String(err);
16524
16809
  endPhase(storyKey, "create-story");
@@ -17041,6 +17326,28 @@ function createImplementationOrchestrator(deps) {
17041
17326
  }, "Phantom review detected (0 issues + error) — retrying review once");
17042
17327
  continue;
17043
17328
  }
17329
+ if (isPhantomReview && timeoutRetried) {
17330
+ logger$27.warn({
17331
+ storyKey,
17332
+ reviewCycles,
17333
+ error: reviewResult.error
17334
+ }, "Consecutive review timeouts detected (original + retry both failed) — escalating immediately");
17335
+ endPhase(storyKey, "code-review");
17336
+ updateStory(storyKey, {
17337
+ phase: "ESCALATED",
17338
+ error: "consecutive-review-timeouts",
17339
+ completedAt: new Date().toISOString()
17340
+ });
17341
+ await writeStoryMetricsBestEffort(storyKey, "escalated", reviewCycles + 1);
17342
+ await emitEscalation({
17343
+ storyKey,
17344
+ lastVerdict: "consecutive-review-timeouts",
17345
+ reviewCycles: reviewCycles + 1,
17346
+ issues: ["Review dispatch failed twice consecutively (original + phantom-retry). Likely resource-constrained or diff too large for reviewer."]
17347
+ });
17348
+ await persistState();
17349
+ return;
17350
+ }
17044
17351
  verdict = reviewResult.verdict;
17045
17352
  issueList = reviewResult.issue_list ?? [];
17046
17353
  if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
@@ -17836,6 +18143,19 @@ function createImplementationOrchestrator(deps) {
17836
18143
  } catch (err) {
17837
18144
  logger$27.error({ err }, "Post-sprint contract verification threw an error — skipping");
17838
18145
  }
18146
+ if (projectRoot !== void 0) try {
18147
+ const indicators = checkProfileStaleness(projectRoot);
18148
+ if (indicators.length > 0) {
18149
+ const message = "Project profile may be outdated — consider running `substrate init --force` to re-detect";
18150
+ eventBus.emit("pipeline:profile-stale", {
18151
+ message,
18152
+ indicators
18153
+ });
18154
+ logger$27.warn({ indicators }, message);
18155
+ }
18156
+ } catch (err) {
18157
+ logger$27.debug({ err }, "Profile staleness check failed (best-effort)");
18158
+ }
17839
18159
  let completed = 0;
17840
18160
  let escalated = 0;
17841
18161
  let failed = 0;
@@ -17966,14 +18286,14 @@ async function resolveStoryKeys(db, projectRoot, opts) {
17966
18286
  function parseStoryKeysFromEpics(content) {
17967
18287
  if (content.length === 0) return [];
17968
18288
  const keys = new Set();
17969
- const explicitKeyPattern = /\*\*Story key:\*\*\s*`?(\d+-\d+)(?:-[^`\s]*)?`?/g;
18289
+ const explicitKeyPattern = /\*\*Story key:\*\*\s*`?([A-Za-z0-9]+-[A-Za-z0-9]+)(?:-[^`\s]*)?`?/g;
17970
18290
  let match$1;
17971
18291
  while ((match$1 = explicitKeyPattern.exec(content)) !== null) if (match$1[1] !== void 0) keys.add(match$1[1]);
17972
- const headingPattern = /^###\s+Story\s+(\d+)[.\-](\d+)/gm;
18292
+ const headingPattern = /^###\s+Story\s+([A-Za-z0-9]+)[.\-]([A-Za-z0-9]+)/gm;
17973
18293
  while ((match$1 = headingPattern.exec(content)) !== null) if (match$1[1] !== void 0 && match$1[2] !== void 0) keys.add(`${match$1[1]}-${match$1[2]}`);
17974
- const inlineStoryPattern = /Story\s+(\d+)-(\d+)[:\s]/g;
18294
+ const inlineStoryPattern = /Story\s+([A-Za-z0-9]+)-([A-Za-z0-9]+)[:\s]/g;
17975
18295
  while ((match$1 = inlineStoryPattern.exec(content)) !== null) if (match$1[1] !== void 0 && match$1[2] !== void 0) keys.add(`${match$1[1]}-${match$1[2]}`);
17976
- const filePathPattern = /_bmad-output\/implementation-artifacts\/(\d+-\d+)-/g;
18296
+ const filePathPattern = /_bmad-output\/implementation-artifacts\/([A-Za-z0-9]+-[A-Za-z0-9]+)-/g;
17977
18297
  while ((match$1 = filePathPattern.exec(content)) !== null) if (match$1[1] !== void 0) keys.add(match$1[1]);
17978
18298
  return sortStoryKeys(Array.from(keys));
17979
18299
  }
@@ -18019,6 +18339,12 @@ function discoverPendingStoryKeys(projectRoot, epicNumber) {
18019
18339
  allKeys = sortStoryKeys([...new Set(allKeys)]);
18020
18340
  }
18021
18341
  }
18342
+ const sprintKeys = parseStoryKeysFromSprintStatus(projectRoot);
18343
+ if (sprintKeys.length > 0) {
18344
+ const merged = new Set(allKeys);
18345
+ for (const k of sprintKeys) merged.add(k);
18346
+ allKeys = sortStoryKeys([...merged]);
18347
+ }
18022
18348
  if (allKeys.length === 0) return [];
18023
18349
  const existingKeys = collectExistingStoryKeys(projectRoot);
18024
18350
  return allKeys.filter((k) => !existingKeys.has(k));
@@ -18069,7 +18395,7 @@ function collectExistingStoryKeys(projectRoot) {
18069
18395
  } catch {
18070
18396
  return existing;
18071
18397
  }
18072
- const filePattern = /^(\d+-\d+)-/;
18398
+ const filePattern = /^([A-Za-z0-9]+-[A-Za-z0-9]+)-/;
18073
18399
  for (const entry of entries) {
18074
18400
  if (!entry.endsWith(".md")) continue;
18075
18401
  const m = filePattern.exec(entry);
@@ -18078,6 +18404,33 @@ function collectExistingStoryKeys(projectRoot) {
18078
18404
  return existing;
18079
18405
  }
18080
18406
  /**
18407
+ * Parse story keys from sprint-status.yaml.
18408
+ * Reads the development_status map and extracts keys that match the
18409
+ * alphanumeric story key pattern (e.g., 1-1a, NEW-26, E5-accessibility).
18410
+ * Filters out epic status entries (epic-N) and retrospective entries.
18411
+ */
18412
+ function parseStoryKeysFromSprintStatus(projectRoot) {
18413
+ const candidates = [join$1(projectRoot, "_bmad-output", "implementation-artifacts", "sprint-status.yaml"), join$1(projectRoot, "_bmad-output", "sprint-status.yaml")];
18414
+ const statusPath = candidates.find((p) => existsSync$1(p));
18415
+ if (!statusPath) return [];
18416
+ try {
18417
+ const content = readFileSync$1(statusPath, "utf-8");
18418
+ const keys = [];
18419
+ const linePattern = /^\s{2}([A-Za-z0-9]+-[A-Za-z0-9]+(?:-[A-Za-z0-9-]*)?)\s*:/gm;
18420
+ let match$1;
18421
+ while ((match$1 = linePattern.exec(content)) !== null) {
18422
+ const fullKey = match$1[1];
18423
+ if (/^epic-\d+$/.test(fullKey)) continue;
18424
+ if (fullKey.includes("retrospective")) continue;
18425
+ const segments = fullKey.split("-");
18426
+ if (segments.length >= 2) keys.push(`${segments[0]}-${segments[1]}`);
18427
+ }
18428
+ return [...new Set(keys)];
18429
+ } catch {
18430
+ return [];
18431
+ }
18432
+ }
18433
+ /**
18081
18434
  * Collect story keys already completed in previous pipeline runs.
18082
18435
  * Scans pipeline_runs with status='completed' and extracts story keys
18083
18436
  * with phase='COMPLETE' from their token_usage_json state.
@@ -18096,16 +18449,26 @@ async function getCompletedStoryKeys(db) {
18096
18449
  return completed;
18097
18450
  }
18098
18451
  /**
18099
- * Sort story keys numerically by epic number (primary) then story number (secondary).
18100
- * E.g. ["10-1", "1-2", "2-1"] ["1-2", "2-1", "10-1"]
18452
+ * Sort story keys: numeric keys first (by epic then story number),
18453
+ * then alphabetic-prefix keys (NEW-*, E-*) sorted lexicographically.
18454
+ * E.g. ["10-1", "1-2a", "1-2", "NEW-26", "E5-acc"] → ["1-2", "1-2a", "10-1", "E5-acc", "NEW-26"]
18101
18455
  */
18102
18456
  function sortStoryKeys(keys) {
18103
18457
  return keys.slice().sort((a, b) => {
18104
- const [ae, as_] = a.split("-").map(Number);
18105
- const [be, bs] = b.split("-").map(Number);
18106
- const epicDiff = (ae ?? 0) - (be ?? 0);
18107
- if (epicDiff !== 0) return epicDiff;
18108
- return (as_ ?? 0) - (bs ?? 0);
18458
+ const aParts = a.split("-");
18459
+ const bParts = b.split("-");
18460
+ const aNum = Number(aParts[0]);
18461
+ const bNum = Number(bParts[0]);
18462
+ if (!isNaN(aNum) && !isNaN(bNum)) {
18463
+ if (aNum !== bNum) return aNum - bNum;
18464
+ const aStory = Number(aParts[1]);
18465
+ const bStory = Number(bParts[1]);
18466
+ if (!isNaN(aStory) && !isNaN(bStory) && aStory !== bStory) return aStory - bStory;
18467
+ return (aParts[1] ?? "").localeCompare(bParts[1] ?? "");
18468
+ }
18469
+ if (!isNaN(aNum)) return -1;
18470
+ if (!isNaN(bNum)) return 1;
18471
+ return a.localeCompare(b);
18109
18472
  });
18110
18473
  }
18111
18474
 
@@ -22196,7 +22559,7 @@ async function runRunAction(options) {
22196
22559
  if (storiesArg !== void 0 && storiesArg !== "") {
22197
22560
  parsedStoryKeys = storiesArg.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
22198
22561
  for (const key of parsedStoryKeys) if (!validateStoryKey(key)) {
22199
- const errorMsg = `Story key '${key}' is not a valid format. Expected: <epic>-<story> (e.g., 10-1)`;
22562
+ const errorMsg = `Story key '${key}' is not a valid format. Expected: <epic>-<story> (e.g., 10-1, 1-1a, NEW-26)`;
22200
22563
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
22201
22564
  else process.stderr.write(`Error: ${errorMsg}\n`);
22202
22565
  return 1;
@@ -22777,6 +23140,14 @@ async function runRunAction(options) {
22777
23140
  verdict: payload.verdict
22778
23141
  });
22779
23142
  });
23143
+ eventBus.on("pipeline:profile-stale", (payload) => {
23144
+ ndjsonEmitter.emit({
23145
+ type: "pipeline:profile-stale",
23146
+ ts: new Date().toISOString(),
23147
+ message: payload.message,
23148
+ indicators: payload.indicators
23149
+ });
23150
+ });
22780
23151
  }
22781
23152
  const ingestionServer = telemetryEnabled ? new IngestionServer({ port: telemetryPort }) : void 0;
22782
23153
  if (telemetryPersistence !== void 0) {
@@ -23301,4 +23672,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
23301
23672
 
23302
23673
  //#endregion
23303
23674
  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 };
23304
- //# sourceMappingURL=run-IDOmPys1.js.map
23675
+ //# sourceMappingURL=run-DCmne2q6.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",