substrate-ai 0.20.141 → 0.20.142

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import { FileKvStore, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createDatabaseAdapter, createDoltOperatorReader, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion } from "../health-BQRsVP6s.js";
2
+ import { FileKvStore, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createDatabaseAdapter, createDoltOperatorReader, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion } from "../health-DTJaiJOd.js";
3
3
  import { createLogger } from "../logger-KeHncl-f.js";
4
4
  import { createEventBus } from "../helpers-CElYrONe.js";
5
5
  import { AdapterRegistry, BudgetConfigSchema, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, ConfigError, CostTrackerConfigSchema, DEFAULT_CONFIG, DoltClient, DoltNotInstalled, GlobalSettingsSchema, InMemoryDatabaseAdapter, IngestionServer, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProvidersSchema, RoutingRecommender, STORY_METRICS, TelemetryConfigSchema, addTokenUsage, aggregateTokenUsageForRun, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDecision, createPipelineRun, createStderrLogger, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initWorkGraphSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, swallowDebug, tagRunAsBaseline, updatePipelineRun } from "../dist-DiNK6QOD.js";
6
- import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR, GitClient, GrammarLoader, Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createGitWorktreeManager, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runSolutioningPhase, unescape, validateStopAfterFromConflict } from "../run-CRvusFIv.js";
6
+ import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR, GitClient, GrammarLoader, Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createGitWorktreeManager, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runSolutioningPhase, unescape, validateStopAfterFromConflict } from "../run-DHHryElL.js";
7
7
  import "../adapter-registry-DIcrxjH8.js";
8
- import { RunManifest, SupervisorLock, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, parseRuntimeProbes, readCurrentRunId, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck } from "../manifest-read-Doh2Ion5.js";
8
+ import { RunManifest, SupervisorLock, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, parseRuntimeProbes, readCurrentRunId, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck } from "../manifest-read-BgZ6mR5R.js";
9
9
  import "../errors-B21JUhcc.js";
10
10
  import "../routing-DFxoKHDt.js";
11
11
  import { WorkGraphRepository } from "../work-graph-repository-DZyJv5pV.js";
12
12
  import "../decisions-CzSIEeGP.js";
13
13
  import "../decision-router-BAPpON_C.js";
14
- import "../interactive-prompt-CgerI5Y6.js";
14
+ import "../interactive-prompt-OMlqP7Lm.js";
15
15
  import "../recovery-engine-BKGBeBnW.js";
16
16
  import "../version-manager-impl-qFBiO4Eh.js";
17
17
  import { registerUpgradeCommand } from "../upgrade-DB7KzD9c.js";
@@ -670,6 +670,7 @@ const WHOLESALE_SUBSTRATE_IGNORES = new Set([
670
670
  ]);
671
671
  const STAR = ".substrate/*";
672
672
  const CONFIG_NEGATION = "!.substrate/config.yaml";
673
+ const PROFILE_NEGATION = "!.substrate/project-profile.yaml";
673
674
  const CODEX_PROMPTS = ".codex/prompts/";
674
675
  const CODEX_SKILLS = ".codex/skills/";
