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
|
|
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-
|
|
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-
|
|
2845
|
+
"../run-CUGB4FQx.js"
|
|
2846
2846
|
);
|
|
2847
2847
|
const runStoryFn = async (opts) => {
|
|
2848
2848
|
const exitCode = await runPipeline({
|
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 >=
|
|
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
|
-
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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-
|
|
11511
|
+
//# sourceMappingURL=run-mFmS2pw6.js.map
|
package/package.json
CHANGED
|
@@ -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
|
|
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
|