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 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-CT8B9gG9.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-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 "../event-bus-CAvDMst7.js";
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-DoxsPIlD.js"
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
- export { createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning };
734
- //# sourceMappingURL=event-bus-CAvDMst7.js.map
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
- /** PID of the stalled child process, or null if not tracked */
1042
- childPid: number | null;
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 "./event-bus-CAvDMst7.js";
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.
@@ -0,0 +1,7 @@
1
+ import { registerRunCommand, runRunAction } from "./run-CEtHPG4I.js";
2
+ import "./logger-D2fS2ccL.js";
3
+ import "./helpers-DljGJnFF.js";
4
+ import "./decisions-Dq4cAA2L.js";
5
+ import "./operational-CnMlvWqc.js";
6
+
7
+ export { runRunAction };