675
676
  /**
@@ -685,6 +686,9 @@ function computeSubstrateGitignore(existing) {
685
686
  const negIdx = trimmed.lastIndexOf(CONFIG_NEGATION);
686
687
  const negationEffective = negIdx !== -1 && negIdx > starIdx;
687
688
  if (!negationEffective) append.push(CONFIG_NEGATION);
689
+ const profileNegIdx = trimmed.lastIndexOf(PROFILE_NEGATION);
690
+ const profileNegationEffective = profileNegIdx !== -1 && profileNegIdx > starIdx;
691
+ if (!profileNegationEffective) append.push(PROFILE_NEGATION);
688
692
  if (!trimmed.includes(CODEX_PROMPTS)) append.push(CODEX_PROMPTS);
689
693
  if (!trimmed.includes(CODEX_SKILLS)) append.push(CODEX_SKILLS);
690
694
  let content = lines.join("\n");
@@ -925,6 +929,18 @@ async function detectSingleProjectStack(dir) {
925
929
  break;
926
930
  }
927
931
  if (marker.file === "pyproject.toml") {
932
+ const hasUv = await fileExists(path$1.join(dir, "uv.lock"));
933
+ if (hasUv) {
934
+ baseEntry = {
935
+ path: dir,
936
+ language: "python",
937
+ buildTool: "uv",
938
+ buildCommand: "uv sync",
939
+ testCommand: "uv run pytest",
940
+ installCommand: "uv add <package>"
941
+ };
942
+ break;
943
+ }
928
944
  const hasPoetry = await fileExists(path$1.join(dir, "poetry.lock"));
929
945
  if (hasPoetry) {
930
946
  baseEntry = {
@@ -7036,7 +7052,7 @@ async function runStatusAction(options) {
7036
7052
  logger$15.debug({ err }, "Work graph query failed, continuing without work graph data");
7037
7053
  }
7038
7054
  if (run === void 0) {
7039
- const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-Cm-pA1mZ.js");
7055
+ const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-Cf5RjwHi.js");
7040
7056
  const substrateDirPath = join(projectRoot, ".substrate");
7041
7057
  const processInfo = inspectProcessTree$1({
7042
7058
  projectRoot,
@@ -8565,7 +8581,7 @@ async function runSupervisorAction(options, deps = {}) {
8565
8581
  await initSchema(expAdapter);
8566
8582
  const { runRunAction: runPipeline } = await import(
8567
8583
  /* @vite-ignore */
8568
- "../run-BHRkyn_H.js"
8584
+ "../run-DStT56J1.js"
8569
8585
  );
