substrate-ai 0.19.49 → 0.19.51

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
@@ -4,7 +4,7 @@ import { createLogger } from "../logger-KeHncl-f.js";
4
4
  import { createEventBus } from "../helpers-CElYrONe.js";
5
5
  import { AdapterRegistry, BudgetConfigSchema, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, ConfigError, CostTrackerConfigSchema, DEFAULT_CONFIG, DoltClient, DoltNotInstalled, GlobalSettingsSchema, IngestionServer, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProvidersSchema, RoutingRecommender, STORY_METRICS, TelemetryConfigSchema, addTokenUsage, aggregateTokenUsageForRun, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDecision, createDoltClient, createPipelineRun, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, tagRunAsBaseline, updatePipelineRun } from "../dist-sNh9XQ6V.js";
6
6
  import "../adapter-registry-DXLMTmfD.js";
7
- import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-DI5SzKf5.js";
7
+ import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-BbwMFNWQ.js";
8
8
  import "../errors-RupuC-ES.js";
9
9
  import "../routing-CcBOCuC9.js";
10
10
  import "../decisions-C0pz9Clx.js";
@@ -1862,6 +1862,72 @@ async function scaffoldClaudeMd(projectRoot, profile) {
1862
1862
  await writeFile(claudeMdPath, newContent, "utf8");
1863
1863
  logger$15.info({ claudeMdPath }, "Wrote substrate section to CLAUDE.md");
1864
1864
  }
