substrate-ai 0.3.1 → 0.3.3
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/{adapter-registry-PsWhP_1Q.js → adapter-registry-DHl0W-YB.js} +8 -1
- package/dist/cli/index.js +258 -28
- package/dist/{config-migrator-DSi8KhQC.js → config-migrator-CQmBdKeG.js} +9 -3
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1 -1
- package/dist/{run-D6WEx9l2.js → run-33J0SBp1.js} +2 -2
- package/dist/{run-DNURadtJ.js → run-DI9s014E.js} +1608 -273
- package/dist/{upgrade-Cvwtnwl4.js → upgrade-DO307rFf.js} +2 -2
- package/dist/{upgrade-CImByfkk.js → upgrade-Ex1ukwsm.js} +3 -3
- package/dist/{version-manager-impl-CizNmmLT.js → version-manager-impl-33JYXsqa.js} +2 -2
- package/dist/version-manager-impl-Dk3S31y6.js +4 -0
- package/package.json +1 -1
- package/dist/version-manager-impl-aL5IemIm.js +0 -4
|
@@ -76,6 +76,13 @@ var ClaudeCodeAdapter = class {
|
|
|
76
76
|
const unsetKeys = ["CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT"];
|
|
77
77
|
if (options.billingMode === "api" && options.apiKey) envEntries.ANTHROPIC_API_KEY = options.apiKey;
|
|
78
78
|
else unsetKeys.push("ANTHROPIC_API_KEY");
|
|
79
|
+
if (options.otlpEndpoint !== void 0) {
|
|
80
|
+
envEntries.CLAUDE_CODE_ENABLE_TELEMETRY = "1";
|
|
81
|
+
envEntries.OTEL_LOGS_EXPORTER = "otlp";
|
|
82
|
+
envEntries.OTEL_METRICS_EXPORTER = "otlp";
|
|
83
|
+
envEntries.OTEL_EXPORTER_OTLP_PROTOCOL = "http/json";
|
|
84
|
+
envEntries.OTEL_EXPORTER_OTLP_ENDPOINT = options.otlpEndpoint;
|
|
85
|
+
}
|
|
79
86
|
return {
|
|
80
87
|
binary: "claude",
|
|
81
88
|
args,
|
|
@@ -815,4 +822,4 @@ var AdapterRegistry = class {
|
|
|
815
822
|
|
|
816
823
|
//#endregion
|
|
817
824
|
export { AdapterRegistry, ClaudeCodeAdapter, CodexCLIAdapter, GeminiCLIAdapter };
|
|
818
|
-
//# sourceMappingURL=adapter-registry-
|
|
825
|
+
//# sourceMappingURL=adapter-registry-DHl0W-YB.js.map
|
package/dist/cli/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DatabaseWrapper, DoltNotInstalled, FileStateStore, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, checkDoltInstalled, createConfigSystem, createContextCompiler, createDispatcher, createDoltClient, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStateStore, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, initializeDolt, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveStoryKeys, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-
|
|
2
|
+
import { DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DatabaseWrapper, DoltNotInstalled, FileStateStore, SUBSTRATE_OWNED_SETTINGS_KEYS, TelemetryPersistence, VALID_PHASES, buildPipelineStatusOutput, checkDoltInstalled, createConfigSystem, createContextCompiler, createDispatcher, createDoltClient, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStateStore, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, initializeDolt, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveStoryKeys, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-DI9s014E.js";
|
|
3
3
|
import { createLogger } from "../logger-D2fS2ccL.js";
|
|
4
|
-
import { AdapterRegistry } from "../adapter-registry-
|
|
5
|
-
import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema } from "../config-migrator-
|
|
4
|
+
import { AdapterRegistry } from "../adapter-registry-DHl0W-YB.js";
|
|
5
|
+
import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema } from "../config-migrator-CQmBdKeG.js";
|
|
6
6
|
import { ConfigError, createEventBus } from "../helpers-RL22dYtn.js";
|
|
7
7
|
import { addTokenUsage, createDecision, createPipelineRun, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-Dq4cAA2L.js";
|
|
8
8
|
import { ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-Bovj4fS-.js";
|
|
9
9
|
import { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, verifyGitVersion } from "../git-utils-CtmrZrHS.js";
|
|
10
|
-
import "../version-manager-impl-
|
|
11
|
-
import { registerUpgradeCommand } from "../upgrade-
|
|
10
|
+
import "../version-manager-impl-33JYXsqa.js";
|
|
11
|
+
import { registerUpgradeCommand } from "../upgrade-DO307rFf.js";
|
|
12
12
|
import { Command } from "commander";
|
|
13
13
|
import { fileURLToPath } from "url";
|
|
14
14
|
import { dirname, join, resolve } from "path";
|
|
@@ -2554,6 +2554,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2554
2554
|
runId,
|
|
2555
2555
|
restartCount: 0
|
|
2556
2556
|
};
|
|
2557
|
+
let maxRestartsExhausted = false;
|
|
2557
2558
|
const startTime = Date.now();
|
|
2558
2559
|
function emitEvent(event) {
|
|
2559
2560
|
if (outputFormat === "json") {
|
|
@@ -2664,7 +2665,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2664
2665
|
const expDb = expDbWrapper.db;
|
|
2665
2666
|
const { runRunAction: runPipeline } = await import(
|
|
2666
2667
|
/* @vite-ignore */
|
|
2667
|
-
"../run-
|
|
2668
|
+
"../run-33J0SBp1.js"
|
|
2668
2669
|
);
|
|
2669
2670
|
const runStoryFn = async (opts) => {
|
|
2670
2671
|
const exitCode = await runPipeline({
|
|
@@ -2729,6 +2730,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2729
2730
|
}
|
|
2730
2731
|
return summary.failed.length > 0 || summary.escalated.length > 0 ? 1 : 0;
|
|
2731
2732
|
}
|
|
2733
|
+
if (maxRestartsExhausted) return 2;
|
|
2732
2734
|
const stallResult = await handleStallRecovery(health, state, {
|
|
2733
2735
|
stallThreshold,
|
|
2734
2736
|
maxRestarts,
|
|
@@ -2745,10 +2747,8 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2745
2747
|
emitEvent,
|
|
2746
2748
|
log
|
|
2747
2749
|
});
|
|
2748
|
-
if (stallResult !== null)
|
|
2749
|
-
|
|
2750
|
-
state = stallResult.state;
|
|
2751
|
-
}
|
|
2750
|
+
if (stallResult !== null) if (stallResult.maxRestartsExceeded) maxRestartsExhausted = true;
|
|
2751
|
+
else state = stallResult.state;
|
|
2752
2752
|
await sleep(pollInterval * 1e3);
|
|
2753
2753
|
}
|
|
2754
2754
|
}
|
|
@@ -2778,6 +2778,7 @@ async function runMultiProjectSupervisor(options, deps = {}) {
|
|
|
2778
2778
|
}]));
|
|
2779
2779
|
const doneProjects = new Set();
|
|
2780
2780
|
const projectExitCodes = new Map();
|
|
2781
|
+
const maxRestartsExhaustedProjects = new Set();
|
|
2781
2782
|
const startTime = Date.now();
|
|
2782
2783
|
function emitEvent(event) {
|
|
2783
2784
|
if (outputFormat === "json") {
|
|
@@ -2834,6 +2835,11 @@ async function runMultiProjectSupervisor(options, deps = {}) {
|
|
|
2834
2835
|
projectExitCodes.set(projectRoot, summary.failed.length > 0 || summary.escalated.length > 0 ? 1 : 0);
|
|
2835
2836
|
continue;
|
|
2836
2837
|
}
|
|
2838
|
+
if (maxRestartsExhaustedProjects.has(projectRoot)) {
|
|
2839
|
+
doneProjects.add(projectRoot);
|
|
2840
|
+
projectExitCodes.set(projectRoot, 2);
|
|
2841
|
+
continue;
|
|
2842
|
+
}
|
|
2837
2843
|
const stallResult = await handleStallRecovery(health, state, {
|
|
2838
2844
|
stallThreshold,
|
|
2839
2845
|
maxRestarts,
|
|
@@ -2846,10 +2852,8 @@ async function runMultiProjectSupervisor(options, deps = {}) {
|
|
|
2846
2852
|
}),
|
|
2847
2853
|
log: (msg) => log(`[${projectRoot}] ${msg}`)
|
|
2848
2854
|
});
|
|
2849
|
-
if (stallResult !== null) if (stallResult.maxRestartsExceeded)
|
|
2850
|
-
|
|
2851
|
-
projectExitCodes.set(projectRoot, 2);
|
|
2852
|
-
} else states.set(projectRoot, stallResult.state);
|
|
2855
|
+
if (stallResult !== null) if (stallResult.maxRestartsExceeded) maxRestartsExhaustedProjects.add(projectRoot);
|
|
2856
|
+
else states.set(projectRoot, stallResult.state);
|
|
2853
2857
|
}
|
|
2854
2858
|
if (doneProjects.size >= projects.length) {
|
|
2855
2859
|
const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
|
|
@@ -2908,8 +2912,217 @@ function registerSupervisorCommand(program, _version = "0.0.0", projectRoot = pr
|
|
|
2908
2912
|
//#endregion
|
|
2909
2913
|
//#region src/cli/commands/metrics.ts
|
|
2910
2914
|
const logger$11 = createLogger("metrics-cmd");
|
|
2915
|
+
async function openTelemetryDb(dbPath) {
|
|
2916
|
+
if (!existsSync(dbPath)) return null;
|
|
2917
|
+
try {
|
|
2918
|
+
const db = new Database(dbPath, { readonly: true });
|
|
2919
|
+
return db;
|
|
2920
|
+
} catch {
|
|
2921
|
+
return null;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
function rowsToEfficiencyScore(rows) {
|
|
2925
|
+
return rows;
|
|
2926
|
+
}
|
|
2927
|
+
function printEfficiencyTable(scores) {
|
|
2928
|
+
process.stdout.write(`\nEfficiency Scores (${scores.length} records)\n`);
|
|
2929
|
+
process.stdout.write("─".repeat(80) + "\n");
|
|
2930
|
+
process.stdout.write(` ${"Story Key".padEnd(14)} ${"Score".padStart(6)} ${"Cache Hit%".padStart(11)} ${"I/O Ratio".padStart(10)} ${"Ctx Mgmt".padStart(9)} Model\n`);
|
|
2931
|
+
process.stdout.write(" " + "─".repeat(76) + "\n");
|
|
2932
|
+
for (const s of scores) {
|
|
2933
|
+
const cacheHitPct = s.totalTurns > 0 ? `${(s.avgCacheHitRate * 100).toFixed(1)}%` : "0.0%";
|
|
2934
|
+
const ioRatio = s.avgIoRatio.toFixed(2);
|
|
2935
|
+
const ctxMgmt = String(Math.round(s.contextManagementSubScore));
|
|
2936
|
+
const model = s.perModelBreakdown.length > 0 ? s.perModelBreakdown[0]?.model ?? "unknown" : "unknown";
|
|
2937
|
+
process.stdout.write(` ${s.storyKey.padEnd(14)} ${String(s.compositeScore).padStart(6)} ${cacheHitPct.padStart(11)} ${ioRatio.padStart(10)} ${ctxMgmt.padStart(9)} ${model}\n`);
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
function printRecommendationTable(recs) {
|
|
2941
|
+
process.stdout.write(`\nRecommendations (${recs.length} records)\n`);
|
|
2942
|
+
process.stdout.write("─".repeat(80) + "\n");
|
|
2943
|
+
process.stdout.write(` ${"Story".padEnd(12)} ${"Severity".padEnd(10)} ${"Rule".padEnd(24)} ${"Savings Tokens".padStart(15)}\n`);
|
|
2944
|
+
process.stdout.write(" " + "─".repeat(64) + "\n");
|
|
2945
|
+
for (const r of recs) {
|
|
2946
|
+
const savings = r.potentialSavingsTokens !== void 0 ? String(r.potentialSavingsTokens) : "-";
|
|
2947
|
+
process.stdout.write(` ${r.storyKey.padEnd(12)} ${r.severity.padEnd(10)} ${r.ruleId.padEnd(24)} ${savings.padStart(15)}\n`);
|
|
2948
|
+
process.stdout.write(` ${r.title}\n`);
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
function printTurnTable(turns, storyKey) {
|
|
2952
|
+
process.stdout.write(`\nTurn Analysis: ${storyKey} (${turns.length} turns)\n`);
|
|
2953
|
+
process.stdout.write("─".repeat(80) + "\n");
|
|
2954
|
+
process.stdout.write(` ${"#".padStart(4)} ${"Tokens In".padStart(10)} ${"Tok Out".padStart(8)} ${"Cache Hit%".padStart(11)} ${"Ctx Size".padStart(9)} Spike\n`);
|
|
2955
|
+
process.stdout.write(" " + "─".repeat(60) + "\n");
|
|
2956
|
+
for (const t of turns) {
|
|
2957
|
+
const cacheHitPct = t.inputTokens > 0 ? `${(t.cacheHitRate * 100).toFixed(1)}%` : "0.0%";
|
|
2958
|
+
const spike = t.isContextSpike ? " ⚠" : "";
|
|
2959
|
+
process.stdout.write(` ${String(t.turnNumber).padStart(4)} ${t.inputTokens.toLocaleString().padStart(10)} ${t.outputTokens.toLocaleString().padStart(8)} ${cacheHitPct.padStart(11)} ${t.contextSize.toLocaleString().padStart(9)}${spike}\n`);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
function printConsumerTable(consumers, storyKey) {
|
|
2963
|
+
process.stdout.write(`\nConsumer Stats: ${storyKey} (${consumers.length} consumers)\n`);
|
|
2964
|
+
process.stdout.write("─".repeat(80) + "\n");
|
|
2965
|
+
process.stdout.write(` ${"Consumer Key".padEnd(36)} ${"Category".padEnd(20)} ${"Tokens".padStart(10)} ${"%".padStart(7)}\n`);
|
|
2966
|
+
process.stdout.write(" " + "─".repeat(76) + "\n");
|
|
2967
|
+
for (const c of consumers) {
|
|
2968
|
+
const key = c.consumerKey.slice(0, 34);
|
|
2969
|
+
const pct = `${c.percentage.toFixed(1)}%`;
|
|
2970
|
+
process.stdout.write(` ${key.padEnd(36)} ${c.category.padEnd(20)} ${c.totalTokens.toLocaleString().padStart(10)} ${pct.padStart(7)}\n`);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
function printCategoryTable(stats, label) {
|
|
2974
|
+
process.stdout.write(`\nCategory Stats${label} (${stats.length} categories)\n`);
|
|
2975
|
+
process.stdout.write("─".repeat(80) + "\n");
|
|
2976
|
+
process.stdout.write(` ${"Category".padEnd(22)} ${"Tokens".padStart(12)} ${"%".padStart(8)} ${"Events".padStart(8)} ${"Avg/Event".padStart(10)} Trend\n`);
|
|
2977
|
+
process.stdout.write(" " + "─".repeat(70) + "\n");
|
|
2978
|
+
const sorted = [...stats].sort((a, b) => b.totalTokens - a.totalTokens);
|
|
2979
|
+
for (const c of sorted) {
|
|
2980
|
+
const pct = `${c.percentage.toFixed(1)}%`;
|
|
2981
|
+
const avg = c.avgTokensPerEvent.toFixed(0);
|
|
2982
|
+
process.stdout.write(` ${c.category.padEnd(22)} ${c.totalTokens.toLocaleString().padStart(12)} ${pct.padStart(8)} ${String(c.eventCount).padStart(8)} ${avg.padStart(10)} ${c.trend}\n`);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2911
2985
|
async function runMetricsAction(options) {
|
|
2912
|
-
const { outputFormat, projectRoot, limit = 10, compare, tagBaseline, analysis, sprint, story, taskType, since, aggregate } = options;
|
|
2986
|
+
const { outputFormat, projectRoot, limit = 10, compare, tagBaseline, analysis, sprint, story, taskType, since, aggregate, efficiency, recommendations, turns, consumers, categories, compareStories } = options;
|
|
2987
|
+
const telemetryModes = [
|
|
2988
|
+
efficiency,
|
|
2989
|
+
recommendations,
|
|
2990
|
+
turns,
|
|
2991
|
+
consumers,
|
|
2992
|
+
categories,
|
|
2993
|
+
compareStories
|
|
2994
|
+
].filter(Boolean);
|
|
2995
|
+
if (telemetryModes.length > 1) {
|
|
2996
|
+
process.stderr.write("Error: --efficiency, --recommendations, --turns, --consumers, --categories, and --compare-stories are mutually exclusive\n");
|
|
2997
|
+
return 1;
|
|
2998
|
+
}
|
|
2999
|
+
const hasTelemetryMode = telemetryModes.length > 0;
|
|
3000
|
+
if (hasTelemetryMode && (compare !== void 0 || tagBaseline !== void 0 || analysis !== void 0)) {
|
|
3001
|
+
process.stderr.write("Error: telemetry modes (--efficiency, --recommendations, --turns, --consumers, --categories, --compare-stories) cannot be combined with --compare, --tag-baseline, or --analysis\n");
|
|
3002
|
+
return 1;
|
|
3003
|
+
}
|
|
3004
|
+
if (hasTelemetryMode) {
|
|
3005
|
+
const dbRoot$1 = await resolveMainRepoRoot(projectRoot);
|
|
3006
|
+
const dbPath$1 = join(dbRoot$1, ".substrate", "substrate.db");
|
|
3007
|
+
const doltStatePath = join(dbRoot$1, ".substrate", "state", ".dolt");
|
|
3008
|
+
const doltExists = existsSync(doltStatePath);
|
|
3009
|
+
if (!doltExists && !existsSync(dbPath$1)) {
|
|
3010
|
+
const msg = "No telemetry data yet — run a pipeline with `telemetry.enabled: true`";
|
|
3011
|
+
if (turns !== void 0 || consumers !== void 0) {
|
|
3012
|
+
process.stderr.write(`Error: ${msg}\n`);
|
|
3013
|
+
return 1;
|
|
3014
|
+
}
|
|
3015
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({ message: msg }, "json", true) + "\n");
|
|
3016
|
+
else process.stdout.write(msg + "\n");
|
|
3017
|
+
return 0;
|
|
3018
|
+
}
|
|
3019
|
+
const sqliteDb = await openTelemetryDb(dbPath$1);
|
|
3020
|
+
if (sqliteDb === null) {
|
|
3021
|
+
const msg = "No telemetry data yet — run a pipeline with `telemetry.enabled: true`";
|
|
3022
|
+
if (turns !== void 0 || consumers !== void 0) {
|
|
3023
|
+
process.stderr.write(`Error: ${msg}\n`);
|
|
3024
|
+
return 1;
|
|
3025
|
+
}
|
|
3026
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({ message: msg }, "json", true) + "\n");
|
|
3027
|
+
else process.stdout.write(msg + "\n");
|
|
3028
|
+
return 0;
|
|
3029
|
+
}
|
|
3030
|
+
try {
|
|
3031
|
+
const telemetryPersistence = new TelemetryPersistence(sqliteDb);
|
|
3032
|
+
telemetryPersistence.initSchema();
|
|
3033
|
+
if (efficiency === true) {
|
|
3034
|
+
const scores = await telemetryPersistence.getEfficiencyScores(20);
|
|
3035
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({ efficiency: rowsToEfficiencyScore(scores) }, "json", true) + "\n");
|
|
3036
|
+
else printEfficiencyTable(scores);
|
|
3037
|
+
return 0;
|
|
3038
|
+
}
|
|
3039
|
+
if (recommendations === true) {
|
|
3040
|
+
const recs = story !== void 0 ? await telemetryPersistence.getRecommendations(story) : await telemetryPersistence.getAllRecommendations(50);
|
|
3041
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({
|
|
3042
|
+
recommendations: recs,
|
|
3043
|
+
...story !== void 0 && { storyKey: story }
|
|
3044
|
+
}, "json", true) + "\n");
|
|
3045
|
+
else if (recs.length === 0) {
|
|
3046
|
+
const msg = story !== void 0 ? `No recommendations found for story '${story}'` : "No recommendations yet — run a pipeline with `telemetry.enabled: true`";
|
|
3047
|
+
process.stdout.write(msg + "\n");
|
|
3048
|
+
} else printRecommendationTable(recs);
|
|
3049
|
+
return 0;
|
|
3050
|
+
}
|
|
3051
|
+
if (turns !== void 0) {
|
|
3052
|
+
const turnData = await telemetryPersistence.getTurnAnalysis(turns);
|
|
3053
|
+
if (turnData.length === 0) {
|
|
3054
|
+
const msg = `No turn analysis data found for story '${turns}'`;
|
|
3055
|
+
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
3056
|
+
else process.stderr.write(`Error: ${msg}\n`);
|
|
3057
|
+
return 1;
|
|
3058
|
+
}
|
|
3059
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({ turns: turnData }, "json", true) + "\n");
|
|
3060
|
+
else printTurnTable(turnData, turns);
|
|
3061
|
+
return 0;
|
|
3062
|
+
}
|
|
3063
|
+
if (consumers !== void 0) {
|
|
3064
|
+
const consumerData = await telemetryPersistence.getConsumerStats(consumers);
|
|
3065
|
+
if (consumerData.length === 0) {
|
|
3066
|
+
const msg = `No consumer stats found for story '${consumers}'`;
|
|
3067
|
+
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
3068
|
+
else process.stderr.write(`Error: ${msg}\n`);
|
|
3069
|
+
return 1;
|
|
3070
|
+
}
|
|
3071
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({ consumers: consumerData }, "json", true) + "\n");
|
|
3072
|
+
else printConsumerTable(consumerData, consumers);
|
|
3073
|
+
return 0;
|
|
3074
|
+
}
|
|
3075
|
+
if (categories === true) {
|
|
3076
|
+
const storyKey = story;
|
|
3077
|
+
const categoryData = await telemetryPersistence.getCategoryStats(storyKey ?? "");
|
|
3078
|
+
const label = storyKey !== void 0 ? `: ${storyKey}` : "";
|
|
3079
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({
|
|
3080
|
+
categories: categoryData,
|
|
3081
|
+
storyKey
|
|
3082
|
+
}, "json", true) + "\n");
|
|
3083
|
+
else printCategoryTable(categoryData, label);
|
|
3084
|
+
return 0;
|
|
3085
|
+
}
|
|
3086
|
+
if (compareStories !== void 0) {
|
|
3087
|
+
const [keyA, keyB] = compareStories;
|
|
3088
|
+
const [scoreA, scoreB] = await Promise.all([telemetryPersistence.getEfficiencyScore(keyA), telemetryPersistence.getEfficiencyScore(keyB)]);
|
|
3089
|
+
if (scoreA === null || scoreB === null) {
|
|
3090
|
+
const missing = [scoreA === null ? keyA : null, scoreB === null ? keyB : null].filter(Boolean).join(", ");
|
|
3091
|
+
const msg = `No efficiency score found for story: ${missing}`;
|
|
3092
|
+
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
3093
|
+
else process.stderr.write(`Error: ${msg}\n`);
|
|
3094
|
+
return 1;
|
|
3095
|
+
}
|
|
3096
|
+
const delta = {
|
|
3097
|
+
compositeScore: scoreB.compositeScore - scoreA.compositeScore,
|
|
3098
|
+
cacheHitSubScore: scoreB.cacheHitSubScore - scoreA.cacheHitSubScore,
|
|
3099
|
+
ioRatioSubScore: scoreB.ioRatioSubScore - scoreA.ioRatioSubScore,
|
|
3100
|
+
contextManagementSubScore: scoreB.contextManagementSubScore - scoreA.contextManagementSubScore
|
|
3101
|
+
};
|
|
3102
|
+
if (outputFormat === "json") process.stdout.write(formatOutput({
|
|
3103
|
+
storyA: scoreA,
|
|
3104
|
+
storyB: scoreB,
|
|
3105
|
+
delta
|
|
3106
|
+
}, "json", true) + "\n");
|
|
3107
|
+
else {
|
|
3108
|
+
const sign = (n) => n > 0 ? "+" : "";
|
|
3109
|
+
process.stdout.write(`\nEfficiency Comparison: ${keyA} vs ${keyB}\n`);
|
|
3110
|
+
process.stdout.write("─".repeat(80) + "\n");
|
|
3111
|
+
process.stdout.write(` ${"Metric".padEnd(30)} ${keyA.padStart(12)} ${keyB.padStart(12)} ${"Delta".padStart(10)}\n`);
|
|
3112
|
+
process.stdout.write(" " + "─".repeat(66) + "\n");
|
|
3113
|
+
process.stdout.write(` ${"Composite Score".padEnd(30)} ${String(scoreA.compositeScore).padStart(12)} ${String(scoreB.compositeScore).padStart(12)} ${`${sign(delta.compositeScore)}${delta.compositeScore}`.padStart(10)}\n`);
|
|
3114
|
+
process.stdout.write(` ${"Cache Hit Sub-Score".padEnd(30)} ${scoreA.cacheHitSubScore.toFixed(1).padStart(12)} ${scoreB.cacheHitSubScore.toFixed(1).padStart(12)} ${`${sign(delta.cacheHitSubScore)}${delta.cacheHitSubScore.toFixed(1)}`.padStart(10)}\n`);
|
|
3115
|
+
process.stdout.write(` ${"I/O Ratio Sub-Score".padEnd(30)} ${scoreA.ioRatioSubScore.toFixed(1).padStart(12)} ${scoreB.ioRatioSubScore.toFixed(1).padStart(12)} ${`${sign(delta.ioRatioSubScore)}${delta.ioRatioSubScore.toFixed(1)}`.padStart(10)}\n`);
|
|
3116
|
+
process.stdout.write(` ${"Context Mgmt Sub-Score".padEnd(30)} ${scoreA.contextManagementSubScore.toFixed(1).padStart(12)} ${scoreB.contextManagementSubScore.toFixed(1).padStart(12)} ${`${sign(delta.contextManagementSubScore)}${delta.contextManagementSubScore.toFixed(1)}`.padStart(10)}\n`);
|
|
3117
|
+
}
|
|
3118
|
+
return 0;
|
|
3119
|
+
}
|
|
3120
|
+
} finally {
|
|
3121
|
+
try {
|
|
3122
|
+
sqliteDb.close();
|
|
3123
|
+
} catch {}
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
2913
3126
|
if (analysis !== void 0) {
|
|
2914
3127
|
const dbRoot$1 = await resolveMainRepoRoot(projectRoot);
|
|
2915
3128
|
const reportBase = join(dbRoot$1, "_bmad-output", "supervisor-reports", `${analysis}-analysis`);
|
|
@@ -3142,26 +3355,43 @@ async function runMetricsAction(options) {
|
|
|
3142
3355
|
}
|
|
3143
3356
|
}
|
|
3144
3357
|
function registerMetricsCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
|
|
3145
|
-
program.command("metrics").description("Show historical pipeline run metrics and cross-run comparison").option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").option("--limit <n>", "Number of runs to show (default: 10)", (v) => parseInt(v, 10), 10).option("--compare <run-id-a,run-id-b>", "Compare two runs side-by-side (comma-separated IDs, e.g. abc123,def456)").option("--tag-baseline <run-id>", "Mark a run as the performance baseline").option("--analysis <run-id>", "Read and output the analysis report for the specified run (AC5 of Story 17-3)").option("--sprint <sprint>", "Filter StateStore metrics by sprint (e.g. sprint-1)").option("--story <story-key>", "Filter StateStore metrics by story key (e.g. 26-1)").option("--task-type <type>", "Filter StateStore metrics by task type (e.g. dev-story)").option("--since <iso-date>", "Filter StateStore metrics at or after this ISO timestamp").option("--aggregate", "Aggregate StateStore metrics grouped by task_type").action(async (opts) => {
|
|
3358
|
+
program.command("metrics").description("Show historical pipeline run metrics and cross-run comparison").option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").option("--limit <n>", "Number of runs to show (default: 10)", (v) => parseInt(v, 10), 10).option("--compare <run-id-a,run-id-b>", "Compare two runs side-by-side (comma-separated IDs, e.g. abc123,def456)").option("--tag-baseline <run-id>", "Mark a run as the performance baseline").option("--analysis <run-id>", "Read and output the analysis report for the specified run (AC5 of Story 17-3)").option("--sprint <sprint>", "Filter StateStore metrics by sprint (e.g. sprint-1)").option("--story <story-key>", "Filter StateStore metrics by story key (e.g. 26-1)").option("--task-type <type>", "Filter StateStore metrics by task type (e.g. dev-story)").option("--since <iso-date>", "Filter StateStore metrics at or after this ISO timestamp").option("--aggregate", "Aggregate StateStore metrics grouped by task_type").option("--efficiency", "Show telemetry efficiency scores for recent stories").option("--recommendations", "Show all telemetry recommendations across stories").option("--turns <storyKey>", "Show per-turn analysis for a specific story").option("--consumers <storyKey>", "Show consumer stats for a specific story").option("--categories", "Show category stats (optionally scoped by --story <storyKey>)").option("--compare-stories <storyA,storyB>", "Compare efficiency scores of two stories side-by-side (comma-separated keys)").action(async (opts) => {
|
|
3146
3359
|
const outputFormat = opts.outputFormat === "json" ? "json" : "human";
|
|
3147
3360
|
let compareIds;
|
|
3148
3361
|
if (opts.compare !== void 0) {
|
|
3149
3362
|
const parts = opts.compare.split(",").map((s) => s.trim());
|
|
3150
3363
|
if (parts.length === 2 && parts[0] && parts[1]) compareIds = [parts[0], parts[1]];
|
|
3151
3364
|
}
|
|
3152
|
-
|
|
3365
|
+
let compareStoriesIds;
|
|
3366
|
+
if (opts.compareStories !== void 0) {
|
|
3367
|
+
const parts = opts.compareStories.split(",").map((s) => s.trim());
|
|
3368
|
+
if (parts.length === 2 && parts[0] && parts[1]) compareStoriesIds = [parts[0], parts[1]];
|
|
3369
|
+
else {
|
|
3370
|
+
process.stderr.write("Error: --compare-stories requires exactly two comma-separated story keys\n");
|
|
3371
|
+
process.exitCode = 1;
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
const metricsOpts = {
|
|
3153
3376
|
outputFormat,
|
|
3154
3377
|
projectRoot: opts.projectRoot,
|
|
3155
3378
|
limit: opts.limit,
|
|
3156
|
-
compare: compareIds,
|
|
3157
|
-
tagBaseline: opts.tagBaseline,
|
|
3158
|
-
analysis: opts.analysis,
|
|
3159
|
-
sprint: opts.sprint,
|
|
3160
|
-
story: opts.story,
|
|
3161
|
-
taskType: opts.taskType,
|
|
3162
|
-
since: opts.since,
|
|
3163
|
-
aggregate: opts.aggregate
|
|
3164
|
-
|
|
3379
|
+
...compareIds !== void 0 && { compare: compareIds },
|
|
3380
|
+
...opts.tagBaseline !== void 0 && { tagBaseline: opts.tagBaseline },
|
|
3381
|
+
...opts.analysis !== void 0 && { analysis: opts.analysis },
|
|
3382
|
+
...opts.sprint !== void 0 && { sprint: opts.sprint },
|
|
3383
|
+
...opts.story !== void 0 && { story: opts.story },
|
|
3384
|
+
...opts.taskType !== void 0 && { taskType: opts.taskType },
|
|
3385
|
+
...opts.since !== void 0 && { since: opts.since },
|
|
3386
|
+
...opts.aggregate !== void 0 && { aggregate: opts.aggregate },
|
|
3387
|
+
...opts.efficiency !== void 0 && { efficiency: opts.efficiency },
|
|
3388
|
+
...opts.recommendations !== void 0 && { recommendations: opts.recommendations },
|
|
3389
|
+
...opts.turns !== void 0 && { turns: opts.turns },
|
|
3390
|
+
...opts.consumers !== void 0 && { consumers: opts.consumers },
|
|
3391
|
+
...opts.categories !== void 0 && { categories: opts.categories },
|
|
3392
|
+
...compareStoriesIds !== void 0 && { compareStories: compareStoriesIds }
|
|
3393
|
+
};
|
|
3394
|
+
const exitCode = await runMetricsAction(metricsOpts);
|
|
3165
3395
|
process.exitCode = exitCode;
|
|
3166
3396
|
});
|
|
3167
3397
|
}
|
|
@@ -7226,8 +7456,8 @@ async function createProgram() {
|
|
|
7226
7456
|
/** Fire-and-forget startup version check (story 8.3, AC3/AC5) */
|
|
7227
7457
|
function checkForUpdatesInBackground(currentVersion) {
|
|
7228
7458
|
if (process.env.SUBSTRATE_NO_UPDATE_CHECK === "1") return;
|
|
7229
|
-
import("../upgrade-
|
|
7230
|
-
const { createVersionManager } = await import("../version-manager-impl-
|
|
7459
|
+
import("../upgrade-Ex1ukwsm.js").then(async () => {
|
|
7460
|
+
const { createVersionManager } = await import("../version-manager-impl-Dk3S31y6.js");
|
|
7231
7461
|
const vm = createVersionManager();
|
|
7232
7462
|
const result = await vm.checkForUpdates();
|
|
7233
7463
|
if (result.updateAvailable) {
|
|
@@ -79,6 +79,10 @@ const TokenCeilingsSchema = z.object({
|
|
|
79
79
|
"test-plan": z.number().int().positive("test-plan token ceiling must be a positive integer").optional(),
|
|
80
80
|
"test-expansion": z.number().int().positive("test-expansion token ceiling must be a positive integer").optional()
|
|
81
81
|
});
|
|
82
|
+
const TelemetryConfigSchema = z.object({
|
|
83
|
+
enabled: z.boolean().default(false),
|
|
84
|
+
port: z.number().int().min(1).max(65535).default(4318)
|
|
85
|
+
}).strict();
|
|
82
86
|
/** Current supported config format version */
|
|
83
87
|
const CURRENT_CONFIG_FORMAT_VERSION = "1";
|
|
84
88
|
/** Current supported task graph version */
|
|
@@ -94,7 +98,8 @@ const SubstrateConfigSchema = z.object({
|
|
|
94
98
|
providers: ProvidersSchema,
|
|
95
99
|
cost_tracker: CostTrackerConfigSchema.optional(),
|
|
96
100
|
budget: BudgetConfigSchema.optional(),
|
|
97
|
-
token_ceilings: TokenCeilingsSchema.optional()
|
|
101
|
+
token_ceilings: TokenCeilingsSchema.optional(),
|
|
102
|
+
telemetry: TelemetryConfigSchema.optional()
|
|
98
103
|
}).strict();
|
|
99
104
|
const PartialProviderConfigSchema = ProviderConfigSchema.partial();
|
|
100
105
|
const PartialGlobalSettingsSchema = GlobalSettingsSchema.partial();
|
|
@@ -109,7 +114,8 @@ const PartialSubstrateConfigSchema = z.object({
|
|
|
109
114
|
}).partial().optional(),
|
|
110
115
|
cost_tracker: CostTrackerConfigSchema.partial().optional(),
|
|
111
116
|
budget: BudgetConfigSchema.partial().optional(),
|
|
112
|
-
token_ceilings: TokenCeilingsSchema.optional()
|
|
117
|
+
token_ceilings: TokenCeilingsSchema.optional(),
|
|
118
|
+
telemetry: TelemetryConfigSchema.partial().optional()
|
|
113
119
|
}).strict();
|
|
114
120
|
|
|
115
121
|
//#endregion
|
|
@@ -241,4 +247,4 @@ const defaultConfigMigrator = new ConfigMigrator();
|
|
|
241
247
|
|
|
242
248
|
//#endregion
|
|
243
249
|
export { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SUPPORTED_TASK_GRAPH_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator };
|
|
244
|
-
//# sourceMappingURL=config-migrator-
|
|
250
|
+
//# sourceMappingURL=config-migrator-CQmBdKeG.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -780,6 +780,10 @@ declare const SubstrateConfigSchema: z.ZodObject<{
|
|
|
780
780
|
'test-plan': z.ZodOptional<z.ZodNumber>;
|
|
781
781
|
'test-expansion': z.ZodOptional<z.ZodNumber>;
|
|
782
782
|
}, z.core.$strip>>;
|
|
783
|
+
telemetry: z.ZodOptional<z.ZodObject<{
|
|
784
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
785
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
786
|
+
}, z.core.$strict>>;
|
|
783
787
|
}, z.core.$strict>;
|
|
784
788
|
type SubstrateConfig = z.infer<typeof SubstrateConfigSchema>;
|
|
785
789
|
|
|
@@ -1412,6 +1416,13 @@ interface AdapterOptions {
|
|
|
1412
1416
|
apiKey?: string;
|
|
1413
1417
|
/** Optional maximum agentic turns (passed as --max-turns to Claude CLI) */
|
|
1414
1418
|
maxTurns?: number;
|
|
1419
|
+
/**
|
|
1420
|
+
* Optional OTLP endpoint URL for telemetry export (Story 27-9).
|
|
1421
|
+
* When set, injects the 5 OTLP env vars into the spawned process so that
|
|
1422
|
+
* Claude Code exports telemetry to the local IngestionServer.
|
|
1423
|
+
* Example: "http://localhost:4318"
|
|
1424
|
+
*/
|
|
1425
|
+
otlpEndpoint?: string;
|
|
1415
1426
|
}
|
|
1416
1427
|
/**
|
|
1417
1428
|
* Capabilities reported by an adapter for this CLI agent.
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { childLogger, createLogger, logger } from "./logger-D2fS2ccL.js";
|
|
2
|
-
import { AdapterRegistry, ClaudeCodeAdapter, CodexCLIAdapter, GeminiCLIAdapter } from "./adapter-registry-
|
|
2
|
+
import { AdapterRegistry, ClaudeCodeAdapter, CodexCLIAdapter, GeminiCLIAdapter } from "./adapter-registry-DHl0W-YB.js";
|
|
3
3
|
import { AdtError, BudgetExceededError, ConfigError, ConfigIncompatibleFormatError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError, assertDefined, createEventBus, createTuiApp, deepClone, formatDuration, generateId, isPlainObject, isTuiCapable, printNonTtyWarning, sleep, withRetry } from "./helpers-RL22dYtn.js";
|
|
4
4
|
|
|
5
5
|
//#region src/core/di.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { registerRunCommand, runRunAction } from "./run-
|
|
1
|
+
import { registerRunCommand, runRunAction } from "./run-DI9s014E.js";
|
|
2
2
|
import "./logger-D2fS2ccL.js";
|
|
3
|
-
import "./config-migrator-
|
|
3
|
+
import "./config-migrator-CQmBdKeG.js";
|
|
4
4
|
import "./helpers-RL22dYtn.js";
|
|
5
5
|
import "./decisions-Dq4cAA2L.js";
|
|
6
6
|
import "./operational-Bovj4fS-.js";
|