8570
8586
  const runStoryFn = async (opts) => {
8571
8587
  const exitCode = await runPipeline({
@@ -1,7 +1,7 @@
1
- import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-BQRsVP6s.js";
1
+ import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-DTJaiJOd.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./dist-DiNK6QOD.js";
4
- import "./manifest-read-Doh2Ion5.js";
4
+ import "./manifest-read-BgZ6mR5R.js";
5
5
  import "./work-graph-repository-DZyJv5pV.js";
6
6
  import "./decisions-CzSIEeGP.js";
7
7
 
@@ -1,6 +1,6 @@
1
1
  import { createLogger } from "./logger-KeHncl-f.js";
2
2
  import { DoltClient, DoltQueryError, createDatabaseAdapter$1 as createDatabaseAdapter, getLatestRun, getPipelineRunById, initSchema } from "./dist-DiNK6QOD.js";
3
- import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-Doh2Ion5.js";
3
+ import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-BgZ6mR5R.js";
4
4
  import { createRequire } from "module";
5
5
  import { dirname, join } from "path";
6
6
  import { existsSync, readFileSync } from "node:fs";
@@ -1000,4 +1000,4 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
1000
1000
 
1001
1001
  //#endregion
1002
1002
  export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, FileKvStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter$1 as createDatabaseAdapter, createDoltOperatorReader, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, runHealthAction, validateStoryKey };
1003
- //# sourceMappingURL=health-BQRsVP6s.js.map
1003
+ //# sourceMappingURL=health-DTJaiJOd.js.map
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from "./logger-KeHncl-f.js";
2
- import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-Doh2Ion5.js";
2
+ import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-BgZ6mR5R.js";
3
3
  import { join } from "node:path";
4
4
  import { mkdir, readFile, writeFile } from "node:fs/promises";
5
5
  import * as readline from "node:readline";
@@ -180,4 +180,4 @@ async function runInteractivePrompt(decisionContext) {
180
180
 
181
181
  //#endregion
182
182
  export { runInteractivePrompt };
183
- //# sourceMappingURL=interactive-prompt-CgerI5Y6.js.map
183
+ //# sourceMappingURL=interactive-prompt-OMlqP7Lm.js.map
@@ -2043,11 +2043,11 @@ function findCodeEvidence(opts) {
2043
2043
  /** Hard timeout for the build command in milliseconds (FR-V11). */
2044
2044
  const BUILD_CHECK_TIMEOUT_MS = 6e4;
2045
2045
  /** Maximum characters to include in details string from build output. */
2046
- const MAX_OUTPUT_CHARS = 2e3;
2046
+ const MAX_OUTPUT_CHARS$1 = 2e3;
2047
2047
  /** Per-stream tail size cap for structured findings (story 55-1 convention). */
2048
2048
  const TAIL_BYTES = 4 * 1024;
2049
2049
  /** Return the last N bytes of a UTF-8 string, sliced by string length for simplicity. */
2050
- function tail$1(text, bytes = TAIL_BYTES) {
2050
+ function tail$2(text, bytes = TAIL_BYTES) {
2051
2051
  return text.length <= bytes ? text : text.slice(text.length - bytes);
2052
2052
  }
2053
2053
  /**
@@ -2061,11 +2061,9 @@ function tail$1(text, bytes = TAIL_BYTES) {
2061
2061
  * This function inlines the detection logic independently.
2062
2062
  */
2063
2063
  function detectBuildCommand(workingDir) {
2064
+ const profileCommand = readProfileBuildCommand(workingDir);
2065
+ if (profileCommand !== void 0) return profileCommand;
2064
2066
  if (existsSync(join$1(workingDir, "turbo.json"))) return "turbo build";
2065
- if (existsSync(join$1(workingDir, "pnpm-lock.yaml"))) return "pnpm run build";
2066
- if (existsSync(join$1(workingDir, "yarn.lock"))) return "yarn build";
2067
- if (existsSync(join$1(workingDir, "bun.lockb"))) return "bun run build";
2068
- if (existsSync(join$1(workingDir, "package.json"))) return "npm run build";
2069
2067
  const nonNodeMarkers = [
2070
2068
  "pyproject.toml",
2071
2069
  "poetry.lock",
@@ -2074,9 +2072,29 @@ function detectBuildCommand(workingDir) {
2074
2072
  "go.mod"
2075
2073
  ];
2076
2074
  for (const marker of nonNodeMarkers) if (existsSync(join$1(workingDir, marker))) return "";
2075
+ if (existsSync(join$1(workingDir, "pnpm-lock.yaml"))) return "pnpm run build";
2076
+ if (existsSync(join$1(workingDir, "yarn.lock"))) return "yarn build";
2077
+ if (existsSync(join$1(workingDir, "bun.lockb"))) return "bun run build";
2078
+ if (existsSync(join$1(workingDir, "package.json"))) return "npm run build";
2077
2079
  return "";
2078
2080
  }
2079
2081
  /**
2082
+ * Read `project.buildCommand` from `.substrate/project-profile.yaml` under
2083
+ * `workingDir`. Line-based parse (same approach as the monolith's
2084
+ * resolveInstallCommand) to avoid a yaml dependency. Returns undefined when
2085
+ * the profile is absent, unreadable, or has no buildCommand.
2086
+ */
2087
+ function readProfileBuildCommand(workingDir) {
2088
+ const profilePath = join$1(workingDir, ".substrate", "project-profile.yaml");
2089
+ if (!existsSync(profilePath)) return void 0;
2090
+ try {
2091
+ const content = readFileSync(profilePath, "utf-8");
2092
+ const match = content.match(/^\s*buildCommand:\s*['"]?(.+?)['"]?\s*$/m);
2093
+ if (match?.[1] !== void 0 && match[1].length > 0) return match[1];
2094
+ } catch {}
2095
+ return void 0;
2096
+ }
2097
+ /**
2080
2098
  * Runs the project's build command and returns pass/warn/fail based on exit code.
2081
2099
  *
2082
2100
  * AC1: exit code 0 → pass
@@ -2139,8 +2157,8 @@ var BuildCheck = class {
2139
2157
  severity: "error",
2140
2158
  message: `command exceeded ${BUILD_CHECK_TIMEOUT_MS}ms`,
2141
2159
  command: cmd,
2142
- stdoutTail: tail$1(stdout),
2143
- stderrTail: tail$1(stderr),
2160
+ stdoutTail: tail$2(stdout),
2161
+ stderrTail: tail$2(stderr),
2144
2162
  durationMs: duration
2145
2163
  }];
2146
2164
  resolve$2({
@@ -2160,13 +2178,169 @@ var BuildCheck = class {
2160
2178
  findings: []
2161
2179
  });
2162
2180
  else {
2163
- const truncated = output.length > MAX_OUTPUT_CHARS ? output.slice(0, MAX_OUTPUT_CHARS) + "... (truncated)" : output;
2181
+ const truncated = output.length > MAX_OUTPUT_CHARS$1 ? output.slice(0, MAX_OUTPUT_CHARS$1) + "... (truncated)" : output;
2164
2182
  const findings = [{
2165
2183
  category: "build-error",
2166
2184
  severity: "error",
2167
2185
  message: `build failed (exit ${code}): ${truncated}`,
2168
2186
  command: cmd,
2169
2187
  ...code !== null ? { exitCode: code } : {},
2188
+ stdoutTail: tail$2(stdout),
2189
+ stderrTail: tail$2(stderr),
2190
+ durationMs: duration
2191
+ }];
2192
+ resolve$2({
2193
+ status: "fail",
2194
+ details: renderFindings(findings),
2195
+ duration_ms: duration,
2196
+ findings
2197
+ });
2198
+ }
2199
+ });
2200
+ });
2201
+ }
2202
+ };
2203
+
2204
+ //#endregion
2205
+ //#region packages/sdlc/dist/verification/checks/test-suite-check.js
2206
+ /** Hard ceiling for the suite run (ms). Suites are slower than builds. */
2207
+ const TEST_SUITE_CHECK_TIMEOUT_MS = 3e5;
2208
+ /** Cap on captured output embedded in the finding. */
2209
+ const MAX_OUTPUT_CHARS = 4e3;
2210
+ function tail$1(s, n = 2e3) {
2211
+ return s.length > n ? s.slice(-n) : s;
2212
+ }
2213
+ /**
2214
+ * Read `project.testCommand` from `.substrate/project-profile.yaml` under
2215
+ * `workingDir`. Line-based parse (no yaml dependency; same pattern as
2216
+ * detectBuildCommand's profile read). Returns undefined when absent.
2217
+ */
2218
+ function detectTestCommand(workingDir) {
2219
+ const profilePath = join$1(workingDir, ".substrate", "project-profile.yaml");
2220
+ if (!existsSync(profilePath)) return void 0;
2221
+ try {
2222
+ const content = readFileSync(profilePath, "utf-8");
2223
+ const match = content.match(/^\s*testCommand:\s*['"]?(.+?)['"]?\s*$/m);
2224
+ if (match?.[1] !== void 0 && match[1].length > 0) return match[1];
2225
+ } catch {}
2226
+ return void 0;
2227
+ }
2228
+ var TestSuiteCheck = class {
2229
+ name = "test-suite";
2230
+ tier = "A";
2231
+ async run(context) {
2232
+ const start = Date.now();
2233
+ const explicitOverride = context.testCommand !== void 0;
2234
+ const cmd = explicitOverride ? context.testCommand : detectTestCommand(context.workingDir);
2235
+ if (!explicitOverride && process.env.VITEST !== void 0 && process.env.SUBSTRATE_ALLOW_NESTED_TESTS !== "1") {
2236
+ const findings = [{
2237
+ category: "test-suite-skip",
2238
+ severity: "warn",
2239
+ message: "recursion guard: refusing to run an auto-detected test suite from inside a test runner (VITEST is set). Pass an explicit testCommand, or set SUBSTRATE_ALLOW_NESTED_TESTS=1 in a harness that deliberately nests suite execution."
2240
+ }];
2241
+ return {
2242
+ status: "warn",
2243
+ details: renderFindings(findings),
2244
+ duration_ms: Date.now() - start,
2245
+ findings
2246
+ };
2247
+ }
2248
+ if (cmd === void 0 || cmd === "") {
2249
+ const findings = [{
2250
+ category: "test-suite-skip",
2251
+ severity: "warn",
2252
+ message: `no test command configured for project at ${context.workingDir} — set project.testCommand in .substrate/project-profile.yaml (substrate init detects it) so verification runs the real suite`
2253
+ }];
2254
+ return {
2255
+ status: "warn",
2256
+ details: renderFindings(findings),
2257
+ duration_ms: Date.now() - start,
2258
+ findings
2259
+ };
2260
+ }
2261
+ return new Promise((resolve$2) => {
2262
+ const env = { ...process.env };
2263
+ const parentNodeOpts = env["NODE_OPTIONS"] ?? "";
2264
+ if (!parentNodeOpts.includes("--max-old-space-size")) env["NODE_OPTIONS"] = `${parentNodeOpts} --max-old-space-size=2048`.trim();
2265
+ const child = spawn(cmd, [], {
2266
+ cwd: context.workingDir,
2267
+ detached: true,
2268
+ shell: true,
2269
+ env,
2270
+ stdio: [
2271
+ "ignore",
2272
+ "pipe",
2273
+ "pipe"
2274
+ ]
2275
+ });
2276
+ let stdout = "";
2277
+ let stderr = "";
2278
+ let output = "";
2279
+ child.stdout?.on("data", (chunk) => {
2280
+ const s = chunk.toString();
2281
+ stdout += s;
2282
+ output += s;
2283
+ });
2284
+ child.stderr?.on("data", (chunk) => {
2285
+ const s = chunk.toString();
2286
+ stderr += s;
2287
+ output += s;
2288
+ });
2289
+ const timeoutHandle = setTimeout(() => {
2290
+ try {
2291
+ process.kill(-child.pid, "SIGKILL");
2292
+ } catch {}
2293
+ const duration = Date.now() - start;
2294
+ const findings = [{
2295
+ category: "test-suite-timeout",
2296
+ severity: "error",
2297
+ message: `test suite exceeded ${TEST_SUITE_CHECK_TIMEOUT_MS}ms`,
2298
+ command: cmd,
2299
+ stdoutTail: tail$1(stdout),
2300
+ stderrTail: tail$1(stderr),
2301
+ durationMs: duration
2302
+ }];
2303
+ resolve$2({
2304
+ status: "fail",
2305
+ details: renderFindings(findings),
2306
+ duration_ms: duration,
2307
+ findings
2308
+ });
2309
+ }, TEST_SUITE_CHECK_TIMEOUT_MS);
2310
+ child.on("error", (err) => {
2311
+ clearTimeout(timeoutHandle);
2312
+ const duration = Date.now() - start;
2313
+ const findings = [{
2314
+ category: "test-suite-error",
2315
+ severity: "error",
2316
+ message: `test command could not be spawned: ${err.message}`,
2317
+ command: cmd,
2318
+ durationMs: duration
2319
+ }];
2320
+ resolve$2({
2321
+ status: "fail",
2322
+ details: renderFindings(findings),
2323
+ duration_ms: duration,
2324
+ findings
2325
+ });
2326
+ });
2327
+ child.on("close", (code) => {
2328
+ clearTimeout(timeoutHandle);
2329
+ const duration = Date.now() - start;
2330
+ if (code === 0) resolve$2({
2331
+ status: "pass",
2332
+ details: `test suite passed (${cmd})`,
2333
+ duration_ms: duration,
2334
+ findings: []
2335
+ });
2336
+ else {
2337
+ const truncated = output.length > MAX_OUTPUT_CHARS ? output.slice(0, MAX_OUTPUT_CHARS) + "... (truncated)" : output;
2338
+ const findings = [{
2339
+ category: "test-suite-fail",
2340
+ severity: "error",
2341
+ message: `test suite failed (exit ${String(code)}): ${truncated}`,
2342
+ command: cmd,
2343
+ ...code !== null ? { exitCode: code } : {},
2170
2344
  stdoutTail: tail$1(stdout),
2171
2345
  stderrTail: tail$1(stderr),
2172
2346
  durationMs: duration
@@ -4082,6 +4256,7 @@ function createDefaultVerificationPipeline(bus, config) {
4082
4256
  new TrivialOutputCheck(config),
4083
4257
  new AcceptanceCriteriaEvidenceCheck(),
4084
4258
  new BuildCheck(),
4259
+ new TestSuiteCheck(),
4085
4260
  new RuntimeProbeCheck(),
4086
4261
  new SourceAcFidelityCheck(),
4087
4262
  new SourceAcShelloutCheck(),
@@ -5865,4 +6040,4 @@ async function resolveRunManifest(dbRoot, runId) {
5865
6040
 
5866
6041
  //#endregion
5867
6042
  export { FindingsInjector, RunManifest, RuntimeProbeListSchema, SupervisorLock, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, parseRuntimeProbes, readCurrentRunId, renderFindings, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck, runStaleVerificationRecovery };
5868
- //# sourceMappingURL=manifest-read-Doh2Ion5.js.map
6043
+ //# sourceMappingURL=manifest-read-BgZ6mR5R.js.map
@@ -1,6 +1,6 @@
1
1
  import "../../logger-KeHncl-f.js";
2
2
  import "../../dist-DiNK6QOD.js";
3
- import "../../manifest-read-Doh2Ion5.js";
4
- import { runInteractivePrompt } from "../../interactive-prompt-CgerI5Y6.js";
3
+ import "../../manifest-read-BgZ6mR5R.js";
4
+ import { runInteractivePrompt } from "../../interactive-prompt-OMlqP7Lm.js";
5
5
 
6
6
  export { runInteractivePrompt };
@@ -1,11 +1,11 @@
1
- import { BMAD_BASELINE_TOKENS_FULL, FileKvStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, validateStoryKey } from "./health-BQRsVP6s.js";
1
+ import { BMAD_BASELINE_TOKENS_FULL, FileKvStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, validateStoryKey } from "./health-DTJaiJOd.js";
2
2
  import { createLogger } from "./logger-KeHncl-f.js";
3
3
  import { TypedEventBusImpl, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-CElYrONe.js";
4
4
  import { ADVISORY_NOTES, CLAUDE_AUTH_FAILURE_HINT, CODEX_SANDBOX_BLOCK_HINT, Categorizer, ConsumerAnalyzer, DEFAULT_GLOBAL_SETTINGS, DispatcherImpl, DoltClient, ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, EfficiencyScorer, IngestionServer, LogTurnAnalyzer, OPERATIONAL_FINDING, Recommender, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, STORY_METRICS, STORY_OUTCOME, SubstrateConfigSchema, TEST_EXPANSION_FINDING, TEST_PLAN, TelemetryNormalizer, TelemetryPipeline, TurnAnalyzer, addTokenUsage, aggregateTokenUsageForRun, aggregateTokenUsageForStory, callLLM, classifyVersionGap, createConfigSystem, createDatabaseAdapter$1, createDecision, createPipelineRun, createRequirement, createStderrLogger, detectClaudeAuthFailure, detectCodexSandboxBlock, detectInterfaceChanges, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunMetrics, getRunningPipelineRuns, getStoryMetricsForRun, getTokenUsageSummary, initSchema, listRequirements, loadModelRoutingConfig, registerArtifact, swallowDebug, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics } from "./dist-DiNK6QOD.js";
5
- import { FindingsInjector, RunManifest, RuntimeProbeListSchema, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, parseRuntimeProbes, renderFindings, resolveGraphPath, resolveMainRepoRoot, runAcTraceabilityCheck, runStaleVerificationRecovery } from "./manifest-read-Doh2Ion5.js";
5
+ import { FindingsInjector, RunManifest, RuntimeProbeListSchema, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, parseRuntimeProbes, renderFindings, resolveGraphPath, resolveMainRepoRoot, runAcTraceabilityCheck, runStaleVerificationRecovery } from "./manifest-read-BgZ6mR5R.js";
6
6
  import { WorkGraphRepository, detectCycles } from "./work-graph-repository-DZyJv5pV.js";
7
7
  import { deriveExitCode, routeDecision } from "./decision-router-BAPpON_C.js";
8
- import { runInteractivePrompt } from "./interactive-prompt-CgerI5Y6.js";
8
+ import { runInteractivePrompt } from "./interactive-prompt-OMlqP7Lm.js";
9
9
  import { runRecoveryEngine } from "./recovery-engine-BKGBeBnW.js";
10
10
  import { basename, dirname, extname, join } from "path";
11
11
  import { access, readFile, readdir, stat } from "fs/promises";
@@ -557,7 +557,8 @@ var GitWorktreeManagerImpl = class {
557
557
  baseBranch,
558
558
  copyFiles: this._copyFiles
559
559
  }, "createWorktree");
560
- await createWorktree(this._projectRoot, taskId, branchName, baseBranch, this._copyFiles);
560
+ const copyFiles = this._copyFiles.includes(".substrate/project-profile.yaml") ? this._copyFiles : [...this._copyFiles, ".substrate/project-profile.yaml"];
561
+ await createWorktree(this._projectRoot, taskId, branchName, baseBranch, copyFiles);
561
562
  const createdAt = new Date();
562
563
  this._eventBus.emit("worktree:created", {
563
564
  taskId,
@@ -8253,6 +8254,22 @@ function resolveDefaultTestPatterns(projectRoot) {
8253
8254
  //#endregion
8254
8255
  //#region src/modules/compiled-workflows/install-command.ts
8255
8256
  /**
8257
+ * Read a single `<key>: value` line from the project profile under
8258
+ * `projectRoot` (H1.2). Line-based parse, no yaml dependency. Returns
8259
+ * undefined when the profile or key is absent.
8260
+ */
8261
+ function resolveProfileCommand(projectRoot, key) {
8262
+ if (!projectRoot) return void 0;
8263
+ const profilePath = join$1(projectRoot, ".substrate", "project-profile.yaml");
8264
+ if (!existsSync(profilePath)) return void 0;
8265
+ try {
8266
+ const content = readFileSync(profilePath, "utf-8");
8267
+ const match$2 = content.match(new RegExp(`^\\s*${key}:\\s*['"]?(.+?)['"]?\\s*$`, "m"));
8268
+ if (match$2?.[1] && match$2[1].length > 0) return match$2[1];
8269
+ } catch {}
8270
+ return void 0;
8271
+ }
8272
+ /**
8256
8273
  * Returns the install command string for the current project.
8257
8274
  * Used as a template variable ({{install_command}}) in the dev-story prompt.
8258
8275
  */
@@ -8276,6 +8293,7 @@ function resolveInstallCommand(projectRoot) {
8276
8293
  cargo: "cargo add <package>",
8277
8294
  pip: "pip install <package>",
8278
8295
  poetry: "poetry add <package>",
8296
+ uv: "uv add <package>",
8279
8297
  gradle: "add dependency to build.gradle",
8280
8298
  maven: "add dependency to pom.xml"
8281
8299
  };
@@ -8516,7 +8534,7 @@ async function runDevStory(deps, params) {
8516
8534
  },
8517
8535
  {
8518
8536
  name: "verify_command",
8519
- content: deps.pack.manifest.verifyCommand !== false ? deps.pack.manifest.verifyCommand ?? "npx turbo build" : "",
8537
+ content: deps.pack.manifest.verifyCommand !== false ? deps.pack.manifest.verifyCommand ?? resolveProfileCommand(deps.projectRoot, "buildCommand") ?? "" : "",
8520
8538
  priority: "optional"
8521
8539
  },
8522
8540
  {
@@ -11740,6 +11758,8 @@ function shouldRunTscCheck(projectRoot) {
11740
11758
  if (!project) return true;
11741
11759
  const packages = project["packages"];
11742
11760
  if (Array.isArray(packages) && packages.length > 0) return packages.some((p) => p.language === "typescript" || p.language === "javascript");
11761
+ const language = project["language"];
11762
+ if (typeof language === "string" && language.length > 0) return language === "typescript" || language === "javascript";
11743
11763
  const buildCommand = project["buildCommand"];
11744
11764
  if (typeof buildCommand === "string" && buildCommand.length > 0) {
11745
11765
  const tsIndicators = [
@@ -12586,6 +12606,8 @@ function assembleVerificationContext(opts) {
12586
12606
  } catch {
12587
12607
  commitSha = "unknown";
12588
12608
  }
12609
+ const profileBuildCommand = readProfileKey(opts.workingDir, "buildCommand");
12610
+ const profileTestCommand = readProfileKey(opts.workingDir, "testCommand");
12589
12611
  return {
12590
12612
  storyKey: opts.storyKey,
12591
12613
  workingDir: opts.workingDir,
@@ -12596,10 +12618,26 @@ function assembleVerificationContext(opts) {
12596
12618
  devStoryResult: opts.devStoryResult,
12597
12619
  outputTokenCount: opts.outputTokenCount,
12598
12620
  sourceEpicContent: opts.sourceEpicContent,
12599
- runId: opts.runId
12621
+ runId: opts.runId,
12622
+ ...profileBuildCommand !== void 0 ? { buildCommand: profileBuildCommand } : {},
12623
+ ...profileTestCommand !== void 0 ? { testCommand: profileTestCommand } : {}
12600
12624
  };
12601
12625
  }
12602
12626
  /**
12627
+ * Read a single `<key>: value` line from `.substrate/project-profile.yaml`
12628
+ * under `workingDir` (H1.2). Line-based parse — no yaml dependency.
12629
+ */
12630
+ function readProfileKey(workingDir, key) {
12631
+ const profilePath = join$1(workingDir, ".substrate", "project-profile.yaml");
12632
+ if (!existsSync(profilePath)) return void 0;
12633
+ try {
12634
+ const content = readFileSync(profilePath, "utf-8");
12635
+ const match$2 = content.match(new RegExp(`^\\s*${key}:\\s*['"]?(.+?)['"]?\\s*$`, "m"));
12636
+ if (match$2?.[1] && match$2[1].length > 0) return match$2[1];
12637
+ } catch {}
12638
+ return void 0;
12639
+ }
12640
+ /**
12603
12641
  * In-memory store for VerificationSummary results, keyed by story key.
12604
12642
  *
12605
12643
  * Persists for the lifetime of the orchestrator instance. Does NOT write
@@ -48030,4 +48068,4 @@ function registerRunCommand(program, version = "0.0.0", projectRoot = process.cw
48030
48068
 
48031
48069
  //#endregion
48032
48070
  export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR$1 as GLOBSTAR, GitClient, GrammarLoader, Minimatch$1 as Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createGitWorktreeManager, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape$1 as escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runRunAction, runSolutioningPhase, unescape$1 as unescape, validateStopAfterFromConflict, wireNdjsonEmitter };
48033
- //# sourceMappingURL=run-CRvusFIv.js.map
48071
+ //# sourceMappingURL=run-DHHryElL.js.map
@@ -1,14 +1,14 @@
1
- import "./health-BQRsVP6s.js";
1
+ import "./health-DTJaiJOd.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./helpers-CElYrONe.js";
4
4
  import "./dist-DiNK6QOD.js";
5
- import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-CRvusFIv.js";
6
- import "./manifest-read-Doh2Ion5.js";
5
+ import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-DHHryElL.js";
6
+ import "./manifest-read-BgZ6mR5R.js";
7
7
  import "./routing-DFxoKHDt.js";
8
8
  import "./work-graph-repository-DZyJv5pV.js";
9
9
  import "./decisions-CzSIEeGP.js";
10
10
  import "./decision-router-BAPpON_C.js";
11
- import "./interactive-prompt-CgerI5Y6.js";
11
+ import "./interactive-prompt-OMlqP7Lm.js";
12
12
  import "./recovery-engine-BKGBeBnW.js";
13
13
 
14
14
  export { runRunAction };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.20.141",
3
+ "version": "0.20.142",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",