substrate-ai 0.1.33 → 0.2.1
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 +33 -63
- package/dist/cli/index.js +4586 -18398
- package/dist/cli/templates/claude-md-substrate-section.md +3 -3
- package/dist/decisions-BBLMsN_c.js +336 -0
- package/dist/decisions-WIsicZiG.js +3 -0
- package/dist/errors-BPqtzQ4U.js +111 -0
- package/dist/event-bus-J-bw-pkp.js +1549 -0
- package/dist/experimenter-BSu2ie3J.js +484 -0
- package/dist/git-utils-BtI5eNoN.js +365 -0
- package/dist/index.d.ts +89 -137
- package/dist/index.js +5 -305
- package/dist/logger-C6n1g8uP.js +119 -0
- package/dist/metrics-BSg8VIHd.js +184 -0
- package/dist/run-CRmhkcwN.js +7 -0
- package/dist/run-DlOWhkIF.js +10725 -0
- package/dist/{upgrade-CS2i2Qy5.js → upgrade-BjYVeC6G.js} +2 -3
- package/dist/{upgrade-CIAel_WS.js → upgrade-rV26kdh3.js} +2 -2
- package/dist/version-manager-impl-9N_519Ey.js +3 -0
- package/dist/{version-manager-impl-CcCP8PzG.js → version-manager-impl-BpVx2DkY.js} +101 -3
- package/package.json +1 -1
- package/packs/bmad/prompts/ux-step-3-journeys.md +3 -3
- package/dist/app-CY3MaJtP.js +0 -6342
- package/dist/cli/templates/parallel.yaml +0 -72
- package/dist/cli/templates/research-then-implement.yaml +0 -103
- package/dist/cli/templates/review-cycle.yaml +0 -91
- package/dist/cli/templates/sequential.yaml +0 -68
- package/dist/config-schema-C9tTMcm1.js +0 -102
- package/dist/version-manager-impl-LhLAyrCL.js +0 -4
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { createLogger } from "./logger-C6n1g8uP.js";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { access, readdir } from "node:fs/promises";
|
|
5
|
+
|
|
6
|
+
//#region src/modules/git-worktree/git-utils.ts
|
|
7
|
+
const logger = createLogger("git-utils");
|
|
8
|
+
const MIN_GIT_MAJOR = 2;
|
|
9
|
+
const MIN_GIT_MINOR = 20;
|
|
10
|
+
/**
|
|
11
|
+
* Spawn a git subprocess with the given args.
|
|
12
|
+
*
|
|
13
|
+
* @param args - Arguments to pass to git (e.g., ['worktree', 'add', ...])
|
|
14
|
+
* @param options - Optional spawn options (cwd, env)
|
|
15
|
+
* @returns - Object with stdout, stderr, and exit code
|
|
16
|
+
*/
|
|
17
|
+
function spawnGit(args, options) {
|
|
18
|
+
return new Promise((resolve$1) => {
|
|
19
|
+
logger.debug({
|
|
20
|
+
args,
|
|
21
|
+
cwd: options?.cwd
|
|
22
|
+
}, "spawnGit");
|
|
23
|
+
const proc = spawn("git", args, {
|
|
24
|
+
cwd: options?.cwd,
|
|
25
|
+
env: options?.env ?? process.env,
|
|
26
|
+
stdio: [
|
|
27
|
+
"ignore",
|
|
28
|
+
"pipe",
|
|
29
|
+
"pipe"
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
let stdout = "";
|
|
33
|
+
let stderr = "";
|
|
34
|
+
proc.stdout?.on("data", (chunk) => {
|
|
35
|
+
stdout += chunk.toString();
|
|
36
|
+
});
|
|
37
|
+
proc.stderr?.on("data", (chunk) => {
|
|
38
|
+
stderr += chunk.toString();
|
|
39
|
+
});
|
|
40
|
+
proc.on("close", (code) => {
|
|
41
|
+
resolve$1({
|
|
42
|
+
stdout: stdout.trim(),
|
|
43
|
+
stderr: stderr.trim(),
|
|
44
|
+
code: code ?? 1
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
proc.on("error", (err) => {
|
|
48
|
+
resolve$1({
|
|
49
|
+
stdout: "",
|
|
50
|
+
stderr: err.message,
|
|
51
|
+
code: 1
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get the installed git version string.
|
|
58
|
+
*
|
|
59
|
+
* @returns Version string like "2.42.0"
|
|
60
|
+
* @throws Error if git is not installed or version cannot be parsed
|
|
61
|
+
*/
|
|
62
|
+
async function getGitVersion() {
|
|
63
|
+
const result = await spawnGit(["--version"]);
|
|
64
|
+
if (result.code !== 0) throw new Error(`git --version failed: ${result.stderr}`);
|
|
65
|
+
const match = /git version\s+(\d+\.\d+(?:\.\d+)?)/.exec(result.stdout);
|
|
66
|
+
if (match === null || match[1] === void 0) throw new Error(`Unable to parse git version from output: "${result.stdout}"`);
|
|
67
|
+
return match[1];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse a git version string into major/minor/patch components.
|
|
71
|
+
*
|
|
72
|
+
* @param versionString - Version string like "2.42.0" or "2.20"
|
|
73
|
+
* @returns - Parsed version components
|
|
74
|
+
*/
|
|
75
|
+
function parseGitVersion(versionString) {
|
|
76
|
+
const parts = versionString.split(".").map(Number);
|
|
77
|
+
return {
|
|
78
|
+
major: parts[0] ?? 0,
|
|
79
|
+
minor: parts[1] ?? 0,
|
|
80
|
+
patch: parts[2] ?? 0
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if the given git version string is >= 2.20.0.
|
|
85
|
+
*
|
|
86
|
+
* @param version - Version string like "2.42.0"
|
|
87
|
+
* @returns - true if version >= 2.20.0
|
|
88
|
+
*/
|
|
89
|
+
function isGitVersionSupported(version) {
|
|
90
|
+
const { major, minor } = parseGitVersion(version);
|
|
91
|
+
if (major > MIN_GIT_MAJOR) return true;
|
|
92
|
+
if (major === MIN_GIT_MAJOR && minor >= MIN_GIT_MINOR) return true;
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Verify that git is installed and version >= 2.20.
|
|
97
|
+
*
|
|
98
|
+
* @throws Error with clear message if git is not installed or too old
|
|
99
|
+
*/
|
|
100
|
+
async function verifyGitVersion() {
|
|
101
|
+
let version;
|
|
102
|
+
try {
|
|
103
|
+
version = await getGitVersion();
|
|
104
|
+
} catch (err) {
|
|
105
|
+
throw new Error(`git is not installed or could not be executed. Please install git 2.20 or newer. Details: ${String(err)}`);
|
|
106
|
+
}
|
|
107
|
+
if (!isGitVersionSupported(version)) {
|
|
108
|
+
const { major, minor, patch } = parseGitVersion(version);
|
|
109
|
+
throw new Error(`Git version ${major}.${minor}.${patch} is too old. Substrate requires git 2.20 or newer. Please upgrade git: https://git-scm.com/downloads`);
|
|
110
|
+
}
|
|
111
|
+
logger.debug({ version }, "git version verified");
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create a git worktree and its associated branch.
|
|
115
|
+
*
|
|
116
|
+
* Uses a single `git worktree add {worktreePath} -b {branchName} {baseBranch}`
|
|
117
|
+
* command to create the worktree with a new branch in one step.
|
|
118
|
+
*
|
|
119
|
+
* @param projectRoot - Absolute path to the git repository root
|
|
120
|
+
* @param taskId - Task identifier (used in path derivation)
|
|
121
|
+
* @param branchName - Branch name to create (e.g., "substrate/task-abc123")
|
|
122
|
+
* @param baseBranch - Branch to base the worktree on (e.g., "main")
|
|
123
|
+
* @returns - Object with the worktreePath
|
|
124
|
+
* @throws - Error if git command fails
|
|
125
|
+
*/
|
|
126
|
+
async function createWorktree(projectRoot, taskId, branchName, baseBranch) {
|
|
127
|
+
const worktreePath = path.join(projectRoot, ".substrate-worktrees", taskId);
|
|
128
|
+
logger.debug({
|
|
129
|
+
projectRoot,
|
|
130
|
+
taskId,
|
|
131
|
+
branchName,
|
|
132
|
+
baseBranch,
|
|
133
|
+
worktreePath
|
|
134
|
+
}, "createWorktree");
|
|
135
|
+
const addResult = await spawnGit([
|
|
136
|
+
"worktree",
|
|
137
|
+
"add",
|
|
138
|
+
worktreePath,
|
|
139
|
+
"-b",
|
|
140
|
+
branchName,
|
|
141
|
+
baseBranch
|
|
142
|
+
], { cwd: projectRoot });
|
|
143
|
+
if (addResult.code !== 0) throw new Error(`git worktree add failed for task "${taskId}": ${addResult.stderr || addResult.stdout}`);
|
|
144
|
+
logger.info({
|
|
145
|
+
taskId,
|
|
146
|
+
branchName,
|
|
147
|
+
worktreePath
|
|
148
|
+
}, "Worktree created");
|
|
149
|
+
return { worktreePath };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Remove a git worktree by path.
|
|
153
|
+
*
|
|
154
|
+
* Uses `git worktree remove --force` to handle unclean worktrees.
|
|
155
|
+
*
|
|
156
|
+
* @param worktreePath - Absolute path to the worktree directory
|
|
157
|
+
* @param projectRoot - Absolute path to the git repository root
|
|
158
|
+
* @throws - Error if git command fails
|
|
159
|
+
*/
|
|
160
|
+
async function removeWorktree(worktreePath, projectRoot) {
|
|
161
|
+
logger.debug({ worktreePath }, "removeWorktree");
|
|
162
|
+
const spawnOpts = {};
|
|
163
|
+
if (projectRoot !== void 0) spawnOpts.cwd = projectRoot;
|
|
164
|
+
const result = await spawnGit([
|
|
165
|
+
"worktree",
|
|
166
|
+
"remove",
|
|
167
|
+
"--force",
|
|
168
|
+
worktreePath
|
|
169
|
+
], spawnOpts);
|
|
170
|
+
if (result.code !== 0) throw new Error(`git worktree remove failed for "${worktreePath}": ${result.stderr || result.stdout}`);
|
|
171
|
+
logger.debug({ worktreePath }, "Worktree removed");
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Delete a git branch using `git branch -D`.
|
|
175
|
+
*
|
|
176
|
+
* @param branchName - Branch name to delete (e.g., "substrate/task-abc123")
|
|
177
|
+
* @param projectRoot - Absolute path to the git repository root
|
|
178
|
+
* @returns - true if branch was successfully deleted, false otherwise
|
|
179
|
+
*/
|
|
180
|
+
async function removeBranch(branchName, projectRoot) {
|
|
181
|
+
logger.debug({ branchName }, "removeBranch");
|
|
182
|
+
const spawnOpts = {};
|
|
183
|
+
if (projectRoot !== void 0) spawnOpts.cwd = projectRoot;
|
|
184
|
+
const result = await spawnGit([
|
|
185
|
+
"branch",
|
|
186
|
+
"-D",
|
|
187
|
+
branchName
|
|
188
|
+
], spawnOpts);
|
|
189
|
+
if (result.code !== 0) {
|
|
190
|
+
logger.warn({
|
|
191
|
+
branchName,
|
|
192
|
+
stderr: result.stderr
|
|
193
|
+
}, "git branch -D failed (may already be deleted)");
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
logger.debug({ branchName }, "Branch deleted");
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Scan the worktrees base directory and return paths of all found worktree directories.
|
|
201
|
+
*
|
|
202
|
+
* @param projectRoot - Absolute path to the git repository root
|
|
203
|
+
* @param baseDirectory - Relative directory name for worktrees (default: '.substrate-worktrees')
|
|
204
|
+
* @returns - Array of absolute worktree directory paths
|
|
205
|
+
*/
|
|
206
|
+
async function getOrphanedWorktrees(projectRoot, baseDirectory = ".substrate-worktrees") {
|
|
207
|
+
const worktreesDir = path.join(projectRoot, baseDirectory);
|
|
208
|
+
try {
|
|
209
|
+
await access(worktreesDir);
|
|
210
|
+
} catch {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
let entries;
|
|
214
|
+
try {
|
|
215
|
+
entries = await readdir(worktreesDir, { withFileTypes: true });
|
|
216
|
+
} catch (err) {
|
|
217
|
+
logger.warn({
|
|
218
|
+
worktreesDir,
|
|
219
|
+
err
|
|
220
|
+
}, "getOrphanedWorktrees: failed to read directory");
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => path.join(worktreesDir, entry.name));
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Simulate a merge using git merge --no-commit --no-ff without committing.
|
|
227
|
+
*
|
|
228
|
+
* This runs in the target branch's working directory (the project root or
|
|
229
|
+
* worktree path). The simulation must be aborted after checking conflicts
|
|
230
|
+
* via abortMerge().
|
|
231
|
+
*
|
|
232
|
+
* @param branchName - The source branch to simulate merging
|
|
233
|
+
* @param cwd - Working directory (must be on the target branch)
|
|
234
|
+
* @returns - true if merge would be clean, false if there are conflicts
|
|
235
|
+
*/
|
|
236
|
+
async function simulateMerge(branchName, cwd) {
|
|
237
|
+
logger.debug({
|
|
238
|
+
branchName,
|
|
239
|
+
cwd
|
|
240
|
+
}, "simulateMerge");
|
|
241
|
+
const result = await spawnGit([
|
|
242
|
+
"merge",
|
|
243
|
+
"--no-commit",
|
|
244
|
+
"--no-ff",
|
|
245
|
+
branchName
|
|
246
|
+
], { cwd });
|
|
247
|
+
if (result.code === 0) return true;
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Abort a merge in progress using git merge --abort.
|
|
252
|
+
*
|
|
253
|
+
* Should be called after simulateMerge() to clean up the merge state,
|
|
254
|
+
* regardless of whether conflicts were found.
|
|
255
|
+
*
|
|
256
|
+
* @param cwd - Working directory (same as used for simulateMerge)
|
|
257
|
+
*/
|
|
258
|
+
async function abortMerge(cwd) {
|
|
259
|
+
logger.debug({ cwd }, "abortMerge");
|
|
260
|
+
const result = await spawnGit(["merge", "--abort"], { cwd });
|
|
261
|
+
if (result.code !== 0) logger.warn({
|
|
262
|
+
cwd,
|
|
263
|
+
stderr: result.stderr
|
|
264
|
+
}, "abortMerge: git merge --abort returned non-zero (may have been nothing to abort)");
|
|
265
|
+
logger.debug({ cwd }, "Merge aborted");
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Get a list of files with conflicts during a merge.
|
|
269
|
+
*
|
|
270
|
+
* Must be called while a merge is in progress (after simulateMerge() and
|
|
271
|
+
* before abortMerge()).
|
|
272
|
+
*
|
|
273
|
+
* @param cwd - Working directory of the repository
|
|
274
|
+
* @returns - Array of conflicting file paths
|
|
275
|
+
*/
|
|
276
|
+
async function getConflictingFiles(cwd) {
|
|
277
|
+
logger.debug({ cwd }, "getConflictingFiles");
|
|
278
|
+
const result = await spawnGit([
|
|
279
|
+
"diff",
|
|
280
|
+
"--name-only",
|
|
281
|
+
"--diff-filter=U"
|
|
282
|
+
], { cwd });
|
|
283
|
+
if (result.code !== 0) {
|
|
284
|
+
logger.warn({
|
|
285
|
+
cwd,
|
|
286
|
+
stderr: result.stderr
|
|
287
|
+
}, "getConflictingFiles: git diff returned non-zero");
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
if (result.stdout.trim() === "") return [];
|
|
291
|
+
return result.stdout.trim().split("\n").filter((f) => f.trim().length > 0);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Perform an actual merge using git merge --no-ff.
|
|
295
|
+
*
|
|
296
|
+
* Should only be called after detectConflicts() confirms there are no conflicts.
|
|
297
|
+
* Creates a merge commit even if fast-forward is possible (--no-ff ensures history).
|
|
298
|
+
*
|
|
299
|
+
* @param branchName - The source branch to merge
|
|
300
|
+
* @param cwd - Working directory (must be on the target branch)
|
|
301
|
+
* @returns - true if merge succeeded, false otherwise
|
|
302
|
+
*/
|
|
303
|
+
async function performMerge(branchName, cwd) {
|
|
304
|
+
logger.debug({
|
|
305
|
+
branchName,
|
|
306
|
+
cwd
|
|
307
|
+
}, "performMerge");
|
|
308
|
+
const result = await spawnGit([
|
|
309
|
+
"merge",
|
|
310
|
+
"--no-ff",
|
|
311
|
+
branchName
|
|
312
|
+
], { cwd });
|
|
313
|
+
if (result.code !== 0) {
|
|
314
|
+
logger.warn({
|
|
315
|
+
branchName,
|
|
316
|
+
cwd,
|
|
317
|
+
stderr: result.stderr
|
|
318
|
+
}, "performMerge: git merge --no-ff failed");
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
logger.debug({
|
|
322
|
+
branchName,
|
|
323
|
+
cwd
|
|
324
|
+
}, "Merge completed");
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Get a list of files changed in the most recent merge.
|
|
329
|
+
*
|
|
330
|
+
* Uses git diff --name-only HEAD~1..HEAD to find files in the merge commit.
|
|
331
|
+
* Falls back to empty array if commit history is insufficient.
|
|
332
|
+
*
|
|
333
|
+
* @param cwd - Working directory of the repository
|
|
334
|
+
* @returns - Array of file paths that were merged
|
|
335
|
+
*/
|
|
336
|
+
async function getMergedFiles(cwd) {
|
|
337
|
+
logger.debug({ cwd }, "getMergedFiles");
|
|
338
|
+
const result = await spawnGit([
|
|
339
|
+
"diff",
|
|
340
|
+
"--name-only",
|
|
341
|
+
"HEAD~1..HEAD"
|
|
342
|
+
], { cwd });
|
|
343
|
+
if (result.code !== 0) {
|
|
344
|
+
const altResult = await spawnGit([
|
|
345
|
+
"show",
|
|
346
|
+
"--name-only",
|
|
347
|
+
"--format=",
|
|
348
|
+
"HEAD"
|
|
349
|
+
], { cwd });
|
|
350
|
+
if (altResult.code !== 0) {
|
|
351
|
+
logger.warn({
|
|
352
|
+
cwd,
|
|
353
|
+
stderr: altResult.stderr
|
|
354
|
+
}, "getMergedFiles: failed to get merged file list");
|
|
355
|
+
return [];
|
|
356
|
+
}
|
|
357
|
+
return altResult.stdout.trim().split("\n").filter((f) => f.trim().length > 0);
|
|
358
|
+
}
|
|
359
|
+
if (result.stdout.trim() === "") return [];
|
|
360
|
+
return result.stdout.trim().split("\n").filter((f) => f.trim().length > 0);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
//#endregion
|
|
364
|
+
export { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, spawnGit, verifyGitVersion };
|
|
365
|
+
//# sourceMappingURL=git-utils-BtI5eNoN.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ interface CostRecord {
|
|
|
77
77
|
* Pipeline event type definitions for the NDJSON event protocol.
|
|
78
78
|
*
|
|
79
79
|
* These types form a discriminated union `PipelineEvent` that is emitted
|
|
80
|
-
* on stdout when `substrate
|
|
80
|
+
* on stdout when `substrate run --events` is active.
|
|
81
81
|
*
|
|
82
82
|
* All events carry a `ts` ISO-8601 timestamp generated at emit time.
|
|
83
83
|
*/
|
|
@@ -293,6 +293,90 @@ interface SupervisorSummaryEvent {
|
|
|
293
293
|
/** Number of restart cycles performed by the supervisor */
|
|
294
294
|
restarts: number;
|
|
295
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Emitted when the supervisor's post-run analysis finishes successfully.
|
|
298
|
+
*/
|
|
299
|
+
interface SupervisorAnalysisCompleteEvent {
|
|
300
|
+
type: 'supervisor:analysis:complete';
|
|
301
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
302
|
+
ts: string;
|
|
303
|
+
/** Pipeline run ID that was analyzed */
|
|
304
|
+
run_id: string | null;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Emitted when the supervisor's post-run analysis fails.
|
|
308
|
+
*/
|
|
309
|
+
interface SupervisorAnalysisErrorEvent {
|
|
310
|
+
type: 'supervisor:analysis:error';
|
|
311
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
312
|
+
ts: string;
|
|
313
|
+
/** Pipeline run ID for which analysis was attempted */
|
|
314
|
+
run_id: string | null;
|
|
315
|
+
/** Error message describing why analysis failed */
|
|
316
|
+
error: string;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Emitted when the supervisor begins an experiment cycle.
|
|
320
|
+
*/
|
|
321
|
+
interface SupervisorExperimentStartEvent {
|
|
322
|
+
type: 'supervisor:experiment:start';
|
|
323
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
324
|
+
ts: string;
|
|
325
|
+
/** Pipeline run ID being experimented on */
|
|
326
|
+
run_id: string | null;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Emitted when the supervisor skips an experiment cycle (no recommendations or no analysis report).
|
|
330
|
+
*/
|
|
331
|
+
interface SupervisorExperimentSkipEvent {
|
|
332
|
+
type: 'supervisor:experiment:skip';
|
|
333
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
334
|
+
ts: string;
|
|
335
|
+
/** Pipeline run ID */
|
|
336
|
+
run_id: string | null;
|
|
337
|
+
/** Why the experiment was skipped */
|
|
338
|
+
reason: string;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Emitted when the supervisor discovers recommendations to experiment with.
|
|
342
|
+
*/
|
|
343
|
+
interface SupervisorExperimentRecommendationsEvent {
|
|
344
|
+
type: 'supervisor:experiment:recommendations';
|
|
345
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
346
|
+
ts: string;
|
|
347
|
+
/** Pipeline run ID */
|
|
348
|
+
run_id: string | null;
|
|
349
|
+
/** Number of recommendations found */
|
|
350
|
+
count: number;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Emitted when the supervisor's experiment cycle completes.
|
|
354
|
+
*/
|
|
355
|
+
interface SupervisorExperimentCompleteEvent {
|
|
356
|
+
type: 'supervisor:experiment:complete';
|
|
357
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
358
|
+
ts: string;
|
|
359
|
+
/** Pipeline run ID */
|
|
360
|
+
run_id: string | null;
|
|
361
|
+
/** Number of experiments that resulted in improvement */
|
|
362
|
+
improved: number;
|
|
363
|
+
/** Number of experiments with mixed results */
|
|
364
|
+
mixed: number;
|
|
365
|
+
/** Number of experiments that caused regression */
|
|
366
|
+
regressed: number;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Emitted when the supervisor's experiment cycle fails.
|
|
370
|
+
*/
|
|
371
|
+
interface SupervisorExperimentErrorEvent {
|
|
372
|
+
type: 'supervisor:experiment:error';
|
|
373
|
+
/** ISO-8601 timestamp generated at emit time */
|
|
374
|
+
ts: string;
|
|
375
|
+
/** Pipeline run ID */
|
|
376
|
+
run_id: string | null;
|
|
377
|
+
/** Error message describing why the experiment failed */
|
|
378
|
+
error: string;
|
|
379
|
+
}
|
|
296
380
|
/**
|
|
297
381
|
* Discriminated union of all pipeline event types.
|
|
298
382
|
*
|
|
@@ -305,7 +389,7 @@ interface SupervisorSummaryEvent {
|
|
|
305
389
|
* }
|
|
306
390
|
* ```
|
|
307
391
|
*/
|
|
308
|
-
type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | StoryPhaseEvent | StoryDoneEvent | StoryEscalationEvent | StoryWarnEvent | StoryLogEvent | PipelineHeartbeatEvent | StoryStallEvent | SupervisorKillEvent | SupervisorRestartEvent | SupervisorAbortEvent | SupervisorSummaryEvent; //#endregion
|
|
392
|
+
type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | StoryPhaseEvent | StoryDoneEvent | StoryEscalationEvent | StoryWarnEvent | StoryLogEvent | PipelineHeartbeatEvent | StoryStallEvent | SupervisorKillEvent | SupervisorRestartEvent | SupervisorAbortEvent | SupervisorSummaryEvent | SupervisorAnalysisCompleteEvent | SupervisorAnalysisErrorEvent | SupervisorExperimentStartEvent | SupervisorExperimentSkipEvent | SupervisorExperimentRecommendationsEvent | SupervisorExperimentCompleteEvent | SupervisorExperimentErrorEvent; //#endregion
|
|
309
393
|
//#region src/core/errors.d.ts
|
|
310
394
|
|
|
311
395
|
/**
|
|
@@ -968,141 +1052,9 @@ interface TypedEventBus {
|
|
|
968
1052
|
*/
|
|
969
1053
|
declare function createEventBus(): TypedEventBus;
|
|
970
1054
|
|
|
971
|
-
//#endregion
|
|
972
|
-
//#region src/core/orchestrator.d.ts
|
|
973
|
-
//# sourceMappingURL=event-bus.d.ts.map
|
|
974
|
-
/**
|
|
975
|
-
* Configuration required to initialize the orchestrator.
|
|
976
|
-
*/
|
|
977
|
-
interface OrchestratorConfig {
|
|
978
|
-
/** Path to the SQLite database file (e.g., ".substrate/substrate.db") */
|
|
979
|
-
databasePath: string;
|
|
980
|
-
/** Working directory for the orchestrated project */
|
|
981
|
-
projectRoot: string;
|
|
982
|
-
/**
|
|
983
|
-
* Maximum number of concurrent worker tasks.
|
|
984
|
-
* @default 4
|
|
985
|
-
*/
|
|
986
|
-
maxConcurrency?: number;
|
|
987
|
-
/**
|
|
988
|
-
* Budget cap in USD (0 = unlimited).
|
|
989
|
-
* @default 0
|
|
990
|
-
*/
|
|
991
|
-
budgetCapUsd?: number;
|
|
992
|
-
/**
|
|
993
|
-
* Budget cap in tokens (0 = unlimited).
|
|
994
|
-
* @default 0
|
|
995
|
-
*/
|
|
996
|
-
budgetCapTokens?: number;
|
|
997
|
-
/** Optional logger level override */
|
|
998
|
-
logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
999
|
-
/**
|
|
1000
|
-
* Path to the substrate config file for hot-reload watching.
|
|
1001
|
-
* If omitted, defaults to <projectRoot>/substrate.config.yaml
|
|
1002
|
-
*/
|
|
1003
|
-
configPath?: string;
|
|
1004
|
-
/**
|
|
1005
|
-
* Enable config hot-reload via file watcher.
|
|
1006
|
-
* When true (default), a file watcher is registered on the config file.
|
|
1007
|
-
* Set to false to disable (e.g. in dry-run or --no-watch-config mode).
|
|
1008
|
-
* @default true
|
|
1009
|
-
*/
|
|
1010
|
-
enableConfigHotReload?: boolean;
|
|
1011
|
-
/**
|
|
1012
|
-
* Monitor agent configuration.
|
|
1013
|
-
* If omitted, monitor agent is initialized with defaults.
|
|
1014
|
-
*/
|
|
1015
|
-
monitor?: {
|
|
1016
|
-
/**
|
|
1017
|
-
* Path to the monitor SQLite database file.
|
|
1018
|
-
* @default ':memory:' (in-memory, non-persistent) when not specified
|
|
1019
|
-
*/
|
|
1020
|
-
databasePath?: string;
|
|
1021
|
-
/**
|
|
1022
|
-
* Number of days to retain task metrics.
|
|
1023
|
-
* @default 90
|
|
1024
|
-
*/
|
|
1025
|
-
retentionDays?: number;
|
|
1026
|
-
/**
|
|
1027
|
-
* Custom task type taxonomy for classification overrides.
|
|
1028
|
-
*/
|
|
1029
|
-
customTaxonomy?: Record<string, string[]>;
|
|
1030
|
-
/**
|
|
1031
|
-
* Enable advisory routing recommendations based on performance data (AC6, FR68).
|
|
1032
|
-
* When true, MonitorAgent.getRecommendation() returns recommendations for routing decisions.
|
|
1033
|
-
* @default false
|
|
1034
|
-
*/
|
|
1035
|
-
use_recommendations?: boolean;
|
|
1036
|
-
/**
|
|
1037
|
-
* Minimum success rate improvement (percentage points) required to generate a recommendation.
|
|
1038
|
-
* @default 5.0
|
|
1039
|
-
*/
|
|
1040
|
-
recommendation_threshold_percentage?: number;
|
|
1041
|
-
/**
|
|
1042
|
-
* Minimum number of tasks per (agent, task_type) pair before a recommendation is generated.
|
|
1043
|
-
* @default 10
|
|
1044
|
-
*/
|
|
1045
|
-
min_sample_size?: number;
|
|
1046
|
-
/**
|
|
1047
|
-
* Number of days of historical data to analyze for recommendations.
|
|
1048
|
-
* @default 90
|
|
1049
|
-
*/
|
|
1050
|
-
recommendation_history_days?: number;
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Central orchestration engine — coordinates all modules via the event bus
|
|
1055
|
-
* and dependency injection.
|
|
1056
|
-
*
|
|
1057
|
-
* Lifecycle:
|
|
1058
|
-
* 1. Create via `createOrchestrator(config)`
|
|
1059
|
-
* 2. Factory emits `orchestrator:ready` when initialization completes
|
|
1060
|
-
* 3. Consumers interact with modules via the event bus or injected interfaces
|
|
1061
|
-
* 4. On SIGTERM/SIGINT, graceful shutdown is triggered automatically
|
|
1062
|
-
* 5. Call `shutdown()` explicitly for programmatic shutdown
|
|
1063
|
-
*/
|
|
1064
|
-
interface Orchestrator {
|
|
1065
|
-
/**
|
|
1066
|
-
* The typed event bus for this orchestrator instance.
|
|
1067
|
-
* Modules and consumers use this to subscribe to and emit events.
|
|
1068
|
-
*/
|
|
1069
|
-
readonly eventBus: TypedEventBus;
|
|
1070
|
-
/**
|
|
1071
|
-
* Whether the orchestrator has been fully initialized.
|
|
1072
|
-
*/
|
|
1073
|
-
readonly isReady: boolean;
|
|
1074
|
-
/**
|
|
1075
|
-
* Perform a graceful shutdown of all modules.
|
|
1076
|
-
* Calls shutdown() on all services in reverse initialization order.
|
|
1077
|
-
* Safe to call multiple times — subsequent calls are no-ops.
|
|
1078
|
-
*/
|
|
1079
|
-
shutdown(): Promise<void>;
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
//#endregion
|
|
1083
|
-
//#region src/core/orchestrator-impl.d.ts
|
|
1084
|
-
//# sourceMappingURL=orchestrator.d.ts.map
|
|
1085
|
-
/**
|
|
1086
|
-
* Initialize the orchestrator with all modules wired via dependency injection.
|
|
1087
|
-
*
|
|
1088
|
-
* Steps performed:
|
|
1089
|
-
* 1. Create the TypedEventBus
|
|
1090
|
-
* 2. Create the SQLite database service
|
|
1091
|
-
* 3. Instantiate all modules (TaskGraphEngine, RoutingEngine, WorkerManager,
|
|
1092
|
-
* BudgetTracker, GitManager) with constructor injection
|
|
1093
|
-
* 4. Register all modules in the ServiceRegistry
|
|
1094
|
-
* 5. Call initialize() on all services in registration order
|
|
1095
|
-
* 6. Register SIGTERM/SIGINT graceful shutdown handlers
|
|
1096
|
-
* 7. Emit orchestrator:ready
|
|
1097
|
-
*
|
|
1098
|
-
* @param config - Orchestrator configuration
|
|
1099
|
-
* @returns Initialized Orchestrator instance
|
|
1100
|
-
*/
|
|
1101
|
-
declare function createOrchestrator(config: OrchestratorConfig): Promise<Orchestrator>;
|
|
1102
|
-
|
|
1103
1055
|
//#endregion
|
|
1104
1056
|
//#region src/core/di.d.ts
|
|
1105
|
-
//# sourceMappingURL=
|
|
1057
|
+
//# sourceMappingURL=event-bus.d.ts.map
|
|
1106
1058
|
/**
|
|
1107
1059
|
* Dependency injection container and service registry.
|
|
1108
1060
|
*
|
|
@@ -1717,7 +1669,7 @@ declare function printNonTtyWarning(): void;
|
|
|
1717
1669
|
//#region src/tui/types.d.ts
|
|
1718
1670
|
//# sourceMappingURL=app.d.ts.map
|
|
1719
1671
|
/**
|
|
1720
|
-
* TUI Dashboard types for substrate
|
|
1672
|
+
* TUI Dashboard types for substrate run --tui
|
|
1721
1673
|
*
|
|
1722
1674
|
* These types define the internal state model used by the TUI components.
|
|
1723
1675
|
*/
|
|
@@ -1792,5 +1744,5 @@ interface TuiState {
|
|
|
1792
1744
|
//#endregion
|
|
1793
1745
|
//# sourceMappingURL=types.d.ts.map
|
|
1794
1746
|
|
|
1795
|
-
export { AdapterCapabilities, AdapterDiscoveryResult, AdapterHealthResult, AdapterOptions, AdapterRegistry, AdtError, AgentCapability, AgentId, BaseService, BillingMode, BudgetExceededError, ClaudeCodeAdapter, CodexCLIAdapter, ConfigError, ConfigIncompatibleFormatError, CostRecord, DiscoveryReport, EscalationIssue, GeminiCLIAdapter, GitError, LogLevel,
|
|
1747
|
+
export { AdapterCapabilities, AdapterDiscoveryResult, AdapterHealthResult, AdapterOptions, AdapterRegistry, AdtError, AgentCapability, AgentId, BaseService, BillingMode, BudgetExceededError, ClaudeCodeAdapter, CodexCLIAdapter, ConfigError, ConfigIncompatibleFormatError, CostRecord, DiscoveryReport, EscalationIssue, GeminiCLIAdapter, GitError, LogLevel, OrchestratorEvents, PipelineCompleteEvent, PipelineEvent, PipelinePhase, PipelineStartEvent, PlanParseResult, PlanRequest, PlannedTask, RecoveryError, ServiceRegistry, SessionConfig, SessionStatus, SpawnCommand, StoryDoneEvent, StoryEscalationEvent, StoryLogEvent, StoryPhaseEvent, StoryPhaseLabel, StoryStatus, StoryWarnEvent, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, TaskId, TaskNode, TaskPriority, TaskResult, TaskStatus, TokenEstimate, TuiApp, TuiLogEntry, TuiState, TuiStoryState, TuiView, TypedEventBus, WorkerAdapter, WorkerError, WorkerId, WorkerNotFoundError, assertDefined, childLogger, createEventBus, createLogger, createTuiApp, deepClone, formatDuration, generateId, isPlainObject, isTuiCapable, logger, printNonTtyWarning, sleep, withRetry };
|
|
1796
1748
|
//# sourceMappingURL=index.d.ts.map
|