substrate-ai 0.2.24 → 0.2.26
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 +3 -3
- package/dist/{event-bus-CAvDMst7.js → helpers-DljGJnFF.js} +79 -2
- package/dist/index.d.ts +63 -3
- package/dist/index.js +1 -78
- package/dist/run-BXKRGSeL.js +7 -0
- package/dist/{run-CT8B9gG9.js → run-CEtHPG4I.js} +951 -534
- package/package.json +1 -1
- package/dist/run-DoxsPIlD.js +0 -7
package/dist/cli/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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-CEtHPG4I.js";
|
|
3
3
|
import { createLogger, deepMask } from "../logger-D2fS2ccL.js";
|
|
4
4
|
import { AdapterRegistry, ConfigError, ConfigIncompatibleFormatError } from "../errors-CswS7Mzg.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";
|
|
6
|
-
import { createEventBus } from "../
|
|
6
|
+
import { createEventBus } from "../helpers-DljGJnFF.js";
|
|
7
7
|
import { addTokenUsage, createDecision, createPipelineRun, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-Dq4cAA2L.js";
|
|
8
8
|
import { ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-CnMlvWqc.js";
|
|
9
9
|
import { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, verifyGitVersion } from "../git-utils-CtmrZrHS.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-BXKRGSeL.js"
|
|
2846
2846
|
);
|
|
2847
2847
|
const runStoryFn = async (opts) => {
|
|
2848
2848
|
const exitCode = await runPipeline({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as readline from "node:readline";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
3
4
|
|
|
4
5
|
//#region src/tui/ansi.ts
|
|
5
6
|
/**
|
|
@@ -730,5 +731,81 @@ function createEventBus() {
|
|
|
730
731
|
}
|
|
731
732
|
|
|
732
733
|
//#endregion
|
|
733
|
-
|
|
734
|
-
|
|
734
|
+
//#region src/utils/helpers.ts
|
|
735
|
+
/**
|
|
736
|
+
* Sleep for a given number of milliseconds
|
|
737
|
+
* @param ms - Milliseconds to sleep
|
|
738
|
+
*/
|
|
739
|
+
function sleep(ms) {
|
|
740
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Assert that a value is defined (not null or undefined)
|
|
744
|
+
* @param value - Value to check
|
|
745
|
+
* @param message - Error message if undefined
|
|
746
|
+
*/
|
|
747
|
+
function assertDefined(value, message) {
|
|
748
|
+
if (value === null || value === void 0) throw new Error(message);
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Format a duration in milliseconds to a human-readable string
|
|
752
|
+
* @param ms - Duration in milliseconds
|
|
753
|
+
*/
|
|
754
|
+
function formatDuration(ms) {
|
|
755
|
+
if (ms < 1e3) return `${String(ms)}ms`;
|
|
756
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
757
|
+
if (ms < 36e5) {
|
|
758
|
+
const minutes$1 = Math.floor(ms / 6e4);
|
|
759
|
+
const seconds = Math.floor(ms % 6e4 / 1e3);
|
|
760
|
+
return `${String(minutes$1)}m ${String(seconds)}s`;
|
|
761
|
+
}
|
|
762
|
+
const hours = Math.floor(ms / 36e5);
|
|
763
|
+
const minutes = Math.floor(ms % 36e5 / 6e4);
|
|
764
|
+
return `${String(hours)}h ${String(minutes)}m`;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Generate a unique identifier using crypto.randomUUID()
|
|
768
|
+
* @param prefix - Optional prefix for the ID
|
|
769
|
+
*/
|
|
770
|
+
function generateId(prefix = "") {
|
|
771
|
+
const uuid = randomUUID();
|
|
772
|
+
return prefix ? `${prefix}-${uuid}` : uuid;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Deep clone an object using structuredClone.
|
|
776
|
+
* Supports most built-in types (Date, Map, Set, ArrayBuffer, etc.)
|
|
777
|
+
* but does NOT support functions, DOM nodes, or symbols as keys.
|
|
778
|
+
* @param obj - Object to clone
|
|
779
|
+
*/
|
|
780
|
+
function deepClone(obj) {
|
|
781
|
+
return structuredClone(obj);
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Check if a value is a plain object (not an array, Date, or other special object)
|
|
785
|
+
* @param value - Value to check
|
|
786
|
+
*/
|
|
787
|
+
function isPlainObject(value) {
|
|
788
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
789
|
+
const proto = Object.getPrototypeOf(value);
|
|
790
|
+
return proto === Object.prototype || proto === null;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Retry an async operation with exponential backoff
|
|
794
|
+
* @param fn - Async function to retry
|
|
795
|
+
* @param maxRetries - Maximum number of retries
|
|
796
|
+
* @param baseDelayMs - Base delay in milliseconds (doubles each retry)
|
|
797
|
+
*/
|
|
798
|
+
async function withRetry(fn, maxRetries = 3, baseDelayMs = 100) {
|
|
799
|
+
let lastError;
|
|
800
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
801
|
+
return await fn();
|
|
802
|
+
} catch (error) {
|
|
803
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
804
|
+
if (attempt < maxRetries) await sleep(baseDelayMs * Math.pow(2, attempt));
|
|
805
|
+
}
|
|
806
|
+
throw lastError ?? new Error("Operation failed after retries");
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
//#endregion
|
|
810
|
+
export { assertDefined, createEventBus, createTuiApp, deepClone, formatDuration, generateId, isPlainObject, isTuiCapable, printNonTtyWarning, sleep, withRetry };
|
|
811
|
+
//# sourceMappingURL=helpers-DljGJnFF.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -172,6 +172,44 @@ interface StoryEscalationEvent {
|
|
|
172
172
|
/** Issues list from the final review (may be empty) */
|
|
173
173
|
issues: EscalationIssue[];
|
|
174
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Emitted when a dev-story agent reported COMPLETE but git diff shows no
|
|
177
|
+
* file changes in the working tree (phantom completion — Story 24-1).
|
|
178
|
+
*/
|
|
179
|
+
interface StoryZeroDiffEscalationEvent {
|
|
180
|
+
type: 'story:zero-diff-escalation';
|
|
181
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
182
|
+
ts: string;
|
|
183
|
+
/** Story key (e.g., "10-1") */
|
|
184
|
+
storyKey: string;
|
|
185
|
+
/** Always "zero-diff-on-complete" */
|
|
186
|
+
reason: string;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Emitted when the build verification command exits with a non-zero code
|
|
190
|
+
* or times out, before code-review is dispatched (Story 24-2).
|
|
191
|
+
*/
|
|
192
|
+
interface StoryBuildVerificationFailedEvent {
|
|
193
|
+
type: 'story:build-verification-failed';
|
|
194
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
195
|
+
ts: string;
|
|
196
|
+
/** Story key (e.g., "24-2") */
|
|
197
|
+
storyKey: string;
|
|
198
|
+
/** Exit code from the build command (-1 for timeout) */
|
|
199
|
+
exitCode: number;
|
|
200
|
+
/** Combined stdout+stderr output, truncated to 2000 chars */
|
|
201
|
+
output: string;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Emitted when the build verification command exits with code 0 (Story 24-2).
|
|
205
|
+
*/
|
|
206
|
+
interface StoryBuildVerificationPassedEvent {
|
|
207
|
+
type: 'story:build-verification-passed';
|
|
208
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
209
|
+
ts: string;
|
|
210
|
+
/** Story key (e.g., "24-2") */
|
|
211
|
+
storyKey: string;
|
|
212
|
+
}
|
|
175
213
|
/**
|
|
176
214
|
* Emitted for non-fatal warnings during pipeline execution
|
|
177
215
|
* (e.g., token ceiling truncation, partial batch failures).
|
|
@@ -230,6 +268,10 @@ interface StoryStallEvent {
|
|
|
230
268
|
phase: string;
|
|
231
269
|
/** Milliseconds since the last progress event */
|
|
232
270
|
elapsed_ms: number;
|
|
271
|
+
/** PIDs of child processes at time of stall detection */
|
|
272
|
+
child_pids: number[];
|
|
273
|
+
/** Whether any child process was actively running (not zombie) */
|
|
274
|
+
child_active: boolean;
|
|
233
275
|
}
|
|
234
276
|
/**
|
|
235
277
|
* Emitted after each `getHealth()` call in the supervisor poll loop.
|
|
@@ -428,7 +470,7 @@ interface SupervisorExperimentErrorEvent {
|
|
|
428
470
|
* }
|
|
429
471
|
* ```
|
|
430
472
|
*/
|
|
431
|
-
type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | StoryPhaseEvent | StoryDoneEvent | StoryEscalationEvent | StoryWarnEvent | StoryLogEvent | PipelineHeartbeatEvent | StoryStallEvent | SupervisorPollEvent | SupervisorKillEvent | SupervisorRestartEvent | SupervisorAbortEvent | SupervisorSummaryEvent | SupervisorAnalysisCompleteEvent | SupervisorAnalysisErrorEvent | SupervisorExperimentStartEvent | SupervisorExperimentSkipEvent | SupervisorExperimentRecommendationsEvent | SupervisorExperimentCompleteEvent | SupervisorExperimentErrorEvent; //#endregion
|
|
473
|
+
type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | StoryPhaseEvent | StoryDoneEvent | StoryEscalationEvent | StoryWarnEvent | StoryLogEvent | PipelineHeartbeatEvent | StoryStallEvent | StoryZeroDiffEscalationEvent | StoryBuildVerificationFailedEvent | StoryBuildVerificationPassedEvent | SupervisorPollEvent | SupervisorKillEvent | SupervisorRestartEvent | SupervisorAbortEvent | SupervisorSummaryEvent | SupervisorAnalysisCompleteEvent | SupervisorAnalysisErrorEvent | SupervisorExperimentStartEvent | SupervisorExperimentSkipEvent | SupervisorExperimentRecommendationsEvent | SupervisorExperimentCompleteEvent | SupervisorExperimentErrorEvent; //#endregion
|
|
432
474
|
//#region src/core/errors.d.ts
|
|
433
475
|
|
|
434
476
|
/**
|
|
@@ -1014,6 +1056,11 @@ interface OrchestratorEvents {
|
|
|
1014
1056
|
storyKey: string;
|
|
1015
1057
|
msg: string;
|
|
1016
1058
|
};
|
|
1059
|
+
/** Zero-diff detection gate: dev-story reported COMPLETE but git diff is empty (Story 24-1) */
|
|
1060
|
+
'orchestrator:zero-diff-escalation': {
|
|
1061
|
+
storyKey: string;
|
|
1062
|
+
reason: string;
|
|
1063
|
+
};
|
|
1017
1064
|
/** Implementation orchestrator has finished all stories */
|
|
1018
1065
|
'orchestrator:complete': {
|
|
1019
1066
|
totalStories: number;
|
|
@@ -1038,8 +1085,10 @@ interface OrchestratorEvents {
|
|
|
1038
1085
|
storyKey: string;
|
|
1039
1086
|
phase: string;
|
|
1040
1087
|
elapsedMs: number;
|
|
1041
|
-
/**
|
|
1042
|
-
|
|
1088
|
+
/** PIDs of child processes at time of stall detection */
|
|
1089
|
+
childPids: number[];
|
|
1090
|
+
/** Whether any child process was actively running (not zombie) */
|
|
1091
|
+
childActive: boolean;
|
|
1043
1092
|
};
|
|
1044
1093
|
/** Readiness check has completed — emitted for all verdicts (READY, NEEDS_WORK, NOT_READY) */
|
|
1045
1094
|
'solutioning:readiness-check': {
|
|
@@ -1061,6 +1110,17 @@ interface OrchestratorEvents {
|
|
|
1061
1110
|
affected_items: string[];
|
|
1062
1111
|
}>;
|
|
1063
1112
|
};
|
|
1113
|
+
/** Build verification command failed with non-zero exit or timeout */
|
|
1114
|
+
'story:build-verification-failed': {
|
|
1115
|
+
storyKey: string;
|
|
1116
|
+
exitCode: number;
|
|
1117
|
+
/** Build output (stdout+stderr), truncated to 2000 chars */
|
|
1118
|
+
output: string;
|
|
1119
|
+
};
|
|
1120
|
+
/** Build verification command exited with code 0 */
|
|
1121
|
+
'story:build-verification-passed': {
|
|
1122
|
+
storyKey: string;
|
|
1123
|
+
};
|
|
1064
1124
|
}
|
|
1065
1125
|
|
|
1066
1126
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,84 +1,7 @@
|
|
|
1
1
|
import { childLogger, createLogger, logger } from "./logger-D2fS2ccL.js";
|
|
2
2
|
import { AdapterRegistry, AdtError, BudgetExceededError, ClaudeCodeAdapter, CodexCLIAdapter, ConfigError, ConfigIncompatibleFormatError, GeminiCLIAdapter, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError } from "./errors-CswS7Mzg.js";
|
|
3
|
-
import { createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning } from "./
|
|
4
|
-
import { randomUUID } from "crypto";
|
|
3
|
+
import { assertDefined, createEventBus, createTuiApp, deepClone, formatDuration, generateId, isPlainObject, isTuiCapable, printNonTtyWarning, sleep, withRetry } from "./helpers-DljGJnFF.js";
|
|
5
4
|
|
|
6
|
-
//#region src/utils/helpers.ts
|
|
7
|
-
/**
|
|
8
|
-
* Sleep for a given number of milliseconds
|
|
9
|
-
* @param ms - Milliseconds to sleep
|
|
10
|
-
*/
|
|
11
|
-
function sleep(ms) {
|
|
12
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Assert that a value is defined (not null or undefined)
|
|
16
|
-
* @param value - Value to check
|
|
17
|
-
* @param message - Error message if undefined
|
|
18
|
-
*/
|
|
19
|
-
function assertDefined(value, message) {
|
|
20
|
-
if (value === null || value === void 0) throw new Error(message);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Format a duration in milliseconds to a human-readable string
|
|
24
|
-
* @param ms - Duration in milliseconds
|
|
25
|
-
*/
|
|
26
|
-
function formatDuration(ms) {
|
|
27
|
-
if (ms < 1e3) return `${String(ms)}ms`;
|
|
28
|
-
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
29
|
-
if (ms < 36e5) {
|
|
30
|
-
const minutes$1 = Math.floor(ms / 6e4);
|
|
31
|
-
const seconds = Math.floor(ms % 6e4 / 1e3);
|
|
32
|
-
return `${String(minutes$1)}m ${String(seconds)}s`;
|
|
33
|
-
}
|
|
34
|
-
const hours = Math.floor(ms / 36e5);
|
|
35
|
-
const minutes = Math.floor(ms % 36e5 / 6e4);
|
|
36
|
-
return `${String(hours)}h ${String(minutes)}m`;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Generate a unique identifier using crypto.randomUUID()
|
|
40
|
-
* @param prefix - Optional prefix for the ID
|
|
41
|
-
*/
|
|
42
|
-
function generateId(prefix = "") {
|
|
43
|
-
const uuid = randomUUID();
|
|
44
|
-
return prefix ? `${prefix}-${uuid}` : uuid;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Deep clone an object using structuredClone.
|
|
48
|
-
* Supports most built-in types (Date, Map, Set, ArrayBuffer, etc.)
|
|
49
|
-
* but does NOT support functions, DOM nodes, or symbols as keys.
|
|
50
|
-
* @param obj - Object to clone
|
|
51
|
-
*/
|
|
52
|
-
function deepClone(obj) {
|
|
53
|
-
return structuredClone(obj);
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Check if a value is a plain object (not an array, Date, or other special object)
|
|
57
|
-
* @param value - Value to check
|
|
58
|
-
*/
|
|
59
|
-
function isPlainObject(value) {
|
|
60
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
61
|
-
const proto = Object.getPrototypeOf(value);
|
|
62
|
-
return proto === Object.prototype || proto === null;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Retry an async operation with exponential backoff
|
|
66
|
-
* @param fn - Async function to retry
|
|
67
|
-
* @param maxRetries - Maximum number of retries
|
|
68
|
-
* @param baseDelayMs - Base delay in milliseconds (doubles each retry)
|
|
69
|
-
*/
|
|
70
|
-
async function withRetry(fn, maxRetries = 3, baseDelayMs = 100) {
|
|
71
|
-
let lastError;
|
|
72
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
73
|
-
return await fn();
|
|
74
|
-
} catch (error) {
|
|
75
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
76
|
-
if (attempt < maxRetries) await sleep(baseDelayMs * Math.pow(2, attempt));
|
|
77
|
-
}
|
|
78
|
-
throw lastError ?? new Error("Operation failed after retries");
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
//#endregion
|
|
82
5
|
//#region src/core/di.ts
|
|
83
6
|
/**
|
|
84
7
|
* Simple service registry — stores named service instances for DI resolution.
|