substrate-ai 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7059,17 +7059,35 @@ var DispatcherImpl = class {
|
|
|
7059
7059
|
/**
|
|
7060
7060
|
* Detect the package manager / build system used in a project.
|
|
7061
7061
|
*
|
|
7062
|
-
* Checks for
|
|
7063
|
-
*
|
|
7064
|
-
*
|
|
7065
|
-
*
|
|
7066
|
-
*
|
|
7067
|
-
*
|
|
7062
|
+
* Checks for build system markers in priority order:
|
|
7063
|
+
* 0. `.substrate/project-profile.yaml` → `project.buildCommand` field (most explicit, wins)
|
|
7064
|
+
* 1. `turbo.json` → `turbo build`
|
|
7065
|
+
* 2. Node.js lockfiles → corresponding `<pm> run build`
|
|
7066
|
+
* 3. Python markers (pyproject.toml, poetry.lock, setup.py) → skip (no universal build step)
|
|
7067
|
+
* 4. Rust (Cargo.toml) → skip
|
|
7068
|
+
* 5. Go (go.mod) → skip
|
|
7069
|
+
* 6. No markers found → skip (empty command)
|
|
7068
7070
|
*
|
|
7069
7071
|
* When a non-Node.js project is detected (or nothing is recognized), the
|
|
7070
7072
|
* returned command is '' which causes runBuildVerification() to skip.
|
|
7071
7073
|
*/
|
|
7072
7074
|
function detectPackageManager(projectRoot) {
|
|
7075
|
+
const profilePath = join$1(projectRoot, ".substrate", "project-profile.yaml");
|
|
7076
|
+
if (existsSync$1(profilePath)) try {
|
|
7077
|
+
const raw = readFileSync$1(profilePath, "utf-8");
|
|
7078
|
+
const parsed = yaml.load(raw);
|
|
7079
|
+
const buildCommand = parsed?.project?.buildCommand;
|
|
7080
|
+
if (typeof buildCommand === "string" && buildCommand.length > 0) return {
|
|
7081
|
+
packageManager: "none",
|
|
7082
|
+
lockfile: "project-profile.yaml",
|
|
7083
|
+
command: buildCommand
|
|
7084
|
+
};
|
|
7085
|
+
} catch {}
|
|
7086
|
+
if (existsSync$1(join$1(projectRoot, "turbo.json"))) return {
|
|
7087
|
+
packageManager: "none",
|
|
7088
|
+
lockfile: "turbo.json",
|
|
7089
|
+
command: "turbo build"
|
|
7090
|
+
};
|
|
7073
7091
|
const nodeCandidates = [
|
|
7074
7092
|
{
|
|
7075
7093
|
file: "pnpm-lock.yaml",
|
|
@@ -8938,6 +8956,11 @@ async function getImplementationDecisions(deps) {
|
|
|
8938
8956
|
*
|
|
8939
8957
|
* Returns the matched section content (from heading to next story heading or end),
|
|
8940
8958
|
* or null if no matching section is found (caller falls back to full shard).
|
|
8959
|
+
*
|
|
8960
|
+
* @deprecated Used only as a migration shim for pre-37-0 projects that have
|
|
8961
|
+
* per-epic (key=epicId) shards in the decision store. Post-37-0 shards are
|
|
8962
|
+
* keyed by storyKey directly and do not need extraction. Do not delete until
|
|
8963
|
+
* all per-epic shards have been superseded by per-story shards (AC6).
|
|
8941
8964
|
*/
|
|
8942
8965
|
function extractStorySection(shardContent, storyKey) {
|
|
8943
8966
|
if (!shardContent || !storyKey) return null;
|
|
@@ -8955,13 +8978,26 @@ function extractStorySection(shardContent, storyKey) {
|
|
|
8955
8978
|
}
|
|
8956
8979
|
/**
|
|
8957
8980
|
* Retrieve the epic shard from the pre-fetched implementation decisions.
|
|
8958
|
-
* Looks for decisions with category='epic-shard', key=epicId.
|
|
8959
|
-
* Falls back to reading _bmad-output/epics.md on disk if decisions are empty.
|
|
8960
8981
|
*
|
|
8961
|
-
*
|
|
8982
|
+
* Lookup order (post-37-0 schema):
|
|
8983
|
+
* 1. Direct per-story lookup: category='epic-shard', key=storyKey → AC4
|
|
8984
|
+
* If found, return content immediately — no extractStorySection() needed.
|
|
8985
|
+
* 2. Migration shim (pre-37-0 fallback): category='epic-shard', key=epicId
|
|
8986
|
+
* + extractStorySection() to narrow to the requested story. → AC6
|
|
8987
|
+
* 3. File-based fallback: read epics.md from disk + extractStorySection(). → AC6
|
|
8962
8988
|
*/
|
|
8963
8989
|
function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
8964
8990
|
try {
|
|
8991
|
+
if (storyKey) {
|
|
8992
|
+
const perStoryShard = decisions.find((d) => d.category === "epic-shard" && d.key === storyKey);
|
|
8993
|
+
if (perStoryShard?.value) {
|
|
8994
|
+
logger$20.debug({
|
|
8995
|
+
epicId,
|
|
8996
|
+
storyKey
|
|
8997
|
+
}, "Found per-story epic shard (direct lookup)");
|
|
8998
|
+
return perStoryShard.value;
|
|
8999
|
+
}
|
|
9000
|
+
}
|
|
8965
9001
|
const epicShard = decisions.find((d) => d.category === "epic-shard" && d.key === epicId);
|
|
8966
9002
|
const shardContent = epicShard?.value;
|
|
8967
9003
|
if (shardContent) {
|
|
@@ -8971,7 +9007,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
8971
9007
|
logger$20.debug({
|
|
8972
9008
|
epicId,
|
|
8973
9009
|
storyKey
|
|
8974
|
-
}, "Extracted per-story section from epic shard");
|
|
9010
|
+
}, "Extracted per-story section from epic shard (pre-37-0 fallback)");
|
|
8975
9011
|
return storySection;
|
|
8976
9012
|
}
|
|
8977
9013
|
logger$20.debug({
|
|
@@ -9525,12 +9561,9 @@ function detectDeprecatedStatusField(content) {
|
|
|
9525
9561
|
}
|
|
9526
9562
|
|
|
9527
9563
|
//#endregion
|
|
9528
|
-
//#region src/modules/compiled-workflows/
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
const DEFAULT_TIMEOUT_MS$1 = 18e5;
|
|
9532
|
-
/** Default Vitest test patterns injected when no test-pattern decisions exist */
|
|
9533
|
-
const DEFAULT_VITEST_PATTERNS = `## Test Patterns (defaults)
|
|
9564
|
+
//#region src/modules/compiled-workflows/default-test-patterns.ts
|
|
9565
|
+
/** Default test patterns for Vitest/Jest/Mocha (Node.js ecosystem) */
|
|
9566
|
+
const VITEST_DEFAULT_PATTERNS = `## Test Patterns (defaults)
|
|
9534
9567
|
- Framework: Vitest (NOT jest — --testPathPattern flag does not work, use -- "pattern")
|
|
9535
9568
|
- Mock approach: vi.mock() with hoisting for module-level mocks
|
|
9536
9569
|
- Assertion style: expect().toBe(), expect().toEqual(), expect().toThrow()
|
|
@@ -9540,6 +9573,105 @@ const DEFAULT_VITEST_PATTERNS = `## Test Patterns (defaults)
|
|
|
9540
9573
|
npx vitest run --no-coverage -- "your-module-name"
|
|
9541
9574
|
- Final validation ONLY: npm test 2>&1 | grep -E "Test Files|Tests " | tail -3
|
|
9542
9575
|
- Do NOT run the full suite (npm test) repeatedly — it consumes excessive memory when multiple agents run in parallel`;
|
|
9576
|
+
/** Default test patterns for Go (stdlib testing) */
|
|
9577
|
+
const GO_DEFAULT_PATTERNS = `## Test Patterns (defaults)
|
|
9578
|
+
- Framework: Go test (stdlib)
|
|
9579
|
+
- Test file naming: <module>_test.go alongside source files
|
|
9580
|
+
- Test structure: table-driven tests using t.Run() subtests
|
|
9581
|
+
- Run all tests: go test ./...
|
|
9582
|
+
- Run specific test: go test ./... -v -run TestFunctionName
|
|
9583
|
+
- IMPORTANT: Run targeted tests during development: go test ./pkg/... -v -run TestSpecific
|
|
9584
|
+
- Assertion style: t.Errorf(), t.Fatalf(); use testify if already in go.mod (require.Equal, assert.NoError)`;
|
|
9585
|
+
/** Default test patterns for Gradle (JUnit 5) */
|
|
9586
|
+
const GRADLE_DEFAULT_PATTERNS = `## Test Patterns (defaults)
|
|
9587
|
+
- Framework: JUnit 5 (Gradle)
|
|
9588
|
+
- Test structure: @Test annotated methods in class under src/test/
|
|
9589
|
+
- Run all tests: ./gradlew test
|
|
9590
|
+
- Run specific test: ./gradlew test --tests "com.example.ClassName.methodName"
|
|
9591
|
+
- IMPORTANT: Run targeted tests during development: ./gradlew test --tests "ClassName"
|
|
9592
|
+
- Assertion style: assertThat(...).isEqualTo(...) (AssertJ) or assertEquals (JUnit)`;
|
|
9593
|
+
/** Default test patterns for Maven (JUnit 5) */
|
|
9594
|
+
const MAVEN_DEFAULT_PATTERNS = `## Test Patterns (defaults)
|
|
9595
|
+
- Framework: JUnit 5 (Maven)
|
|
9596
|
+
- Test structure: @Test annotated methods in class under src/test/
|
|
9597
|
+
- Run all tests: mvn test
|
|
9598
|
+
- Run specific test: mvn test -Dtest="ClassName#methodName"
|
|
9599
|
+
- IMPORTANT: Run targeted tests during development: mvn test -Dtest="ClassName"
|
|
9600
|
+
- Assertion style: assertThat(...).isEqualTo(...) (AssertJ) or assertEquals (JUnit)`;
|
|
9601
|
+
/** Default test patterns for Cargo (Rust) */
|
|
9602
|
+
const CARGO_DEFAULT_PATTERNS = `## Test Patterns (defaults)
|
|
9603
|
+
- Framework: Rust test (cargo)
|
|
9604
|
+
- Test file naming: #[cfg(test)] module in same file, or tests/ directory for integration tests
|
|
9605
|
+
- Test structure: #[test] annotated functions
|
|
9606
|
+
- Run all tests: cargo test
|
|
9607
|
+
- Run specific test: cargo test test_function_name
|
|
9608
|
+
- IMPORTANT: Run targeted tests during development: cargo test --lib test_module
|
|
9609
|
+
- Assertion style: assert_eq!, assert!, assert_ne! macros`;
|
|
9610
|
+
/** Default test patterns for pytest (Python) */
|
|
9611
|
+
const PYTEST_DEFAULT_PATTERNS = `## Test Patterns (defaults)
|
|
9612
|
+
- Framework: pytest
|
|
9613
|
+
- Test file naming: test_<module>.py or <module>_test.py
|
|
9614
|
+
- Test structure: test_* functions or Test* classes with test_* methods
|
|
9615
|
+
- Run all tests: pytest
|
|
9616
|
+
- Run specific test: pytest tests/test_foo.py::test_bar -v
|
|
9617
|
+
- IMPORTANT: Run targeted tests during development: pytest -k "test_name" -v
|
|
9618
|
+
- Assertion style: plain assert statements; use pytest.raises() for exceptions`;
|
|
9619
|
+
/**
|
|
9620
|
+
* Resolve the appropriate default test pattern block for the project.
|
|
9621
|
+
*
|
|
9622
|
+
* Algorithm:
|
|
9623
|
+
* 1. If projectRoot is undefined or empty → return VITEST_DEFAULT_PATTERNS
|
|
9624
|
+
* 2. Build profile path: join(projectRoot, '.substrate/project-profile.yaml')
|
|
9625
|
+
* 3. If file does not exist → return VITEST_DEFAULT_PATTERNS
|
|
9626
|
+
* 4. Parse YAML; on error → return VITEST_DEFAULT_PATTERNS
|
|
9627
|
+
* 5. Match project.testCommand (case-insensitive substring):
|
|
9628
|
+
* go test → GO, gradlew/gradle → GRADLE, mvn → MAVEN,
|
|
9629
|
+
* cargo test → CARGO, pytest → PYTEST, vitest/jest/mocha/npm → VITEST
|
|
9630
|
+
* 6. If testCommand unmatched, try project.language:
|
|
9631
|
+
* go → GO, kotlin/java → GRADLE, rust → CARGO, python → PYTEST,
|
|
9632
|
+
* typescript/javascript → VITEST
|
|
9633
|
+
* 7. Nothing matched → return VITEST_DEFAULT_PATTERNS
|
|
9634
|
+
*
|
|
9635
|
+
* @param projectRoot - Absolute path to the project root (or undefined)
|
|
9636
|
+
* @returns Stack-appropriate test pattern block string
|
|
9637
|
+
*/
|
|
9638
|
+
function resolveDefaultTestPatterns(projectRoot) {
|
|
9639
|
+
if (!projectRoot) return VITEST_DEFAULT_PATTERNS;
|
|
9640
|
+
const profilePath = join$1(projectRoot, ".substrate/project-profile.yaml");
|
|
9641
|
+
if (!existsSync$1(profilePath)) return VITEST_DEFAULT_PATTERNS;
|
|
9642
|
+
let profile = null;
|
|
9643
|
+
try {
|
|
9644
|
+
const content = readFileSync$1(profilePath, "utf-8");
|
|
9645
|
+
profile = yaml.load(content);
|
|
9646
|
+
} catch {
|
|
9647
|
+
return VITEST_DEFAULT_PATTERNS;
|
|
9648
|
+
}
|
|
9649
|
+
if (!profile) return VITEST_DEFAULT_PATTERNS;
|
|
9650
|
+
const project = profile["project"];
|
|
9651
|
+
if (!project) return VITEST_DEFAULT_PATTERNS;
|
|
9652
|
+
const testCommand = (project["testCommand"] ?? "").toLowerCase();
|
|
9653
|
+
if (testCommand) {
|
|
9654
|
+
if (testCommand.includes("cargo test")) return CARGO_DEFAULT_PATTERNS;
|
|
9655
|
+
if (testCommand.includes("go test")) return GO_DEFAULT_PATTERNS;
|
|
9656
|
+
if (testCommand.includes("gradlew") || testCommand.includes("gradle")) return GRADLE_DEFAULT_PATTERNS;
|
|
9657
|
+
if (testCommand.includes("mvn")) return MAVEN_DEFAULT_PATTERNS;
|
|
9658
|
+
if (testCommand.includes("pytest")) return PYTEST_DEFAULT_PATTERNS;
|
|
9659
|
+
if (testCommand.includes("vitest") || testCommand.includes("jest") || testCommand.includes("mocha") || testCommand.includes("npm")) return VITEST_DEFAULT_PATTERNS;
|
|
9660
|
+
}
|
|
9661
|
+
const language = (project["language"] ?? "").toLowerCase();
|
|
9662
|
+
if (language === "go") return GO_DEFAULT_PATTERNS;
|
|
9663
|
+
if (language === "kotlin" || language === "java") return GRADLE_DEFAULT_PATTERNS;
|
|
9664
|
+
if (language === "rust") return CARGO_DEFAULT_PATTERNS;
|
|
9665
|
+
if (language === "python") return PYTEST_DEFAULT_PATTERNS;
|
|
9666
|
+
if (language === "typescript" || language === "javascript") return VITEST_DEFAULT_PATTERNS;
|
|
9667
|
+
return VITEST_DEFAULT_PATTERNS;
|
|
9668
|
+
}
|
|
9669
|
+
|
|
9670
|
+
//#endregion
|
|
9671
|
+
//#region src/modules/compiled-workflows/dev-story.ts
|
|
9672
|
+
const logger$16 = createLogger("compiled-workflows:dev-story");
|
|
9673
|
+
/** Default timeout for dev-story dispatches in milliseconds (30 min) */
|
|
9674
|
+
const DEFAULT_TIMEOUT_MS$1 = 18e5;
|
|
9543
9675
|
/**
|
|
9544
9676
|
* Execute the compiled dev-story workflow.
|
|
9545
9677
|
*
|
|
@@ -9654,8 +9786,8 @@ async function runDevStory(deps, params) {
|
|
|
9654
9786
|
count: testPatternDecisions.length
|
|
9655
9787
|
}, "Loaded test patterns from decision store");
|
|
9656
9788
|
} else {
|
|
9657
|
-
testPatternsContent =
|
|
9658
|
-
logger$16.debug({ storyKey }, "No test-pattern decisions
|
|
9789
|
+
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
9790
|
+
logger$16.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
|
|
9659
9791
|
}
|
|
9660
9792
|
} catch (err) {
|
|
9661
9793
|
const error = err instanceof Error ? err.message : String(err);
|
|
@@ -9663,7 +9795,7 @@ async function runDevStory(deps, params) {
|
|
|
9663
9795
|
storyKey,
|
|
9664
9796
|
error
|
|
9665
9797
|
}, "Failed to load test patterns — using defaults");
|
|
9666
|
-
testPatternsContent =
|
|
9798
|
+
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
9667
9799
|
}
|
|
9668
9800
|
const taskScopeContent = taskScope !== void 0 && taskScope.trim().length > 0 ? `## Task Scope for This Batch\n\nImplement ONLY the following tasks from the story:\n\n${taskScope}\n\nDo NOT implement tasks outside this list. Other tasks will be handled in separate batch dispatches.` : "";
|
|
9669
9801
|
const priorFilesContent = priorFiles !== void 0 && priorFiles.length > 0 ? `## Files Modified by Previous Batches\n\nThe following files were created or modified by prior batch dispatches. Review them for context before implementing:\n\n${priorFiles.map((f) => `- ${f}`).join("\n")}` : "";
|
|
@@ -10362,15 +10494,40 @@ async function runTestPlan(deps, params) {
|
|
|
10362
10494
|
return makeTestPlanFailureResult(`story_file_read_error: ${error}`);
|
|
10363
10495
|
}
|
|
10364
10496
|
const archConstraintsContent = await getArchConstraints$1(deps);
|
|
10365
|
-
|
|
10366
|
-
|
|
10367
|
-
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
|
|
10371
|
-
|
|
10372
|
-
|
|
10373
|
-
|
|
10497
|
+
let testPatternsContent = "";
|
|
10498
|
+
try {
|
|
10499
|
+
const solutioningDecisions = await getDecisionsByPhase(deps.db, "solutioning");
|
|
10500
|
+
const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
|
|
10501
|
+
if (testPatternDecisions.length > 0) {
|
|
10502
|
+
testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
|
|
10503
|
+
logger$14.debug({
|
|
10504
|
+
storyKey,
|
|
10505
|
+
count: testPatternDecisions.length
|
|
10506
|
+
}, "Loaded test patterns from decision store");
|
|
10507
|
+
} else {
|
|
10508
|
+
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
10509
|
+
logger$14.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
|
|
10510
|
+
}
|
|
10511
|
+
} catch {
|
|
10512
|
+
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
10513
|
+
}
|
|
10514
|
+
const { prompt, tokenCount, truncated } = assemblePrompt(template, [
|
|
10515
|
+
{
|
|
10516
|
+
name: "story_content",
|
|
10517
|
+
content: storyContent,
|
|
10518
|
+
priority: "required"
|
|
10519
|
+
},
|
|
10520
|
+
{
|
|
10521
|
+
name: "architecture_constraints",
|
|
10522
|
+
content: archConstraintsContent,
|
|
10523
|
+
priority: "optional"
|
|
10524
|
+
},
|
|
10525
|
+
{
|
|
10526
|
+
name: "test_patterns",
|
|
10527
|
+
content: testPatternsContent,
|
|
10528
|
+
priority: "optional"
|
|
10529
|
+
}
|
|
10530
|
+
], TOKEN_CEILING);
|
|
10374
10531
|
logger$14.info({
|
|
10375
10532
|
storyKey,
|
|
10376
10533
|
tokenCount,
|
|
@@ -10575,6 +10732,23 @@ async function runTestExpansion(deps, params) {
|
|
|
10575
10732
|
});
|
|
10576
10733
|
}
|
|
10577
10734
|
const archConstraintsContent = await getArchConstraints(deps);
|
|
10735
|
+
let testPatternsContent = "";
|
|
10736
|
+
try {
|
|
10737
|
+
const solutioningDecisions = await getDecisionsByPhase(deps.db, "solutioning");
|
|
10738
|
+
const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
|
|
10739
|
+
if (testPatternDecisions.length > 0) {
|
|
10740
|
+
testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
|
|
10741
|
+
logger$13.debug({
|
|
10742
|
+
storyKey,
|
|
10743
|
+
count: testPatternDecisions.length
|
|
10744
|
+
}, "Loaded test patterns from decision store");
|
|
10745
|
+
} else {
|
|
10746
|
+
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
10747
|
+
logger$13.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
|
|
10748
|
+
}
|
|
10749
|
+
} catch {
|
|
10750
|
+
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
10751
|
+
}
|
|
10578
10752
|
let gitDiffContent = "";
|
|
10579
10753
|
if (filesModified && filesModified.length > 0) try {
|
|
10580
10754
|
const templateTokens = countTokens(template);
|
|
@@ -10611,6 +10785,11 @@ async function runTestExpansion(deps, params) {
|
|
|
10611
10785
|
content: gitDiffContent,
|
|
10612
10786
|
priority: "important"
|
|
10613
10787
|
},
|
|
10788
|
+
{
|
|
10789
|
+
name: "test_patterns",
|
|
10790
|
+
content: testPatternsContent,
|
|
10791
|
+
priority: "optional"
|
|
10792
|
+
},
|
|
10614
10793
|
{
|
|
10615
10794
|
name: "arch_constraints",
|
|
10616
10795
|
content: archConstraintsContent,
|
|
@@ -11517,7 +11696,7 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
11517
11696
|
const logger$11 = createLogger("implementation-orchestrator:seed");
|
|
11518
11697
|
/** Max chars for the architecture summary seeded into decisions */
|
|
11519
11698
|
const MAX_ARCH_CHARS = 6e3;
|
|
11520
|
-
/** Max chars per epic
|
|
11699
|
+
/** Max chars per epic-shard decision value (per-story or per-epic fallback) */
|
|
11521
11700
|
const MAX_EPIC_SHARD_CHARS = 12e3;
|
|
11522
11701
|
/** Max chars for test patterns */
|
|
11523
11702
|
const MAX_TEST_PATTERNS_CHARS = 2e3;
|
|
@@ -11634,15 +11813,18 @@ async function seedEpicShards(db, projectRoot) {
|
|
|
11634
11813
|
const shards = parseEpicShards(content);
|
|
11635
11814
|
let count = 0;
|
|
11636
11815
|
for (const shard of shards) {
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11816
|
+
const subsections = parseStorySubsections(shard.epicId, shard.content);
|
|
11817
|
+
for (const subsection of subsections) {
|
|
11818
|
+
await createDecision(db, {
|
|
11819
|
+
pipeline_run_id: null,
|
|
11820
|
+
phase: "implementation",
|
|
11821
|
+
category: "epic-shard",
|
|
11822
|
+
key: subsection.key,
|
|
11823
|
+
value: subsection.content.slice(0, MAX_EPIC_SHARD_CHARS),
|
|
11824
|
+
rationale: "Seeded from planning artifacts at orchestrator startup"
|
|
11825
|
+
});
|
|
11826
|
+
count++;
|
|
11827
|
+
}
|
|
11646
11828
|
}
|
|
11647
11829
|
await db.exec("DELETE FROM decisions WHERE phase = 'implementation' AND category = 'epic-shard-hash' AND key = 'epics-file'");
|
|
11648
11830
|
await createDecision(db, {
|
|
@@ -11751,13 +11933,99 @@ function parseEpicShards(content) {
|
|
|
11751
11933
|
return shards;
|
|
11752
11934
|
}
|
|
11753
11935
|
/**
|
|
11936
|
+
* Parse an epic section's content into per-story subsections.
|
|
11937
|
+
*
|
|
11938
|
+
* Matches story headings using three patterns:
|
|
11939
|
+
* - Markdown headings: #{2,6} Story \d+-\d+ (e.g., ### Story 37-1: Title)
|
|
11940
|
+
* - Bold: **Story \d+-\d+** (e.g., **Story 37-1**)
|
|
11941
|
+
* - Bare key: \d+-\d+:\s (e.g., 37-1: Title — must start at line start)
|
|
11942
|
+
*
|
|
11943
|
+
* Each subsection spans from its heading to the next matching heading or EOF.
|
|
11944
|
+
*
|
|
11945
|
+
* AC3: If no story headings are found, returns a single per-epic fallback entry
|
|
11946
|
+
* keyed by epicId — preserving backward-compatible behaviour for unstructured epics.
|
|
11947
|
+
*/
|
|
11948
|
+
function parseStorySubsections(epicId, epicContent) {
|
|
11949
|
+
const storyPattern = /(?:^#{2,6}\s+Story\s+(\d+-\d+)|^\*\*Story\s+(\d+-\d+)\*\*|^(\d+-\d+):\s)/gim;
|
|
11950
|
+
const matches = [];
|
|
11951
|
+
let match$1;
|
|
11952
|
+
while ((match$1 = storyPattern.exec(epicContent)) !== null) {
|
|
11953
|
+
const storyKey = match$1[1] ?? match$1[2] ?? match$1[3];
|
|
11954
|
+
if (storyKey !== void 0) matches.push({
|
|
11955
|
+
storyKey,
|
|
11956
|
+
startIdx: match$1.index
|
|
11957
|
+
});
|
|
11958
|
+
}
|
|
11959
|
+
if (matches.length === 0) return [{
|
|
11960
|
+
key: epicId,
|
|
11961
|
+
content: epicContent
|
|
11962
|
+
}];
|
|
11963
|
+
const result = [];
|
|
11964
|
+
for (let i = 0; i < matches.length; i++) {
|
|
11965
|
+
const entry = matches[i];
|
|
11966
|
+
const nextEntry = matches[i + 1];
|
|
11967
|
+
const start = entry.startIdx;
|
|
11968
|
+
const end = nextEntry !== void 0 ? nextEntry.startIdx : epicContent.length;
|
|
11969
|
+
const sectionContent = epicContent.slice(start, end).trim();
|
|
11970
|
+
if (sectionContent.length > 0) result.push({
|
|
11971
|
+
key: entry.storyKey,
|
|
11972
|
+
content: sectionContent
|
|
11973
|
+
});
|
|
11974
|
+
}
|
|
11975
|
+
return result;
|
|
11976
|
+
}
|
|
11977
|
+
/**
|
|
11978
|
+
* Read the project profile YAML synchronously.
|
|
11979
|
+
* Returns null on missing file, parse error, or unexpected shape.
|
|
11980
|
+
* Does NOT import from src/modules/project-profile/ — inline parse only.
|
|
11981
|
+
*
|
|
11982
|
+
* @internal
|
|
11983
|
+
*/
|
|
11984
|
+
function readProfileSync(projectRoot) {
|
|
11985
|
+
const profilePath = join$1(projectRoot, ".substrate", "project-profile.yaml");
|
|
11986
|
+
if (!existsSync$1(profilePath)) return null;
|
|
11987
|
+
try {
|
|
11988
|
+
const content = readFileSync$1(profilePath, "utf-8");
|
|
11989
|
+
const parsed = yaml.load(content);
|
|
11990
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
11991
|
+
return null;
|
|
11992
|
+
} catch {
|
|
11993
|
+
return null;
|
|
11994
|
+
}
|
|
11995
|
+
}
|
|
11996
|
+
/**
|
|
11754
11997
|
* Detect test framework and patterns from project configuration.
|
|
11755
|
-
*
|
|
11998
|
+
*
|
|
11999
|
+
* Detection priority:
|
|
12000
|
+
* 1. Profile present + packages[] non-empty → buildMonorepoTestPatterns(packages)
|
|
12001
|
+
* 2. Profile present + testCommand non-empty → mapTestCommandToPatterns(testCommand)
|
|
12002
|
+
* 3. No profile / profile fallthrough:
|
|
12003
|
+
* a. package.json present → existing vitest/jest/mocha detection
|
|
12004
|
+
* b. go.mod present → buildGoTestPatterns(projectRoot)
|
|
12005
|
+
* c. build.gradle.kts or build.gradle → buildGradleTestPatterns(projectRoot)
|
|
12006
|
+
* d. pom.xml → buildMavenTestPatterns()
|
|
12007
|
+
* e. Cargo.toml → buildCargoTestPatterns()
|
|
12008
|
+
* f. pyproject.toml OR conftest.py → buildPytestPatterns(projectRoot)
|
|
12009
|
+
* 4. Nothing matched → undefined
|
|
12010
|
+
*
|
|
12011
|
+
* @internal exported for direct unit testing
|
|
11756
12012
|
*/
|
|
11757
12013
|
function detectTestPatterns(projectRoot) {
|
|
12014
|
+
const profile = readProfileSync(projectRoot);
|
|
12015
|
+
if (profile !== null) {
|
|
12016
|
+
const project = profile["project"];
|
|
12017
|
+
if (project !== void 0) {
|
|
12018
|
+
const packages = project["packages"];
|
|
12019
|
+
if (Array.isArray(packages) && packages.length > 0) return buildMonorepoTestPatterns(packages);
|
|
12020
|
+
const testCommand = project["testCommand"];
|
|
12021
|
+
if (typeof testCommand === "string" && testCommand.length > 0) {
|
|
12022
|
+
const mapped = mapTestCommandToPatterns(testCommand);
|
|
12023
|
+
if (mapped !== void 0) return mapped;
|
|
12024
|
+
}
|
|
12025
|
+
}
|
|
12026
|
+
}
|
|
11758
12027
|
const pkgPath = join$1(projectRoot, "package.json");
|
|
11759
|
-
if (
|
|
11760
|
-
try {
|
|
12028
|
+
if (existsSync$1(pkgPath)) try {
|
|
11761
12029
|
const pkg = JSON.parse(readFileSync$1(pkgPath, "utf-8"));
|
|
11762
12030
|
const allDeps = {
|
|
11763
12031
|
...pkg.dependencies,
|
|
@@ -11777,9 +12045,17 @@ function detectTestPatterns(projectRoot) {
|
|
|
11777
12045
|
if (firstTest.includes("@jest") || firstTest.includes("jest.mock")) return buildJestPatterns(testScript);
|
|
11778
12046
|
}
|
|
11779
12047
|
return void 0;
|
|
11780
|
-
} catch {
|
|
11781
|
-
|
|
11782
|
-
|
|
12048
|
+
} catch {}
|
|
12049
|
+
if (existsSync$1(join$1(projectRoot, "go.mod"))) return buildGoTestPatterns(projectRoot);
|
|
12050
|
+
if (existsSync$1(join$1(projectRoot, "build.gradle.kts")) || existsSync$1(join$1(projectRoot, "build.gradle"))) return buildGradleTestPatterns(projectRoot);
|
|
12051
|
+
if (existsSync$1(join$1(projectRoot, "pom.xml"))) return buildMavenTestPatterns();
|
|
12052
|
+
if (existsSync$1(join$1(projectRoot, "Cargo.toml"))) return buildCargoTestPatterns();
|
|
12053
|
+
if (existsSync$1(join$1(projectRoot, "conftest.py"))) return buildPytestPatterns(projectRoot);
|
|
12054
|
+
if (existsSync$1(join$1(projectRoot, "pyproject.toml"))) try {
|
|
12055
|
+
const pyprojectContent = readFileSync$1(join$1(projectRoot, "pyproject.toml"), "utf-8");
|
|
12056
|
+
if (pyprojectContent.includes("[tool.pytest")) return buildPytestPatterns(projectRoot);
|
|
12057
|
+
} catch {}
|
|
12058
|
+
return void 0;
|
|
11783
12059
|
}
|
|
11784
12060
|
function buildVitestPatterns(testScript) {
|
|
11785
12061
|
const runCmd = testScript || "npx vitest run";
|
|
@@ -11816,6 +12092,196 @@ function buildMochaPatterns() {
|
|
|
11816
12092
|
].join("\n");
|
|
11817
12093
|
}
|
|
11818
12094
|
/**
|
|
12095
|
+
* Build Go test patterns.
|
|
12096
|
+
* Optionally detects testify from go.mod if projectRoot is non-empty.
|
|
12097
|
+
*
|
|
12098
|
+
* @internal
|
|
12099
|
+
*/
|
|
12100
|
+
function buildGoTestPatterns(projectRoot) {
|
|
12101
|
+
let hasTestify = false;
|
|
12102
|
+
if (projectRoot.length > 0) try {
|
|
12103
|
+
const goModPath = join$1(projectRoot, "go.mod");
|
|
12104
|
+
if (existsSync$1(goModPath)) {
|
|
12105
|
+
const content = readFileSync$1(goModPath, "utf-8");
|
|
12106
|
+
hasTestify = content.includes("github.com/stretchr/testify");
|
|
12107
|
+
}
|
|
12108
|
+
} catch {}
|
|
12109
|
+
return [
|
|
12110
|
+
"## Test Patterns",
|
|
12111
|
+
"- Framework: Go test (stdlib)",
|
|
12112
|
+
"- Test file naming: <module>_test.go alongside source files",
|
|
12113
|
+
"- Test structure: table-driven tests with t.Run() subtests",
|
|
12114
|
+
"- Run all tests: go test ./...",
|
|
12115
|
+
"- Run specific test: go test ./... -v -run TestFunctionName",
|
|
12116
|
+
"- Assertion style: t.Errorf(), t.Fatalf()",
|
|
12117
|
+
hasTestify ? "- testify available: use require.Equal(), assert.NoError(), etc." : ""
|
|
12118
|
+
].filter(Boolean).join("\n");
|
|
12119
|
+
}
|
|
12120
|
+
/**
|
|
12121
|
+
* Build Gradle (JVM) test patterns.
|
|
12122
|
+
* Detects JUnit5 vs JUnit4 if projectRoot is non-empty.
|
|
12123
|
+
*
|
|
12124
|
+
* @internal
|
|
12125
|
+
*/
|
|
12126
|
+
function buildGradleTestPatterns(projectRoot) {
|
|
12127
|
+
let hasJunit5 = false;
|
|
12128
|
+
if (projectRoot.length > 0) try {
|
|
12129
|
+
const ktsPath = join$1(projectRoot, "build.gradle.kts");
|
|
12130
|
+
const groovyPath = join$1(projectRoot, "build.gradle");
|
|
12131
|
+
const buildFilePath = existsSync$1(ktsPath) ? ktsPath : groovyPath;
|
|
12132
|
+
if (existsSync$1(buildFilePath)) {
|
|
12133
|
+
const content = readFileSync$1(buildFilePath, "utf-8");
|
|
12134
|
+
hasJunit5 = content.includes("junit-jupiter");
|
|
12135
|
+
}
|
|
12136
|
+
} catch {}
|
|
12137
|
+
return [
|
|
12138
|
+
"## Test Patterns",
|
|
12139
|
+
`- Framework: ${hasJunit5 ? "JUnit 5" : "JUnit"}`,
|
|
12140
|
+
"- Run all tests: ./gradlew test",
|
|
12141
|
+
"- Run specific test: ./gradlew test --tests \"com.example.ClassName\"",
|
|
12142
|
+
"- Test annotation: @Test",
|
|
12143
|
+
hasJunit5 ? "- Assertion style: assertThat() (AssertJ), assertEquals()" : "- Assertion style: assertEquals(), assertThat()"
|
|
12144
|
+
].join("\n");
|
|
12145
|
+
}
|
|
12146
|
+
/**
|
|
12147
|
+
* Build Maven (JVM) test patterns.
|
|
12148
|
+
*
|
|
12149
|
+
* @internal
|
|
12150
|
+
*/
|
|
12151
|
+
function buildMavenTestPatterns() {
|
|
12152
|
+
return [
|
|
12153
|
+
"## Test Patterns",
|
|
12154
|
+
"- Framework: JUnit (Maven)",
|
|
12155
|
+
"- Run all tests: mvn test",
|
|
12156
|
+
"- Run specific test: mvn test -Dtest=ClassName",
|
|
12157
|
+
"- Test annotation: @Test",
|
|
12158
|
+
"- Assertion style: assertEquals(), assertThat()"
|
|
12159
|
+
].join("\n");
|
|
12160
|
+
}
|
|
12161
|
+
/**
|
|
12162
|
+
* Build Cargo/Rust test patterns.
|
|
12163
|
+
*
|
|
12164
|
+
* @internal
|
|
12165
|
+
*/
|
|
12166
|
+
function buildCargoTestPatterns() {
|
|
12167
|
+
return [
|
|
12168
|
+
"## Test Patterns",
|
|
12169
|
+
"- Framework: Rust built-in test harness (cargo test)",
|
|
12170
|
+
"- Run all tests: cargo test",
|
|
12171
|
+
"- Run specific test: cargo test module_name",
|
|
12172
|
+
"- Test annotation: #[test]",
|
|
12173
|
+
"- Assertion macros: assert_eq!(), assert!(), assert_ne!()",
|
|
12174
|
+
"- Test module structure: #[cfg(test)] mod tests { ... }"
|
|
12175
|
+
].join("\n");
|
|
12176
|
+
}
|
|
12177
|
+
/**
|
|
12178
|
+
* Build pytest (Python) test patterns.
|
|
12179
|
+
* Checks for conftest.py and pyproject.toml for context.
|
|
12180
|
+
*
|
|
12181
|
+
* @internal
|
|
12182
|
+
*/
|
|
12183
|
+
function buildPytestPatterns(projectRoot) {
|
|
12184
|
+
let hasConftest = false;
|
|
12185
|
+
if (projectRoot.length > 0) try {
|
|
12186
|
+
hasConftest = existsSync$1(join$1(projectRoot, "conftest.py"));
|
|
12187
|
+
} catch {}
|
|
12188
|
+
return [
|
|
12189
|
+
"## Test Patterns",
|
|
12190
|
+
"- Framework: pytest",
|
|
12191
|
+
"- Run all tests: pytest",
|
|
12192
|
+
"- Run specific test: pytest tests/test_file.py -v -k \"test_name\"",
|
|
12193
|
+
"- Fixture pattern: @pytest.fixture (define in conftest.py for sharing)",
|
|
12194
|
+
"- Assertion style: assert statement (plain Python)",
|
|
12195
|
+
hasConftest ? "- conftest.py detected: shared fixtures available" : ""
|
|
12196
|
+
].filter(Boolean).join("\n");
|
|
12197
|
+
}
|
|
12198
|
+
/**
|
|
12199
|
+
* Map a profile testCommand string to appropriate pattern builder output.
|
|
12200
|
+
* Returns undefined for unrecognized commands.
|
|
12201
|
+
*
|
|
12202
|
+
* @internal
|
|
12203
|
+
*/
|
|
12204
|
+
function mapTestCommandToPatterns(testCommand) {
|
|
12205
|
+
if (testCommand.includes("go test")) return buildGoTestPatterns("");
|
|
12206
|
+
if (testCommand.includes("gradlew") || testCommand.includes("gradle")) return buildGradleTestPatterns("");
|
|
12207
|
+
if (testCommand.includes("mvn")) return buildMavenTestPatterns();
|
|
12208
|
+
if (testCommand.includes("cargo test")) return buildCargoTestPatterns();
|
|
12209
|
+
if (testCommand.includes("pytest")) return buildPytestPatterns("");
|
|
12210
|
+
if (testCommand.includes("vitest")) return buildVitestPatterns(testCommand);
|
|
12211
|
+
if (testCommand.includes("jest")) return buildJestPatterns(testCommand);
|
|
12212
|
+
if (testCommand.includes("mocha")) return buildMochaPatterns();
|
|
12213
|
+
return void 0;
|
|
12214
|
+
}
|
|
12215
|
+
/**
|
|
12216
|
+
* Build combined test patterns for a monorepo with multiple language packages.
|
|
12217
|
+
* Emits a concise per-language block for each distinct language, prefixed with package path.
|
|
12218
|
+
*
|
|
12219
|
+
* @internal
|
|
12220
|
+
*/
|
|
12221
|
+
function buildMonorepoTestPatterns(packages) {
|
|
12222
|
+
const seen = new Set();
|
|
12223
|
+
const entries = [];
|
|
12224
|
+
for (const pkg of packages) if (typeof pkg.language === "string" && pkg.language.length > 0 && !seen.has(pkg.language)) {
|
|
12225
|
+
seen.add(pkg.language);
|
|
12226
|
+
entries.push({
|
|
12227
|
+
language: pkg.language,
|
|
12228
|
+
path: pkg.path ?? ""
|
|
12229
|
+
});
|
|
12230
|
+
}
|
|
12231
|
+
const blocks = [];
|
|
12232
|
+
for (const entry of entries) {
|
|
12233
|
+
const header = entry.path.length > 0 ? `# ${entry.path} (${entry.language})` : `# ${entry.language}`;
|
|
12234
|
+
let block;
|
|
12235
|
+
switch (entry.language) {
|
|
12236
|
+
case "go":
|
|
12237
|
+
block = [
|
|
12238
|
+
header,
|
|
12239
|
+
"- go test ./...",
|
|
12240
|
+
"- go test ./... -v -run TestName",
|
|
12241
|
+
"- File naming: _test.go"
|
|
12242
|
+
].join("\n");
|
|
12243
|
+
break;
|
|
12244
|
+
case "typescript":
|
|
12245
|
+
case "javascript":
|
|
12246
|
+
block = [
|
|
12247
|
+
header,
|
|
12248
|
+
"- npx vitest run (or npm test)",
|
|
12249
|
+
"- vi.mock() for mocking",
|
|
12250
|
+
"- describe/it structure"
|
|
12251
|
+
].join("\n");
|
|
12252
|
+
break;
|
|
12253
|
+
case "java":
|
|
12254
|
+
case "kotlin":
|
|
12255
|
+
block = [
|
|
12256
|
+
header,
|
|
12257
|
+
"- ./gradlew test",
|
|
12258
|
+
"- @Test annotation",
|
|
12259
|
+
"- assertEquals() / assertThat()"
|
|
12260
|
+
].join("\n");
|
|
12261
|
+
break;
|
|
12262
|
+
case "rust":
|
|
12263
|
+
block = [
|
|
12264
|
+
header,
|
|
12265
|
+
"- cargo test",
|
|
12266
|
+
"- #[test] attribute",
|
|
12267
|
+
"- assert_eq!() / assert!()"
|
|
12268
|
+
].join("\n");
|
|
12269
|
+
break;
|
|
12270
|
+
case "python":
|
|
12271
|
+
block = [
|
|
12272
|
+
header,
|
|
12273
|
+
"- pytest",
|
|
12274
|
+
"- @pytest.fixture",
|
|
12275
|
+
"- assert statement style"
|
|
12276
|
+
].join("\n");
|
|
12277
|
+
break;
|
|
12278
|
+
default: block = [header, `- Run tests for ${entry.language} package`].join("\n");
|
|
12279
|
+
}
|
|
12280
|
+
blocks.push(block);
|
|
12281
|
+
}
|
|
12282
|
+
return ["## Test Patterns", ...blocks].join("\n\n");
|
|
12283
|
+
}
|
|
12284
|
+
/**
|
|
11819
12285
|
* Find a few test files in the project to help detect the test framework.
|
|
11820
12286
|
*/
|
|
11821
12287
|
function findTestFiles(projectRoot) {
|
|
@@ -11992,6 +12458,47 @@ function parseInterfaceContracts(storyContent, storyKey) {
|
|
|
11992
12458
|
//#endregion
|
|
11993
12459
|
//#region src/modules/implementation-orchestrator/contract-verifier.ts
|
|
11994
12460
|
/**
|
|
12461
|
+
* Reads .substrate/project-profile.yaml (Story 37-1) and determines whether
|
|
12462
|
+
* TypeScript type-checking is appropriate for this project.
|
|
12463
|
+
*
|
|
12464
|
+
* Detection order:
|
|
12465
|
+
* 1. No profile → true (preserve pre-37-4 behavior)
|
|
12466
|
+
* 2. `packages` array non-empty → true iff any package is typescript/javascript
|
|
12467
|
+
* 3. `packages` empty/absent → infer from `buildCommand` — true for npm/pnpm/yarn/bun/turbo/tsc
|
|
12468
|
+
* 4. Parse error → true (conservative, allow tsc)
|
|
12469
|
+
*
|
|
12470
|
+
* Uses synchronous I/O to avoid making verifyContracts async (Story 37-3 pattern).
|
|
12471
|
+
* Does NOT import from src/modules/project-profile/ to avoid circular-dependency risk.
|
|
12472
|
+
*/
|
|
12473
|
+
function shouldRunTscCheck(projectRoot) {
|
|
12474
|
+
const profilePath = join$1(projectRoot, ".substrate", "project-profile.yaml");
|
|
12475
|
+
if (!existsSync$1(profilePath)) return true;
|
|
12476
|
+
try {
|
|
12477
|
+
const raw = readFileSync$1(profilePath, "utf-8");
|
|
12478
|
+
const parsed = yaml.load(raw);
|
|
12479
|
+
if (!parsed) return true;
|
|
12480
|
+
const project = parsed?.project;
|
|
12481
|
+
if (!project) return true;
|
|
12482
|
+
const packages = project["packages"];
|
|
12483
|
+
if (Array.isArray(packages) && packages.length > 0) return packages.some((p) => p.language === "typescript" || p.language === "javascript");
|
|
12484
|
+
const buildCommand = project["buildCommand"];
|
|
12485
|
+
if (typeof buildCommand === "string" && buildCommand.length > 0) {
|
|
12486
|
+
const tsIndicators = [
|
|
12487
|
+
"npm",
|
|
12488
|
+
"pnpm",
|
|
12489
|
+
"yarn",
|
|
12490
|
+
"bun",
|
|
12491
|
+
"tsc",
|
|
12492
|
+
"turbo"
|
|
12493
|
+
];
|
|
12494
|
+
return tsIndicators.some((ind) => buildCommand.includes(ind));
|
|
12495
|
+
}
|
|
12496
|
+
return true;
|
|
12497
|
+
} catch {
|
|
12498
|
+
return true;
|
|
12499
|
+
}
|
|
12500
|
+
}
|
|
12501
|
+
/**
|
|
11995
12502
|
* Verify all declared contract export/import pairs after sprint completion.
|
|
11996
12503
|
*
|
|
11997
12504
|
* @param declarations - All ContractDeclaration entries from the decision store
|
|
@@ -12023,80 +12530,82 @@ function verifyContracts(declarations, projectRoot) {
|
|
|
12023
12530
|
});
|
|
12024
12531
|
}
|
|
12025
12532
|
}
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
}
|
|
12052
|
-
if (tscFailed) {
|
|
12053
|
-
const truncatedOutput = tscOutput.slice(0, 1e3);
|
|
12054
|
-
const matchedExports = new Set();
|
|
12055
|
-
for (const exp of exports) {
|
|
12056
|
-
if (!exp.filePath) continue;
|
|
12057
|
-
if (tscOutput.includes(exp.filePath)) {
|
|
12058
|
-
matchedExports.add(exp.contractName);
|
|
12059
|
-
const importers = imports.filter((i) => i.contractName === exp.contractName);
|
|
12060
|
-
if (importers.length > 0) for (const imp of importers) mismatches.push({
|
|
12061
|
-
exporter: exp.storyKey,
|
|
12062
|
-
importer: imp.storyKey,
|
|
12063
|
-
contractName: exp.contractName,
|
|
12064
|
-
mismatchDescription: `TypeScript type-check failed for ${exp.filePath}: ${truncatedOutput}`
|
|
12065
|
-
});
|
|
12066
|
-
else mismatches.push({
|
|
12067
|
-
exporter: exp.storyKey,
|
|
12068
|
-
importer: null,
|
|
12069
|
-
contractName: exp.contractName,
|
|
12070
|
-
mismatchDescription: `TypeScript type-check failed for ${exp.filePath}: ${truncatedOutput}`
|
|
12071
|
-
});
|
|
12533
|
+
if (shouldRunTscCheck(projectRoot)) {
|
|
12534
|
+
const tsconfigPath = join$1(projectRoot, "tsconfig.json");
|
|
12535
|
+
const tscBinPath = join$1(projectRoot, "node_modules", ".bin", "tsc");
|
|
12536
|
+
if (existsSync$1(tsconfigPath) && existsSync$1(tscBinPath)) {
|
|
12537
|
+
let tscOutput = "";
|
|
12538
|
+
let tscFailed = false;
|
|
12539
|
+
try {
|
|
12540
|
+
execSync(`"${tscBinPath}" --noEmit`, {
|
|
12541
|
+
cwd: projectRoot,
|
|
12542
|
+
timeout: 12e4,
|
|
12543
|
+
encoding: "utf-8",
|
|
12544
|
+
stdio: [
|
|
12545
|
+
"pipe",
|
|
12546
|
+
"pipe",
|
|
12547
|
+
"pipe"
|
|
12548
|
+
]
|
|
12549
|
+
});
|
|
12550
|
+
} catch (err) {
|
|
12551
|
+
tscFailed = true;
|
|
12552
|
+
if (err != null && typeof err === "object") {
|
|
12553
|
+
const e = err;
|
|
12554
|
+
const stdoutStr = typeof e.stdout === "string" ? e.stdout : Buffer.isBuffer(e.stdout) ? e.stdout.toString("utf-8") : "";
|
|
12555
|
+
const stderrStr = typeof e.stderr === "string" ? e.stderr : Buffer.isBuffer(e.stderr) ? e.stderr.toString("utf-8") : "";
|
|
12556
|
+
tscOutput = [stdoutStr, stderrStr].filter((s) => s.length > 0).join("\n");
|
|
12557
|
+
if (!tscOutput && e.message) tscOutput = e.message;
|
|
12072
12558
|
}
|
|
12073
12559
|
}
|
|
12074
|
-
if (
|
|
12075
|
-
const
|
|
12560
|
+
if (tscFailed) {
|
|
12561
|
+
const truncatedOutput = tscOutput.slice(0, 1e3);
|
|
12562
|
+
const matchedExports = new Set();
|
|
12076
12563
|
for (const exp of exports) {
|
|
12077
|
-
|
|
12078
|
-
if (
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12564
|
+
if (!exp.filePath) continue;
|
|
12565
|
+
if (tscOutput.includes(exp.filePath)) {
|
|
12566
|
+
matchedExports.add(exp.contractName);
|
|
12567
|
+
const importers = imports.filter((i) => i.contractName === exp.contractName);
|
|
12568
|
+
if (importers.length > 0) for (const imp of importers) mismatches.push({
|
|
12569
|
+
exporter: exp.storyKey,
|
|
12570
|
+
importer: imp.storyKey,
|
|
12571
|
+
contractName: exp.contractName,
|
|
12572
|
+
mismatchDescription: `TypeScript type-check failed for ${exp.filePath}: ${truncatedOutput}`
|
|
12573
|
+
});
|
|
12574
|
+
else mismatches.push({
|
|
12575
|
+
exporter: exp.storyKey,
|
|
12576
|
+
importer: null,
|
|
12577
|
+
contractName: exp.contractName,
|
|
12578
|
+
mismatchDescription: `TypeScript type-check failed for ${exp.filePath}: ${truncatedOutput}`
|
|
12579
|
+
});
|
|
12089
12580
|
}
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
|
|
12099
|
-
|
|
12581
|
+
}
|
|
12582
|
+
if (matchedExports.size === 0) {
|
|
12583
|
+
const reportedPairs = new Set();
|
|
12584
|
+
for (const exp of exports) {
|
|
12585
|
+
const importers = imports.filter((i) => i.contractName === exp.contractName);
|
|
12586
|
+
if (importers.length > 0) for (const imp of importers) {
|
|
12587
|
+
const pairKey = `${exp.storyKey}:${imp.storyKey}:${exp.contractName}`;
|
|
12588
|
+
if (!reportedPairs.has(pairKey)) {
|
|
12589
|
+
reportedPairs.add(pairKey);
|
|
12590
|
+
mismatches.push({
|
|
12591
|
+
exporter: exp.storyKey,
|
|
12592
|
+
importer: imp.storyKey,
|
|
12593
|
+
contractName: exp.contractName,
|
|
12594
|
+
mismatchDescription: `TypeScript type-check failed: ${truncatedOutput}`
|
|
12595
|
+
});
|
|
12596
|
+
}
|
|
12597
|
+
}
|
|
12598
|
+
else {
|
|
12599
|
+
const pairKey = `${exp.storyKey}:null:${exp.contractName}`;
|
|
12600
|
+
if (!reportedPairs.has(pairKey)) {
|
|
12601
|
+
reportedPairs.add(pairKey);
|
|
12602
|
+
mismatches.push({
|
|
12603
|
+
exporter: exp.storyKey,
|
|
12604
|
+
importer: null,
|
|
12605
|
+
contractName: exp.contractName,
|
|
12606
|
+
mismatchDescription: `TypeScript type-check failed: ${truncatedOutput}`
|
|
12607
|
+
});
|
|
12608
|
+
}
|
|
12100
12609
|
}
|
|
12101
12610
|
}
|
|
12102
12611
|
}
|
|
@@ -22792,4 +23301,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
22792
23301
|
|
|
22793
23302
|
//#endregion
|
|
22794
23303
|
export { AdapterTelemetryPersistence, AppError, DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DoltClient, DoltNotInstalled, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, FileStateStore, GitClient, GrammarLoader, IngestionServer, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SUBSTRATE_OWNED_SETTINGS_KEYS, SymbolParser, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, checkDoltInstalled, createConfigSystem, createContextCompiler, createDatabaseAdapter, createDispatcher, createDoltClient, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStateStore, createStopAfterGate, createTelemetryAdvisor, detectCycles, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, initSchema, initializeDolt, isSyncAdapter, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
|
|
22795
|
-
//# sourceMappingURL=run-
|
|
23304
|
+
//# sourceMappingURL=run-IDOmPys1.js.map
|