substrate-ai 0.2.17 → 0.2.19

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Substrate
6
6
 
7
- Substrate is an autonomous software development pipeline you operate from Claude Code. Describe your project, and Substrate coordinates multiple AI coding agents (Claude Code, Codex, Gemini CLI) working in parallel across isolated branches from concept through implementation and code review. A built-in supervisor watches for stalls, auto-recovers, and after each run analyzes what happened and experiments with improvements closing the loop automatically.
7
+ Most multi-agent coding tools help you run AI sessions in parallel — but leave planning, quality control, and learning up to you. Substrate is different: it packages structured planning methodology, multi-agent parallel execution, automated code review cycles, and self-improvement into a single pipeline. Describe your project concept, and Substrate takes it from analysis through implementation and review — coordinating multiple AI coding agents (Claude Code, Codex, Gemini CLI) across isolated worktree branches while a supervisor watches for stalls, auto-recovers, and after each run experiments with improvements to close the loop automatically.
8
8
 
9
9
  Unlike API-based orchestrators, Substrate routes work through the CLI tools you already have installed, maximizing your existing AI subscriptions before falling back to pay-per-token billing. Runs are persistent and resumable with full cost visibility across every provider.
10
10
 
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-CLpXJNQ8.js";
2
+ import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-mFmS2pw6.js";
3
3
  import { createLogger, deepMask } from "../logger-D2fS2ccL.js";
4
4
  import { AdapterRegistry, createEventBus } from "../event-bus-BMxhfxfT.js";
5
5
  import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "../version-manager-impl-CZ6KF1Ds.js";
