substrate-ai 0.2.20 → 0.2.21
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 +343 -123
- package/dist/{experimenter-prkFLFPw.js → experimenter-bc40oi8p.js} +2 -2
- package/dist/{operational-Dq4IfJzE.js → operational-CnMlvWqc.js} +47 -2
- package/dist/{run-CcWb6Kb-.js → run-BLIgARum.js} +776 -167
- package/dist/run-gmS6DsGT.js +7 -0
- package/package.json +1 -1
- package/packs/bmad/prompts/analysis-step-1-vision.md +4 -1
- package/packs/bmad/prompts/test-expansion.md +65 -0
- package/packs/bmad/prompts/test-plan.md +41 -0
- package/dist/run-C5zfaWYN.js +0 -7
package/dist/cli/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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-BLIgARum.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";
|
|
6
6
|
import { ConfigError, ConfigIncompatibleFormatError } from "../errors-BPqtzQ4U.js";
|
|
7
|
-
import { addTokenUsage, createDecision, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-Dq4cAA2L.js";
|
|
8
|
-
import { EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-
|
|
7
|
+
import { addTokenUsage, createDecision, createPipelineRun, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-Dq4cAA2L.js";
|
|
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";
|
|
10
10
|
import { registerUpgradeCommand } from "../upgrade-CjjAx5kD.js";
|
|
11
11
|
import { Command } from "commander";
|
|
@@ -324,7 +324,7 @@ const DEFAULT_CONFIG = {
|
|
|
324
324
|
|
|
325
325
|
//#endregion
|
|
326
326
|
//#region src/cli/commands/init.ts
|
|
327
|
-
const logger$
|
|
327
|
+
const logger$17 = createLogger("init");
|
|
328
328
|
const __dirname = dirname(new URL(import.meta.url).pathname);
|
|
329
329
|
const INIT_EXIT_SUCCESS = 0;
|
|
330
330
|
const INIT_EXIT_ERROR = 1;
|
|
@@ -345,7 +345,7 @@ async function scaffoldBmadFramework(projectRoot, force, outputFormat) {
|
|
|
345
345
|
const version = resolveBmadMethodVersion();
|
|
346
346
|
if (force && bmadExists) process.stderr.write(`Warning: Replacing existing _bmad/ framework with bmad-method@${version}\n`);
|
|
347
347
|
process.stdout.write(`Scaffolding BMAD framework from bmad-method@${version}\n`);
|
|
348
|
-
logger$
|
|
348
|
+
logger$17.info({
|
|
349
349
|
version,
|
|
350
350
|
dest: bmadDest
|
|
351
351
|
}, "Scaffolding BMAD framework");
|
|
@@ -355,7 +355,7 @@ async function scaffoldBmadFramework(projectRoot, force, outputFormat) {
|
|
|
355
355
|
const destDir = join(bmadDest, dir);
|
|
356
356
|
mkdirSync(destDir, { recursive: true });
|
|
357
357
|
cpSync(srcDir, destDir, { recursive: true });
|
|
358
|
-
logger$
|
|
358
|
+
logger$17.info({
|
|
359
359
|
dir,
|
|
360
360
|
dest: destDir
|
|
361
361
|
}, "Scaffolded BMAD framework directory");
|
|
@@ -374,7 +374,7 @@ async function scaffoldBmadFramework(projectRoot, force, outputFormat) {
|
|
|
374
374
|
"document_output_language: English"
|
|
375
375
|
].join("\n") + "\n";
|
|
376
376
|
await writeFile(configFile, configStub, "utf8");
|
|
377
|
-
logger$
|
|
377
|
+
logger$17.info({ configFile }, "Generated _bmad/_config/config.yaml stub");
|
|
378
378
|
}
|
|
379
379
|
}
|
|
380
380
|
const CLAUDE_MD_START_MARKER = "<!-- substrate:start -->";
|
|
@@ -389,7 +389,7 @@ async function scaffoldClaudeMd(projectRoot) {
|
|
|
389
389
|
try {
|
|
390
390
|
sectionContent = await readFile(templatePath, "utf8");
|
|
391
391
|
} catch {
|
|
392
|
-
logger$
|
|
392
|
+
logger$17.warn({ templatePath }, "CLAUDE.md substrate section template not found; skipping");
|
|
393
393
|
return;
|
|
394
394
|
}
|
|
395
395
|
if (!sectionContent.endsWith("\n")) sectionContent += "\n";
|
|
@@ -407,7 +407,7 @@ async function scaffoldClaudeMd(projectRoot) {
|
|
|
407
407
|
newContent = existingContent + separator + sectionContent;
|
|
408
408
|
}
|
|
409
409
|
await writeFile(claudeMdPath, newContent, "utf8");
|
|
410
|
-
logger$
|
|
410
|
+
logger$17.info({ claudeMdPath }, "Wrote substrate section to CLAUDE.md");
|
|
411
411
|
}
|
|
412
412
|
async function scaffoldStatuslineScript(projectRoot) {
|
|
413
413
|
const pkgRoot = findPackageRoot(__dirname);
|
|
@@ -418,7 +418,7 @@ async function scaffoldStatuslineScript(projectRoot) {
|
|
|
418
418
|
try {
|
|
419
419
|
content = await readFile(templatePath, "utf8");
|
|
420
420
|
} catch {
|
|
421
|
-
logger$
|
|
421
|
+
logger$17.warn({ templatePath }, "statusline.sh template not found; skipping");
|
|
422
422
|
return;
|
|
423
423
|
}
|
|
424
424
|
const claudeDir = join(projectRoot, ".claude");
|
|
@@ -426,7 +426,7 @@ async function scaffoldStatuslineScript(projectRoot) {
|
|
|
426
426
|
mkdirSync(claudeDir, { recursive: true });
|
|
427
427
|
await writeFile(statuslinePath, content, "utf8");
|
|
428
428
|
chmodSync(statuslinePath, 493);
|
|
429
|
-
logger$
|
|
429
|
+
logger$17.info({ statuslinePath }, "Wrote .claude/statusline.sh");
|
|
430
430
|
}
|
|
431
431
|
async function scaffoldClaudeSettings(projectRoot) {
|
|
432
432
|
const claudeDir = join(projectRoot, ".claude");
|
|
@@ -442,7 +442,7 @@ async function scaffoldClaudeSettings(projectRoot) {
|
|
|
442
442
|
if (!merged["$schema"]) merged["$schema"] = "https://json.schemastore.org/claude-code-settings.json";
|
|
443
443
|
mkdirSync(claudeDir, { recursive: true });
|
|
444
444
|
await writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
445
|
-
logger$
|
|
445
|
+
logger$17.info({ settingsPath }, "Wrote substrate settings to .claude/settings.json");
|
|
446
446
|
}
|
|
447
447
|
function resolveBmadMethodInstallerLibPath(fromDir = __dirname) {
|
|
448
448
|
try {
|
|
@@ -512,7 +512,7 @@ async function compileBmadAgents(bmadDir) {
|
|
|
512
512
|
writeFileSync(mdPath, result.xml, "utf-8");
|
|
513
513
|
compiled++;
|
|
514
514
|
} catch (compileErr) {
|
|
515
|
-
logger$
|
|
515
|
+
logger$17.debug({
|
|
516
516
|
err: compileErr,
|
|
517
517
|
file
|
|
518
518
|
}, "Failed to compile agent YAML");
|
|
@@ -533,9 +533,9 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
|
|
|
533
533
|
const _require = createRequire(join(__dirname, "synthetic.js"));
|
|
534
534
|
try {
|
|
535
535
|
const compiledCount = await compileBmadAgents(bmadDir);
|
|
536
|
-
if (compiledCount > 0) logger$
|
|
536
|
+
if (compiledCount > 0) logger$17.info({ compiledCount }, "Compiled agent YAML files to MD");
|
|
537
537
|
} catch (compileErr) {
|
|
538
|
-
logger$
|
|
538
|
+
logger$17.warn({ err: compileErr }, "Agent compilation failed; agent commands may be incomplete");
|
|
539
539
|
}
|
|
540
540
|
const { AgentCommandGenerator } = _require(join(installerLibPath, "ide", "shared", "agent-command-generator.js"));
|
|
541
541
|
const { WorkflowCommandGenerator } = _require(join(installerLibPath, "ide", "shared", "workflow-command-generator.js"));
|
|
@@ -547,7 +547,7 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
|
|
|
547
547
|
const manifestGen = new ManifestGenerator();
|
|
548
548
|
await manifestGen.generateManifests(bmadDir, allModules, [], { ides: ["claude-code"] });
|
|
549
549
|
} catch (manifestErr) {
|
|
550
|
-
logger$
|
|
550
|
+
logger$17.warn({ err: manifestErr }, "ManifestGenerator failed; workflow/task commands may be incomplete");
|
|
551
551
|
}
|
|
552
552
|
const commandsDir = join(projectRoot, ".claude", "commands");
|
|
553
553
|
mkdirSync(commandsDir, { recursive: true });
|
|
@@ -563,7 +563,7 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
|
|
|
563
563
|
const taskToolCount = await taskToolGen.writeDashArtifacts(commandsDir, taskToolArtifacts);
|
|
564
564
|
const total = agentCount + workflowCount + taskToolCount;
|
|
565
565
|
if (outputFormat !== "json") process.stdout.write(`Generated ${String(total)} Claude Code commands (${String(agentCount)} agents, ${String(workflowCount)} workflows, ${String(taskToolCount)} tasks/tools)\n`);
|
|
566
|
-
logger$
|
|
566
|
+
logger$17.info({
|
|
567
567
|
agentCount,
|
|
568
568
|
workflowCount,
|
|
569
569
|
taskToolCount,
|
|
@@ -573,7 +573,7 @@ async function scaffoldClaudeCommands(projectRoot, outputFormat) {
|
|
|
573
573
|
} catch (err) {
|
|
574
574
|
const msg = err instanceof Error ? err.message : String(err);
|
|
575
575
|
if (outputFormat !== "json") process.stderr.write(`Warning: .claude/commands/ generation failed: ${msg}\n`);
|
|
576
|
-
logger$
|
|
576
|
+
logger$17.warn({ err }, "scaffoldClaudeCommands failed; init continues");
|
|
577
577
|
}
|
|
578
578
|
}
|
|
579
579
|
const PROVIDER_DEFAULTS = DEFAULT_CONFIG.providers;
|
|
@@ -647,7 +647,7 @@ async function runInitAction(options) {
|
|
|
647
647
|
discoveryReport = await registry.discoverAndRegister();
|
|
648
648
|
} catch (err) {
|
|
649
649
|
const message = err instanceof Error ? err.message : String(err);
|
|
650
|
-
logger$
|
|
650
|
+
logger$17.error({ err }, "Adapter discovery failed");
|
|
651
651
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, `Adapter discovery failed: ${message}`) + "\n");
|
|
652
652
|
else process.stderr.write(` Error: adapter discovery failed — ${message}\n`);
|
|
653
653
|
return INIT_EXIT_ERROR;
|
|
@@ -696,12 +696,12 @@ async function runInitAction(options) {
|
|
|
696
696
|
return INIT_EXIT_ERROR;
|
|
697
697
|
}
|
|
698
698
|
if (force && existsSync(localManifest)) {
|
|
699
|
-
logger$
|
|
699
|
+
logger$17.info({ pack: packName }, "Replacing existing pack with bundled version");
|
|
700
700
|
process.stderr.write(`Warning: Replacing existing pack '${packName}' with bundled version\n`);
|
|
701
701
|
}
|
|
702
702
|
mkdirSync(dirname(packPath), { recursive: true });
|
|
703
703
|
cpSync(bundledPackPath, packPath, { recursive: true });
|
|
704
|
-
logger$
|
|
704
|
+
logger$17.info({
|
|
705
705
|
pack: packName,
|
|
706
706
|
dest: packPath
|
|
707
707
|
}, "Scaffolded methodology pack");
|
|
@@ -754,7 +754,7 @@ async function runInitAction(options) {
|
|
|
754
754
|
const msg = err instanceof Error ? err.message : String(err);
|
|
755
755
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
756
756
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
757
|
-
logger$
|
|
757
|
+
logger$17.error({ err }, "init failed");
|
|
758
758
|
return INIT_EXIT_ERROR;
|
|
759
759
|
}
|
|
760
760
|
}
|
|
@@ -800,7 +800,7 @@ function formatUnsupportedVersionError(formatType, version, supported) {
|
|
|
800
800
|
|
|
801
801
|
//#endregion
|
|
802
802
|
//#region src/modules/config/config-system-impl.ts
|
|
803
|
-
const logger$
|
|
803
|
+
const logger$16 = createLogger("config");
|
|
804
804
|
function deepMerge(base, override) {
|
|
805
805
|
const result = { ...base };
|
|
806
806
|
for (const [key, val] of Object.entries(override)) if (val !== null && val !== void 0 && typeof val === "object" && !Array.isArray(val) && typeof result[key] === "object" && result[key] !== null && !Array.isArray(result[key])) result[key] = deepMerge(result[key], val);
|
|
@@ -845,7 +845,7 @@ function readEnvOverrides() {
|
|
|
845
845
|
}
|
|
846
846
|
const parsed = PartialSubstrateConfigSchema.safeParse(overrides);
|
|
847
847
|
if (!parsed.success) {
|
|
848
|
-
logger$
|
|
848
|
+
logger$16.warn({ errors: parsed.error.issues }, "Invalid environment variable overrides ignored");
|
|
849
849
|
return {};
|
|
850
850
|
}
|
|
851
851
|
return parsed.data;
|
|
@@ -909,7 +909,7 @@ var ConfigSystemImpl = class {
|
|
|
909
909
|
throw new ConfigError(`Configuration validation failed:\n${issues}`, { issues: result.error.issues });
|
|
910
910
|
}
|
|
911
911
|
this._config = result.data;
|
|
912
|
-
logger$
|
|
912
|
+
logger$16.debug("Configuration loaded successfully");
|
|
913
913
|
}
|
|
914
914
|
getConfig() {
|
|
915
915
|
if (this._config === null) throw new ConfigError("Configuration has not been loaded. Call load() before getConfig().", {});
|
|
@@ -972,7 +972,7 @@ var ConfigSystemImpl = class {
|
|
|
972
972
|
if (version !== void 0 && typeof version === "string" && !isVersionSupported(version, SUPPORTED_CONFIG_FORMAT_VERSIONS)) if (defaultConfigMigrator.canMigrate(version, CURRENT_CONFIG_FORMAT_VERSION)) {
|
|
973
973
|
const migrationOutput = defaultConfigMigrator.migrate(rawObj, version, CURRENT_CONFIG_FORMAT_VERSION, filePath);
|
|
974
974
|
if (migrationOutput.result.success) {
|
|
975
|
-
logger$
|
|
975
|
+
logger$16.info({
|
|
976
976
|
from: version,
|
|
977
977
|
to: CURRENT_CONFIG_FORMAT_VERSION,
|
|
978
978
|
backup: migrationOutput.result.backupPath
|
|
@@ -1015,7 +1015,7 @@ function createConfigSystem(options = {}) {
|
|
|
1015
1015
|
|
|
1016
1016
|
//#endregion
|
|
1017
1017
|
//#region src/cli/commands/config.ts
|
|
1018
|
-
const logger$
|
|
1018
|
+
const logger$15 = createLogger("config-cmd");
|
|
1019
1019
|
const CONFIG_EXIT_SUCCESS = 0;
|
|
1020
1020
|
const CONFIG_EXIT_ERROR = 1;
|
|
1021
1021
|
const CONFIG_EXIT_INVALID = 2;
|
|
@@ -1041,7 +1041,7 @@ async function runConfigShow(opts = {}) {
|
|
|
1041
1041
|
return CONFIG_EXIT_INVALID;
|
|
1042
1042
|
}
|
|
1043
1043
|
const message = err instanceof Error ? err.message : String(err);
|
|
1044
|
-
logger$
|
|
1044
|
+
logger$15.error({ err }, "Failed to load configuration");
|
|
1045
1045
|
process.stderr.write(` Error loading configuration: ${message}\n`);
|
|
1046
1046
|
return CONFIG_EXIT_ERROR;
|
|
1047
1047
|
}
|
|
@@ -1115,7 +1115,7 @@ async function runConfigExport(opts = {}) {
|
|
|
1115
1115
|
return CONFIG_EXIT_INVALID;
|
|
1116
1116
|
}
|
|
1117
1117
|
const message = err instanceof Error ? err.message : String(err);
|
|
1118
|
-
logger$
|
|
1118
|
+
logger$15.error({ err }, "Failed to load configuration");
|
|
1119
1119
|
process.stderr.write(`Error loading configuration: ${message}\n`);
|
|
1120
1120
|
return CONFIG_EXIT_ERROR;
|
|
1121
1121
|
}
|
|
@@ -1269,9 +1269,9 @@ function registerConfigCommand(program, _version) {
|
|
|
1269
1269
|
|
|
1270
1270
|
//#endregion
|
|
1271
1271
|
//#region src/cli/commands/resume.ts
|
|
1272
|
-
const logger$
|
|
1272
|
+
const logger$14 = createLogger("resume-cmd");
|
|
1273
1273
|
async function runResumeAction(options) {
|
|
1274
|
-
const { runId: specifiedRunId, stopAfter, outputFormat, projectRoot, concurrency, pack: packName } = options;
|
|
1274
|
+
const { runId: specifiedRunId, stopAfter, outputFormat, projectRoot, concurrency, pack: packName, registry } = options;
|
|
1275
1275
|
if (stopAfter !== void 0 && !VALID_PHASES.includes(stopAfter)) {
|
|
1276
1276
|
const errorMsg = `Invalid phase: "${stopAfter}". Valid phases: ${VALID_PHASES.join(", ")}`;
|
|
1277
1277
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
|
|
@@ -1345,13 +1345,14 @@ async function runResumeAction(options) {
|
|
|
1345
1345
|
concurrency,
|
|
1346
1346
|
outputFormat,
|
|
1347
1347
|
existingRunId: runId,
|
|
1348
|
-
projectRoot
|
|
1348
|
+
projectRoot,
|
|
1349
|
+
registry
|
|
1349
1350
|
});
|
|
1350
1351
|
} catch (err) {
|
|
1351
1352
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1352
1353
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
1353
1354
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
1354
|
-
logger$
|
|
1355
|
+
logger$14.error({ err }, "auto resume failed");
|
|
1355
1356
|
return 1;
|
|
1356
1357
|
} finally {
|
|
1357
1358
|
try {
|
|
@@ -1360,7 +1361,7 @@ async function runResumeAction(options) {
|
|
|
1360
1361
|
}
|
|
1361
1362
|
}
|
|
1362
1363
|
async function runFullPipelineFromPhase(options) {
|
|
1363
|
-
const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, existingRunId, projectRoot } = options;
|
|
1364
|
+
const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, existingRunId, projectRoot, registry: injectedRegistry } = options;
|
|
1364
1365
|
if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });
|
|
1365
1366
|
const dbWrapper = new DatabaseWrapper(dbPath);
|
|
1366
1367
|
try {
|
|
@@ -1380,8 +1381,8 @@ async function runFullPipelineFromPhase(options) {
|
|
|
1380
1381
|
}
|
|
1381
1382
|
const eventBus = createEventBus();
|
|
1382
1383
|
const contextCompiler = createContextCompiler({ db });
|
|
1383
|
-
const adapterRegistry = new AdapterRegistry();
|
|
1384
|
-
await adapterRegistry.discoverAndRegister();
|
|
1384
|
+
const adapterRegistry = injectedRegistry ?? new AdapterRegistry();
|
|
1385
|
+
if (injectedRegistry === void 0) await adapterRegistry.discoverAndRegister();
|
|
1385
1386
|
const dispatcher = createDispatcher({
|
|
1386
1387
|
eventBus,
|
|
1387
1388
|
adapterRegistry
|
|
@@ -1502,7 +1503,7 @@ async function runFullPipelineFromPhase(options) {
|
|
|
1502
1503
|
});
|
|
1503
1504
|
}
|
|
1504
1505
|
} catch (err) {
|
|
1505
|
-
logger$
|
|
1506
|
+
logger$14.warn({ err }, "Failed to record token usage");
|
|
1506
1507
|
}
|
|
1507
1508
|
});
|
|
1508
1509
|
const storyDecisions = db.prepare(`SELECT description FROM requirements WHERE pipeline_run_id = ? AND source = 'solutioning-phase'`).all(runId);
|
|
@@ -1561,7 +1562,7 @@ async function runFullPipelineFromPhase(options) {
|
|
|
1561
1562
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1562
1563
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
1563
1564
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
1564
|
-
logger$
|
|
1565
|
+
logger$14.error({ err }, "pipeline from phase failed");
|
|
1565
1566
|
return 1;
|
|
1566
1567
|
} finally {
|
|
1567
1568
|
try {
|
|
@@ -1569,7 +1570,7 @@ async function runFullPipelineFromPhase(options) {
|
|
|
1569
1570
|
} catch {}
|
|
1570
1571
|
}
|
|
1571
1572
|
}
|
|
1572
|
-
function registerResumeCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
|
|
1573
|
+
function registerResumeCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
|
|
1573
1574
|
program.command("resume").description("Resume a previously interrupted pipeline run").option("--run-id <id>", "Pipeline run ID to resume (defaults to latest)").option("--pack <name>", "Methodology pack name", "bmad").option("--stop-after <phase>", "Stop pipeline after this phase completes (overrides saved state)").option("--concurrency <n>", "Maximum parallel conflict groups", (v) => parseInt(v, 10), 3).option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").action(async (opts) => {
|
|
1574
1575
|
const outputFormat = opts.outputFormat === "json" ? "json" : "human";
|
|
1575
1576
|
const exitCode = await runResumeAction({
|
|
@@ -1578,7 +1579,8 @@ function registerResumeCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
1578
1579
|
outputFormat,
|
|
1579
1580
|
projectRoot: opts.projectRoot,
|
|
1580
1581
|
concurrency: opts.concurrency,
|
|
1581
|
-
pack: opts.pack
|
|
1582
|
+
pack: opts.pack,
|
|
1583
|
+
registry
|
|
1582
1584
|
});
|
|
1583
1585
|
process.exitCode = exitCode;
|
|
1584
1586
|
});
|
|
@@ -1586,7 +1588,7 @@ function registerResumeCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
1586
1588
|
|
|
1587
1589
|
//#endregion
|
|
1588
1590
|
//#region src/cli/commands/status.ts
|
|
1589
|
-
const logger$
|
|
1591
|
+
const logger$13 = createLogger("status-cmd");
|
|
1590
1592
|
async function runStatusAction(options) {
|
|
1591
1593
|
const { outputFormat, runId, projectRoot } = options;
|
|
1592
1594
|
const dbRoot = await resolveMainRepoRoot(projectRoot);
|
|
@@ -1663,7 +1665,7 @@ async function runStatusAction(options) {
|
|
|
1663
1665
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1664
1666
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
1665
1667
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
1666
|
-
logger$
|
|
1668
|
+
logger$13.error({ err }, "status action failed");
|
|
1667
1669
|
return 1;
|
|
1668
1670
|
} finally {
|
|
1669
1671
|
try {
|
|
@@ -2087,7 +2089,7 @@ Analyze thoroughly and return ONLY the JSON array with no additional text.`;
|
|
|
2087
2089
|
|
|
2088
2090
|
//#endregion
|
|
2089
2091
|
//#region src/cli/commands/amend.ts
|
|
2090
|
-
const logger$
|
|
2092
|
+
const logger$12 = createLogger("amend-cmd");
|
|
2091
2093
|
/**
|
|
2092
2094
|
* Detect and apply supersessions after a phase completes in an amendment run.
|
|
2093
2095
|
*
|
|
@@ -2118,7 +2120,7 @@ function runPostPhaseSupersessionDetection(db, amendmentRunId, currentPhase, han
|
|
|
2118
2120
|
});
|
|
2119
2121
|
} catch (err) {
|
|
2120
2122
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2121
|
-
logger$
|
|
2123
|
+
logger$12.warn({
|
|
2122
2124
|
err,
|
|
2123
2125
|
originalId: parentMatch.id,
|
|
2124
2126
|
supersedingId: newDec.id
|
|
@@ -2127,7 +2129,7 @@ function runPostPhaseSupersessionDetection(db, amendmentRunId, currentPhase, han
|
|
|
2127
2129
|
}
|
|
2128
2130
|
}
|
|
2129
2131
|
async function runAmendAction(options) {
|
|
2130
|
-
const { concept: conceptArg, conceptFile, runId: specifiedRunId, stopAfter, from: startPhase, projectRoot, pack: packName } = options;
|
|
2132
|
+
const { concept: conceptArg, conceptFile, runId: specifiedRunId, stopAfter, from: startPhase, projectRoot, pack: packName, registry: injectedRegistry } = options;
|
|
2131
2133
|
let concept;
|
|
2132
2134
|
if (conceptFile !== void 0 && conceptFile !== "") try {
|
|
2133
2135
|
concept = await readFile(conceptFile, "utf-8");
|
|
@@ -2214,8 +2216,8 @@ async function runAmendAction(options) {
|
|
|
2214
2216
|
}
|
|
2215
2217
|
const eventBus = createEventBus();
|
|
2216
2218
|
const contextCompiler = createContextCompiler({ db });
|
|
2217
|
-
const adapterRegistry = new AdapterRegistry();
|
|
2218
|
-
await adapterRegistry.discoverAndRegister();
|
|
2219
|
+
const adapterRegistry = injectedRegistry ?? new AdapterRegistry();
|
|
2220
|
+
if (injectedRegistry === void 0) await adapterRegistry.discoverAndRegister();
|
|
2219
2221
|
const dispatcher = createDispatcher({
|
|
2220
2222
|
eventBus,
|
|
2221
2223
|
adapterRegistry
|
|
@@ -2253,7 +2255,7 @@ async function runAmendAction(options) {
|
|
|
2253
2255
|
for (let i = startIdx; i < phaseOrder.length; i++) {
|
|
2254
2256
|
const currentPhase = phaseOrder[i];
|
|
2255
2257
|
const amendmentContext = handler.loadContextForPhase(currentPhase);
|
|
2256
|
-
logger$
|
|
2258
|
+
logger$12.info({
|
|
2257
2259
|
phase: currentPhase,
|
|
2258
2260
|
amendmentContextLen: amendmentContext.length
|
|
2259
2261
|
}, "Amendment context loaded for phase");
|
|
@@ -2373,7 +2375,7 @@ async function runAmendAction(options) {
|
|
|
2373
2375
|
} catch (err) {
|
|
2374
2376
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2375
2377
|
process.stderr.write(`Error: ${msg}\n`);
|
|
2376
|
-
logger$
|
|
2378
|
+
logger$12.error({ err }, "amend failed");
|
|
2377
2379
|
return 1;
|
|
2378
2380
|
} finally {
|
|
2379
2381
|
try {
|
|
@@ -2381,7 +2383,7 @@ async function runAmendAction(options) {
|
|
|
2381
2383
|
} catch {}
|
|
2382
2384
|
}
|
|
2383
2385
|
}
|
|
2384
|
-
function registerAmendCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
|
|
2386
|
+
function registerAmendCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
|
|
2385
2387
|
program.command("amend").description("Run an amendment pipeline against a completed run and an existing run").option("--concept <text>", "Amendment concept description (inline)").option("--concept-file <path>", "Path to concept file").option("--run-id <id>", "Parent run ID (defaults to latest completed run)").option("--stop-after <phase>", "Stop pipeline after this phase completes").option("--from <phase>", "Start pipeline from this phase").option("--pack <name>", "Methodology pack name", "bmad").option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").action(async (opts) => {
|
|
2386
2388
|
const exitCode = await runAmendAction({
|
|
2387
2389
|
concept: opts.concept,
|
|
@@ -2390,7 +2392,8 @@ function registerAmendCommand(program, _version = "0.0.0", projectRoot = process
|
|
|
2390
2392
|
stopAfter: opts.stopAfter,
|
|
2391
2393
|
from: opts.from,
|
|
2392
2394
|
projectRoot: opts.projectRoot,
|
|
2393
|
-
pack: opts.pack
|
|
2395
|
+
pack: opts.pack,
|
|
2396
|
+
registry
|
|
2394
2397
|
});
|
|
2395
2398
|
process.exitCode = exitCode;
|
|
2396
2399
|
});
|
|
@@ -2828,7 +2831,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2828
2831
|
try {
|
|
2829
2832
|
const { createExperimenter } = await import(
|
|
2830
2833
|
/* @vite-ignore */
|
|
2831
|
-
"../experimenter-
|
|
2834
|
+
"../experimenter-bc40oi8p.js"
|
|
2832
2835
|
);
|
|
2833
2836
|
const { getLatestRun: getLatest } = await import(
|
|
2834
2837
|
/* @vite-ignore */
|
|
@@ -2842,7 +2845,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2842
2845
|
const expDb = expDbWrapper.db;
|
|
2843
2846
|
const { runRunAction: runPipeline } = await import(
|
|
2844
2847
|
/* @vite-ignore */
|
|
2845
|
-
"../run-
|
|
2848
|
+
"../run-gmS6DsGT.js"
|
|
2846
2849
|
);
|
|
2847
2850
|
const runStoryFn = async (opts) => {
|
|
2848
2851
|
const exitCode = await runPipeline({
|
|
@@ -3085,7 +3088,7 @@ function registerSupervisorCommand(program, _version = "0.0.0", projectRoot = pr
|
|
|
3085
3088
|
|
|
3086
3089
|
//#endregion
|
|
3087
3090
|
//#region src/cli/commands/metrics.ts
|
|
3088
|
-
const logger$
|
|
3091
|
+
const logger$11 = createLogger("metrics-cmd");
|
|
3089
3092
|
async function runMetricsAction(options) {
|
|
3090
3093
|
const { outputFormat, projectRoot, limit = 10, compare, tagBaseline, analysis } = options;
|
|
3091
3094
|
if (analysis !== void 0) {
|
|
@@ -3237,7 +3240,7 @@ async function runMetricsAction(options) {
|
|
|
3237
3240
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3238
3241
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
3239
3242
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
3240
|
-
logger$
|
|
3243
|
+
logger$11.error({ err }, "metrics action failed");
|
|
3241
3244
|
return 1;
|
|
3242
3245
|
} finally {
|
|
3243
3246
|
try {
|
|
@@ -3491,7 +3494,7 @@ function getPlanningCostTotal(db, sessionId) {
|
|
|
3491
3494
|
function getLatestSessionId(_db) {
|
|
3492
3495
|
return null;
|
|
3493
3496
|
}
|
|
3494
|
-
const logger$
|
|
3497
|
+
const logger$10 = createLogger("cost-cmd");
|
|
3495
3498
|
const COST_EXIT_SUCCESS = 0;
|
|
3496
3499
|
const COST_EXIT_ERROR = 1;
|
|
3497
3500
|
/**
|
|
@@ -3737,7 +3740,7 @@ async function runCostAction(options) {
|
|
|
3737
3740
|
} catch (err) {
|
|
3738
3741
|
const message = err instanceof Error ? err.message : String(err);
|
|
3739
3742
|
process.stderr.write(`Error: ${message}\n`);
|
|
3740
|
-
logger$
|
|
3743
|
+
logger$10.error({ err }, "runCostAction failed");
|
|
3741
3744
|
return COST_EXIT_ERROR;
|
|
3742
3745
|
} finally {
|
|
3743
3746
|
if (wrapper !== null) try {
|
|
@@ -3839,7 +3842,7 @@ function applyMonitorSchema(db) {
|
|
|
3839
3842
|
|
|
3840
3843
|
//#endregion
|
|
3841
3844
|
//#region src/persistence/monitor-database.ts
|
|
3842
|
-
const logger$
|
|
3845
|
+
const logger$9 = createLogger("persistence:monitor-db");
|
|
3843
3846
|
var MonitorDatabaseImpl = class {
|
|
3844
3847
|
_db = null;
|
|
3845
3848
|
_path;
|
|
@@ -3850,10 +3853,10 @@ var MonitorDatabaseImpl = class {
|
|
|
3850
3853
|
this._open();
|
|
3851
3854
|
}
|
|
3852
3855
|
_open() {
|
|
3853
|
-
logger$
|
|
3856
|
+
logger$9.info({ path: this._path }, "Opening monitor database");
|
|
3854
3857
|
this._db = new BetterSqlite3(this._path);
|
|
3855
3858
|
const walResult = this._db.pragma("journal_mode = WAL");
|
|
3856
|
-
if (walResult?.[0]?.journal_mode !== "wal") logger$
|
|
3859
|
+
if (walResult?.[0]?.journal_mode !== "wal") logger$9.warn({ result: walResult?.[0]?.journal_mode }, "Monitor DB: WAL pragma did not confirm wal mode");
|
|
3857
3860
|
this._db.pragma("synchronous = NORMAL");
|
|
3858
3861
|
this._db.pragma("busy_timeout = 5000");
|
|
3859
3862
|
this._db.pragma("foreign_keys = ON");
|
|
@@ -3888,7 +3891,7 @@ var MonitorDatabaseImpl = class {
|
|
|
3888
3891
|
total_retries = total_retries + @retries,
|
|
3889
3892
|
last_updated = @lastUpdated
|
|
3890
3893
|
`);
|
|
3891
|
-
logger$
|
|
3894
|
+
logger$9.info({ path: this._path }, "Monitor database ready");
|
|
3892
3895
|
}
|
|
3893
3896
|
_assertOpen() {
|
|
3894
3897
|
if (this._db === null) throw new Error("MonitorDatabase: connection is closed");
|
|
@@ -4037,7 +4040,7 @@ var MonitorDatabaseImpl = class {
|
|
|
4037
4040
|
const db = this._assertOpen();
|
|
4038
4041
|
const cutoff = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1e3).toISOString();
|
|
4039
4042
|
const result = db.prepare("DELETE FROM task_metrics WHERE recorded_at < @cutoff").run({ cutoff });
|
|
4040
|
-
logger$
|
|
4043
|
+
logger$9.info({
|
|
4041
4044
|
cutoff,
|
|
4042
4045
|
deleted: result.changes
|
|
4043
4046
|
}, "Pruned old task_metrics rows");
|
|
@@ -4076,13 +4079,13 @@ var MonitorDatabaseImpl = class {
|
|
|
4076
4079
|
db.exec("ROLLBACK");
|
|
4077
4080
|
throw err;
|
|
4078
4081
|
}
|
|
4079
|
-
logger$
|
|
4082
|
+
logger$9.info("Rebuilt performance_aggregates from task_metrics");
|
|
4080
4083
|
}
|
|
4081
4084
|
resetAllData() {
|
|
4082
4085
|
const db = this._assertOpen();
|
|
4083
4086
|
db.exec("DELETE FROM task_metrics");
|
|
4084
4087
|
db.exec("DELETE FROM performance_aggregates");
|
|
4085
|
-
logger$
|
|
4088
|
+
logger$9.info({ path: this._path }, "Monitor data reset — all rows deleted");
|
|
4086
4089
|
}
|
|
4087
4090
|
getTaskMetricsDateRange() {
|
|
4088
4091
|
const db = this._assertOpen();
|
|
@@ -4099,7 +4102,7 @@ var MonitorDatabaseImpl = class {
|
|
|
4099
4102
|
if (this._db === null) return;
|
|
4100
4103
|
this._db.close();
|
|
4101
4104
|
this._db = null;
|
|
4102
|
-
logger$
|
|
4105
|
+
logger$9.info({ path: this._path }, "Monitor database closed");
|
|
4103
4106
|
}
|
|
4104
4107
|
/**
|
|
4105
4108
|
* Access the raw underlying database for testing purposes only.
|
|
@@ -4112,7 +4115,7 @@ var MonitorDatabaseImpl = class {
|
|
|
4112
4115
|
|
|
4113
4116
|
//#endregion
|
|
4114
4117
|
//#region src/modules/monitor/recommendation-engine.ts
|
|
4115
|
-
const logger$
|
|
4118
|
+
const logger$8 = createLogger("monitor:recommendations");
|
|
4116
4119
|
var RecommendationEngine = class {
|
|
4117
4120
|
_monitorDb;
|
|
4118
4121
|
_filters;
|
|
@@ -4145,7 +4148,7 @@ var RecommendationEngine = class {
|
|
|
4145
4148
|
const sinceDate = new Date(Date.now() - this._historyDays * 24 * 60 * 60 * 1e3).toISOString();
|
|
4146
4149
|
const aggregates = this._monitorDb.getAggregates({ sinceDate });
|
|
4147
4150
|
if (aggregates.length === 0) {
|
|
4148
|
-
logger$
|
|
4151
|
+
logger$8.debug("No performance aggregates found — no recommendations to generate");
|
|
4149
4152
|
return [];
|
|
4150
4153
|
}
|
|
4151
4154
|
const byTaskType = new Map();
|
|
@@ -4210,7 +4213,7 @@ var RecommendationEngine = class {
|
|
|
4210
4213
|
if (confDiff !== 0) return confDiff;
|
|
4211
4214
|
return b.improvement_percentage - a.improvement_percentage;
|
|
4212
4215
|
});
|
|
4213
|
-
logger$
|
|
4216
|
+
logger$8.debug({ count: recommendations.length }, "Generated routing recommendations");
|
|
4214
4217
|
return recommendations;
|
|
4215
4218
|
}
|
|
4216
4219
|
/**
|
|
@@ -4376,7 +4379,7 @@ function generateMonitorReport(monitorDb, options = {}) {
|
|
|
4376
4379
|
|
|
4377
4380
|
//#endregion
|
|
4378
4381
|
//#region src/cli/commands/monitor.ts
|
|
4379
|
-
const logger$
|
|
4382
|
+
const logger$7 = createLogger("monitor-cmd");
|
|
4380
4383
|
const MONITOR_EXIT_SUCCESS = 0;
|
|
4381
4384
|
const MONITOR_EXIT_ERROR = 1;
|
|
4382
4385
|
/**
|
|
@@ -4579,7 +4582,7 @@ async function runMonitorReportAction(options) {
|
|
|
4579
4582
|
} catch (err) {
|
|
4580
4583
|
const message = err instanceof Error ? err.message : String(err);
|
|
4581
4584
|
process.stderr.write(`Error: ${message}\n`);
|
|
4582
|
-
logger$
|
|
4585
|
+
logger$7.error({ err }, "runMonitorReportAction failed");
|
|
4583
4586
|
return MONITOR_EXIT_ERROR;
|
|
4584
4587
|
} finally {
|
|
4585
4588
|
if (monitorDb !== null) try {
|
|
@@ -4641,7 +4644,7 @@ async function runMonitorStatusAction(options) {
|
|
|
4641
4644
|
} catch (err) {
|
|
4642
4645
|
const message = err instanceof Error ? err.message : String(err);
|
|
4643
4646
|
process.stderr.write(`Error: ${message}\n`);
|
|
4644
|
-
logger$
|
|
4647
|
+
logger$7.error({ err }, "runMonitorStatusAction failed");
|
|
4645
4648
|
return MONITOR_EXIT_ERROR;
|
|
4646
4649
|
} finally {
|
|
4647
4650
|
if (monitorDb !== null) try {
|
|
@@ -4676,7 +4679,7 @@ async function runMonitorResetAction(options) {
|
|
|
4676
4679
|
} catch (err) {
|
|
4677
4680
|
const message = err instanceof Error ? err.message : String(err);
|
|
4678
4681
|
process.stderr.write(`Error: ${message}\n`);
|
|
4679
|
-
logger$
|
|
4682
|
+
logger$7.error({ err }, "runMonitorResetAction failed");
|
|
4680
4683
|
return MONITOR_EXIT_ERROR;
|
|
4681
4684
|
} finally {
|
|
4682
4685
|
if (monitorDb !== null) try {
|
|
@@ -4724,7 +4727,7 @@ async function runMonitorRecommendationsAction(options) {
|
|
|
4724
4727
|
} catch (err) {
|
|
4725
4728
|
const message = err instanceof Error ? err.message : String(err);
|
|
4726
4729
|
process.stderr.write(`Error: ${message}\n`);
|
|
4727
|
-
logger$
|
|
4730
|
+
logger$7.error({ err }, "runMonitorRecommendationsAction failed");
|
|
4728
4731
|
return MONITOR_EXIT_ERROR;
|
|
4729
4732
|
} finally {
|
|
4730
4733
|
if (monitorDb !== null) try {
|
|
@@ -4802,7 +4805,7 @@ function registerMonitorCommand(program, version = "0.0.0", projectRoot = proces
|
|
|
4802
4805
|
|
|
4803
4806
|
//#endregion
|
|
4804
4807
|
//#region src/modules/git-worktree/git-worktree-manager-impl.ts
|
|
4805
|
-
const logger$
|
|
4808
|
+
const logger$6 = createLogger("git-worktree");
|
|
4806
4809
|
const BRANCH_PREFIX = "substrate/task-";
|
|
4807
4810
|
const DEFAULT_WORKTREE_BASE = ".substrate-worktrees";
|
|
4808
4811
|
var GitWorktreeManagerImpl = class {
|
|
@@ -4821,7 +4824,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
4821
4824
|
this._db = db;
|
|
4822
4825
|
this._onTaskReady = ({ taskId }) => {
|
|
4823
4826
|
this._handleTaskReady(taskId).catch((err) => {
|
|
4824
|
-
logger$
|
|
4827
|
+
logger$6.error({
|
|
4825
4828
|
taskId,
|
|
4826
4829
|
err
|
|
4827
4830
|
}, "Unhandled error in _handleTaskReady");
|
|
@@ -4835,40 +4838,40 @@ var GitWorktreeManagerImpl = class {
|
|
|
4835
4838
|
};
|
|
4836
4839
|
}
|
|
4837
4840
|
async initialize() {
|
|
4838
|
-
logger$
|
|
4841
|
+
logger$6.info({ projectRoot: this._projectRoot }, "GitWorktreeManager.initialize()");
|
|
4839
4842
|
await this.verifyGitVersion();
|
|
4840
4843
|
const cleaned = await this.cleanupAllWorktrees();
|
|
4841
|
-
if (cleaned > 0) logger$
|
|
4844
|
+
if (cleaned > 0) logger$6.info({ cleaned }, "Recovered orphaned worktrees on startup");
|
|
4842
4845
|
this._eventBus.on("task:ready", this._onTaskReady);
|
|
4843
4846
|
this._eventBus.on("task:complete", this._onTaskComplete);
|
|
4844
4847
|
this._eventBus.on("task:failed", this._onTaskFailed);
|
|
4845
|
-
logger$
|
|
4848
|
+
logger$6.info("GitWorktreeManager initialized");
|
|
4846
4849
|
}
|
|
4847
4850
|
async shutdown() {
|
|
4848
|
-
logger$
|
|
4851
|
+
logger$6.info("GitWorktreeManager.shutdown()");
|
|
4849
4852
|
this._eventBus.off("task:ready", this._onTaskReady);
|
|
4850
4853
|
this._eventBus.off("task:complete", this._onTaskComplete);
|
|
4851
4854
|
this._eventBus.off("task:failed", this._onTaskFailed);
|
|
4852
4855
|
await this.cleanupAllWorktrees();
|
|
4853
|
-
logger$
|
|
4856
|
+
logger$6.info("GitWorktreeManager shutdown complete");
|
|
4854
4857
|
}
|
|
4855
4858
|
async _handleTaskReady(taskId) {
|
|
4856
|
-
logger$
|
|
4859
|
+
logger$6.debug({ taskId }, "task:ready — creating worktree");
|
|
4857
4860
|
try {
|
|
4858
4861
|
await this.createWorktree(taskId);
|
|
4859
4862
|
} catch (err) {
|
|
4860
|
-
logger$
|
|
4863
|
+
logger$6.error({
|
|
4861
4864
|
taskId,
|
|
4862
4865
|
err
|
|
4863
4866
|
}, "Failed to create worktree for task");
|
|
4864
4867
|
}
|
|
4865
4868
|
}
|
|
4866
4869
|
async _handleTaskDone(taskId) {
|
|
4867
|
-
logger$
|
|
4870
|
+
logger$6.debug({ taskId }, "task done — cleaning up worktree");
|
|
4868
4871
|
try {
|
|
4869
4872
|
await this.cleanupWorktree(taskId);
|
|
4870
4873
|
} catch (err) {
|
|
4871
|
-
logger$
|
|
4874
|
+
logger$6.warn({
|
|
4872
4875
|
taskId,
|
|
4873
4876
|
err
|
|
4874
4877
|
}, "Failed to cleanup worktree for task");
|
|
@@ -4878,7 +4881,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
4878
4881
|
if (!taskId || taskId.trim().length === 0) throw new Error("createWorktree: taskId must be a non-empty string");
|
|
4879
4882
|
const branchName = BRANCH_PREFIX + taskId;
|
|
4880
4883
|
const worktreePath = this.getWorktreePath(taskId);
|
|
4881
|
-
logger$
|
|
4884
|
+
logger$6.debug({
|
|
4882
4885
|
taskId,
|
|
4883
4886
|
branchName,
|
|
4884
4887
|
worktreePath,
|
|
@@ -4898,7 +4901,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
4898
4901
|
worktreePath,
|
|
4899
4902
|
createdAt
|
|
4900
4903
|
};
|
|
4901
|
-
logger$
|
|
4904
|
+
logger$6.info({
|
|
4902
4905
|
taskId,
|
|
4903
4906
|
branchName,
|
|
4904
4907
|
worktreePath
|
|
@@ -4908,7 +4911,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
4908
4911
|
async cleanupWorktree(taskId) {
|
|
4909
4912
|
const branchName = BRANCH_PREFIX + taskId;
|
|
4910
4913
|
const worktreePath = this.getWorktreePath(taskId);
|
|
4911
|
-
logger$
|
|
4914
|
+
logger$6.debug({
|
|
4912
4915
|
taskId,
|
|
4913
4916
|
branchName,
|
|
4914
4917
|
worktreePath
|
|
@@ -4918,7 +4921,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
4918
4921
|
await access$1(worktreePath);
|
|
4919
4922
|
worktreeExists = true;
|
|
4920
4923
|
} catch {
|
|
4921
|
-
logger$
|
|
4924
|
+
logger$6.debug({
|
|
4922
4925
|
taskId,
|
|
4923
4926
|
worktreePath
|
|
4924
4927
|
}, "cleanupWorktree: worktree does not exist, skipping removal");
|
|
@@ -4926,7 +4929,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
4926
4929
|
if (worktreeExists) try {
|
|
4927
4930
|
await removeWorktree(worktreePath, this._projectRoot);
|
|
4928
4931
|
} catch (err) {
|
|
4929
|
-
logger$
|
|
4932
|
+
logger$6.warn({
|
|
4930
4933
|
taskId,
|
|
4931
4934
|
worktreePath,
|
|
4932
4935
|
err
|
|
@@ -4935,7 +4938,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
4935
4938
|
try {
|
|
4936
4939
|
await removeBranch(branchName, this._projectRoot);
|
|
4937
4940
|
} catch (err) {
|
|
4938
|
-
logger$
|
|
4941
|
+
logger$6.warn({
|
|
4939
4942
|
taskId,
|
|
4940
4943
|
branchName,
|
|
4941
4944
|
err
|
|
@@ -4945,13 +4948,13 @@ var GitWorktreeManagerImpl = class {
|
|
|
4945
4948
|
taskId,
|
|
4946
4949
|
branchName
|
|
4947
4950
|
});
|
|
4948
|
-
logger$
|
|
4951
|
+
logger$6.info({
|
|
4949
4952
|
taskId,
|
|
4950
4953
|
branchName
|
|
4951
4954
|
}, "Worktree cleaned up");
|
|
4952
4955
|
}
|
|
4953
4956
|
async cleanupAllWorktrees() {
|
|
4954
|
-
logger$
|
|
4957
|
+
logger$6.debug({ projectRoot: this._projectRoot }, "cleanupAllWorktrees");
|
|
4955
4958
|
const orphanedPaths = await getOrphanedWorktrees(this._projectRoot, this._baseDirectory);
|
|
4956
4959
|
let cleaned = 0;
|
|
4957
4960
|
for (const worktreePath of orphanedPaths) {
|
|
@@ -4960,12 +4963,12 @@ var GitWorktreeManagerImpl = class {
|
|
|
4960
4963
|
try {
|
|
4961
4964
|
await removeWorktree(worktreePath, this._projectRoot);
|
|
4962
4965
|
worktreeRemoved = true;
|
|
4963
|
-
logger$
|
|
4966
|
+
logger$6.debug({
|
|
4964
4967
|
taskId,
|
|
4965
4968
|
worktreePath
|
|
4966
4969
|
}, "cleanupAllWorktrees: removed orphaned worktree");
|
|
4967
4970
|
} catch (err) {
|
|
4968
|
-
logger$
|
|
4971
|
+
logger$6.warn({
|
|
4969
4972
|
taskId,
|
|
4970
4973
|
worktreePath,
|
|
4971
4974
|
err
|
|
@@ -4975,12 +4978,12 @@ var GitWorktreeManagerImpl = class {
|
|
|
4975
4978
|
let branchRemoved = false;
|
|
4976
4979
|
try {
|
|
4977
4980
|
branchRemoved = await removeBranch(branchName, this._projectRoot);
|
|
4978
|
-
if (branchRemoved) logger$
|
|
4981
|
+
if (branchRemoved) logger$6.debug({
|
|
4979
4982
|
taskId,
|
|
4980
4983
|
branchName
|
|
4981
4984
|
}, "cleanupAllWorktrees: removed orphaned branch");
|
|
4982
4985
|
} catch (err) {
|
|
4983
|
-
logger$
|
|
4986
|
+
logger$6.warn({
|
|
4984
4987
|
taskId,
|
|
4985
4988
|
branchName,
|
|
4986
4989
|
err
|
|
@@ -4988,14 +4991,14 @@ var GitWorktreeManagerImpl = class {
|
|
|
4988
4991
|
}
|
|
4989
4992
|
if (worktreeRemoved) cleaned++;
|
|
4990
4993
|
}
|
|
4991
|
-
if (cleaned > 0) logger$
|
|
4994
|
+
if (cleaned > 0) logger$6.info({ cleaned }, "cleanupAllWorktrees: recovered orphaned worktrees");
|
|
4992
4995
|
return cleaned;
|
|
4993
4996
|
}
|
|
4994
4997
|
async detectConflicts(taskId, targetBranch = "main") {
|
|
4995
4998
|
if (!taskId || taskId.trim().length === 0) throw new Error("detectConflicts: taskId must be a non-empty string");
|
|
4996
4999
|
const branchName = BRANCH_PREFIX + taskId;
|
|
4997
5000
|
const worktreePath = this.getWorktreePath(taskId);
|
|
4998
|
-
logger$
|
|
5001
|
+
logger$6.debug({
|
|
4999
5002
|
taskId,
|
|
5000
5003
|
branchName,
|
|
5001
5004
|
targetBranch
|
|
@@ -5023,7 +5026,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
5023
5026
|
branch: branchName,
|
|
5024
5027
|
conflictingFiles: report.conflictingFiles
|
|
5025
5028
|
});
|
|
5026
|
-
logger$
|
|
5029
|
+
logger$6.info({
|
|
5027
5030
|
taskId,
|
|
5028
5031
|
hasConflicts: report.hasConflicts,
|
|
5029
5032
|
conflictCount: conflictingFiles.length
|
|
@@ -5033,14 +5036,14 @@ var GitWorktreeManagerImpl = class {
|
|
|
5033
5036
|
async mergeWorktree(taskId, targetBranch = "main") {
|
|
5034
5037
|
if (!taskId || taskId.trim().length === 0) throw new Error("mergeWorktree: taskId must be a non-empty string");
|
|
5035
5038
|
const branchName = BRANCH_PREFIX + taskId;
|
|
5036
|
-
logger$
|
|
5039
|
+
logger$6.debug({
|
|
5037
5040
|
taskId,
|
|
5038
5041
|
branchName,
|
|
5039
5042
|
targetBranch
|
|
5040
5043
|
}, "mergeWorktree");
|
|
5041
5044
|
const conflictReport = await this.detectConflicts(taskId, targetBranch);
|
|
5042
5045
|
if (conflictReport.hasConflicts) {
|
|
5043
|
-
logger$
|
|
5046
|
+
logger$6.info({
|
|
5044
5047
|
taskId,
|
|
5045
5048
|
conflictCount: conflictReport.conflictingFiles.length
|
|
5046
5049
|
}, "Merge skipped due to conflicts");
|
|
@@ -5062,7 +5065,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
5062
5065
|
success: true,
|
|
5063
5066
|
mergedFiles
|
|
5064
5067
|
};
|
|
5065
|
-
logger$
|
|
5068
|
+
logger$6.info({
|
|
5066
5069
|
taskId,
|
|
5067
5070
|
branchName,
|
|
5068
5071
|
mergedFileCount: mergedFiles.length
|
|
@@ -5070,7 +5073,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
5070
5073
|
return result;
|
|
5071
5074
|
}
|
|
5072
5075
|
async listWorktrees() {
|
|
5073
|
-
logger$
|
|
5076
|
+
logger$6.debug({
|
|
5074
5077
|
projectRoot: this._projectRoot,
|
|
5075
5078
|
baseDirectory: this._baseDirectory
|
|
5076
5079
|
}, "listWorktrees");
|
|
@@ -5094,7 +5097,7 @@ var GitWorktreeManagerImpl = class {
|
|
|
5094
5097
|
createdAt
|
|
5095
5098
|
});
|
|
5096
5099
|
}
|
|
5097
|
-
logger$
|
|
5100
|
+
logger$6.debug({ count: results.length }, "listWorktrees: found worktrees");
|
|
5098
5101
|
return results;
|
|
5099
5102
|
}
|
|
5100
5103
|
getWorktreePath(taskId) {
|
|
@@ -5114,7 +5117,7 @@ function createGitWorktreeManager(options) {
|
|
|
5114
5117
|
|
|
5115
5118
|
//#endregion
|
|
5116
5119
|
//#region src/cli/commands/merge.ts
|
|
5117
|
-
const logger$
|
|
5120
|
+
const logger$5 = createLogger("merge-cmd");
|
|
5118
5121
|
const MERGE_EXIT_SUCCESS = 0;
|
|
5119
5122
|
const MERGE_EXIT_CONFLICT = 1;
|
|
5120
5123
|
const MERGE_EXIT_ERROR = 2;
|
|
@@ -5152,7 +5155,7 @@ async function mergeTask(taskId, targetBranch, projectRoot) {
|
|
|
5152
5155
|
projectRoot
|
|
5153
5156
|
});
|
|
5154
5157
|
try {
|
|
5155
|
-
logger$
|
|
5158
|
+
logger$5.info({
|
|
5156
5159
|
taskId,
|
|
5157
5160
|
targetBranch
|
|
5158
5161
|
}, "Running conflict detection...");
|
|
@@ -5174,7 +5177,7 @@ async function mergeTask(taskId, targetBranch, projectRoot) {
|
|
|
5174
5177
|
} catch (err) {
|
|
5175
5178
|
const message = err instanceof Error ? err.message : String(err);
|
|
5176
5179
|
console.error(`Error merging task "${taskId}": ${message}`);
|
|
5177
|
-
logger$
|
|
5180
|
+
logger$5.error({
|
|
5178
5181
|
taskId,
|
|
5179
5182
|
err
|
|
5180
5183
|
}, "merge --task failed");
|
|
@@ -5228,7 +5231,7 @@ async function mergeAll(targetBranch, projectRoot, taskIds) {
|
|
|
5228
5231
|
error: message
|
|
5229
5232
|
});
|
|
5230
5233
|
console.log(` Error for task "${taskId}": ${message}`);
|
|
5231
|
-
logger$
|
|
5234
|
+
logger$5.error({
|
|
5232
5235
|
taskId,
|
|
5233
5236
|
err
|
|
5234
5237
|
}, "merge --all: task failed");
|
|
@@ -5281,7 +5284,7 @@ function registerMergeCommand(program, projectRoot = process.cwd()) {
|
|
|
5281
5284
|
|
|
5282
5285
|
//#endregion
|
|
5283
5286
|
//#region src/cli/commands/worktrees.ts
|
|
5284
|
-
const logger$
|
|
5287
|
+
const logger$4 = createLogger("worktrees-cmd");
|
|
5285
5288
|
const WORKTREES_EXIT_SUCCESS = 0;
|
|
5286
5289
|
const WORKTREES_EXIT_ERROR = 1;
|
|
5287
5290
|
/** Valid task statuses for filtering */
|
|
@@ -5408,7 +5411,7 @@ async function listWorktreesAction(options) {
|
|
|
5408
5411
|
try {
|
|
5409
5412
|
worktreeInfos = await manager.listWorktrees();
|
|
5410
5413
|
} catch (err) {
|
|
5411
|
-
logger$
|
|
5414
|
+
logger$4.error({ err }, "Failed to list worktrees");
|
|
5412
5415
|
const message = err instanceof Error ? err.message : String(err);
|
|
5413
5416
|
process.stderr.write(`Error listing worktrees: ${message}\n`);
|
|
5414
5417
|
return WORKTREES_EXIT_ERROR;
|
|
@@ -5435,7 +5438,7 @@ async function listWorktreesAction(options) {
|
|
|
5435
5438
|
} catch (err) {
|
|
5436
5439
|
const message = err instanceof Error ? err.message : String(err);
|
|
5437
5440
|
process.stderr.write(`Error: ${message}\n`);
|
|
5438
|
-
logger$
|
|
5441
|
+
logger$4.error({ err }, "listWorktreesAction failed");
|
|
5439
5442
|
return WORKTREES_EXIT_ERROR;
|
|
5440
5443
|
}
|
|
5441
5444
|
}
|
|
@@ -5476,7 +5479,7 @@ function registerWorktreesCommand(program, version = "0.0.0", projectRoot = proc
|
|
|
5476
5479
|
|
|
5477
5480
|
//#endregion
|
|
5478
5481
|
//#region src/cli/commands/brainstorm.ts
|
|
5479
|
-
const logger$
|
|
5482
|
+
const logger$3 = createLogger("brainstorm-cmd");
|
|
5480
5483
|
/**
|
|
5481
5484
|
* Detect whether the project has existing planning artifacts that indicate
|
|
5482
5485
|
* this is an amendment session (vs. a brand-new project brainstorm).
|
|
@@ -5522,13 +5525,13 @@ async function loadAmendmentContextDocuments(projectRoot) {
|
|
|
5522
5525
|
try {
|
|
5523
5526
|
brief = await readFile(briefPath, "utf-8");
|
|
5524
5527
|
} catch {
|
|
5525
|
-
logger$
|
|
5528
|
+
logger$3.warn({ briefPath }, "product-brief.md not found — continuing without brief context");
|
|
5526
5529
|
process.stderr.write(`Warning: product-brief.md not found at ${briefPath}\n`);
|
|
5527
5530
|
}
|
|
5528
5531
|
try {
|
|
5529
5532
|
prd = await readFile(prdPath, "utf-8");
|
|
5530
5533
|
} catch {
|
|
5531
|
-
logger$
|
|
5534
|
+
logger$3.warn({ prdPath }, "requirements.md not found — continuing without PRD context");
|
|
5532
5535
|
process.stderr.write(`Warning: requirements.md not found at ${prdPath}\n`);
|
|
5533
5536
|
}
|
|
5534
5537
|
return {
|
|
@@ -5737,7 +5740,7 @@ async function dispatchToPersonas(userPrompt, context, llmDispatch) {
|
|
|
5737
5740
|
}
|
|
5738
5741
|
];
|
|
5739
5742
|
const defaultDispatch = async (prompt, personaName) => {
|
|
5740
|
-
logger$
|
|
5743
|
+
logger$3.debug({
|
|
5741
5744
|
personaName,
|
|
5742
5745
|
promptLength: prompt.length
|
|
5743
5746
|
}, "Dispatching to persona (stub mode)");
|
|
@@ -5754,7 +5757,7 @@ async function dispatchToPersonas(userPrompt, context, llmDispatch) {
|
|
|
5754
5757
|
};
|
|
5755
5758
|
} catch (err) {
|
|
5756
5759
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5757
|
-
logger$
|
|
5760
|
+
logger$3.error({
|
|
5758
5761
|
err,
|
|
5759
5762
|
personaName: persona.name
|
|
5760
5763
|
}, "Persona dispatch failed");
|
|
@@ -5906,7 +5909,7 @@ async function runBrainstormSession(options, llmDispatch, rlInterface) {
|
|
|
5906
5909
|
}
|
|
5907
5910
|
});
|
|
5908
5911
|
rl.on("error", (err) => {
|
|
5909
|
-
logger$
|
|
5912
|
+
logger$3.error({ err }, "readline error");
|
|
5910
5913
|
if (!sessionEnded) endSession(false);
|
|
5911
5914
|
});
|
|
5912
5915
|
});
|
|
@@ -6496,7 +6499,7 @@ function renderReadinessReport(decisions) {
|
|
|
6496
6499
|
|
|
6497
6500
|
//#endregion
|
|
6498
6501
|
//#region src/cli/commands/export.ts
|
|
6499
|
-
const logger$
|
|
6502
|
+
const logger$2 = createLogger("export-cmd");
|
|
6500
6503
|
/**
|
|
6501
6504
|
* Execute the export action.
|
|
6502
6505
|
* Returns an exit code (0 = success, 1 = error).
|
|
@@ -6623,7 +6626,7 @@ async function runExportAction(options) {
|
|
|
6623
6626
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6624
6627
|
if (outputFormat === "json") process.stdout.write(JSON.stringify({ error: msg }) + "\n");
|
|
6625
6628
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
6626
|
-
logger$
|
|
6629
|
+
logger$2.error({ err }, "export action failed");
|
|
6627
6630
|
return 1;
|
|
6628
6631
|
} finally {
|
|
6629
6632
|
if (dbWrapper !== void 0) try {
|
|
@@ -6645,6 +6648,220 @@ function registerExportCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
6645
6648
|
});
|
|
6646
6649
|
}
|
|
6647
6650
|
|
|
6651
|
+
//#endregion
|
|
6652
|
+
//#region src/persistence/queries/retry-escalated.ts
|
|
6653
|
+
/**
|
|
6654
|
+
* Query the decision store for escalation-diagnosis decisions and classify
|
|
6655
|
+
* each story key as retryable or skipped.
|
|
6656
|
+
*
|
|
6657
|
+
* Key format in the DB: `{storyKey}:{runId}`
|
|
6658
|
+
*
|
|
6659
|
+
* - When `runId` is provided, only decisions whose key contains that runId
|
|
6660
|
+
* are considered (AC5 scoping).
|
|
6661
|
+
* - When `runId` is omitted, the runId of the last (most recently created)
|
|
6662
|
+
* escalation-diagnosis decision is used as the default (AC1 defaulting).
|
|
6663
|
+
*
|
|
6664
|
+
* @param db The SQLite database connection
|
|
6665
|
+
* @param runId Optional run ID to scope the query
|
|
6666
|
+
*/
|
|
6667
|
+
function getRetryableEscalations(db, runId) {
|
|
6668
|
+
const decisions = getDecisionsByCategory(db, ESCALATION_DIAGNOSIS);
|
|
6669
|
+
const result = {
|
|
6670
|
+
retryable: [],
|
|
6671
|
+
skipped: []
|
|
6672
|
+
};
|
|
6673
|
+
if (decisions.length === 0) return result;
|
|
6674
|
+
const parsed = [];
|
|
6675
|
+
for (const decision of decisions) {
|
|
6676
|
+
const colonIdx = decision.key.indexOf(":");
|
|
6677
|
+
if (colonIdx === -1) continue;
|
|
6678
|
+
const storyKey = decision.key.slice(0, colonIdx);
|
|
6679
|
+
const decisionRunId = decision.key.slice(colonIdx + 1);
|
|
6680
|
+
let diagnosis;
|
|
6681
|
+
try {
|
|
6682
|
+
diagnosis = JSON.parse(decision.value);
|
|
6683
|
+
} catch {
|
|
6684
|
+
continue;
|
|
6685
|
+
}
|
|
6686
|
+
parsed.push({
|
|
6687
|
+
storyKey,
|
|
6688
|
+
decisionRunId,
|
|
6689
|
+
diagnosis
|
|
6690
|
+
});
|
|
6691
|
+
}
|
|
6692
|
+
if (parsed.length === 0) return result;
|
|
6693
|
+
const effectiveRunId = runId ?? parsed[parsed.length - 1].decisionRunId;
|
|
6694
|
+
const lastEntryByKey = new Map();
|
|
6695
|
+
for (const entry of parsed) {
|
|
6696
|
+
if (entry.decisionRunId !== effectiveRunId) continue;
|
|
6697
|
+
lastEntryByKey.set(entry.storyKey, entry);
|
|
6698
|
+
}
|
|
6699
|
+
for (const [storyKey, entry] of lastEntryByKey.entries()) {
|
|
6700
|
+
const { recommendedAction } = entry.diagnosis;
|
|
6701
|
+
if (recommendedAction === "retry-targeted") result.retryable.push(storyKey);
|
|
6702
|
+
else if (recommendedAction === "human-intervention") result.skipped.push({
|
|
6703
|
+
key: storyKey,
|
|
6704
|
+
reason: "needs human review"
|
|
6705
|
+
});
|
|
6706
|
+
else if (recommendedAction === "split-story") result.skipped.push({
|
|
6707
|
+
key: storyKey,
|
|
6708
|
+
reason: "story should be split"
|
|
6709
|
+
});
|
|
6710
|
+
}
|
|
6711
|
+
return result;
|
|
6712
|
+
}
|
|
6713
|
+
|
|
6714
|
+
//#endregion
|
|
6715
|
+
//#region src/cli/commands/retry-escalated.ts
|
|
6716
|
+
const logger$1 = createLogger("retry-escalated-cmd");
|
|
6717
|
+
async function runRetryEscalatedAction(options) {
|
|
6718
|
+
const { runId, dryRun, outputFormat, projectRoot, concurrency, pack: packName, registry: injectedRegistry } = options;
|
|
6719
|
+
const dbRoot = await resolveMainRepoRoot(projectRoot);
|
|
6720
|
+
const dbPath = join(dbRoot, ".substrate", "substrate.db");
|
|
6721
|
+
if (!existsSync(dbPath)) {
|
|
6722
|
+
const errorMsg = `Decision store not initialized. Run 'substrate init' first.`;
|
|
6723
|
+
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
|
|
6724
|
+
else process.stderr.write(`Error: ${errorMsg}\n`);
|
|
6725
|
+
return 1;
|
|
6726
|
+
}
|
|
6727
|
+
const dbWrapper = new DatabaseWrapper(dbPath);
|
|
6728
|
+
try {
|
|
6729
|
+
dbWrapper.open();
|
|
6730
|
+
runMigrations(dbWrapper.db);
|
|
6731
|
+
const db = dbWrapper.db;
|
|
6732
|
+
const { retryable, skipped } = getRetryableEscalations(db, runId);
|
|
6733
|
+
if (retryable.length === 0) {
|
|
6734
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({
|
|
6735
|
+
retryKeys: [],
|
|
6736
|
+
skippedKeys: skipped
|
|
6737
|
+
}, "json", true) + "\n");
|
|
6738
|
+
else process.stdout.write("No retry-targeted escalations found.\n");
|
|
6739
|
+
return 0;
|
|
6740
|
+
}
|
|
6741
|
+
if (dryRun) {
|
|
6742
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({
|
|
6743
|
+
retryKeys: retryable,
|
|
6744
|
+
skippedKeys: skipped
|
|
6745
|
+
}, "json", true) + "\n");
|
|
6746
|
+
else {
|
|
6747
|
+
const count = retryable.length;
|
|
6748
|
+
process.stdout.write(`Retrying: ${count} ${count === 1 ? "story" : "stories"} — ${retryable.join(", ")}\n`);
|
|
6749
|
+
for (const s of skipped) process.stdout.write(`Skipping: ${s.key} (${s.reason})\n`);
|
|
6750
|
+
}
|
|
6751
|
+
return 0;
|
|
6752
|
+
}
|
|
6753
|
+
const packPath = join(projectRoot, "packs", packName);
|
|
6754
|
+
const packLoader = createPackLoader();
|
|
6755
|
+
let pack;
|
|
6756
|
+
try {
|
|
6757
|
+
pack = await packLoader.load(packPath);
|
|
6758
|
+
} catch (err) {
|
|
6759
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6760
|
+
const errorMsg = `Methodology pack '${packName}' not found. Run 'substrate init' first.\n${msg}`;
|
|
6761
|
+
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
|
|
6762
|
+
else process.stderr.write(`Error: ${errorMsg}\n`);
|
|
6763
|
+
return 1;
|
|
6764
|
+
}
|
|
6765
|
+
if (outputFormat === "human") {
|
|
6766
|
+
const count = retryable.length;
|
|
6767
|
+
process.stdout.write(`Retrying: ${count} ${count === 1 ? "story" : "stories"} — ${retryable.join(", ")}\n`);
|
|
6768
|
+
for (const s of skipped) process.stdout.write(`Skipping: ${s.key} (${s.reason})\n`);
|
|
6769
|
+
}
|
|
6770
|
+
const pipelineRun = createPipelineRun(db, {
|
|
6771
|
+
methodology: pack.manifest.name,
|
|
6772
|
+
start_phase: "implementation",
|
|
6773
|
+
config_json: JSON.stringify({
|
|
6774
|
+
storyKeys: retryable,
|
|
6775
|
+
concurrency,
|
|
6776
|
+
retryRun: true
|
|
6777
|
+
})
|
|
6778
|
+
});
|
|
6779
|
+
const eventBus = createEventBus();
|
|
6780
|
+
const contextCompiler = createContextCompiler({ db });
|
|
6781
|
+
const adapterRegistry = injectedRegistry ?? new AdapterRegistry();
|
|
6782
|
+
if (injectedRegistry === void 0) await adapterRegistry.discoverAndRegister();
|
|
6783
|
+
const dispatcher = createDispatcher({
|
|
6784
|
+
eventBus,
|
|
6785
|
+
adapterRegistry
|
|
6786
|
+
});
|
|
6787
|
+
const orchestrator = createImplementationOrchestrator({
|
|
6788
|
+
db,
|
|
6789
|
+
pack,
|
|
6790
|
+
contextCompiler,
|
|
6791
|
+
dispatcher,
|
|
6792
|
+
eventBus,
|
|
6793
|
+
config: {
|
|
6794
|
+
maxConcurrency: concurrency,
|
|
6795
|
+
maxReviewCycles: 2,
|
|
6796
|
+
pipelineRunId: pipelineRun.id
|
|
6797
|
+
},
|
|
6798
|
+
projectRoot
|
|
6799
|
+
});
|
|
6800
|
+
eventBus.on("orchestrator:story-phase-complete", (payload) => {
|
|
6801
|
+
try {
|
|
6802
|
+
const result = payload.result;
|
|
6803
|
+
if (result?.tokenUsage !== void 0) {
|
|
6804
|
+
const { input, output } = result.tokenUsage;
|
|
6805
|
+
const costUsd = (input * 3 + output * 15) / 1e6;
|
|
6806
|
+
addTokenUsage(db, pipelineRun.id, {
|
|
6807
|
+
phase: payload.phase,
|
|
6808
|
+
agent: "claude-code",
|
|
6809
|
+
input_tokens: input,
|
|
6810
|
+
output_tokens: output,
|
|
6811
|
+
cost_usd: costUsd
|
|
6812
|
+
});
|
|
6813
|
+
}
|
|
6814
|
+
} catch (err) {
|
|
6815
|
+
logger$1.warn({ err }, "Failed to record token usage");
|
|
6816
|
+
}
|
|
6817
|
+
});
|
|
6818
|
+
if (outputFormat === "human") {
|
|
6819
|
+
eventBus.on("orchestrator:story-complete", (payload) => {
|
|
6820
|
+
process.stdout.write(` [COMPLETE] ${payload.storyKey} (${payload.reviewCycles} review cycle(s))\n`);
|
|
6821
|
+
});
|
|
6822
|
+
eventBus.on("orchestrator:story-escalated", (payload) => {
|
|
6823
|
+
process.stdout.write(` [ESCALATED] ${payload.storyKey}: ${payload.lastVerdict}\n`);
|
|
6824
|
+
});
|
|
6825
|
+
}
|
|
6826
|
+
await orchestrator.run(retryable);
|
|
6827
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({
|
|
6828
|
+
retryKeys: retryable,
|
|
6829
|
+
skippedKeys: skipped
|
|
6830
|
+
}, "json", true) + "\n");
|
|
6831
|
+
else process.stdout.write("[RETRY] Complete\n");
|
|
6832
|
+
return 0;
|
|
6833
|
+
} catch (err) {
|
|
6834
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6835
|
+
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
6836
|
+
else process.stderr.write(`Error: ${msg}\n`);
|
|
6837
|
+
logger$1.error({ err }, "retry-escalated failed");
|
|
6838
|
+
return 1;
|
|
6839
|
+
} finally {
|
|
6840
|
+
try {
|
|
6841
|
+
dbWrapper.close();
|
|
6842
|
+
} catch {}
|
|
6843
|
+
}
|
|
6844
|
+
}
|
|
6845
|
+
function registerRetryEscalatedCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
|
|
6846
|
+
program.command("retry-escalated").description("Retry escalated stories flagged as retry-targeted by escalation diagnosis").option("--run-id <id>", "Scope to a specific pipeline run ID (defaults to latest run with escalations)").option("--dry-run", "Print retryable and skipped stories without invoking the orchestrator").option("--concurrency <n>", "Maximum parallel story executions", (v) => {
|
|
6847
|
+
const n = parseInt(v, 10);
|
|
6848
|
+
if (isNaN(n) || n < 1) throw new Error(`--concurrency must be a positive integer, got: ${v}`);
|
|
6849
|
+
return n;
|
|
6850
|
+
}, 3).option("--pack <name>", "Methodology pack name", "bmad").option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").action(async (opts) => {
|
|
6851
|
+
const outputFormat = opts.outputFormat === "json" ? "json" : "human";
|
|
6852
|
+
const exitCode = await runRetryEscalatedAction({
|
|
6853
|
+
runId: opts.runId,
|
|
6854
|
+
dryRun: opts.dryRun === true,
|
|
6855
|
+
outputFormat,
|
|
6856
|
+
projectRoot: opts.projectRoot,
|
|
6857
|
+
concurrency: opts.concurrency,
|
|
6858
|
+
pack: opts.pack,
|
|
6859
|
+
registry
|
|
6860
|
+
});
|
|
6861
|
+
process.exitCode = exitCode;
|
|
6862
|
+
});
|
|
6863
|
+
}
|
|
6864
|
+
|
|
6648
6865
|
//#endregion
|
|
6649
6866
|
//#region src/cli/index.ts
|
|
6650
6867
|
process.setMaxListeners(20);
|
|
@@ -6680,16 +6897,19 @@ async function createProgram() {
|
|
|
6680
6897
|
const version = await getPackageVersion();
|
|
6681
6898
|
const program = new Command();
|
|
6682
6899
|
program.name("substrate").description("Substrate - Autonomous implementation pipeline for AI coding agents").version(version, "-v, --version", "Output the current version");
|
|
6683
|
-
|
|
6900
|
+
const registry = new AdapterRegistry();
|
|
6901
|
+
await registry.discoverAndRegister();
|
|
6902
|
+
registerAdaptersCommand(program, version, registry);
|
|
6684
6903
|
registerInitCommand(program, version);
|
|
6685
6904
|
registerConfigCommand(program, version);
|
|
6686
|
-
registerRunCommand(program, version);
|
|
6687
|
-
registerResumeCommand(program, version);
|
|
6905
|
+
registerRunCommand(program, version, process.cwd(), registry);
|
|
6906
|
+
registerResumeCommand(program, version, process.cwd(), registry);
|
|
6688
6907
|
registerStatusCommand(program, version);
|
|
6689
|
-
registerAmendCommand(program, version);
|
|
6908
|
+
registerAmendCommand(program, version, process.cwd(), registry);
|
|
6690
6909
|
registerHealthCommand(program, version);
|
|
6691
6910
|
registerSupervisorCommand(program, version);
|
|
6692
6911
|
registerMetricsCommand(program, version);
|
|
6912
|
+
registerRetryEscalatedCommand(program, version, process.cwd(), registry);
|
|
6693
6913
|
registerCostCommand(program, version);
|
|
6694
6914
|
registerMonitorCommand(program, version);
|
|
6695
6915
|
registerMergeCommand(program);
|