1865
+ async function scaffoldAgentsMd(projectRoot, profile) {
1866
+ const agentsMdPath = join(projectRoot, "AGENTS.md");
1867
+ const pkgRoot = findPackageRoot(__dirname);
1868
+ const templateName = "agents-md-substrate-section.md";
1869
+ let templatePath = join(pkgRoot, "dist", "cli", "templates", templateName);
1870
+ if (!existsSync$1(templatePath)) templatePath = join(pkgRoot, "src", "cli", "templates", templateName);
1871
+ let sectionContent;
1872
+ try {
1873
+ sectionContent = await readFile(templatePath, "utf8");
1874
+ } catch {
1875
+ logger$15.warn({ templatePath }, "AGENTS.md substrate section template not found; skipping");
1876
+ return;
1877
+ }
1878
+ const substrateVersion = readSubstrateVersion(pkgRoot);
1879
+ sectionContent = sectionContent.replace("{{SUBSTRATE_VERSION}}", substrateVersion);
1880
+ if (!sectionContent.endsWith("\n")) sectionContent += "\n";
1881
+ const devNotesSection = buildStackAwareDevNotes(profile ?? null);
1882
+ let existingContent = "";
1883
+ let fileExists$1 = false;
1884
+ try {
1885
+ existingContent = await readFile(agentsMdPath, "utf8");
1886
+ fileExists$1 = true;
1887
+ } catch {}
1888
+ let newContent;
1889
+ if (!fileExists$1) newContent = devNotesSection ? devNotesSection + "\n\n" + sectionContent : sectionContent;
1890
+ else if (existingContent.includes(CLAUDE_MD_START_MARKER)) newContent = existingContent.replace(new RegExp(`${CLAUDE_MD_START_MARKER.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${CLAUDE_MD_END_MARKER.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), sectionContent.trimEnd());
1891
+ else {
1892
+ const separator = existingContent.endsWith("\n") ? "\n" : "\n\n";
1893
+ newContent = existingContent + separator + sectionContent;
1894
+ }
1895
+ await writeFile(agentsMdPath, newContent, "utf8");
1896
+ logger$15.info({ agentsMdPath }, "Wrote substrate section to AGENTS.md");
1897
+ }
1898
+ async function scaffoldGeminiMd(projectRoot, profile) {
1899
+ const geminiMdPath = join(projectRoot, "GEMINI.md");
1900
+ const pkgRoot = findPackageRoot(__dirname);
1901
+ const templateName = "gemini-md-substrate-section.md";
1902
+ let templatePath = join(pkgRoot, "dist", "cli", "templates", templateName);
1903
+ if (!existsSync$1(templatePath)) templatePath = join(pkgRoot, "src", "cli", "templates", templateName);
1904
+ let sectionContent;
1905
+ try {
1906
+ sectionContent = await readFile(templatePath, "utf8");
1907
+ } catch {
1908
+ logger$15.warn({ templatePath }, "GEMINI.md substrate section template not found; skipping");
1909
+ return;
1910
+ }
1911
+ const substrateVersion = readSubstrateVersion(pkgRoot);
1912
+ sectionContent = sectionContent.replace("{{SUBSTRATE_VERSION}}", substrateVersion);
1913
+ if (!sectionContent.endsWith("\n")) sectionContent += "\n";
1914
+ const devNotesSection = buildStackAwareDevNotes(profile ?? null);
1915
+ let existingContent = "";
1916
+ let fileExists$1 = false;
1917
+ try {
1918
+ existingContent = await readFile(geminiMdPath, "utf8");
1919
+ fileExists$1 = true;
1920
+ } catch {}
1921
+ let newContent;
1922
+ if (!fileExists$1) newContent = devNotesSection ? devNotesSection + "\n\n" + sectionContent : sectionContent;
1923
+ else if (existingContent.includes(CLAUDE_MD_START_MARKER)) newContent = existingContent.replace(new RegExp(`${CLAUDE_MD_START_MARKER.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${CLAUDE_MD_END_MARKER.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), sectionContent.trimEnd());
1924
+ else {
1925
+ const separator = existingContent.endsWith("\n") ? "\n" : "\n\n";
1926
+ newContent = existingContent + separator + sectionContent;
1927
+ }
1928
+ await writeFile(geminiMdPath, newContent, "utf8");
1929
+ logger$15.info({ geminiMdPath }, "Wrote substrate section to GEMINI.md");
1930
+ }
1865
1931
  async function scaffoldStatuslineScript(projectRoot) {
1866
1932
  const pkgRoot = findPackageRoot(__dirname);
1867
1933
  const templateName = "statusline.sh";
@@ -2441,6 +2507,8 @@ async function runInitAction(options) {
2441
2507
  await initSchema(dbAdapter);
2442
2508
  await dbAdapter.close();
2443
2509
  await scaffoldClaudeMd(projectRoot, detectedProfile);
2510
+ await scaffoldAgentsMd(projectRoot, detectedProfile);
2511
+ await scaffoldGeminiMd(projectRoot, detectedProfile);
2444
2512
  await scaffoldStatuslineScript(projectRoot);
2445
2513
  await scaffoldClaudeSettings(projectRoot);
2446
2514
  await scaffoldClaudeCommands(projectRoot, outputFormat);
@@ -2512,6 +2580,8 @@ async function runInitAction(options) {
2512
2580
  }
2513
2581
  process.stdout.write(` Scaffolded:\n`);
2514
2582
  process.stdout.write(` CLAUDE.md pipeline instructions for Claude Code\n`);
2583
+ process.stdout.write(` AGENTS.md pipeline instructions for Codex CLI\n`);
2584
+ process.stdout.write(` GEMINI.md pipeline instructions for Gemini CLI\n`);
2515
2585
  process.stdout.write(` .claude/commands/ /substrate-run, /substrate-supervisor, /substrate-metrics\n`);
2516
2586
  process.stdout.write(` .substrate/ config, database, routing policy\n`);
2517
2587
  if (doltInitialized) process.stdout.write(`✓ Dolt state store initialized at .substrate/state/\n`);
@@ -4952,7 +5022,7 @@ async function runSupervisorAction(options, deps = {}) {
4952
5022
  await initSchema(expAdapter);
4953
5023
  const { runRunAction: runPipeline } = await import(
4954
5024
  /* @vite-ignore */
4955
- "../run-zENUXBXk.js"
5025
+ "../run-d6XoJl27.js"
4956
5026
  );
4957
5027
  const runStoryFn = async (opts) => {
4958
5028
  const exitCode = await runPipeline({
@@ -0,0 +1,63 @@
1
+ <!-- substrate:start -->
2
+ <!-- substrate:version={{SUBSTRATE_VERSION}} -->
3
+ ## Substrate Pipeline
4
+
5
+ This project uses Substrate for automated implementation pipelines. When asked to implement, build, or run the pipeline, go straight to running substrate. Do not explore the codebase, read source files, or plan the implementation yourself. Substrate orchestrates sub-agents that handle all of that.
6
+
7
+ ### Running the Pipeline
8
+
9
+ Substrate auto-detects which pipeline phase to start from (analysis, planning, solutioning, implementation) and auto-discovers pending stories.
10
+
11
+ ```
12
+ substrate run --events
13
+ ```
14
+
15
+ To target specific stories:
16
+ ```
17
+ substrate run --events --stories 1-1,1-2,1-3
18
+ ```
19
+
20
+ If substrate needs input it can't auto-detect (e.g., a project concept for analysis), it will exit with a clear error message telling you what to provide.
21
+
22
+ Scope warning: Without `--stories`, substrate auto-discovers ALL pending stories across ALL epics and may dispatch 30+ stories at once. For controlled runs, always specify story keys explicitly with `--stories`.
23
+
24
+ Execution rules:
25
+ - Pipeline runs take 5-40 minutes. Use long timeouts.
26
+ - Never pipe substrate output to head, tail, grep, or any command that may close the pipe early.
27
+ - For full event protocol and command reference: `substrate run --help-agent`
28
+
29
+ ### Monitoring
30
+
31
+ Poll status periodically (every 60-90s):
32
+ ```
33
+ substrate status --output-format json
34
+ ```
35
+
36
+ Check process health:
37
+ ```
38
+ substrate health --output-format json
39
+ ```
40
+
41
+ ### After Pipeline Completes
42
+
43
+ 1. Summarize results: X succeeded, Y failed, Z escalated
44
+ 2. Check metrics: `substrate metrics --output-format json`
45
+
46
+ ### Handling Escalations
47
+
48
+ - On story escalation: read the flagged files and issues, propose a fix, ask the user before applying
49
+ - On minor fix verdict (NEEDS_MINOR_FIXES): offer to fix automatically
50
+ - On build verification failure: read the build output, diagnose the error, propose a fix
51
+ - Never re-run a failed story without explicit user confirmation
52
+
53
+ ### Key Commands
54
+
55
+ | Command | Purpose |
56
+ |---|---|
57
+ | `substrate run --events` | Run pipeline with NDJSON event stream |
58
+ | `substrate status --output-format json` | Poll current pipeline state |
59
+ | `substrate health --output-format json` | Check process health |
60
+ | `substrate metrics --output-format json` | View historical run metrics |
61
+ | `substrate resume` | Resume an interrupted pipeline run |
62
+ | `substrate run --help-agent` | Full agent instruction reference |
63
+ <!-- substrate:end -->
@@ -0,0 +1,63 @@
1
+ <!-- substrate:start -->
2
+ <!-- substrate:version={{SUBSTRATE_VERSION}} -->
3
+ ## Substrate Pipeline
4
+
5
+ This project uses Substrate for automated implementation pipelines. When asked to implement, build, or run the pipeline, go straight to running substrate. Do not explore the codebase, read source files, or plan the implementation yourself. Substrate orchestrates sub-agents that handle all of that.
6
+
7
+ ### Running the Pipeline
8
+
9
+ Substrate auto-detects which pipeline phase to start from (analysis, planning, solutioning, implementation) and auto-discovers pending stories.
10
+
11
+ ```
12
+ substrate run --events
13
+ ```
14
+
15
+ To target specific stories:
16
+ ```
17
+ substrate run --events --stories 1-1,1-2,1-3
18
+ ```
19
+
20
+ If substrate needs input it can't auto-detect (e.g., a project concept for analysis), it will exit with a clear error message telling you what to provide.
21
+
22
+ Scope warning: Without `--stories`, substrate auto-discovers ALL pending stories across ALL epics and may dispatch 30+ stories at once. For controlled runs, always specify story keys explicitly with `--stories`.
23
+
24
+ Execution rules:
25
+ - Pipeline runs take 5-40 minutes. Use long timeouts.
26
+ - Never pipe substrate output to head, tail, grep, or any command that may close the pipe early.
27
+ - For full event protocol and command reference: `substrate run --help-agent`
28
+
29
+ ### Monitoring
30
+
31
+ Poll status periodically (every 60-90s):
32
+ ```
33
+ substrate status --output-format json
34
+ ```
35
+
36
+ Check process health:
37
+ ```
38
+ substrate health --output-format json
39
+ ```
40
+
41
+ ### After Pipeline Completes
42
+
43
+ 1. Summarize results: X succeeded, Y failed, Z escalated
44
+ 2. Check metrics: `substrate metrics --output-format json`
45
+
46
+ ### Handling Escalations
47
+
48
+ - On story escalation: read the flagged files and issues, propose a fix, ask the user before applying
49
+ - On minor fix verdict (NEEDS_MINOR_FIXES): offer to fix automatically
50
+ - On build verification failure: read the build output, diagnose the error, propose a fix
51
+ - Never re-run a failed story without explicit user confirmation
52
+
53
+ ### Key Commands
54
+
55
+ | Command | Purpose |
56
+ |---|---|
57
+ | `substrate run --events` | Run pipeline with NDJSON event stream |
58
+ | `substrate status --output-format json` | Poll current pipeline state |
59
+ | `substrate health --output-format json` | Check process health |
60
+ | `substrate metrics --output-format json` | View historical run metrics |
61
+ | `substrate resume` | Resume an interrupted pipeline run |
62
+ | `substrate run --help-agent` | Full agent instruction reference |
63
+ <!-- substrate:end -->
@@ -6213,6 +6213,29 @@ async function getGitDiffStatSummary(workingDirectory = process.cwd()) {
6213
6213
  ], workingDirectory, "git-diff-stat");
6214
6214
  }
6215
6215
  /**
6216
+ * Capture the diff between two commits (e.g., baseline..HEAD).
6217
+ * Used when the dev-story agent committed its work, making `git diff HEAD`
6218
+ * empty. This shows what was actually changed.
6219
+ *
6220
+ * @param baseCommit - Starting commit SHA
6221
+ * @param endCommit - Ending commit SHA (defaults to 'HEAD')
6222
+ * @param workingDirectory - Directory to run git in
6223
+ * @returns The diff string, or '' on error
6224
+ */
6225
+ async function getGitDiffBetweenCommits(baseCommit, endCommit = "HEAD", workingDirectory = process.cwd()) {
6226
+ return runGitCommand(["diff", `${baseCommit}..${endCommit}`], workingDirectory, "git-diff-commits");
6227
+ }
6228
+ /**
6229
+ * Capture the stat-only diff between two commits.
6230
+ */
6231
+ async function getGitDiffStatBetweenCommits(baseCommit, endCommit = "HEAD", workingDirectory = process.cwd()) {
6232
+ return runGitCommand([
6233
+ "diff",
6234
+ "--stat",
6235
+ `${baseCommit}..${endCommit}`
6236
+ ], workingDirectory, "git-diff-stat-commits");
6237
+ }
6238
+ /**
6216
6239
  * Capture the git diff scoped to specific files.
6217
6240
  *
6218
6241
  * Runs `git diff HEAD -- file1.ts file2.ts ...` to produce a diff
@@ -7738,7 +7761,7 @@ async function countTestMetrics(filesModified, cwd) {
7738
7761
  * @returns Promise resolving to CodeReviewResult
7739
7762
  */
7740
7763
  async function runCodeReview(deps, params) {
7741
- const { storyKey, storyFilePath, workingDirectory, pipelineRunId, filesModified, previousIssues, buildPassed } = params;
7764
+ const { storyKey, storyFilePath, workingDirectory, pipelineRunId, filesModified, previousIssues, buildPassed, baselineCommit } = params;
7742
7765
  const cwd = workingDirectory ?? process.cwd();
7743
7766
  logger$14.debug({
7744
7767
  storyKey,
@@ -7814,6 +7837,22 @@ async function runCodeReview(deps, params) {
7814
7837
  gitDiffContent = await getGitDiffStatSummary(cwd);
7815
7838
  }
7816
7839
  }
7840
+ if (gitDiffContent.trim().length === 0 && baselineCommit) {
7841
+ logger$14.info({
7842
+ storyKey,
7843
+ baselineCommit
7844
+ }, "Working tree diff empty — diffing against baseline commit");
7845
+ const commitDiff = await getGitDiffBetweenCommits(baselineCommit, "HEAD", cwd);
7846
+ const commitTotal = nonDiffTokens + countTokens(commitDiff);
7847
+ if (commitDiff.trim().length > 0) if (commitTotal <= TOKEN_CEILING) gitDiffContent = commitDiff;
7848
+ else {
7849
+ logger$14.warn({
7850
+ estimatedTotal: commitTotal,
7851
+ ceiling: TOKEN_CEILING
7852
+ }, "Baseline..HEAD diff exceeds token ceiling — using stat-only summary");
7853
+ gitDiffContent = await getGitDiffStatBetweenCommits(baselineCommit, "HEAD", cwd);
7854
+ }
7855
+ }
7817
7856
  if (gitDiffContent.trim().length === 0) {
7818
7857
  logger$14.info({ storyKey }, "Empty git diff — skipping review with SHIP_IT");
7819
7858
  return {
@@ -12974,11 +13013,26 @@ function createImplementationOrchestrator(deps) {
12974
13013
  }).trim();
12975
13014
  hasNewCommits = currentHead !== baselineHeadSha;
12976
13015
  } catch {}
12977
- if (hasNewCommits) logger$24.info({
12978
- storyKey,
12979
- baselineHeadSha
12980
- }, "Working tree clean but new commits detected since dispatch — skipping zero-diff escalation");
12981
- else {
13016
+ if (hasNewCommits && baselineHeadSha) {
13017
+ try {
13018
+ const committedFiles = execSync(`git diff --name-only ${baselineHeadSha}..HEAD`, {
13019
+ cwd: projectRoot ?? process.cwd(),
13020
+ encoding: "utf-8",
13021
+ timeout: 5e3,
13022
+ stdio: [
13023
+ "ignore",
13024
+ "pipe",
13025
+ "pipe"
13026
+ ]
13027
+ }).trim();
13028
+ if (committedFiles.length > 0) gitDiffFiles = committedFiles.split("\n").filter(Boolean);
13029
+ } catch {}
13030
+ logger$24.info({
13031
+ storyKey,
13032
+ baselineHeadSha,
13033
+ committedFileCount: gitDiffFiles?.length ?? 0
13034
+ }, "Working tree clean but new commits detected since dispatch — skipping zero-diff escalation");
13035
+ } else {
12982
13036
  logger$24.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes and no new commits");
12983
13037
  eventBus.emit("orchestrator:zero-diff-escalation", {
12984
13038
  storyKey,
@@ -13367,7 +13421,8 @@ function createImplementationOrchestrator(deps) {
13367
13421
  pipelineRunId: config.pipelineRunId,
13368
13422
  filesModified: devFilesModified,
13369
13423
  buildPassed: _buildPassed,
13370
- ...previousIssueList.length > 0 ? { previousIssues: previousIssueList } : {}
13424
+ ...previousIssueList.length > 0 ? { previousIssues: previousIssueList } : {},
13425
+ ...baselineHeadSha ? { baselineCommit: baselineHeadSha } : {}
13371
13426
  });
13372
13427
  }
13373
13428
  if (config.pipelineRunId !== void 0 && reviewResult.tokenUsage !== void 0) try {
@@ -43021,4 +43076,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
43021
43076
 
43022
43077
  //#endregion
43023
43078
  export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict, wireNdjsonEmitter };
43024
- //# sourceMappingURL=run-DI5SzKf5.js.map
43079
+ //# sourceMappingURL=run-BbwMFNWQ.js.map
@@ -2,7 +2,7 @@ import "./health-DrZiqv4h.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./helpers-CElYrONe.js";
4
4
  import "./dist-sNh9XQ6V.js";
5
- import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-DI5SzKf5.js";
5
+ import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-BbwMFNWQ.js";
6
6
  import "./routing-CcBOCuC9.js";
7
7
  import "./decisions-C0pz9Clx.js";
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.19.49",
3
+ "version": "0.19.51",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",