@@ -2842,7 +2842,7 @@ async function runSupervisorAction(options, deps = {}) {
2842
2842
  const expDb = expDbWrapper.db;
2843
2843
  const { runRunAction: runPipeline } = await import(
2844
2844
  /* @vite-ignore */
2845
- "../run-DPrhfU2T.js"
2845
+ "../run-CUGB4FQx.js"
2846
2846
  );
2847
2847
  const runStoryFn = async (opts) => {
2848
2848
  const exitCode = await runPipeline({
@@ -1,4 +1,4 @@
1
- import { registerRunCommand, runRunAction } from "./run-CLpXJNQ8.js";
1
+ import { registerRunCommand, runRunAction } from "./run-mFmS2pw6.js";
2
2
  import "./logger-D2fS2ccL.js";
3
3
  import "./event-bus-BMxhfxfT.js";
4
4
  import "./decisions-Dq4cAA2L.js";
@@ -2806,7 +2806,24 @@ function extractYamlBlock(output) {
2806
2806
  if (!output || output.trim() === "") return null;
2807
2807
  const fencedResult = extractLastFencedYaml(output);
2808
2808
  if (fencedResult !== null) return fencedResult;
2809
- return extractUnfencedYaml(output);
2809
+ const stripped = stripTrailingFence(output);
2810
+ return extractUnfencedYaml(stripped);
2811
+ }
2812
+ /**
2813
+ * Strip a trailing markdown fence that wraps the entire remaining output.
2814
+ * Handles: "```yaml\nverdict: ...\n```" → "verdict: ..."
2815
+ */
2816
+ function stripTrailingFence(output) {
2817
+ const trimmed = output.trimEnd();
2818
+ if (!trimmed.endsWith("```")) return output;
2819
+ const body = trimmed.slice(0, -3).trimEnd();
2820
+ const lastOpen = body.lastIndexOf("```");
2821
+ if (lastOpen === -1) return output;
2822
+ const beforeFence = body.slice(0, lastOpen);
2823
+ const fenceLine = body.slice(lastOpen);
2824
+ if (beforeFence.length > 0 && !beforeFence.endsWith("\n")) return output;
2825
+ const afterOpen = fenceLine.replace(/^```(?:yaml)?\s*\n?/, "");
2826
+ return beforeFence + afterOpen;
2810
2827
  }
2811
2828
  /**
2812
2829
  * Find all fenced YAML blocks (```yaml...``` or ```...```) and return the last one.
@@ -2907,7 +2924,7 @@ const MIN_FREE_MEMORY_BYTES = (() => {
2907
2924
  const parsed = parseInt(envMB, 10);
2908
2925
  if (!isNaN(parsed) && parsed >= 0) return parsed * 1024 * 1024;
2909
2926
  }
2910
- return 512 * 1024 * 1024;
2927
+ return 256 * 1024 * 1024;
2911
2928
  })();
2912
2929
  const MEMORY_PRESSURE_POLL_MS = 1e4;
2913
2930
  /**
@@ -2920,7 +2937,12 @@ const MEMORY_PRESSURE_POLL_MS = 1e4;
2920
2937
  *
2921
2938
  * New approach:
2922
2939
  * 1. Check kern.memorystatus_vm_pressure_level — the kernel's own assessment.
2923
- * Level >= 2 (warn/critical) means the system is already pressured.
2940
+ * Level 4 (critical) = hard gate, return 0.
2941
+ * Level 2 (warn) = halve the vm_stat estimate as a conservative signal.
2942
+ * Level 1 (normal) = trust vm_stat as-is.
2943
+ * Note: level 2 fires frequently on macOS when the compressor is active,
2944
+ * even with gigabytes of reclaimable memory. Hard-gating at 2 caused
2945
+ * false stalls on 24GB+ machines with >50% free RAM.
2924
2946
  * 2. Use a conservative page calculation: free + purgeable + speculative.
2925
2947
  * These categories are truly reclaimable without I/O or decompression.
2926
2948
  * Inactive pages are excluded because they may require disk I/O,
@@ -2928,13 +2950,14 @@ const MEMORY_PRESSURE_POLL_MS = 1e4;
2928
2950
  */
2929
2951
  function getAvailableMemory() {
2930
2952
  if (platform() === "darwin") {
2953
+ let pressureLevel = 0;
2931
2954
  try {
2932
- const pressureLevel = parseInt(execSync("sysctl -n kern.memorystatus_vm_pressure_level", {
2955
+ pressureLevel = parseInt(execSync("sysctl -n kern.memorystatus_vm_pressure_level", {
2933
2956
  timeout: 1e3,
2934
2957
  encoding: "utf-8"
2935
2958
  }).trim(), 10);
2936
- if (pressureLevel >= 2) {
2937
- logger$13.warn({ pressureLevel }, "macOS kernel reports memory pressure");
2959
+ if (pressureLevel >= 4) {
2960
+ logger$13.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
2938
2961
  return 0;
2939
2962
  }
2940
2963
  } catch {}
@@ -2947,7 +2970,15 @@ function getAvailableMemory() {
2947
2970
  const free = parseInt(vmstat.match(/Pages free:\s+(\d+)/)?.[1] ?? "0", 10);
2948
2971
  const purgeable = parseInt(vmstat.match(/Pages purgeable:\s+(\d+)/)?.[1] ?? "0", 10);
2949
2972
  const speculative = parseInt(vmstat.match(/Pages speculative:\s+(\d+)/)?.[1] ?? "0", 10);
2950
- return (free + purgeable + speculative) * pageSize;
2973
+ const available = (free + purgeable + speculative) * pageSize;
2974
+ if (pressureLevel >= 2) {
2975
+ logger$13.warn({
2976
+ pressureLevel,
2977
+ availableBeforeDiscount: available
2978
+ }, "macOS kernel reports memory pressure — discounting estimate");
2979
+ return Math.floor(available / 2);
2980
+ }
2981
+ return available;
2951
2982
  } catch {
2952
2983
  return freemem();
2953
2984
  }
@@ -4458,10 +4489,13 @@ const logger$8 = createLogger("compiled-workflows:code-review");
4458
4489
  const TOKEN_CEILING = 1e5;
4459
4490
  /**
4460
4491
  * Default fallback result when dispatch fails or times out.
4492
+ * Uses NEEDS_MINOR_FIXES (not NEEDS_MAJOR_REWORK) so a parse/schema failure
4493
+ * doesn't trigger a full rework cycle. The orchestrator's phantom-review
4494
+ * detection handles retries, and escalation kicks in at max review cycles.
4461
4495
  */
4462
4496
  function defaultFailResult(error, tokenUsage) {
4463
4497
  return {
4464
- verdict: "NEEDS_MAJOR_REWORK",
4498
+ verdict: "NEEDS_MINOR_FIXES",
4465
4499
  issues: 0,
4466
4500
  issue_list: [],
4467
4501
  error,
@@ -4649,7 +4683,7 @@ async function runCodeReview(deps, params) {
4649
4683
  details
4650
4684
  }, "Code-review output schema validation failed");
4651
4685
  return {
4652
- verdict: "NEEDS_MAJOR_REWORK",
4686
+ verdict: "NEEDS_MINOR_FIXES",
4653
4687
  issues: 0,
4654
4688
  issue_list: [],
4655
4689
  error: "schema_validation_failed",
@@ -4666,7 +4700,7 @@ async function runCodeReview(deps, params) {
4666
4700
  details
4667
4701
  }, "Code-review output failed schema validation");
4668
4702
  return {
4669
- verdict: "NEEDS_MAJOR_REWORK",
4703
+ verdict: "NEEDS_MINOR_FIXES",
4670
4704
  issues: 0,
4671
4705
  issue_list: [],
4672
4706
  error: "schema_validation_failed",
@@ -5575,6 +5609,7 @@ function createImplementationOrchestrator(deps) {
5575
5609
  storyKey,
5576
5610
  storyFilePath
5577
5611
  }, "Found existing story file — skipping create-story");
5612
+ endPhase(storyKey, "create-story");
5578
5613
  eventBus.emit("orchestrator:story-phase-complete", {
5579
5614
  storyKey,
5580
5615
  phase: "IN_STORY_CREATION",
@@ -11473,4 +11508,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
11473
11508
 
11474
11509
  //#endregion
11475
11510
  export { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
11476
- //# sourceMappingURL=run-CLpXJNQ8.js.map
11511
+ //# sourceMappingURL=run-mFmS2pw6.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@ Adversarial code review. Find what's wrong. Validate story claims against actual
45
45
 
46
46
  ## Output Contract
47
47
 
48
- After completing the review, emit ONLY this YAML block — no other text:
48
+ After completing the review, emit ONLY raw YAML — no markdown fences, no ``` wrappers, no other text:
49
49
 
50
50
  ```yaml
51
51
  verdict: SHIP_IT