@nathapp/nax 0.67.11 → 0.67.13
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/nax.js +821 -401
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -32839,6 +32839,122 @@ var init_categorization = __esm(() => {
|
|
|
32839
32839
|
MECHANICAL_REVIEW_CHECKS = new Set(ORDERED_MECHANICAL_REVIEW_CHECKS);
|
|
32840
32840
|
LLM_REVIEW_CHECKS = new Set(ORDERED_LLM_REVIEW_CHECKS);
|
|
32841
32841
|
});
|
|
32842
|
+
|
|
32843
|
+
// src/review/prepare-inputs.ts
|
|
32844
|
+
import { relative as relative8, sep as sep2 } from "path";
|
|
32845
|
+
function derivePackageDirs(workdir, projectDir) {
|
|
32846
|
+
const repoRoot = projectDir ?? workdir;
|
|
32847
|
+
const packageDir = workdir !== repoRoot ? workdir : undefined;
|
|
32848
|
+
let packageDirRelative;
|
|
32849
|
+
if (projectDir && workdir !== projectDir) {
|
|
32850
|
+
const rel = relative8(projectDir, workdir);
|
|
32851
|
+
if (rel !== ".." && !rel.startsWith(`..${sep2}`)) {
|
|
32852
|
+
packageDirRelative = rel && rel !== "." ? rel : undefined;
|
|
32853
|
+
}
|
|
32854
|
+
}
|
|
32855
|
+
return { repoRoot, packageDir, packageDirRelative };
|
|
32856
|
+
}
|
|
32857
|
+
async function prepareSemanticReviewInput(args) {
|
|
32858
|
+
const { workdir, projectDir, storyId, storyGitRef, config: config2, naxIgnoreIndex, semanticConfig } = args;
|
|
32859
|
+
const effectiveRef = await resolveEffectiveRef(workdir, storyGitRef, storyId);
|
|
32860
|
+
if (!effectiveRef) {
|
|
32861
|
+
return {
|
|
32862
|
+
effectiveRef: undefined,
|
|
32863
|
+
stat: "",
|
|
32864
|
+
diff: undefined,
|
|
32865
|
+
excludePatterns: [],
|
|
32866
|
+
skipReason: "no git ref"
|
|
32867
|
+
};
|
|
32868
|
+
}
|
|
32869
|
+
const { packageDir, packageDirRelative } = derivePackageDirs(workdir, projectDir);
|
|
32870
|
+
const stat = await collectDiffStat(workdir, effectiveRef, { naxIgnoreIndex, packageDir });
|
|
32871
|
+
const resolved = await resolveTestFilePatterns(config2 ?? reviewConfigSelector.select(DEFAULT_CONFIG), projectDir ?? workdir, packageDirRelative);
|
|
32872
|
+
const excludePatterns = [...resolveReviewExcludePatterns(semanticConfig.excludePatterns, resolved)];
|
|
32873
|
+
const diffMode = semanticConfig.diffMode ?? "ref";
|
|
32874
|
+
if (diffMode === "ref") {
|
|
32875
|
+
if (!stat) {
|
|
32876
|
+
return { effectiveRef, stat: "", diff: undefined, excludePatterns, skipReason: "no changes detected" };
|
|
32877
|
+
}
|
|
32878
|
+
return { effectiveRef, stat, diff: undefined, excludePatterns };
|
|
32879
|
+
}
|
|
32880
|
+
const rawDiff = await collectDiff(workdir, effectiveRef, excludePatterns, { naxIgnoreIndex, packageDir });
|
|
32881
|
+
const diff = truncateDiff(rawDiff, rawDiff.length > DIFF_CAP_BYTES ? stat : undefined);
|
|
32882
|
+
if (!diff) {
|
|
32883
|
+
return { effectiveRef, stat, diff: undefined, excludePatterns, skipReason: "no production code changes" };
|
|
32884
|
+
}
|
|
32885
|
+
return { effectiveRef, stat, diff, excludePatterns };
|
|
32886
|
+
}
|
|
32887
|
+
async function prepareAdversarialReviewInput(args) {
|
|
32888
|
+
const { workdir, projectDir, storyId, storyGitRef, config: config2, naxIgnoreIndex, adversarialConfig } = args;
|
|
32889
|
+
const effectiveRef = await resolveEffectiveRef(workdir, storyGitRef, storyId);
|
|
32890
|
+
if (!effectiveRef) {
|
|
32891
|
+
return {
|
|
32892
|
+
effectiveRef: undefined,
|
|
32893
|
+
stat: "",
|
|
32894
|
+
diff: undefined,
|
|
32895
|
+
testInventory: undefined,
|
|
32896
|
+
excludePatterns: [],
|
|
32897
|
+
testGlobs: [],
|
|
32898
|
+
refExcludePatterns: [],
|
|
32899
|
+
skipReason: "no git ref"
|
|
32900
|
+
};
|
|
32901
|
+
}
|
|
32902
|
+
const { packageDir, packageDirRelative } = derivePackageDirs(workdir, projectDir);
|
|
32903
|
+
const stat = await collectDiffStat(workdir, effectiveRef, { naxIgnoreIndex, packageDir });
|
|
32904
|
+
const effectiveConfig = config2 ?? reviewConfigSelector.select(DEFAULT_CONFIG);
|
|
32905
|
+
const resolved = await resolveTestFilePatterns(effectiveConfig, projectDir ?? workdir, packageDirRelative);
|
|
32906
|
+
const refExcludePatterns = [...resolveReviewExcludePatterns(adversarialConfig.excludePatterns, resolved)];
|
|
32907
|
+
const testGlobs = resolved.globs ?? [];
|
|
32908
|
+
const excludePatterns = [...adversarialConfig.excludePatterns ?? []];
|
|
32909
|
+
const diffMode = adversarialConfig.diffMode ?? "ref";
|
|
32910
|
+
const testFilePatterns = (typeof config2?.execution?.smartTestRunner === "object" ? config2.execution.smartTestRunner?.testFilePatterns : undefined) ?? undefined;
|
|
32911
|
+
if (diffMode === "ref") {
|
|
32912
|
+
if (!stat) {
|
|
32913
|
+
return {
|
|
32914
|
+
effectiveRef,
|
|
32915
|
+
stat: "",
|
|
32916
|
+
diff: undefined,
|
|
32917
|
+
testInventory: undefined,
|
|
32918
|
+
excludePatterns,
|
|
32919
|
+
testGlobs,
|
|
32920
|
+
refExcludePatterns,
|
|
32921
|
+
skipReason: "no changes detected"
|
|
32922
|
+
};
|
|
32923
|
+
}
|
|
32924
|
+
return {
|
|
32925
|
+
effectiveRef,
|
|
32926
|
+
stat,
|
|
32927
|
+
diff: undefined,
|
|
32928
|
+
testInventory: undefined,
|
|
32929
|
+
excludePatterns,
|
|
32930
|
+
testGlobs,
|
|
32931
|
+
refExcludePatterns
|
|
32932
|
+
};
|
|
32933
|
+
}
|
|
32934
|
+
const diff = await collectDiff(workdir, effectiveRef, excludePatterns, { naxIgnoreIndex, packageDir });
|
|
32935
|
+
if (!diff) {
|
|
32936
|
+
return {
|
|
32937
|
+
effectiveRef,
|
|
32938
|
+
stat,
|
|
32939
|
+
diff: undefined,
|
|
32940
|
+
testInventory: undefined,
|
|
32941
|
+
excludePatterns,
|
|
32942
|
+
testGlobs,
|
|
32943
|
+
refExcludePatterns,
|
|
32944
|
+
skipReason: "no code changes"
|
|
32945
|
+
};
|
|
32946
|
+
}
|
|
32947
|
+
const testInventory = await computeTestInventory(workdir, effectiveRef, testFilePatterns, {
|
|
32948
|
+
naxIgnoreIndex,
|
|
32949
|
+
packageDir
|
|
32950
|
+
});
|
|
32951
|
+
return { effectiveRef, stat, diff, testInventory, excludePatterns, testGlobs, refExcludePatterns };
|
|
32952
|
+
}
|
|
32953
|
+
var init_prepare_inputs = __esm(() => {
|
|
32954
|
+
init_config();
|
|
32955
|
+
init_test_runners();
|
|
32956
|
+
init_diff_utils();
|
|
32957
|
+
});
|
|
32842
32958
|
// src/utils/process-kill.ts
|
|
32843
32959
|
function killProcessGroup(pid, signal) {
|
|
32844
32960
|
try {
|
|
@@ -33271,7 +33387,7 @@ var init_runners = __esm(() => {
|
|
|
33271
33387
|
});
|
|
33272
33388
|
|
|
33273
33389
|
// src/verification/smart-runner.ts
|
|
33274
|
-
import { join as join19, relative as
|
|
33390
|
+
import { join as join19, relative as relative9 } from "path";
|
|
33275
33391
|
function extractPatternSuffix(pattern) {
|
|
33276
33392
|
const lastStar = pattern.lastIndexOf("*");
|
|
33277
33393
|
if (lastStar === -1)
|
|
@@ -33381,7 +33497,7 @@ async function getChangedNonTestFiles(workdir, baseRef, packagePrefix, testFileR
|
|
|
33381
33497
|
let effectivePrefix = packagePrefix;
|
|
33382
33498
|
if (packagePrefix && repoRoot) {
|
|
33383
33499
|
const gitRoot = await _gitUtilDeps.getGitRoot(workdir);
|
|
33384
|
-
const extraPrefix2 = gitRoot && gitRoot !== repoRoot ?
|
|
33500
|
+
const extraPrefix2 = gitRoot && gitRoot !== repoRoot ? relative9(gitRoot, repoRoot) : "";
|
|
33385
33501
|
effectivePrefix = extraPrefix2 ? `${extraPrefix2}/${packagePrefix}` : packagePrefix;
|
|
33386
33502
|
}
|
|
33387
33503
|
const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
|
|
@@ -33408,7 +33524,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
33408
33524
|
const packageDir = packagePrefix ? join19(repoRoot, packagePrefix) : undefined;
|
|
33409
33525
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
|
|
33410
33526
|
const gitRoot = await _gitUtilDeps.getGitRoot(workdir);
|
|
33411
|
-
const extraPrefix = gitRoot && gitRoot !== repoRoot ?
|
|
33527
|
+
const extraPrefix = gitRoot && gitRoot !== repoRoot ? relative9(gitRoot, repoRoot) : "";
|
|
33412
33528
|
const effectivePrefix = packagePrefix ? extraPrefix ? `${extraPrefix}/${packagePrefix}` : packagePrefix : undefined;
|
|
33413
33529
|
const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
|
|
33414
33530
|
const scoped2 = filterNaxInternalPaths(scopedRaw, ignoreMatchers);
|
|
@@ -33841,12 +33957,12 @@ function acceptanceDiagnoseRawArrayToFindings(raw) {
|
|
|
33841
33957
|
}
|
|
33842
33958
|
|
|
33843
33959
|
// src/findings/path-utils.ts
|
|
33844
|
-
import { relative as
|
|
33960
|
+
import { relative as relative10, resolve as resolve11 } from "path";
|
|
33845
33961
|
function rebaseToWorkdir(rawPath, cwd, workdir) {
|
|
33846
33962
|
if (rawPath.startsWith("/")) {
|
|
33847
|
-
return
|
|
33963
|
+
return relative10(workdir, rawPath);
|
|
33848
33964
|
}
|
|
33849
|
-
return
|
|
33965
|
+
return relative10(workdir, resolve11(cwd, rawPath));
|
|
33850
33966
|
}
|
|
33851
33967
|
var init_path_utils = () => {};
|
|
33852
33968
|
|
|
@@ -35772,6 +35888,16 @@ function parseTestEditDeclarations(output) {
|
|
|
35772
35888
|
}
|
|
35773
35889
|
return result;
|
|
35774
35890
|
}
|
|
35891
|
+
function normaliseWs(s) {
|
|
35892
|
+
return s.replace(/\s+/g, " ").replace(/\s*([(),<>])\s*/g, "$1").replace(/\s*:\s*/g, ": ").trim();
|
|
35893
|
+
}
|
|
35894
|
+
function validatePrdQuote(prdQuote, story) {
|
|
35895
|
+
if (!prdQuote.trim())
|
|
35896
|
+
return false;
|
|
35897
|
+
const needle = normaliseWs(prdQuote);
|
|
35898
|
+
const haystack = normaliseWs([story.description, ...story.acceptanceCriteria].join(" "));
|
|
35899
|
+
return haystack.includes(needle);
|
|
35900
|
+
}
|
|
35775
35901
|
var REASON_RE;
|
|
35776
35902
|
var init_test_edit_declaration = __esm(() => {
|
|
35777
35903
|
REASON_RE = /^TEST_EDIT_REASON:\s*(prd_contract|lint_only|sibling_scope|mock_structure)\s*$/m;
|
|
@@ -37896,7 +38022,7 @@ var init__finding_to_check = __esm(() => {
|
|
|
37896
38022
|
});
|
|
37897
38023
|
|
|
37898
38024
|
// src/operations/autofix-implementer-strategy.ts
|
|
37899
|
-
function makeAutofixImplementerStrategy(story, config2) {
|
|
38025
|
+
function makeAutofixImplementerStrategy(story, config2, sink) {
|
|
37900
38026
|
return {
|
|
37901
38027
|
name: "autofix-implementer",
|
|
37902
38028
|
appliesTo: (f) => f.fixTarget === "source" && IMPLEMENTER_SOURCES.has(f.source),
|
|
@@ -37905,10 +38031,19 @@ function makeAutofixImplementerStrategy(story, config2) {
|
|
|
37905
38031
|
failedChecks: findingsToFailedChecks(findings),
|
|
37906
38032
|
story
|
|
37907
38033
|
}),
|
|
37908
|
-
extractApplied: (output) =>
|
|
37909
|
-
|
|
37910
|
-
|
|
37911
|
-
|
|
38034
|
+
extractApplied: (output) => {
|
|
38035
|
+
for (const decl of output.testEditDeclarations) {
|
|
38036
|
+
if (decl.reason === "mock_structure" && decl.files && decl.reasonDetail) {
|
|
38037
|
+
sink.mockHandoffs.push({ files: decl.files, reasonDetail: decl.reasonDetail });
|
|
38038
|
+
} else if (decl.reason !== "mock_structure") {
|
|
38039
|
+
sink.testEdits.push(decl);
|
|
38040
|
+
}
|
|
38041
|
+
}
|
|
38042
|
+
return {
|
|
38043
|
+
summary: output.unresolvedReason ?? "",
|
|
38044
|
+
unresolved: output.unresolvedReason
|
|
38045
|
+
};
|
|
38046
|
+
},
|
|
37912
38047
|
maxAttempts: config2.execution.rectification.maxAttemptsPerStrategy,
|
|
37913
38048
|
coRun: "co-run-sequential"
|
|
37914
38049
|
};
|
|
@@ -37921,16 +38056,42 @@ var init_autofix_implementer_strategy = __esm(() => {
|
|
|
37921
38056
|
});
|
|
37922
38057
|
|
|
37923
38058
|
// src/operations/autofix-test-writer-strategy.ts
|
|
37924
|
-
function makeAutofixTestWriterStrategy(story, config2) {
|
|
38059
|
+
function makeAutofixTestWriterStrategy(story, config2, sink) {
|
|
37925
38060
|
return {
|
|
37926
38061
|
name: "autofix-test-writer",
|
|
37927
|
-
appliesTo: (f) => f.fixTarget === "test" || f.source === "adversarial-review",
|
|
38062
|
+
appliesTo: (f) => f.fixTarget === "test" || f.source === "adversarial-review" || sink.mockHandoffs.length > 0,
|
|
37928
38063
|
fixOp: testWriterRectifyOp,
|
|
37929
|
-
buildInput: (findings, _prior, _cycleCtx) =>
|
|
37930
|
-
|
|
37931
|
-
|
|
37932
|
-
|
|
37933
|
-
|
|
38064
|
+
buildInput: (findings, _prior, _cycleCtx) => {
|
|
38065
|
+
if (sink.mockHandoffs.length > 0) {
|
|
38066
|
+
const handoffs = sink.mockHandoffs.splice(0);
|
|
38067
|
+
const seenFiles = new Set;
|
|
38068
|
+
const handoffFiles = [];
|
|
38069
|
+
for (const h of handoffs) {
|
|
38070
|
+
for (const f of h.files) {
|
|
38071
|
+
if (!seenFiles.has(f)) {
|
|
38072
|
+
seenFiles.add(f);
|
|
38073
|
+
handoffFiles.push(f);
|
|
38074
|
+
}
|
|
38075
|
+
}
|
|
38076
|
+
}
|
|
38077
|
+
const handoffReason = handoffs.map((h) => h.reasonDetail).join(`
|
|
38078
|
+
---
|
|
38079
|
+
`);
|
|
38080
|
+
return {
|
|
38081
|
+
failedChecks: findingsToFailedChecks(findings),
|
|
38082
|
+
story,
|
|
38083
|
+
mode: "mock-restructure",
|
|
38084
|
+
blockingThreshold: config2.review?.blockingThreshold,
|
|
38085
|
+
handoffReason,
|
|
38086
|
+
handoffFiles
|
|
38087
|
+
};
|
|
38088
|
+
}
|
|
38089
|
+
return {
|
|
38090
|
+
failedChecks: findingsToFailedChecks(findings),
|
|
38091
|
+
story,
|
|
38092
|
+
blockingThreshold: config2.review?.blockingThreshold
|
|
38093
|
+
};
|
|
38094
|
+
},
|
|
37934
38095
|
maxAttempts: config2.execution.rectification.maxAttemptsPerStrategy,
|
|
37935
38096
|
coRun: "co-run-sequential"
|
|
37936
38097
|
};
|
|
@@ -37940,6 +38101,100 @@ var init_autofix_test_writer_strategy = __esm(() => {
|
|
|
37940
38101
|
init_autofix_test_writer();
|
|
37941
38102
|
});
|
|
37942
38103
|
|
|
38104
|
+
// src/operations/apply-test-edit-declarations.ts
|
|
38105
|
+
function applyTestEditDeclarations(findings, declarations, story, invalidMockStructure) {
|
|
38106
|
+
let result = [...findings];
|
|
38107
|
+
const advisories = [];
|
|
38108
|
+
for (const d of declarations) {
|
|
38109
|
+
if (d.reason === "prd_contract") {
|
|
38110
|
+
const prdQuote = d.prdQuote ?? "";
|
|
38111
|
+
const valid = validatePrdQuote(prdQuote, story);
|
|
38112
|
+
if (valid) {
|
|
38113
|
+
result = result.map((f) => {
|
|
38114
|
+
if (f.file === d.file && f.fixTarget === "source") {
|
|
38115
|
+
return {
|
|
38116
|
+
...f,
|
|
38117
|
+
fixTarget: "test",
|
|
38118
|
+
meta: {
|
|
38119
|
+
...f.meta,
|
|
38120
|
+
prdContractDeclaration: d
|
|
38121
|
+
}
|
|
38122
|
+
};
|
|
38123
|
+
}
|
|
38124
|
+
return f;
|
|
38125
|
+
});
|
|
38126
|
+
} else {
|
|
38127
|
+
advisories.push({
|
|
38128
|
+
source: "autofix",
|
|
38129
|
+
severity: "warning",
|
|
38130
|
+
category: "prd_quote_mismatch",
|
|
38131
|
+
message: `PRD quote not found verbatim in story text for file: ${d.file}`,
|
|
38132
|
+
file: d.file,
|
|
38133
|
+
fixTarget: "source"
|
|
38134
|
+
});
|
|
38135
|
+
}
|
|
38136
|
+
}
|
|
38137
|
+
}
|
|
38138
|
+
if (invalidMockStructure && invalidMockStructure.length > 0) {
|
|
38139
|
+
for (const d of invalidMockStructure) {
|
|
38140
|
+
const fileList = (d.files ?? [d.file]).join(", ");
|
|
38141
|
+
advisories.push({
|
|
38142
|
+
source: "autofix",
|
|
38143
|
+
severity: "warning",
|
|
38144
|
+
category: "mock_structure_invalid_files",
|
|
38145
|
+
message: `Mock structure handoff references file that does not exist or is not a test file: ${fileList}`,
|
|
38146
|
+
fixTarget: "source"
|
|
38147
|
+
});
|
|
38148
|
+
}
|
|
38149
|
+
}
|
|
38150
|
+
return [...result, ...advisories];
|
|
38151
|
+
}
|
|
38152
|
+
var init_apply_test_edit_declarations = __esm(() => {
|
|
38153
|
+
init_test_edit_declaration();
|
|
38154
|
+
});
|
|
38155
|
+
|
|
38156
|
+
// src/operations/validate-mock-structure-files.ts
|
|
38157
|
+
import { join as join23 } from "path";
|
|
38158
|
+
async function validateMockStructureFiles(declarations, resolvedTestPatterns, packageDir, deps) {
|
|
38159
|
+
const fileExists = deps?.fileExists ?? defaultFileExists;
|
|
38160
|
+
const valid = [];
|
|
38161
|
+
const invalid = [];
|
|
38162
|
+
for (const d of declarations) {
|
|
38163
|
+
if (d.reason !== "mock_structure") {
|
|
38164
|
+
valid.push(d);
|
|
38165
|
+
continue;
|
|
38166
|
+
}
|
|
38167
|
+
const files = d.files ?? [d.file];
|
|
38168
|
+
let allValid = true;
|
|
38169
|
+
for (const file3 of files) {
|
|
38170
|
+
const absolutePath = join23(packageDir, file3);
|
|
38171
|
+
const exists = await fileExists(absolutePath);
|
|
38172
|
+
if (!exists) {
|
|
38173
|
+
allValid = false;
|
|
38174
|
+
break;
|
|
38175
|
+
}
|
|
38176
|
+
const matchesPattern = resolvedTestPatterns.regex.some((re) => re.test(file3));
|
|
38177
|
+
if (!matchesPattern) {
|
|
38178
|
+
allValid = false;
|
|
38179
|
+
break;
|
|
38180
|
+
}
|
|
38181
|
+
}
|
|
38182
|
+
if (allValid) {
|
|
38183
|
+
valid.push(d);
|
|
38184
|
+
} else {
|
|
38185
|
+
invalid.push(d);
|
|
38186
|
+
}
|
|
38187
|
+
}
|
|
38188
|
+
return { valid, invalid };
|
|
38189
|
+
}
|
|
38190
|
+
var defaultFileExists = (p) => Bun.file(p).exists();
|
|
38191
|
+
var init_validate_mock_structure_files = () => {};
|
|
38192
|
+
|
|
38193
|
+
// src/operations/declaration-sink.ts
|
|
38194
|
+
function makeDeclarationSink() {
|
|
38195
|
+
return { testEdits: [], mockHandoffs: [] };
|
|
38196
|
+
}
|
|
38197
|
+
|
|
37943
38198
|
// src/operations/mechanical-lintfix-strategy.ts
|
|
37944
38199
|
function shellQuotePath2(path5) {
|
|
37945
38200
|
return `'${path5.replaceAll("'", `'\\''`)}'`;
|
|
@@ -38420,6 +38675,8 @@ var init_operations = __esm(() => {
|
|
|
38420
38675
|
init_full_suite_rectify();
|
|
38421
38676
|
init_autofix_implementer_strategy();
|
|
38422
38677
|
init_autofix_test_writer_strategy();
|
|
38678
|
+
init_apply_test_edit_declarations();
|
|
38679
|
+
init_validate_mock_structure_files();
|
|
38423
38680
|
init__finding_to_check();
|
|
38424
38681
|
init_mechanical_lintfix_strategy();
|
|
38425
38682
|
init_mechanical_formatfix_strategy();
|
|
@@ -39070,7 +39327,7 @@ var init_lint_parsing = __esm(() => {
|
|
|
39070
39327
|
});
|
|
39071
39328
|
|
|
39072
39329
|
// src/review/scoped-lint.ts
|
|
39073
|
-
import { join as
|
|
39330
|
+
import { join as join24, relative as relative11 } from "path";
|
|
39074
39331
|
function shellQuotePath4(path5) {
|
|
39075
39332
|
return `'${path5.replaceAll("'", "'\\''")}'`;
|
|
39076
39333
|
}
|
|
@@ -39107,7 +39364,7 @@ async function listChangedFiles(workdir, baseRef) {
|
|
|
39107
39364
|
function inferActivePackageDir(workdir, projectDir) {
|
|
39108
39365
|
if (!projectDir)
|
|
39109
39366
|
return;
|
|
39110
|
-
const rel = normalizePath3(
|
|
39367
|
+
const rel = normalizePath3(relative11(projectDir, workdir));
|
|
39111
39368
|
if (!rel || rel === "." || rel.startsWith(".."))
|
|
39112
39369
|
return;
|
|
39113
39370
|
return rel;
|
|
@@ -39118,7 +39375,7 @@ function uniqueFiles(files) {
|
|
|
39118
39375
|
async function filterFilesToScope(files, workdir, projectDir, activePackageDir) {
|
|
39119
39376
|
const inScope = [];
|
|
39120
39377
|
for (const relPath of files) {
|
|
39121
|
-
const absPath =
|
|
39378
|
+
const absPath = join24(workdir, relPath);
|
|
39122
39379
|
const exists = await _scopedLintDeps.fileExists(absPath);
|
|
39123
39380
|
if (!exists)
|
|
39124
39381
|
continue;
|
|
@@ -39592,7 +39849,7 @@ var init_semantic_debate = __esm(() => {
|
|
|
39592
39849
|
});
|
|
39593
39850
|
|
|
39594
39851
|
// src/review/semantic.ts
|
|
39595
|
-
import { relative as
|
|
39852
|
+
import { relative as relative12, sep as sep3 } from "path";
|
|
39596
39853
|
function recordSemanticAudit(opts) {
|
|
39597
39854
|
opts.runtime?.dispatchEvents.emitReviewDecision({
|
|
39598
39855
|
kind: "review-decision",
|
|
@@ -39658,8 +39915,8 @@ async function runSemanticReview(opts) {
|
|
|
39658
39915
|
const packageDir = workdir !== repoRoot ? workdir : undefined;
|
|
39659
39916
|
const stat = await collectDiffStat(workdir, effectiveRef, { naxIgnoreIndex, packageDir });
|
|
39660
39917
|
const packageDirRelative = projectDir && workdir !== projectDir ? (() => {
|
|
39661
|
-
const rel =
|
|
39662
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
39918
|
+
const rel = relative12(projectDir, workdir);
|
|
39919
|
+
if (rel === ".." || rel.startsWith(`..${sep3}`))
|
|
39663
39920
|
return;
|
|
39664
39921
|
return rel && rel !== "." ? rel : undefined;
|
|
39665
39922
|
})() : undefined;
|
|
@@ -40377,6 +40634,7 @@ var init_review = __esm(() => {
|
|
|
40377
40634
|
init_semantic_evidence();
|
|
40378
40635
|
init_categorization();
|
|
40379
40636
|
init_diff_utils();
|
|
40637
|
+
init_prepare_inputs();
|
|
40380
40638
|
init_finding_projection();
|
|
40381
40639
|
init_runner2();
|
|
40382
40640
|
init_requote_response();
|
|
@@ -40384,9 +40642,34 @@ var init_review = __esm(() => {
|
|
|
40384
40642
|
});
|
|
40385
40643
|
|
|
40386
40644
|
// src/prompts/builders/rectifier-builder-helpers.ts
|
|
40645
|
+
function buildEscapeHatch(opts) {
|
|
40646
|
+
const exceptions = [EXCEPTION_1_LINT_ONLY, EXCEPTION_2_PRD_CONTRACT, EXCEPTION_3_SIBLING_SCOPE];
|
|
40647
|
+
if (opts.includeMockHandoff)
|
|
40648
|
+
exceptions.push(EXCEPTION_4_MOCK_HANDOFF);
|
|
40649
|
+
const count = exceptions.length;
|
|
40650
|
+
const countWord = ["zero", "one", "two", "three", "four"][count];
|
|
40651
|
+
return `
|
|
40652
|
+
If two findings in this list contradict each other and you cannot satisfy both, do not guess.
|
|
40653
|
+
Emit fixes for defects you can resolve, then output a line in this exact format:
|
|
40654
|
+
UNRESOLVED: <brief explanation of which findings conflicted and why they cannot both be satisfied>
|
|
40655
|
+
|
|
40656
|
+
Before emitting UNRESOLVED, confirm none of Exceptions 1\u2013${count} apply.
|
|
40657
|
+
|
|
40658
|
+
## Test-file edit exceptions
|
|
40659
|
+
|
|
40660
|
+
The "do not modify test files" rule has ${countWord} narrow escape valves. Each requires a
|
|
40661
|
+
declaration in your output. Outside these ${countWord} cases the rule is absolute.
|
|
40662
|
+
|
|
40663
|
+
${exceptions.join(`
|
|
40664
|
+
|
|
40665
|
+
`)}`;
|
|
40666
|
+
}
|
|
40667
|
+
function exceptionCountWord(story) {
|
|
40668
|
+
return THREE_SESSION_STRATEGIES.has(story.routing?.testStrategy ?? "") ? "four" : "three";
|
|
40669
|
+
}
|
|
40387
40670
|
function escapeHatchFor(story) {
|
|
40388
40671
|
const isTdd = THREE_SESSION_STRATEGIES.has(story.routing?.testStrategy ?? "");
|
|
40389
|
-
return
|
|
40672
|
+
return buildEscapeHatch({ includeMockHandoff: isTdd });
|
|
40390
40673
|
}
|
|
40391
40674
|
function noTestIsolationBlock(story) {
|
|
40392
40675
|
if (story.routing?.testStrategy !== "no-test")
|
|
@@ -40456,7 +40739,7 @@ ${errors3}
|
|
|
40456
40739
|
2. Only fix findings that are actually valid problems
|
|
40457
40740
|
3. Do NOT add keys, functions, or imports that already exist \u2014 check first
|
|
40458
40741
|
|
|
40459
|
-
Do NOT change test files or test behavior \u2014 see the
|
|
40742
|
+
Do NOT change test files or test behavior \u2014 see the ${exceptionCountWord(story)} narrow exceptions appended below.
|
|
40460
40743
|
Do NOT add new features \u2014 only fix valid issues.
|
|
40461
40744
|
Commit your fixes when done.${scopeConstraint}${noTestIsolationBlock(story)}${escapeHatchFor(story)}`;
|
|
40462
40745
|
}
|
|
@@ -40518,21 +40801,11 @@ The following quality checks failed after implementation:
|
|
|
40518
40801
|
|
|
40519
40802
|
${errors3}
|
|
40520
40803
|
|
|
40521
|
-
Fix all errors listed above that are within this story's scope \u2014 see the
|
|
40804
|
+
Fix all errors listed above that are within this story's scope \u2014 see the ${exceptionCountWord(story)} narrow exceptions appended below for sibling-story spillover. Do NOT change test files or test behavior except via those exceptions.
|
|
40522
40805
|
Do NOT add new features \u2014 only fix the quality check errors.
|
|
40523
40806
|
After fixing, re-run the failing check(s) to verify they pass, then commit your changes.${scopeConstraint}${noTestIsolationBlock(story)}${escapeHatchFor(story)}`;
|
|
40524
40807
|
}
|
|
40525
|
-
var
|
|
40526
|
-
If two findings in this list contradict each other and you cannot satisfy both, do not guess.
|
|
40527
|
-
Emit fixes for defects you can resolve, then output a line in this exact format:
|
|
40528
|
-
UNRESOLVED: <brief explanation of which findings conflicted and why they cannot both be satisfied>
|
|
40529
|
-
|
|
40530
|
-
## Test-file edit exceptions
|
|
40531
|
-
|
|
40532
|
-
The "do not modify test files" rule has three narrow escape valves. Each requires a
|
|
40533
|
-
declaration in your output. Outside these three cases the rule is absolute.
|
|
40534
|
-
|
|
40535
|
-
### Exception 1 \u2014 Lint-only edit
|
|
40808
|
+
var EXCEPTION_1_LINT_ONLY = `### Exception 1 \u2014 Lint-only edit
|
|
40536
40809
|
|
|
40537
40810
|
You MAY edit a test file ONLY when ALL of the following hold:
|
|
40538
40811
|
- The failing check is \`lint\` \u2014 not \`test\`, \`typecheck\`, \`semantic\`, or \`adversarial\`.
|
|
@@ -40548,9 +40821,7 @@ TEST_EDIT_REASON: lint_only
|
|
|
40548
40821
|
FILE: <test file path>
|
|
40549
40822
|
FINDING: <lint rule or message verbatim>
|
|
40550
40823
|
CHANGE: <before line> \u2192 <after line>
|
|
40551
|
-
|
|
40552
|
-
|
|
40553
|
-
### Exception 2 \u2014 PRD-contract mismatch
|
|
40824
|
+
\`\`\``, EXCEPTION_2_PRD_CONTRACT = `### Exception 2 \u2014 PRD-contract mismatch
|
|
40554
40825
|
|
|
40555
40826
|
You MAY correct a test's argument arity, type, or return-handling ONLY when the test's
|
|
40556
40827
|
call contradicts a literal interface signature stated in this story's description or
|
|
@@ -40566,9 +40837,7 @@ TEST_AFTER: <corrected call line>
|
|
|
40566
40837
|
\`\`\`
|
|
40567
40838
|
|
|
40568
40839
|
Do NOT use this exception to change test logic, assertions, or mock setup \u2014 only call
|
|
40569
|
-
signatures that directly contradict a quoted PRD interface
|
|
40570
|
-
|
|
40571
|
-
### Exception 3 \u2014 Unrelated sibling spillover
|
|
40840
|
+
signatures that directly contradict a quoted PRD interface.`, EXCEPTION_3_SIBLING_SCOPE = `### Exception 3 \u2014 Unrelated sibling spillover
|
|
40572
40841
|
|
|
40573
40842
|
When a lint or typecheck error is outside this story's intended scope, do NOT edit that
|
|
40574
40843
|
file. If the smallest package-local fix is required to satisfy this story's acceptance
|
|
@@ -40578,46 +40847,37 @@ TEST_EDIT_REASON: sibling_scope
|
|
|
40578
40847
|
SIBLING_FILE: <file path>
|
|
40579
40848
|
FINDING: <error summary>
|
|
40580
40849
|
\`\`\`
|
|
40581
|
-
and continue. Sibling-scope failures do not block your story
|
|
40582
|
-
|
|
40583
|
-
### Exception 4 \u2014 Mock-structure handoff
|
|
40850
|
+
and continue. Sibling-scope failures do not block your story.`, EXCEPTION_4_MOCK_HANDOFF = `### Exception 4 \u2014 Mock-structure handoff
|
|
40584
40851
|
|
|
40585
40852
|
Use ONLY when the only path to satisfy the ACs requires a structural test rewrite
|
|
40586
|
-
that does NOT fit Exception 2.
|
|
40587
|
-
bypasses; assertion topology must change to match a new dispatch shape.
|
|
40588
|
-
|
|
40589
|
-
Declare with:
|
|
40590
|
-
\`\`\`
|
|
40591
|
-
TEST_EDIT_REASON: mock_structure
|
|
40592
|
-
FILES: <comma-separated test file paths>
|
|
40593
|
-
REASON: <one paragraph: which mock is wrong vs which dispatch the new code uses>
|
|
40594
|
-
\`\`\`
|
|
40853
|
+
that does NOT fit Exception 2. Two cases qualify:
|
|
40595
40854
|
|
|
40596
|
-
|
|
40597
|
-
|
|
40598
|
-
- Do NOT also emit \`UNRESOLVED:\` in the same turn \u2014 this declaration IS the handoff.
|
|
40599
|
-
- FILES must list real test files. Each path must exist and be a test file.`, EXCEPTION_4_MOCK_HANDOFF = `
|
|
40600
|
-
### Exception 4 \u2014 Mock-structure handoff
|
|
40855
|
+
(a) Existing mocks are wrong \u2014 mocks reference primitives the new code bypasses,
|
|
40856
|
+
or assertion topology must change to match a new dispatch shape.
|
|
40601
40857
|
|
|
40602
|
-
|
|
40603
|
-
|
|
40604
|
-
|
|
40858
|
+
(b) Required test-infrastructure does not yet exist and must be introduced \u2014
|
|
40859
|
+
e.g. in-process fake servers, network-level request interception, hermetic
|
|
40860
|
+
fixture-backed HTTP, or equivalent. Applies whenever the AC describes a
|
|
40861
|
+
hermetic/fixture-backed test surface that the current test setup cannot
|
|
40862
|
+
satisfy without new infrastructure.
|
|
40605
40863
|
|
|
40606
40864
|
Declare with:
|
|
40607
40865
|
\`\`\`
|
|
40608
40866
|
TEST_EDIT_REASON: mock_structure
|
|
40609
40867
|
FILES: <comma-separated test file paths>
|
|
40610
|
-
REASON: <one paragraph: which mock is wrong vs which dispatch the new code uses
|
|
40868
|
+
REASON: <one paragraph: which mock is wrong vs which dispatch the new code uses,
|
|
40869
|
+
or what infrastructure must be introduced>
|
|
40611
40870
|
\`\`\`
|
|
40612
40871
|
|
|
40613
40872
|
Rules:
|
|
40614
40873
|
- Do NOT make any edits yourself; the test-writer will fulfill.
|
|
40615
40874
|
- Do NOT also emit \`UNRESOLVED:\` in the same turn \u2014 this declaration IS the handoff.
|
|
40616
|
-
- FILES must list real test files. Each path must exist and be a test file.`, THREE_SESSION_STRATEGIES, MAX_STRUCTURED_FINDINGS = 10, RAW_WITH_FINDINGS_LIMIT = 1000, RAW_FALLBACK_LIMIT = 4000;
|
|
40875
|
+
- FILES must list real test files. Each path must exist and be a test file.`, THREE_SESSION_STRATEGIES, CONTRADICTION_ESCAPE_HATCH, MAX_STRUCTURED_FINDINGS = 10, RAW_WITH_FINDINGS_LIMIT = 1000, RAW_FALLBACK_LIMIT = 4000;
|
|
40617
40876
|
var init_rectifier_builder_helpers = __esm(() => {
|
|
40618
40877
|
init_review();
|
|
40619
40878
|
init_sections2();
|
|
40620
40879
|
THREE_SESSION_STRATEGIES = new Set(["three-session-tdd", "three-session-tdd-lite"]);
|
|
40880
|
+
CONTRADICTION_ESCAPE_HATCH = buildEscapeHatch({ includeMockHandoff: false });
|
|
40621
40881
|
});
|
|
40622
40882
|
|
|
40623
40883
|
// src/prompts/builders/rectifier-builder.ts
|
|
@@ -40698,15 +40958,16 @@ function renderPrioritizedFailures(failedChecks, opts) {
|
|
|
40698
40958
|
}
|
|
40699
40959
|
|
|
40700
40960
|
class RectifierPromptBuilder {
|
|
40701
|
-
static firstAttemptDelta(failedChecks, maxAttempts, guardrailLevel) {
|
|
40961
|
+
static firstAttemptDelta(failedChecks, maxAttempts, guardrailLevel, story) {
|
|
40702
40962
|
const parts = [];
|
|
40703
40963
|
const attemptWord = maxAttempts === 1 ? "1 attempt" : `${maxAttempts} attempts`;
|
|
40964
|
+
const exCount = story ? exceptionCountWord(story) : "three";
|
|
40704
40965
|
parts.push(`Review failed after your implementation. Fix the following issues (${attemptWord} available before escalation):
|
|
40705
40966
|
`);
|
|
40706
40967
|
parts.push(renderPrioritizedFailures(failedChecks));
|
|
40707
40968
|
parts.push(`
|
|
40708
|
-
Fix in priority order. After fixing each priority, re-run the failing check(s) at that level to verify they pass before moving on. Do NOT change test files or test behavior \u2014 see the
|
|
40709
|
-
parts.push(CONTRADICTION_ESCAPE_HATCH);
|
|
40969
|
+
Fix in priority order. After fixing each priority, re-run the failing check(s) at that level to verify they pass before moving on. Do NOT change test files or test behavior \u2014 see the ${exCount} narrow exceptions appended below. Commit your changes when all checks pass.`);
|
|
40970
|
+
parts.push(story ? escapeHatchFor(story) : CONTRADICTION_ESCAPE_HATCH);
|
|
40710
40971
|
const guardrails = buildBehavioralGuardrailsSection("implementer", guardrailLevel ?? "lite");
|
|
40711
40972
|
if (guardrails) {
|
|
40712
40973
|
parts.push(`
|
|
@@ -40716,7 +40977,7 @@ ${guardrails}`);
|
|
|
40716
40977
|
return parts.join(`
|
|
40717
40978
|
`);
|
|
40718
40979
|
}
|
|
40719
|
-
static continuation(failedChecks, attempt, rethinkAtAttempt, urgencyAtAttempt, guardrailLevel) {
|
|
40980
|
+
static continuation(failedChecks, attempt, rethinkAtAttempt, urgencyAtAttempt, guardrailLevel, story) {
|
|
40720
40981
|
const parts = [];
|
|
40721
40982
|
parts.push(`Your previous fix attempt did not resolve all issues. Here are the remaining failures:
|
|
40722
40983
|
`);
|
|
@@ -40729,7 +40990,7 @@ ${guardrails}`);
|
|
|
40729
40990
|
if (attempt >= urgencyAtAttempt) {
|
|
40730
40991
|
parts.push("\n**URGENT: This is your final attempt.** If you cannot fix all issues, emit `UNRESOLVED: <reason>` to escalate.\n");
|
|
40731
40992
|
}
|
|
40732
|
-
parts.push(CONTRADICTION_ESCAPE_HATCH);
|
|
40993
|
+
parts.push(story ? escapeHatchFor(story) : CONTRADICTION_ESCAPE_HATCH);
|
|
40733
40994
|
const guardrails = buildBehavioralGuardrailsSection("implementer", guardrailLevel ?? "lite");
|
|
40734
40995
|
if (guardrails) {
|
|
40735
40996
|
parts.push(`
|
|
@@ -40851,7 +41112,7 @@ ${importantNote}
|
|
|
40851
41112
|
|
|
40852
41113
|
Commit your fixes when done.${scopeConstraint}`;
|
|
40853
41114
|
}
|
|
40854
|
-
static noOpReprompt(failedChecks, noOpCount, maxNoOpReprompts, opts) {
|
|
41115
|
+
static noOpReprompt(failedChecks, noOpCount, maxNoOpReprompts, opts, story) {
|
|
40855
41116
|
const parts = [];
|
|
40856
41117
|
parts.push(`**Your previous turn produced no committed file changes.**
|
|
40857
41118
|
|
|
@@ -40892,7 +41153,7 @@ ${output}
|
|
|
40892
41153
|
`);
|
|
40893
41154
|
}
|
|
40894
41155
|
}
|
|
40895
|
-
parts.push(CONTRADICTION_ESCAPE_HATCH);
|
|
41156
|
+
parts.push(story ? escapeHatchFor(story) : CONTRADICTION_ESCAPE_HATCH);
|
|
40896
41157
|
return parts.join("");
|
|
40897
41158
|
}
|
|
40898
41159
|
static escalated(failures, story, priorAttempts, originalTier, targetTier, config2, testCommand, testScopedTemplate) {
|
|
@@ -40968,11 +41229,11 @@ ${testCommands}
|
|
|
40968
41229
|
6. Ensure ALL tests pass before completing.
|
|
40969
41230
|
|
|
40970
41231
|
**IMPORTANT:**
|
|
40971
|
-
- Do NOT modify test files \u2014 see the
|
|
41232
|
+
- Do NOT modify test files \u2014 see the ${exceptionCountWord(story)} narrow exceptions appended below if you believe a test has a lint error, a PRD-contract mismatch, or belongs to a sibling story.
|
|
40972
41233
|
- Do NOT loosen assertions to mask implementation bugs.
|
|
40973
41234
|
- Focus on fixing the source code to meet the test requirements.
|
|
40974
41235
|
- When running tests, run ONLY the failing test files shown above${cmd ? ` \u2014 NEVER run \`${cmd}\` without a file filter` : " \u2014 never run the full test suite without a file filter"}.
|
|
40975
|
-
`;
|
|
41236
|
+
${escapeHatchFor(story)}`;
|
|
40976
41237
|
}
|
|
40977
41238
|
static reviewRectification(failedChecks, story, opts) {
|
|
40978
41239
|
const scopeConstraint = story.workdir ? `
|
|
@@ -41029,7 +41290,7 @@ ${llmSection}
|
|
|
41029
41290
|
**Important:** LLM reviewers may flag false positives. Before making changes for LLM review findings, read the relevant files to verify each finding is a real issue. Do NOT add keys, functions, or imports that already exist.
|
|
41030
41291
|
|
|
41031
41292
|
Do NOT add new features \u2014 only fix the identified issues.
|
|
41032
|
-
Commit your fixes when done.${scopeConstraint}${
|
|
41293
|
+
Commit your fixes when done.${scopeConstraint}${escapeHatchFor(story)}`;
|
|
41033
41294
|
}
|
|
41034
41295
|
static dialogueAwareRectification(failedChecks, story, opts) {
|
|
41035
41296
|
const scopeConstraint = story.workdir ? `
|
|
@@ -41068,9 +41329,9 @@ ${errors3}${reasoningSection}${historySection}
|
|
|
41068
41329
|
2. Only fix findings that are actually valid problems
|
|
41069
41330
|
3. Do NOT add keys, functions, or imports that already exist \u2014 check first
|
|
41070
41331
|
|
|
41071
|
-
Do NOT change test files or test behavior \u2014 see the
|
|
41332
|
+
Do NOT change test files or test behavior \u2014 see the ${exceptionCountWord(story)} narrow exceptions appended below.
|
|
41072
41333
|
Do NOT add new features \u2014 only fix valid issues.
|
|
41073
|
-
Commit your fixes when done.${scopeConstraint}${
|
|
41334
|
+
Commit your fixes when done.${scopeConstraint}${escapeHatchFor(story)}`;
|
|
41074
41335
|
}
|
|
41075
41336
|
static swapHandoff(basePrompt, pushMarkdown) {
|
|
41076
41337
|
const trimmed = pushMarkdown?.trim();
|
|
@@ -41162,9 +41423,10 @@ Tests are failing. Fix the source so all tests pass \u2014 not just the ones lis
|
|
|
41162
41423
|
4. Do not declare done until step 3 shows 0 failures.
|
|
41163
41424
|
|
|
41164
41425
|
**IMPORTANT:**
|
|
41165
|
-
- Do NOT modify test files \u2014 see the
|
|
41426
|
+
- Do NOT modify test files \u2014 see the ${exceptionCountWord(opts.story)} narrow exceptions appended below if you believe a test has a lint error, a PRD-contract mismatch, or belongs to a sibling story.
|
|
41166
41427
|
- Do NOT loosen assertions to mask implementation bugs.
|
|
41167
41428
|
- Focus on fixing the source code to meet the test requirements.`);
|
|
41429
|
+
parts.push(escapeHatchFor(opts.story));
|
|
41168
41430
|
return parts.join("");
|
|
41169
41431
|
}
|
|
41170
41432
|
static failingTestContext(findings) {
|
|
@@ -42498,7 +42760,7 @@ var init_call = __esm(() => {
|
|
|
42498
42760
|
|
|
42499
42761
|
// src/runtime/cost-aggregator.ts
|
|
42500
42762
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
42501
|
-
import { join as
|
|
42763
|
+
import { join as join25 } from "path";
|
|
42502
42764
|
function makeCorrelationId() {
|
|
42503
42765
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
42504
42766
|
}
|
|
@@ -42689,7 +42951,7 @@ class CostAggregator {
|
|
|
42689
42951
|
if (events.length === 0 && errors3.length === 0)
|
|
42690
42952
|
return;
|
|
42691
42953
|
mkdirSync2(this._drainDir, { recursive: true });
|
|
42692
|
-
const path5 =
|
|
42954
|
+
const path5 = join25(this._drainDir, `${this._runId}.jsonl`);
|
|
42693
42955
|
const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
|
|
42694
42956
|
await _costAggDeps.write(path5, `${sorted.map((e) => JSON.stringify(e)).join(`
|
|
42695
42957
|
`)}
|
|
@@ -42729,7 +42991,7 @@ var init_cost_aggregator = __esm(() => {
|
|
|
42729
42991
|
// src/runtime/prompt-auditor.ts
|
|
42730
42992
|
import { appendFileSync } from "fs";
|
|
42731
42993
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
42732
|
-
import { join as
|
|
42994
|
+
import { join as join26 } from "path";
|
|
42733
42995
|
function createNoOpPromptAuditor() {
|
|
42734
42996
|
return {
|
|
42735
42997
|
record() {},
|
|
@@ -42795,8 +43057,8 @@ class PromptAuditor {
|
|
|
42795
43057
|
_jsonlPath;
|
|
42796
43058
|
_featureDir;
|
|
42797
43059
|
constructor(runId, flushDir, featureName) {
|
|
42798
|
-
this._featureDir =
|
|
42799
|
-
this._jsonlPath =
|
|
43060
|
+
this._featureDir = join26(flushDir, featureName);
|
|
43061
|
+
this._jsonlPath = join26(this._featureDir, `${runId}.jsonl`);
|
|
42800
43062
|
}
|
|
42801
43063
|
record(entry) {
|
|
42802
43064
|
this._enqueue(entry);
|
|
@@ -42845,7 +43107,7 @@ class PromptAuditor {
|
|
|
42845
43107
|
const auditEntry = entry;
|
|
42846
43108
|
const filename = deriveTxtFilename(auditEntry);
|
|
42847
43109
|
try {
|
|
42848
|
-
await _promptAuditorDeps.write(
|
|
43110
|
+
await _promptAuditorDeps.write(join26(this._featureDir, filename), buildTxtContent(auditEntry));
|
|
42849
43111
|
} catch (err) {
|
|
42850
43112
|
throw tagAuditError(err, "txt");
|
|
42851
43113
|
}
|
|
@@ -43933,9 +44195,9 @@ var init_pid_registry = __esm(() => {
|
|
|
43933
44195
|
// src/session/manager-deps.ts
|
|
43934
44196
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
43935
44197
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
43936
|
-
import { isAbsolute as isAbsolute9, join as
|
|
44198
|
+
import { isAbsolute as isAbsolute9, join as join27, relative as relative13, sep as sep4 } from "path";
|
|
43937
44199
|
function resolveProjectDirFromScratchDir(scratchDir) {
|
|
43938
|
-
const marker = `${
|
|
44200
|
+
const marker = `${sep4}.nax${sep4}features${sep4}`;
|
|
43939
44201
|
const markerIdx = scratchDir.lastIndexOf(marker);
|
|
43940
44202
|
if (markerIdx > 0)
|
|
43941
44203
|
return scratchDir.slice(0, markerIdx);
|
|
@@ -43945,7 +44207,7 @@ function resolveProjectDirFromScratchDir(scratchDir) {
|
|
|
43945
44207
|
return;
|
|
43946
44208
|
}
|
|
43947
44209
|
function toProjectRelativePath(projectDir, pathValue) {
|
|
43948
|
-
const relativePath = isAbsolute9(pathValue) ?
|
|
44210
|
+
const relativePath = isAbsolute9(pathValue) ? relative13(projectDir, pathValue) : pathValue;
|
|
43949
44211
|
return relativePath === "" ? "." : relativePath;
|
|
43950
44212
|
}
|
|
43951
44213
|
var _sessionManagerDeps;
|
|
@@ -43954,7 +44216,7 @@ var init_manager_deps = __esm(() => {
|
|
|
43954
44216
|
now: () => new Date().toISOString(),
|
|
43955
44217
|
nowMs: () => Date.now(),
|
|
43956
44218
|
uuid: () => randomUUID3(),
|
|
43957
|
-
sessionScratchDir: (projectDir, featureName, sessionId) =>
|
|
44219
|
+
sessionScratchDir: (projectDir, featureName, sessionId) => join27(projectDir, ".nax", "features", featureName, "sessions", sessionId),
|
|
43958
44220
|
writeDescriptor: async (scratchDir, descriptor, projectDir) => {
|
|
43959
44221
|
await mkdir5(scratchDir, { recursive: true });
|
|
43960
44222
|
const { handle: _handle, ...persistable } = descriptor;
|
|
@@ -43965,7 +44227,7 @@ var init_manager_deps = __esm(() => {
|
|
|
43965
44227
|
persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
|
|
43966
44228
|
}
|
|
43967
44229
|
}
|
|
43968
|
-
await Bun.write(
|
|
44230
|
+
await Bun.write(join27(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
|
|
43969
44231
|
}
|
|
43970
44232
|
};
|
|
43971
44233
|
});
|
|
@@ -44716,7 +44978,7 @@ __export(exports_runtime, {
|
|
|
44716
44978
|
CostAggregator: () => CostAggregator,
|
|
44717
44979
|
AgentStreamEventBus: () => AgentStreamEventBus
|
|
44718
44980
|
});
|
|
44719
|
-
import { basename as basename5, join as
|
|
44981
|
+
import { basename as basename5, join as join28 } from "path";
|
|
44720
44982
|
function createRuntime(config2, workdir, opts) {
|
|
44721
44983
|
const runId = crypto.randomUUID();
|
|
44722
44984
|
const controller = new AbortController;
|
|
@@ -44732,10 +44994,10 @@ function createRuntime(config2, workdir, opts) {
|
|
|
44732
44994
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
44733
44995
|
const globalDir = globalOutputDir();
|
|
44734
44996
|
const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
|
|
44735
|
-
const costDir =
|
|
44997
|
+
const costDir = join28(outputDir, "cost");
|
|
44736
44998
|
const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
|
|
44737
44999
|
const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
|
|
44738
|
-
const auditDir = config2.agent?.promptAudit?.dir ??
|
|
45000
|
+
const auditDir = config2.agent?.promptAudit?.dir ?? join28(outputDir, "prompt-audit");
|
|
44739
45001
|
let promptAuditor;
|
|
44740
45002
|
if (opts?.promptAuditor) {
|
|
44741
45003
|
promptAuditor = opts.promptAuditor;
|
|
@@ -44886,9 +45148,9 @@ async function allSettledBounded(tasks, limit) {
|
|
|
44886
45148
|
|
|
44887
45149
|
// src/context/injector.ts
|
|
44888
45150
|
import { existsSync as existsSync8 } from "fs";
|
|
44889
|
-
import { join as
|
|
45151
|
+
import { join as join29 } from "path";
|
|
44890
45152
|
async function detectNode(workdir) {
|
|
44891
|
-
const pkgPath =
|
|
45153
|
+
const pkgPath = join29(workdir, "package.json");
|
|
44892
45154
|
if (!existsSync8(pkgPath))
|
|
44893
45155
|
return null;
|
|
44894
45156
|
try {
|
|
@@ -44905,7 +45167,7 @@ async function detectNode(workdir) {
|
|
|
44905
45167
|
}
|
|
44906
45168
|
}
|
|
44907
45169
|
async function detectGo(workdir) {
|
|
44908
|
-
const goMod =
|
|
45170
|
+
const goMod = join29(workdir, "go.mod");
|
|
44909
45171
|
if (!existsSync8(goMod))
|
|
44910
45172
|
return null;
|
|
44911
45173
|
try {
|
|
@@ -44929,7 +45191,7 @@ async function detectGo(workdir) {
|
|
|
44929
45191
|
}
|
|
44930
45192
|
}
|
|
44931
45193
|
async function detectRust(workdir) {
|
|
44932
|
-
const cargoPath =
|
|
45194
|
+
const cargoPath = join29(workdir, "Cargo.toml");
|
|
44933
45195
|
if (!existsSync8(cargoPath))
|
|
44934
45196
|
return null;
|
|
44935
45197
|
try {
|
|
@@ -44945,8 +45207,8 @@ async function detectRust(workdir) {
|
|
|
44945
45207
|
}
|
|
44946
45208
|
}
|
|
44947
45209
|
async function detectPython(workdir) {
|
|
44948
|
-
const pyproject =
|
|
44949
|
-
const requirements =
|
|
45210
|
+
const pyproject = join29(workdir, "pyproject.toml");
|
|
45211
|
+
const requirements = join29(workdir, "requirements.txt");
|
|
44950
45212
|
if (!existsSync8(pyproject) && !existsSync8(requirements))
|
|
44951
45213
|
return null;
|
|
44952
45214
|
try {
|
|
@@ -44965,7 +45227,7 @@ async function detectPython(workdir) {
|
|
|
44965
45227
|
}
|
|
44966
45228
|
}
|
|
44967
45229
|
async function detectPhp(workdir) {
|
|
44968
|
-
const composerPath =
|
|
45230
|
+
const composerPath = join29(workdir, "composer.json");
|
|
44969
45231
|
if (!existsSync8(composerPath))
|
|
44970
45232
|
return null;
|
|
44971
45233
|
try {
|
|
@@ -44978,7 +45240,7 @@ async function detectPhp(workdir) {
|
|
|
44978
45240
|
}
|
|
44979
45241
|
}
|
|
44980
45242
|
async function detectRuby(workdir) {
|
|
44981
|
-
const gemfile =
|
|
45243
|
+
const gemfile = join29(workdir, "Gemfile");
|
|
44982
45244
|
if (!existsSync8(gemfile))
|
|
44983
45245
|
return null;
|
|
44984
45246
|
try {
|
|
@@ -44990,9 +45252,9 @@ async function detectRuby(workdir) {
|
|
|
44990
45252
|
}
|
|
44991
45253
|
}
|
|
44992
45254
|
async function detectJvm(workdir) {
|
|
44993
|
-
const pom =
|
|
44994
|
-
const gradle =
|
|
44995
|
-
const gradleKts =
|
|
45255
|
+
const pom = join29(workdir, "pom.xml");
|
|
45256
|
+
const gradle = join29(workdir, "build.gradle");
|
|
45257
|
+
const gradleKts = join29(workdir, "build.gradle.kts");
|
|
44996
45258
|
if (!existsSync8(pom) && !existsSync8(gradle) && !existsSync8(gradleKts))
|
|
44997
45259
|
return null;
|
|
44998
45260
|
try {
|
|
@@ -45000,7 +45262,7 @@ async function detectJvm(workdir) {
|
|
|
45000
45262
|
const content2 = await Bun.file(pom).text();
|
|
45001
45263
|
const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
45002
45264
|
const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
|
|
45003
|
-
const lang2 = existsSync8(
|
|
45265
|
+
const lang2 = existsSync8(join29(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
|
|
45004
45266
|
return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
|
|
45005
45267
|
}
|
|
45006
45268
|
const gradleFile = existsSync8(gradleKts) ? gradleKts : gradle;
|
|
@@ -45254,7 +45516,7 @@ var init_windsurf = __esm(() => {
|
|
|
45254
45516
|
|
|
45255
45517
|
// src/context/generator.ts
|
|
45256
45518
|
import { existsSync as existsSync9 } from "fs";
|
|
45257
|
-
import { join as
|
|
45519
|
+
import { join as join30, relative as relative14 } from "path";
|
|
45258
45520
|
async function loadContextContent(options, config2) {
|
|
45259
45521
|
if (!_generatorDeps.existsSync(options.contextPath)) {
|
|
45260
45522
|
throw new Error(`Context file not found: ${options.contextPath}`);
|
|
@@ -45272,7 +45534,7 @@ async function generateFor(agent, options, config2) {
|
|
|
45272
45534
|
try {
|
|
45273
45535
|
const context = await loadContextContent(options, config2);
|
|
45274
45536
|
const content = generator.generate(context);
|
|
45275
|
-
const outputPath =
|
|
45537
|
+
const outputPath = join30(options.outputDir, generator.outputFile);
|
|
45276
45538
|
validateFilePath(outputPath, options.outputDir);
|
|
45277
45539
|
if (!options.dryRun) {
|
|
45278
45540
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -45290,7 +45552,7 @@ async function generateAll(options, config2, agentFilter) {
|
|
|
45290
45552
|
for (const [agentKey, generator] of entries) {
|
|
45291
45553
|
try {
|
|
45292
45554
|
const content = generator.generate(context);
|
|
45293
|
-
const outputPath =
|
|
45555
|
+
const outputPath = join30(options.outputDir, generator.outputFile);
|
|
45294
45556
|
validateFilePath(outputPath, options.outputDir);
|
|
45295
45557
|
if (!options.dryRun) {
|
|
45296
45558
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -45310,7 +45572,7 @@ async function discoverPackages(repoRoot) {
|
|
|
45310
45572
|
const glob = new Bun.Glob(pattern);
|
|
45311
45573
|
for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
|
|
45312
45574
|
const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
|
|
45313
|
-
const pkgAbsolute =
|
|
45575
|
+
const pkgAbsolute = join30(repoRoot, pkgRelative);
|
|
45314
45576
|
if (!seen.has(pkgAbsolute)) {
|
|
45315
45577
|
seen.add(pkgAbsolute);
|
|
45316
45578
|
packages.push(pkgAbsolute);
|
|
@@ -45342,14 +45604,14 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
45342
45604
|
}
|
|
45343
45605
|
}
|
|
45344
45606
|
}
|
|
45345
|
-
const turboPath =
|
|
45607
|
+
const turboPath = join30(repoRoot, "turbo.json");
|
|
45346
45608
|
try {
|
|
45347
45609
|
const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
|
|
45348
45610
|
if (Array.isArray(turbo.packages)) {
|
|
45349
45611
|
await resolveGlobs(turbo.packages);
|
|
45350
45612
|
}
|
|
45351
45613
|
} catch {}
|
|
45352
|
-
const pkgPath =
|
|
45614
|
+
const pkgPath = join30(repoRoot, "package.json");
|
|
45353
45615
|
try {
|
|
45354
45616
|
const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
|
|
45355
45617
|
const ws = pkg.workspaces;
|
|
@@ -45357,7 +45619,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
45357
45619
|
if (patterns.length > 0)
|
|
45358
45620
|
await resolveGlobs(patterns);
|
|
45359
45621
|
} catch {}
|
|
45360
|
-
const pnpmPath =
|
|
45622
|
+
const pnpmPath = join30(repoRoot, "pnpm-workspace.yaml");
|
|
45361
45623
|
try {
|
|
45362
45624
|
const raw = await _generatorDeps.readTextFile(pnpmPath);
|
|
45363
45625
|
const lines = raw.split(`
|
|
@@ -45382,8 +45644,8 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
45382
45644
|
}
|
|
45383
45645
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
45384
45646
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
45385
|
-
const relativePkgPath =
|
|
45386
|
-
const contextPath =
|
|
45647
|
+
const relativePkgPath = relative14(resolvedRepoRoot, packageDir);
|
|
45648
|
+
const contextPath = join30(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
45387
45649
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
45388
45650
|
return [
|
|
45389
45651
|
{
|
|
@@ -45451,7 +45713,7 @@ var init_generator2 = __esm(() => {
|
|
|
45451
45713
|
});
|
|
45452
45714
|
|
|
45453
45715
|
// src/analyze/scanner.ts
|
|
45454
|
-
import { join as
|
|
45716
|
+
import { join as join31 } from "path";
|
|
45455
45717
|
function resolveFrameworkAndRunner(language, pkg) {
|
|
45456
45718
|
if (language === "go")
|
|
45457
45719
|
return { framework: "", testRunner: "go-test" };
|
|
@@ -45473,7 +45735,7 @@ async function scanSourceRoots(workdir) {
|
|
|
45473
45735
|
});
|
|
45474
45736
|
try {
|
|
45475
45737
|
const language = await deps.detectLanguage(workdir);
|
|
45476
|
-
const pkg = await deps.readPackageJson(
|
|
45738
|
+
const pkg = await deps.readPackageJson(join31(workdir, "package.json"));
|
|
45477
45739
|
const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
|
|
45478
45740
|
return [{ path: ".", language, framework, testRunner }];
|
|
45479
45741
|
} catch {
|
|
@@ -45491,9 +45753,9 @@ async function scanSourceRoots(workdir) {
|
|
|
45491
45753
|
packages = packages.slice(0, MAX_SOURCE_ROOTS);
|
|
45492
45754
|
}
|
|
45493
45755
|
return Promise.all(packages.map(async (pkgPath) => {
|
|
45494
|
-
const pkgDir = pkgPath === "." ? workdir :
|
|
45756
|
+
const pkgDir = pkgPath === "." ? workdir : join31(workdir, pkgPath);
|
|
45495
45757
|
const language = await deps.detectLanguage(pkgDir);
|
|
45496
|
-
const pkg = await deps.readPackageJson(
|
|
45758
|
+
const pkg = await deps.readPackageJson(join31(pkgDir, "package.json"));
|
|
45497
45759
|
const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
|
|
45498
45760
|
return { path: pkgPath, language, framework, testRunner };
|
|
45499
45761
|
}));
|
|
@@ -45526,7 +45788,7 @@ var init_analyze = __esm(() => {
|
|
|
45526
45788
|
});
|
|
45527
45789
|
|
|
45528
45790
|
// src/debate/pre-phase/grounder.ts
|
|
45529
|
-
import { join as
|
|
45791
|
+
import { join as join32 } from "path";
|
|
45530
45792
|
async function buildCodebaseContext(workdir) {
|
|
45531
45793
|
const roots = await _grounderDeps.scanSourceRoots(workdir);
|
|
45532
45794
|
return buildSourceRootsSection(normalizeRoots(workdir, roots));
|
|
@@ -45538,7 +45800,7 @@ function normalizeRoots(workdir, roots) {
|
|
|
45538
45800
|
}));
|
|
45539
45801
|
}
|
|
45540
45802
|
async function writeManifestArtifact(ctx, manifest) {
|
|
45541
|
-
const manifestPath =
|
|
45803
|
+
const manifestPath = join32(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
|
|
45542
45804
|
await _grounderDeps.write(manifestPath, JSON.stringify(manifest, null, 2));
|
|
45543
45805
|
}
|
|
45544
45806
|
var _grounderDeps, grounderStrategy = async (ctx) => {
|
|
@@ -45899,7 +46161,7 @@ function formatSpecDeltas(blockers, manifest) {
|
|
|
45899
46161
|
|
|
45900
46162
|
// src/debate/verifiers/checks.ts
|
|
45901
46163
|
import { existsSync as defaultExistsSync } from "fs";
|
|
45902
|
-
import { join as
|
|
46164
|
+
import { join as join33 } from "path";
|
|
45903
46165
|
function checkFilesExist(prd, workdir, deps) {
|
|
45904
46166
|
const existsSync10 = deps?.existsSync ?? defaultExistsSync;
|
|
45905
46167
|
const findings = [];
|
|
@@ -45909,7 +46171,7 @@ function checkFilesExist(prd, workdir, deps) {
|
|
|
45909
46171
|
for (const entry of story.contextFiles) {
|
|
45910
46172
|
const filePath = typeof entry === "string" ? entry : entry.path;
|
|
45911
46173
|
const factId = typeof entry === "string" ? undefined : entry.factId;
|
|
45912
|
-
const absPath =
|
|
46174
|
+
const absPath = join33(workdir, filePath);
|
|
45913
46175
|
if (existsSync10(absPath))
|
|
45914
46176
|
continue;
|
|
45915
46177
|
if (factId) {
|
|
@@ -46020,7 +46282,7 @@ var init_checks3 = () => {};
|
|
|
46020
46282
|
|
|
46021
46283
|
// src/debate/verifiers/plan-checklist.ts
|
|
46022
46284
|
import { existsSync as existsSync10 } from "fs";
|
|
46023
|
-
import { join as
|
|
46285
|
+
import { join as join34 } from "path";
|
|
46024
46286
|
function parsePrd(output) {
|
|
46025
46287
|
if (!output)
|
|
46026
46288
|
return null;
|
|
@@ -46031,7 +46293,7 @@ function parsePrd(output) {
|
|
|
46031
46293
|
}
|
|
46032
46294
|
}
|
|
46033
46295
|
async function loadManifest(ctx) {
|
|
46034
|
-
const manifestPath =
|
|
46296
|
+
const manifestPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
|
|
46035
46297
|
const raw = await _planChecklistDeps.readFile(manifestPath);
|
|
46036
46298
|
if (!raw)
|
|
46037
46299
|
return null;
|
|
@@ -46044,7 +46306,7 @@ async function loadManifest(ctx) {
|
|
|
46044
46306
|
}
|
|
46045
46307
|
}
|
|
46046
46308
|
async function emitSpecDeltas(ctx, blockers, manifest) {
|
|
46047
|
-
const artifactPath =
|
|
46309
|
+
const artifactPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
|
|
46048
46310
|
const content = formatSpecDeltas(blockers, manifest ?? { repoFacts: [], specClaims: [], gaps: [] });
|
|
46049
46311
|
await _planChecklistDeps.write(artifactPath, content);
|
|
46050
46312
|
return artifactPath;
|
|
@@ -46395,7 +46657,7 @@ var init_runner_plan_helpers = __esm(() => {
|
|
|
46395
46657
|
});
|
|
46396
46658
|
|
|
46397
46659
|
// src/debate/runner-plan.ts
|
|
46398
|
-
import { join as
|
|
46660
|
+
import { join as join35 } from "path";
|
|
46399
46661
|
async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
46400
46662
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
46401
46663
|
const config2 = ctx.stageConfig;
|
|
@@ -46454,7 +46716,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
|
46454
46716
|
sessionMode: ctx.stageConfig.sessionMode ?? "one-shot",
|
|
46455
46717
|
proposers: ctx.stageConfig.proposers
|
|
46456
46718
|
});
|
|
46457
|
-
const outputPaths = resolved.map((_, i) =>
|
|
46719
|
+
const outputPaths = resolved.map((_, i) => join35(opts.outputDir, `prd-debate-${i}.json`));
|
|
46458
46720
|
const successful = [];
|
|
46459
46721
|
let rebuttalList;
|
|
46460
46722
|
if (selectorKind === "verifier-pick") {
|
|
@@ -48702,9 +48964,9 @@ function validateFeatureName(feature) {
|
|
|
48702
48964
|
|
|
48703
48965
|
// src/plan/critic.ts
|
|
48704
48966
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
48705
|
-
import { dirname as dirname7, join as
|
|
48967
|
+
import { dirname as dirname7, join as join38 } from "path";
|
|
48706
48968
|
async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
|
|
48707
|
-
const path7 =
|
|
48969
|
+
const path7 = join38(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
|
|
48708
48970
|
await mkdir6(dirname7(path7), { recursive: true });
|
|
48709
48971
|
await Bun.write(path7, formatSpecDeltas(findings, manifest));
|
|
48710
48972
|
return path7;
|
|
@@ -49917,9 +50179,9 @@ __export(exports_plan_decompose, {
|
|
|
49917
50179
|
runReplanLoop: () => runReplanLoop,
|
|
49918
50180
|
planDecomposeCommand: () => planDecomposeCommand
|
|
49919
50181
|
});
|
|
49920
|
-
import { join as
|
|
50182
|
+
import { join as join39 } from "path";
|
|
49921
50183
|
async function planDecomposeCommand(workdir, config2, options) {
|
|
49922
|
-
const prdPath =
|
|
50184
|
+
const prdPath = join39(workdir, ".nax", "features", options.feature, "prd.json");
|
|
49923
50185
|
if (!_planDeps.existsSync(prdPath)) {
|
|
49924
50186
|
throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
|
|
49925
50187
|
stage: "decompose",
|
|
@@ -50093,7 +50355,7 @@ var init_plan_decompose = __esm(() => {
|
|
|
50093
50355
|
|
|
50094
50356
|
// src/cli/plan-runtime.ts
|
|
50095
50357
|
import { existsSync as existsSync15 } from "fs";
|
|
50096
|
-
import { join as
|
|
50358
|
+
import { join as join40 } from "path";
|
|
50097
50359
|
function isRuntimeWithAgentManager(value) {
|
|
50098
50360
|
return typeof value === "object" && value !== null && "agentManager" in value;
|
|
50099
50361
|
}
|
|
@@ -50145,7 +50407,7 @@ var init_plan_runtime = __esm(() => {
|
|
|
50145
50407
|
writeFile: (path7, content) => Bun.write(path7, content).then(() => {}),
|
|
50146
50408
|
scanSourceRoots: (workdir) => scanSourceRoots(workdir),
|
|
50147
50409
|
createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
|
|
50148
|
-
readPackageJson: (workdir) => Bun.file(
|
|
50410
|
+
readPackageJson: (workdir) => Bun.file(join40(workdir, "package.json")).json().catch(() => null),
|
|
50149
50411
|
spawnSync: (cmd, opts) => {
|
|
50150
50412
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
|
|
50151
50413
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
@@ -50530,7 +50792,7 @@ var init_metrics = __esm(() => {
|
|
|
50530
50792
|
|
|
50531
50793
|
// src/commands/common.ts
|
|
50532
50794
|
import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync3 } from "fs";
|
|
50533
|
-
import { join as
|
|
50795
|
+
import { join as join41, resolve as resolve13 } from "path";
|
|
50534
50796
|
function resolveProject(options = {}) {
|
|
50535
50797
|
const { dir, feature } = options;
|
|
50536
50798
|
let projectRoot;
|
|
@@ -50538,12 +50800,12 @@ function resolveProject(options = {}) {
|
|
|
50538
50800
|
let configPath;
|
|
50539
50801
|
if (dir) {
|
|
50540
50802
|
projectRoot = realpathSync3(resolve13(dir));
|
|
50541
|
-
naxDir =
|
|
50803
|
+
naxDir = join41(projectRoot, ".nax");
|
|
50542
50804
|
if (!existsSync16(naxDir)) {
|
|
50543
50805
|
throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
|
|
50544
50806
|
Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
|
|
50545
50807
|
}
|
|
50546
|
-
configPath =
|
|
50808
|
+
configPath = join41(naxDir, "config.json");
|
|
50547
50809
|
if (!existsSync16(configPath)) {
|
|
50548
50810
|
throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
|
|
50549
50811
|
Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
@@ -50551,17 +50813,17 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
|
50551
50813
|
} else {
|
|
50552
50814
|
const found = findProjectRoot(process.cwd());
|
|
50553
50815
|
if (!found) {
|
|
50554
|
-
const cwdNaxDir =
|
|
50816
|
+
const cwdNaxDir = join41(process.cwd(), ".nax");
|
|
50555
50817
|
if (existsSync16(cwdNaxDir)) {
|
|
50556
|
-
const cwdConfigPath =
|
|
50818
|
+
const cwdConfigPath = join41(cwdNaxDir, "config.json");
|
|
50557
50819
|
throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
|
|
50558
50820
|
Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
|
|
50559
50821
|
}
|
|
50560
50822
|
throw new NaxError("No nax project found. Run this command from within a nax project directory, or use -d flag to specify the project path.", "PROJECT_NOT_FOUND", { cwd: process.cwd() });
|
|
50561
50823
|
}
|
|
50562
50824
|
projectRoot = found;
|
|
50563
|
-
naxDir =
|
|
50564
|
-
configPath =
|
|
50825
|
+
naxDir = join41(projectRoot, ".nax");
|
|
50826
|
+
configPath = join41(naxDir, "config.json");
|
|
50565
50827
|
}
|
|
50566
50828
|
let featureDir;
|
|
50567
50829
|
if (feature) {
|
|
@@ -50570,8 +50832,8 @@ Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, co
|
|
|
50570
50832
|
} catch (error48) {
|
|
50571
50833
|
throw new NaxError(error48.message, "FEATURE_INVALID", { feature });
|
|
50572
50834
|
}
|
|
50573
|
-
const featuresDir =
|
|
50574
|
-
featureDir =
|
|
50835
|
+
const featuresDir = join41(naxDir, "features");
|
|
50836
|
+
featureDir = join41(featuresDir, feature);
|
|
50575
50837
|
if (!existsSync16(featureDir)) {
|
|
50576
50838
|
const availableFeatures = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
|
|
50577
50839
|
const availableMsg = availableFeatures.length > 0 ? `
|
|
@@ -50604,7 +50866,7 @@ async function resolveProjectAsync(options = {}) {
|
|
|
50604
50866
|
}
|
|
50605
50867
|
const isPlainName = !dir.includes("/") && !dir.includes("\\");
|
|
50606
50868
|
if (isPlainName) {
|
|
50607
|
-
const registryIdentityPath =
|
|
50869
|
+
const registryIdentityPath = join41(globalConfigDir(), dir, ".identity");
|
|
50608
50870
|
const identityFile = Bun.file(registryIdentityPath);
|
|
50609
50871
|
if (await identityFile.exists()) {
|
|
50610
50872
|
try {
|
|
@@ -50636,12 +50898,12 @@ function findProjectRoot(startDir) {
|
|
|
50636
50898
|
let current = resolve13(startDir);
|
|
50637
50899
|
let depth = 0;
|
|
50638
50900
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
50639
|
-
const naxDir =
|
|
50640
|
-
const configPath =
|
|
50901
|
+
const naxDir = join41(current, ".nax");
|
|
50902
|
+
const configPath = join41(naxDir, "config.json");
|
|
50641
50903
|
if (existsSync16(configPath)) {
|
|
50642
50904
|
return realpathSync3(current);
|
|
50643
50905
|
}
|
|
50644
|
-
const parent =
|
|
50906
|
+
const parent = join41(current, "..");
|
|
50645
50907
|
if (parent === current) {
|
|
50646
50908
|
break;
|
|
50647
50909
|
}
|
|
@@ -51800,10 +52062,10 @@ var init_effectiveness = __esm(() => {
|
|
|
51800
52062
|
|
|
51801
52063
|
// src/execution/progress.ts
|
|
51802
52064
|
import { appendFile as appendFile3, mkdir as mkdir7 } from "fs/promises";
|
|
51803
|
-
import { join as
|
|
52065
|
+
import { join as join44 } from "path";
|
|
51804
52066
|
async function appendProgress(featureDir, storyId, status, message) {
|
|
51805
52067
|
await mkdir7(featureDir, { recursive: true });
|
|
51806
|
-
const progressPath =
|
|
52068
|
+
const progressPath = join44(featureDir, "progress.txt");
|
|
51807
52069
|
const timestamp = new Date().toISOString();
|
|
51808
52070
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
51809
52071
|
`;
|
|
@@ -51992,7 +52254,7 @@ var init_completion = __esm(() => {
|
|
|
51992
52254
|
|
|
51993
52255
|
// src/constitution/loader.ts
|
|
51994
52256
|
import { existsSync as existsSync19 } from "fs";
|
|
51995
|
-
import { join as
|
|
52257
|
+
import { join as join45 } from "path";
|
|
51996
52258
|
function truncateToTokens(text, maxTokens) {
|
|
51997
52259
|
const maxChars = maxTokens * 3;
|
|
51998
52260
|
if (text.length <= maxChars) {
|
|
@@ -52014,7 +52276,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
52014
52276
|
}
|
|
52015
52277
|
let combinedContent = "";
|
|
52016
52278
|
if (!config2.skipGlobal) {
|
|
52017
|
-
const globalPath =
|
|
52279
|
+
const globalPath = join45(globalConfigDir(), config2.path);
|
|
52018
52280
|
if (existsSync19(globalPath)) {
|
|
52019
52281
|
const validatedPath = validateFilePath(globalPath, globalConfigDir());
|
|
52020
52282
|
const globalFile = Bun.file(validatedPath);
|
|
@@ -52024,7 +52286,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
52024
52286
|
}
|
|
52025
52287
|
}
|
|
52026
52288
|
}
|
|
52027
|
-
const projectPath =
|
|
52289
|
+
const projectPath = join45(projectDir, config2.path);
|
|
52028
52290
|
if (existsSync19(projectPath)) {
|
|
52029
52291
|
const validatedPath = validateFilePath(projectPath, projectDir);
|
|
52030
52292
|
const projectFile = Bun.file(validatedPath);
|
|
@@ -52538,26 +52800,34 @@ function phaseExplicitlyPassed(output) {
|
|
|
52538
52800
|
const r = output;
|
|
52539
52801
|
return r.success === true || r.passed === true;
|
|
52540
52802
|
}
|
|
52541
|
-
function phasePassed(opName, output) {
|
|
52803
|
+
function phasePassed(opName, output, storyId) {
|
|
52804
|
+
const strictVerdictPhase = STRICT_VERDICT_PHASE_NAMES.has(opName);
|
|
52542
52805
|
if (output === null || output === undefined) {
|
|
52543
|
-
getSafeLogger()?.warn("story-orchestrator", "Phase produced no output \u2014 treating as pass", {
|
|
52544
|
-
storyId
|
|
52806
|
+
getSafeLogger()?.warn("story-orchestrator", strictVerdictPhase ? "Strict phase produced no output \u2014 treating as fail" : "Phase produced no output \u2014 treating as pass", {
|
|
52807
|
+
storyId,
|
|
52545
52808
|
phase: opName
|
|
52546
52809
|
});
|
|
52547
|
-
return
|
|
52810
|
+
return !strictVerdictPhase;
|
|
52811
|
+
}
|
|
52812
|
+
if (typeof output !== "object") {
|
|
52813
|
+
if (!strictVerdictPhase)
|
|
52814
|
+
return true;
|
|
52815
|
+
getSafeLogger()?.warn("story-orchestrator", "Strict phase produced non-object output \u2014 treating as fail", {
|
|
52816
|
+
storyId,
|
|
52817
|
+
phase: opName
|
|
52818
|
+
});
|
|
52819
|
+
return false;
|
|
52548
52820
|
}
|
|
52549
|
-
if (typeof output !== "object")
|
|
52550
|
-
return true;
|
|
52551
52821
|
const r = output;
|
|
52552
52822
|
if ("success" in r)
|
|
52553
52823
|
return r.success !== false;
|
|
52554
52824
|
if ("passed" in r)
|
|
52555
52825
|
return r.passed !== false;
|
|
52556
|
-
getSafeLogger()?.warn("story-orchestrator", "Phase output has neither 'success' nor 'passed' \u2014 treating as pass", {
|
|
52557
|
-
storyId
|
|
52826
|
+
getSafeLogger()?.warn("story-orchestrator", strictVerdictPhase ? "Strict phase output has neither 'success' nor 'passed' \u2014 treating as fail" : "Phase output has neither 'success' nor 'passed' \u2014 treating as pass", {
|
|
52827
|
+
storyId,
|
|
52558
52828
|
phase: opName
|
|
52559
52829
|
});
|
|
52560
|
-
return
|
|
52830
|
+
return !strictVerdictPhase;
|
|
52561
52831
|
}
|
|
52562
52832
|
function isFinding(value) {
|
|
52563
52833
|
return typeof value === "object" && value !== null && typeof value.source === "string" && value.source.length > 0;
|
|
@@ -52680,6 +52950,29 @@ function logUnifiedReviewPhaseStart(storyId, opName) {
|
|
|
52680
52950
|
logger?.info("review", "Running adversarial check", { storyId });
|
|
52681
52951
|
}
|
|
52682
52952
|
}
|
|
52953
|
+
function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase) {
|
|
52954
|
+
if (isTddPhase)
|
|
52955
|
+
return;
|
|
52956
|
+
if (opName === "semantic-review" || opName === "adversarial-review")
|
|
52957
|
+
return;
|
|
52958
|
+
if (output === null || output === undefined || typeof output !== "object")
|
|
52959
|
+
return;
|
|
52960
|
+
const logger = getSafeLogger();
|
|
52961
|
+
const r = output;
|
|
52962
|
+
const success2 = r.success === true || r.passed === true;
|
|
52963
|
+
const findingsCount = Array.isArray(r.findings) ? r.findings.length : undefined;
|
|
52964
|
+
const status = typeof r.status === "string" ? r.status : undefined;
|
|
52965
|
+
const data = { storyId, phase: opName, durationMs };
|
|
52966
|
+
if (findingsCount !== undefined)
|
|
52967
|
+
data.findingsCount = findingsCount;
|
|
52968
|
+
if (status !== undefined)
|
|
52969
|
+
data.status = status;
|
|
52970
|
+
if (success2) {
|
|
52971
|
+
logger?.info("story-orchestrator", `Phase passed: ${opName}`, data);
|
|
52972
|
+
} else {
|
|
52973
|
+
logger?.warn("story-orchestrator", `Phase failed: ${opName}`, data);
|
|
52974
|
+
}
|
|
52975
|
+
}
|
|
52683
52976
|
function logUnifiedReviewPhaseResult(storyId, opName, output) {
|
|
52684
52977
|
const logger = getSafeLogger();
|
|
52685
52978
|
const payload = toReviewDecisionPayload(opName, output);
|
|
@@ -52749,6 +53042,7 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
|
|
|
52749
53042
|
phaseOutputs[opName] = output;
|
|
52750
53043
|
emitReviewDecision(ctx, opName, output);
|
|
52751
53044
|
logUnifiedReviewPhaseResult(ctx.storyId, opName, output);
|
|
53045
|
+
logDeterministicPhaseOutcome(ctx.storyId, opName, output, Date.now() - phaseStartedAt, isTddPhase);
|
|
52752
53046
|
if (isTddPhase) {
|
|
52753
53047
|
const durationMs = Date.now() - phaseStartedAt;
|
|
52754
53048
|
logger?.info("tdd", `Session complete: ${opName}`, {
|
|
@@ -52847,13 +53141,27 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
|
52847
53141
|
continue;
|
|
52848
53142
|
findings.push(...extractPhaseFindings(phaseOutputs[phase.slot.op.name]));
|
|
52849
53143
|
}
|
|
52850
|
-
return findings;
|
|
53144
|
+
return rectification2.postValidate ? await rectification2.postValidate(findings, _validateCtx) : findings;
|
|
52851
53145
|
}
|
|
52852
53146
|
};
|
|
52853
53147
|
const cycleResult = await _storyOrchestratorDeps.runFixCycle(cycle, ctx, "story-orchestrator-rectification", { callOp: wrappedCallOp });
|
|
52854
53148
|
phaseOutputs.rectification = { iterationCount: cycleResult.iterations.length };
|
|
53149
|
+
const rectLogger = getSafeLogger();
|
|
53150
|
+
const rectSummary = {
|
|
53151
|
+
storyId: ctx.storyId,
|
|
53152
|
+
initialFindingsCount: initialFindings.length,
|
|
53153
|
+
iterationCount: cycleResult.iterations.length,
|
|
53154
|
+
finalFindingsCount: cycleResult.finalFindings.length,
|
|
53155
|
+
exitReason: cycleResult.exitReason,
|
|
53156
|
+
costUsd: cycleResult.costUsd
|
|
53157
|
+
};
|
|
53158
|
+
if (cycleResult.exitReason === "resolved") {
|
|
53159
|
+
rectLogger?.info("story-orchestrator", "Rectification resolved all findings", rectSummary);
|
|
53160
|
+
} else {
|
|
53161
|
+
rectLogger?.warn("story-orchestrator", `Rectification exited: ${cycleResult.exitReason}`, rectSummary);
|
|
53162
|
+
}
|
|
52855
53163
|
if (cycleResult.exitReason === "validator-error") {
|
|
52856
|
-
|
|
53164
|
+
rectLogger?.warn("story-orchestrator", "rectification cycle aborted \u2014 validator infrastructure error", {
|
|
52857
53165
|
storyId: ctx.storyId
|
|
52858
53166
|
});
|
|
52859
53167
|
}
|
|
@@ -52861,7 +53169,8 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
|
52861
53169
|
"max-attempts-total",
|
|
52862
53170
|
"max-attempts-per-strategy",
|
|
52863
53171
|
"bail-when",
|
|
52864
|
-
"no-strategy"
|
|
53172
|
+
"no-strategy",
|
|
53173
|
+
"agent-gave-up"
|
|
52865
53174
|
]);
|
|
52866
53175
|
if (exhaustedReasons.has(cycleResult.exitReason) && cycleResult.finalFindings.length > 0) {
|
|
52867
53176
|
return { rectificationExhausted: true, unfixedFindings: cycleResult.finalFindings };
|
|
@@ -52908,8 +53217,12 @@ class ExecutionPlan {
|
|
|
52908
53217
|
});
|
|
52909
53218
|
throw error48;
|
|
52910
53219
|
}
|
|
52911
|
-
if (!phasePassed(phase.slot.op.name, phaseOutputs[phase.slot.op.name])) {
|
|
53220
|
+
if (!phasePassed(phase.slot.op.name, phaseOutputs[phase.slot.op.name], this.ctx.storyId)) {
|
|
52912
53221
|
if (!shortCircuitExempt.has(phase.slot.op.name)) {
|
|
53222
|
+
logger?.warn("story-orchestrator", "Short-circuiting on phase failure", {
|
|
53223
|
+
storyId: this.ctx.storyId,
|
|
53224
|
+
phase: phase.slot.op.name
|
|
53225
|
+
});
|
|
52913
53226
|
break;
|
|
52914
53227
|
}
|
|
52915
53228
|
}
|
|
@@ -52918,20 +53231,43 @@ class ExecutionPlan {
|
|
|
52918
53231
|
const verifierName = this.state.verifier?.slot.op.name;
|
|
52919
53232
|
const gateName = this.state.fullSuiteGate?.slot.op.name;
|
|
52920
53233
|
const verifierPassedSsot = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
|
|
52921
|
-
if (verifierPassedSsot && gateName !== undefined && !phasePassed(gateName, phaseOutputs[gateName])) {
|
|
53234
|
+
if (verifierPassedSsot && gateName !== undefined && !phasePassed(gateName, phaseOutputs[gateName], this.ctx.storyId)) {
|
|
52922
53235
|
logger?.warn("story-orchestrator", "Full-suite gate failed but verifier judged story OK \u2014 treating gate failures as unrelated regressions", { storyId: this.ctx.storyId, packageDir: this.ctx.packageDir });
|
|
52923
53236
|
}
|
|
52924
53237
|
const success2 = Object.entries(phaseOutputs).every(([name, output]) => {
|
|
52925
53238
|
if (verifierPassedSsot && name === gateName)
|
|
52926
53239
|
return true;
|
|
52927
|
-
return phasePassed(name, output);
|
|
53240
|
+
return phasePassed(name, output, this.ctx.storyId);
|
|
52928
53241
|
});
|
|
52929
53242
|
const totalCostUsd = Object.values(phaseCosts).reduce((sum, cost) => sum + cost, 0);
|
|
53243
|
+
const durationMs = Date.now() - startedAt;
|
|
53244
|
+
const failedPhases = Object.entries(phaseOutputs).filter(([name, output]) => {
|
|
53245
|
+
if (verifierPassedSsot && name === gateName)
|
|
53246
|
+
return false;
|
|
53247
|
+
return !phasePassed(name, output, this.ctx.storyId);
|
|
53248
|
+
}).map(([name]) => name);
|
|
53249
|
+
const summary = {
|
|
53250
|
+
storyId: this.ctx.storyId,
|
|
53251
|
+
success: success2,
|
|
53252
|
+
totalCostUsd,
|
|
53253
|
+
durationMs,
|
|
53254
|
+
phaseCount: Object.keys(phaseOutputs).length,
|
|
53255
|
+
failedPhases: failedPhases.length > 0 ? failedPhases : undefined
|
|
53256
|
+
};
|
|
53257
|
+
if (rectResult.rectificationExhausted)
|
|
53258
|
+
summary.rectificationExhausted = true;
|
|
53259
|
+
if (rectResult.unfixedFindings)
|
|
53260
|
+
summary.unfixedFindingsCount = rectResult.unfixedFindings.length;
|
|
53261
|
+
if (success2) {
|
|
53262
|
+
logger?.info("story-orchestrator", "Story orchestration complete", summary);
|
|
53263
|
+
} else {
|
|
53264
|
+
logger?.warn("story-orchestrator", "Story orchestration failed", summary);
|
|
53265
|
+
}
|
|
52930
53266
|
return {
|
|
52931
53267
|
success: success2,
|
|
52932
53268
|
phaseCosts,
|
|
52933
53269
|
totalCostUsd,
|
|
52934
|
-
durationMs
|
|
53270
|
+
durationMs,
|
|
52935
53271
|
phaseOutputs,
|
|
52936
53272
|
...rectResult
|
|
52937
53273
|
};
|
|
@@ -52991,7 +53327,7 @@ class StoryOrchestratorBuilder {
|
|
|
52991
53327
|
return new ExecutionPlan(ctx, { ...this.state }, opts.isThreeSession ?? false);
|
|
52992
53328
|
}
|
|
52993
53329
|
}
|
|
52994
|
-
var _storyOrchestratorDeps, TDD_OP_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES;
|
|
53330
|
+
var _storyOrchestratorDeps, TDD_OP_NAMES, STRICT_VERDICT_PHASE_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES;
|
|
52995
53331
|
var init_story_orchestrator = __esm(() => {
|
|
52996
53332
|
init_errors();
|
|
52997
53333
|
init_findings();
|
|
@@ -53005,6 +53341,13 @@ var init_story_orchestrator = __esm(() => {
|
|
|
53005
53341
|
captureGitRef
|
|
53006
53342
|
};
|
|
53007
53343
|
TDD_OP_NAMES = new Set(["test-writer", "implementer", "verifier"]);
|
|
53344
|
+
STRICT_VERDICT_PHASE_NAMES = new Set([
|
|
53345
|
+
fullSuiteGateOp.name,
|
|
53346
|
+
verifyScopedOp.name,
|
|
53347
|
+
lintCheckOp.name,
|
|
53348
|
+
typecheckCheckOp.name,
|
|
53349
|
+
verifierOp.name
|
|
53350
|
+
]);
|
|
53008
53351
|
CANONICAL_ORDER = [
|
|
53009
53352
|
"test-writer",
|
|
53010
53353
|
"greenfield-gate",
|
|
@@ -53046,6 +53389,7 @@ var init_story_orchestrator = __esm(() => {
|
|
|
53046
53389
|
});
|
|
53047
53390
|
|
|
53048
53391
|
// src/execution/build-plan-for-strategy.ts
|
|
53392
|
+
import { join as join46 } from "path";
|
|
53049
53393
|
function isThreeSessionStrategy(strategy) {
|
|
53050
53394
|
return THREE_SESSION_STRATEGIES2.has(strategy);
|
|
53051
53395
|
}
|
|
@@ -53057,7 +53401,7 @@ function isFreshRun(story) {
|
|
|
53057
53401
|
const hasReviewEscalation = (story.priorFailures ?? []).some((f) => f.stage === "review");
|
|
53058
53402
|
return !hasAttempts && !hasReviewEscalation;
|
|
53059
53403
|
}
|
|
53060
|
-
function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
53404
|
+
async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
53061
53405
|
const isThreeSession = isThreeSessionStrategy(testStrategy);
|
|
53062
53406
|
const freshRun = isFreshRun(story);
|
|
53063
53407
|
const builder = new StoryOrchestratorBuilder;
|
|
@@ -53092,6 +53436,9 @@ function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
53092
53436
|
builder.addAdversarialReview(inputs.adversarialReview);
|
|
53093
53437
|
}
|
|
53094
53438
|
if (shouldRunRectification(config2) && inputs.rectification) {
|
|
53439
|
+
const sink = makeDeclarationSink();
|
|
53440
|
+
const packageDir = join46(ctx.packageDir, story.workdir ?? "");
|
|
53441
|
+
const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
|
|
53095
53442
|
const strategies = [];
|
|
53096
53443
|
if (config2.quality.commands.lintFix || config2.quality.commands.lintFixScoped) {
|
|
53097
53444
|
strategies.push(makeMechanicalLintFixStrategy());
|
|
@@ -53103,12 +53450,28 @@ function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
53103
53450
|
strategies.push(makeFullSuiteRectifyStrategy(story, config2));
|
|
53104
53451
|
}
|
|
53105
53452
|
if (config2.quality.autofix?.enabled !== false) {
|
|
53106
|
-
strategies.push(makeAutofixImplementerStrategy(story, config2));
|
|
53107
|
-
strategies.push(makeAutofixTestWriterStrategy(story, config2));
|
|
53108
|
-
}
|
|
53453
|
+
strategies.push(makeAutofixImplementerStrategy(story, config2, sink));
|
|
53454
|
+
strategies.push(makeAutofixTestWriterStrategy(story, config2, sink));
|
|
53455
|
+
}
|
|
53456
|
+
const postValidate = async (findings, _validateCtx) => {
|
|
53457
|
+
if (sink.testEdits.length === 0 && sink.mockHandoffs.length === 0)
|
|
53458
|
+
return findings;
|
|
53459
|
+
const pendingMock = sink.mockHandoffs.map((h) => ({
|
|
53460
|
+
reason: "mock_structure",
|
|
53461
|
+
file: h.files[0] ?? "",
|
|
53462
|
+
files: h.files,
|
|
53463
|
+
reasonDetail: h.reasonDetail
|
|
53464
|
+
}));
|
|
53465
|
+
const { valid, invalid } = await validateMockStructureFiles(pendingMock, resolvedTestPatterns, packageDir);
|
|
53466
|
+
sink.mockHandoffs = valid.map((d) => ({ files: d.files ?? [], reasonDetail: d.reasonDetail ?? "" }));
|
|
53467
|
+
const allDeclarations = [...sink.testEdits, ...valid];
|
|
53468
|
+
sink.testEdits = [];
|
|
53469
|
+
return applyTestEditDeclarations(findings, allDeclarations, story, invalid);
|
|
53470
|
+
};
|
|
53109
53471
|
const rectOpts = {
|
|
53110
53472
|
...inputs.rectification,
|
|
53111
|
-
strategies: [...strategies, ...inputs.rectification.strategies]
|
|
53473
|
+
strategies: [...strategies, ...inputs.rectification.strategies],
|
|
53474
|
+
postValidate
|
|
53112
53475
|
};
|
|
53113
53476
|
builder.addRectification(rectOpts);
|
|
53114
53477
|
}
|
|
@@ -53119,6 +53482,7 @@ var init_build_plan_for_strategy = __esm(() => {
|
|
|
53119
53482
|
init_operations();
|
|
53120
53483
|
init_execution_gates();
|
|
53121
53484
|
init_full_suite_rectify();
|
|
53485
|
+
init_test_runners();
|
|
53122
53486
|
init_story_orchestrator();
|
|
53123
53487
|
THREE_SESSION_STRATEGIES2 = new Set(["three-session-tdd", "three-session-tdd-lite"]);
|
|
53124
53488
|
});
|
|
@@ -53225,24 +53589,63 @@ async function assemblePlanInputsFromCtx(ctx) {
|
|
|
53225
53589
|
const verifyScopedInput = !_isTdd ? { workdir: ctx.workdir, storyId: story.id } : undefined;
|
|
53226
53590
|
const lintCheckInput = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("lint") && ctx.config.quality.commands.lint ? { workdir: ctx.workdir, storyId: story.id } : undefined;
|
|
53227
53591
|
const typecheckCheckInput = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("typecheck") && ctx.config.quality.commands.typecheck ? { workdir: ctx.workdir, storyId: story.id } : undefined;
|
|
53228
|
-
const
|
|
53229
|
-
|
|
53230
|
-
|
|
53231
|
-
|
|
53232
|
-
|
|
53233
|
-
|
|
53234
|
-
|
|
53235
|
-
|
|
53236
|
-
|
|
53237
|
-
|
|
53238
|
-
|
|
53239
|
-
|
|
53240
|
-
|
|
53241
|
-
|
|
53242
|
-
|
|
53243
|
-
|
|
53244
|
-
|
|
53245
|
-
|
|
53592
|
+
const semanticEnabled = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("semantic") && !!ctx.config.review.semantic;
|
|
53593
|
+
const semanticReviewInput = semanticEnabled ? await (async () => {
|
|
53594
|
+
const prepared = await prepareSemanticReviewInput({
|
|
53595
|
+
workdir: ctx.workdir,
|
|
53596
|
+
projectDir: ctx.projectDir,
|
|
53597
|
+
storyId: story.id,
|
|
53598
|
+
storyGitRef: ctx.storyGitRef,
|
|
53599
|
+
config: ctx.config,
|
|
53600
|
+
naxIgnoreIndex: ctx.naxIgnoreIndex,
|
|
53601
|
+
semanticConfig: ctx.config.review.semantic
|
|
53602
|
+
});
|
|
53603
|
+
if (prepared.skipReason)
|
|
53604
|
+
return;
|
|
53605
|
+
return {
|
|
53606
|
+
workdir: ctx.workdir,
|
|
53607
|
+
story,
|
|
53608
|
+
semanticConfig: ctx.config.review.semantic,
|
|
53609
|
+
mode: ctx.config.review.semantic.diffMode,
|
|
53610
|
+
storyGitRef: prepared.effectiveRef,
|
|
53611
|
+
stat: prepared.stat,
|
|
53612
|
+
diff: prepared.diff,
|
|
53613
|
+
excludePatterns: prepared.excludePatterns,
|
|
53614
|
+
featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-semantic"),
|
|
53615
|
+
priorSemanticIterations: ctx.priorSemanticIterations,
|
|
53616
|
+
blockingThreshold: ctx.config.review.blockingThreshold
|
|
53617
|
+
};
|
|
53618
|
+
})() : undefined;
|
|
53619
|
+
const adversarialEnabled = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("adversarial") && !!ctx.config.review.adversarial;
|
|
53620
|
+
const adversarialReviewInput = adversarialEnabled ? await (async () => {
|
|
53621
|
+
const prepared = await prepareAdversarialReviewInput({
|
|
53622
|
+
workdir: ctx.workdir,
|
|
53623
|
+
projectDir: ctx.projectDir,
|
|
53624
|
+
storyId: story.id,
|
|
53625
|
+
storyGitRef: ctx.storyGitRef,
|
|
53626
|
+
config: ctx.config,
|
|
53627
|
+
naxIgnoreIndex: ctx.naxIgnoreIndex,
|
|
53628
|
+
adversarialConfig: ctx.config.review.adversarial
|
|
53629
|
+
});
|
|
53630
|
+
if (prepared.skipReason)
|
|
53631
|
+
return;
|
|
53632
|
+
return {
|
|
53633
|
+
workdir: ctx.workdir,
|
|
53634
|
+
story,
|
|
53635
|
+
adversarialConfig: ctx.config.review.adversarial,
|
|
53636
|
+
mode: ctx.config.review.adversarial.diffMode,
|
|
53637
|
+
storyGitRef: prepared.effectiveRef,
|
|
53638
|
+
stat: prepared.stat,
|
|
53639
|
+
diff: prepared.diff,
|
|
53640
|
+
testInventory: prepared.testInventory,
|
|
53641
|
+
excludePatterns: prepared.excludePatterns,
|
|
53642
|
+
testGlobs: prepared.testGlobs,
|
|
53643
|
+
refExcludePatterns: prepared.refExcludePatterns,
|
|
53644
|
+
featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-adversarial"),
|
|
53645
|
+
priorAdversarialIterations: ctx.priorAdversarialIterations,
|
|
53646
|
+
blockingThreshold: ctx.config.review.blockingThreshold
|
|
53647
|
+
};
|
|
53648
|
+
})() : undefined;
|
|
53246
53649
|
const rectificationInput = ctx.config.execution?.rectification?.enabled === true ? {
|
|
53247
53650
|
maxAttempts: ctx.config.execution.rectification.maxAttemptsTotal,
|
|
53248
53651
|
strategies: [],
|
|
@@ -53268,23 +53671,28 @@ var init_plan_inputs = __esm(() => {
|
|
|
53268
53671
|
init_context();
|
|
53269
53672
|
init_errors();
|
|
53270
53673
|
init_prompts();
|
|
53674
|
+
init_review();
|
|
53271
53675
|
init_resolver();
|
|
53272
53676
|
init_build_plan_for_strategy();
|
|
53273
53677
|
});
|
|
53274
53678
|
|
|
53275
53679
|
// src/pipeline/stages/execution-helpers.ts
|
|
53276
|
-
function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason) {
|
|
53680
|
+
function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason, failureDetail) {
|
|
53681
|
+
const buildReason = (category) => {
|
|
53682
|
+
const trimmedDetail = failureDetail?.trim();
|
|
53683
|
+
return trimmedDetail ? `TDD ${category}: ${trimmedDetail}` : `TDD ${category}`;
|
|
53684
|
+
};
|
|
53277
53685
|
if (failureCategory === "isolation-violation") {
|
|
53278
53686
|
if (!isLiteMode) {
|
|
53279
53687
|
ctx.retryAsLite = true;
|
|
53280
53688
|
}
|
|
53281
|
-
return { action: "escalate" };
|
|
53689
|
+
return { action: "escalate", reason: buildReason("isolation-violation") };
|
|
53282
53690
|
}
|
|
53283
53691
|
if (failureCategory === "session-failure" || failureCategory === "tests-failing" || failureCategory === "full-suite-gate-exhausted" || failureCategory === "verifier-rejected") {
|
|
53284
|
-
return { action: "escalate" };
|
|
53692
|
+
return { action: "escalate", reason: buildReason(failureCategory) };
|
|
53285
53693
|
}
|
|
53286
53694
|
if (failureCategory === "greenfield-no-tests") {
|
|
53287
|
-
return { action: "escalate" };
|
|
53695
|
+
return { action: "escalate", reason: buildReason("greenfield-no-tests") };
|
|
53288
53696
|
}
|
|
53289
53697
|
return {
|
|
53290
53698
|
action: "pause",
|
|
@@ -53738,7 +54146,16 @@ Category: ${failureCategory ?? "unknown"}`,
|
|
|
53738
54146
|
logger.warn("execution", "Rate limited \u2014 will retry", { storyId: ctx.story.id });
|
|
53739
54147
|
}
|
|
53740
54148
|
await cleanupSessionOnFailure(ctx);
|
|
53741
|
-
|
|
54149
|
+
const failedPhaseNames = Object.keys(failedPhases);
|
|
54150
|
+
const reasonParts = [];
|
|
54151
|
+
reasonParts.push(`agent session failed (exit ${agentResult.exitCode ?? "?"})`);
|
|
54152
|
+
if (failureCategory)
|
|
54153
|
+
reasonParts.push(`category=${failureCategory}`);
|
|
54154
|
+
if (agentResult.rateLimited)
|
|
54155
|
+
reasonParts.push("rate-limited");
|
|
54156
|
+
if (failedPhaseNames.length > 0)
|
|
54157
|
+
reasonParts.push(`phases=${failedPhaseNames.join(",")}`);
|
|
54158
|
+
return { action: "escalate", reason: reasonParts.join("; ") };
|
|
53742
54159
|
}
|
|
53743
54160
|
if (!isTdd) {
|
|
53744
54161
|
await _postRunDeps.autoCommitIfDirty(ctx.workdir, "execution", "single-session", ctx.story.id);
|
|
@@ -53837,7 +54254,7 @@ var init_execution = __esm(() => {
|
|
|
53837
54254
|
} : null;
|
|
53838
54255
|
const initialRef = tddMode ? await _executionDeps.captureGitRef(ctx.workdir) ?? "HEAD" : null;
|
|
53839
54256
|
const inputs = await _executionDeps.assemblePlanInputsFromCtx(ctx);
|
|
53840
|
-
const plan = buildPlanForStrategy(callCtx, ctx.story, ctx.config, ctx.routing.testStrategy, inputs);
|
|
54257
|
+
const plan = await buildPlanForStrategy(callCtx, ctx.story, ctx.config, ctx.routing.testStrategy, inputs);
|
|
53841
54258
|
let planResult;
|
|
53842
54259
|
try {
|
|
53843
54260
|
planResult = await plan.run();
|
|
@@ -54784,7 +55201,7 @@ function buildFrontmatter(story, ctx, role) {
|
|
|
54784
55201
|
}
|
|
54785
55202
|
|
|
54786
55203
|
// src/cli/prompts-tdd.ts
|
|
54787
|
-
import { join as
|
|
55204
|
+
import { join as join47 } from "path";
|
|
54788
55205
|
async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
|
|
54789
55206
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
|
|
54790
55207
|
TddPromptBuilder.for("test-writer", { isolation: "strict" }).withLoader(ctx.workdir, ctx.config).story(story).context(ctx.contextMarkdown).constitution(ctx.constitution?.content).testCommand(ctx.config.quality?.commands?.test).build(),
|
|
@@ -54803,7 +55220,7 @@ ${frontmatter}---
|
|
|
54803
55220
|
|
|
54804
55221
|
${session.prompt}`;
|
|
54805
55222
|
if (outputDir) {
|
|
54806
|
-
const promptFile =
|
|
55223
|
+
const promptFile = join47(outputDir, `${story.id}.${session.role}.md`);
|
|
54807
55224
|
await Bun.write(promptFile, fullOutput);
|
|
54808
55225
|
logger.info("cli", "Written TDD prompt file", {
|
|
54809
55226
|
storyId: story.id,
|
|
@@ -54819,7 +55236,7 @@ ${"=".repeat(80)}`);
|
|
|
54819
55236
|
}
|
|
54820
55237
|
}
|
|
54821
55238
|
if (outputDir && ctx.contextMarkdown) {
|
|
54822
|
-
const contextFile =
|
|
55239
|
+
const contextFile = join47(outputDir, `${story.id}.context.md`);
|
|
54823
55240
|
const frontmatter = buildFrontmatter(story, ctx);
|
|
54824
55241
|
const contextOutput = `---
|
|
54825
55242
|
${frontmatter}---
|
|
@@ -54834,16 +55251,16 @@ var init_prompts_tdd = __esm(() => {
|
|
|
54834
55251
|
|
|
54835
55252
|
// src/cli/prompts-main.ts
|
|
54836
55253
|
import { existsSync as existsSync20, mkdirSync as mkdirSync3 } from "fs";
|
|
54837
|
-
import { join as
|
|
55254
|
+
import { join as join48 } from "path";
|
|
54838
55255
|
async function promptsCommand(options) {
|
|
54839
55256
|
const logger = getLogger();
|
|
54840
55257
|
const { feature, workdir, config: config2, storyId, outputDir } = options;
|
|
54841
|
-
const naxDir =
|
|
55258
|
+
const naxDir = join48(workdir, ".nax");
|
|
54842
55259
|
if (!existsSync20(naxDir)) {
|
|
54843
55260
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
54844
55261
|
}
|
|
54845
|
-
const featureDir =
|
|
54846
|
-
const prdPath =
|
|
55262
|
+
const featureDir = join48(naxDir, "features", feature);
|
|
55263
|
+
const prdPath = join48(featureDir, "prd.json");
|
|
54847
55264
|
if (!existsSync20(prdPath)) {
|
|
54848
55265
|
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
54849
55266
|
}
|
|
@@ -54910,10 +55327,10 @@ ${frontmatter}---
|
|
|
54910
55327
|
|
|
54911
55328
|
${ctx.prompt}`;
|
|
54912
55329
|
if (outputDir) {
|
|
54913
|
-
const promptFile =
|
|
55330
|
+
const promptFile = join48(outputDir, `${story.id}.prompt.md`);
|
|
54914
55331
|
await Bun.write(promptFile, fullOutput);
|
|
54915
55332
|
if (ctx.contextMarkdown) {
|
|
54916
|
-
const contextFile =
|
|
55333
|
+
const contextFile = join48(outputDir, `${story.id}.context.md`);
|
|
54917
55334
|
const contextOutput = `---
|
|
54918
55335
|
${frontmatter}---
|
|
54919
55336
|
|
|
@@ -54949,12 +55366,12 @@ var init_prompts_main = __esm(() => {
|
|
|
54949
55366
|
|
|
54950
55367
|
// src/cli/prompts-init.ts
|
|
54951
55368
|
import { existsSync as existsSync21, mkdirSync as mkdirSync4 } from "fs";
|
|
54952
|
-
import { join as
|
|
55369
|
+
import { join as join49 } from "path";
|
|
54953
55370
|
async function promptsInitCommand(options) {
|
|
54954
55371
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
54955
|
-
const templatesDir =
|
|
55372
|
+
const templatesDir = join49(workdir, ".nax", "templates");
|
|
54956
55373
|
mkdirSync4(templatesDir, { recursive: true });
|
|
54957
|
-
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(
|
|
55374
|
+
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join49(templatesDir, f)));
|
|
54958
55375
|
if (existingFiles.length > 0 && !force) {
|
|
54959
55376
|
_promptsInitDeps.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
54960
55377
|
Pass --force to overwrite existing templates.`);
|
|
@@ -54962,7 +55379,7 @@ async function promptsInitCommand(options) {
|
|
|
54962
55379
|
}
|
|
54963
55380
|
const written = [];
|
|
54964
55381
|
for (const template of TEMPLATE_ROLES) {
|
|
54965
|
-
const filePath =
|
|
55382
|
+
const filePath = join49(templatesDir, template.file);
|
|
54966
55383
|
const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
|
|
54967
55384
|
const content = TEMPLATE_HEADER + roleBody;
|
|
54968
55385
|
await Bun.write(filePath, content);
|
|
@@ -54978,7 +55395,7 @@ async function promptsInitCommand(options) {
|
|
|
54978
55395
|
return written;
|
|
54979
55396
|
}
|
|
54980
55397
|
async function autoWirePromptsConfig(workdir) {
|
|
54981
|
-
const configPath =
|
|
55398
|
+
const configPath = join49(workdir, "nax.config.json");
|
|
54982
55399
|
if (!existsSync21(configPath)) {
|
|
54983
55400
|
const exampleConfig = JSON.stringify({
|
|
54984
55401
|
prompts: {
|
|
@@ -55148,7 +55565,7 @@ __export(exports_init_context, {
|
|
|
55148
55565
|
});
|
|
55149
55566
|
import { existsSync as existsSync22 } from "fs";
|
|
55150
55567
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
55151
|
-
import { basename as basename9, join as
|
|
55568
|
+
import { basename as basename9, join as join50 } from "path";
|
|
55152
55569
|
async function findFiles(dir, maxFiles = 200) {
|
|
55153
55570
|
try {
|
|
55154
55571
|
const proc = Bun.spawnSync([
|
|
@@ -55176,7 +55593,7 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
55176
55593
|
return [];
|
|
55177
55594
|
}
|
|
55178
55595
|
async function readPackageManifest(projectRoot) {
|
|
55179
|
-
const packageJsonPath =
|
|
55596
|
+
const packageJsonPath = join50(projectRoot, "package.json");
|
|
55180
55597
|
if (!existsSync22(packageJsonPath)) {
|
|
55181
55598
|
return null;
|
|
55182
55599
|
}
|
|
@@ -55194,7 +55611,7 @@ async function readPackageManifest(projectRoot) {
|
|
|
55194
55611
|
}
|
|
55195
55612
|
}
|
|
55196
55613
|
async function readReadmeSnippet(projectRoot) {
|
|
55197
|
-
const readmePath =
|
|
55614
|
+
const readmePath = join50(projectRoot, "README.md");
|
|
55198
55615
|
if (!existsSync22(readmePath)) {
|
|
55199
55616
|
return null;
|
|
55200
55617
|
}
|
|
@@ -55212,7 +55629,7 @@ async function detectEntryPoints(projectRoot) {
|
|
|
55212
55629
|
const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
|
|
55213
55630
|
const found = [];
|
|
55214
55631
|
for (const candidate of candidates) {
|
|
55215
|
-
const path14 =
|
|
55632
|
+
const path14 = join50(projectRoot, candidate);
|
|
55216
55633
|
if (existsSync22(path14)) {
|
|
55217
55634
|
found.push(candidate);
|
|
55218
55635
|
}
|
|
@@ -55223,7 +55640,7 @@ async function detectConfigFiles(projectRoot) {
|
|
|
55223
55640
|
const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
|
|
55224
55641
|
const found = [];
|
|
55225
55642
|
for (const candidate of candidates) {
|
|
55226
|
-
const path14 =
|
|
55643
|
+
const path14 = join50(projectRoot, candidate);
|
|
55227
55644
|
if (existsSync22(path14)) {
|
|
55228
55645
|
found.push(candidate);
|
|
55229
55646
|
}
|
|
@@ -55384,8 +55801,8 @@ function generatePackageContextTemplate(packagePath) {
|
|
|
55384
55801
|
}
|
|
55385
55802
|
async function initPackage(repoRoot, packagePath, force = false) {
|
|
55386
55803
|
const logger = getLogger();
|
|
55387
|
-
const naxDir =
|
|
55388
|
-
const contextPath =
|
|
55804
|
+
const naxDir = join50(repoRoot, ".nax", "mono", packagePath);
|
|
55805
|
+
const contextPath = join50(naxDir, "context.md");
|
|
55389
55806
|
if (existsSync22(contextPath) && !force) {
|
|
55390
55807
|
logger.info("init", "Package context.md already exists (use --force to overwrite)", { path: contextPath });
|
|
55391
55808
|
return;
|
|
@@ -55399,8 +55816,8 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
55399
55816
|
}
|
|
55400
55817
|
async function initContext(projectRoot, options = {}) {
|
|
55401
55818
|
const logger = getLogger();
|
|
55402
|
-
const naxDir =
|
|
55403
|
-
const contextPath =
|
|
55819
|
+
const naxDir = join50(projectRoot, ".nax");
|
|
55820
|
+
const contextPath = join50(naxDir, "context.md");
|
|
55404
55821
|
if (existsSync22(contextPath) && !options.force) {
|
|
55405
55822
|
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", { path: contextPath });
|
|
55406
55823
|
return;
|
|
@@ -55430,9 +55847,9 @@ var init_init_context = __esm(() => {
|
|
|
55430
55847
|
|
|
55431
55848
|
// src/cli/init-detect.ts
|
|
55432
55849
|
import { existsSync as existsSync23, readFileSync } from "fs";
|
|
55433
|
-
import { join as
|
|
55850
|
+
import { join as join51 } from "path";
|
|
55434
55851
|
function readPackageJson(projectRoot) {
|
|
55435
|
-
const pkgPath =
|
|
55852
|
+
const pkgPath = join51(projectRoot, "package.json");
|
|
55436
55853
|
if (!existsSync23(pkgPath))
|
|
55437
55854
|
return;
|
|
55438
55855
|
try {
|
|
@@ -55475,41 +55892,41 @@ function detectStack(projectRoot) {
|
|
|
55475
55892
|
};
|
|
55476
55893
|
}
|
|
55477
55894
|
function detectRuntime(projectRoot) {
|
|
55478
|
-
if (existsSync23(
|
|
55895
|
+
if (existsSync23(join51(projectRoot, "bun.lockb")) || existsSync23(join51(projectRoot, "bunfig.toml"))) {
|
|
55479
55896
|
return "bun";
|
|
55480
55897
|
}
|
|
55481
|
-
if (existsSync23(
|
|
55898
|
+
if (existsSync23(join51(projectRoot, "package-lock.json")) || existsSync23(join51(projectRoot, "yarn.lock")) || existsSync23(join51(projectRoot, "pnpm-lock.yaml"))) {
|
|
55482
55899
|
return "node";
|
|
55483
55900
|
}
|
|
55484
55901
|
return "unknown";
|
|
55485
55902
|
}
|
|
55486
55903
|
function detectLanguage2(projectRoot) {
|
|
55487
|
-
if (existsSync23(
|
|
55904
|
+
if (existsSync23(join51(projectRoot, "tsconfig.json")))
|
|
55488
55905
|
return "typescript";
|
|
55489
|
-
if (existsSync23(
|
|
55906
|
+
if (existsSync23(join51(projectRoot, "pyproject.toml")) || existsSync23(join51(projectRoot, "setup.py"))) {
|
|
55490
55907
|
return "python";
|
|
55491
55908
|
}
|
|
55492
|
-
if (existsSync23(
|
|
55909
|
+
if (existsSync23(join51(projectRoot, "Cargo.toml")))
|
|
55493
55910
|
return "rust";
|
|
55494
|
-
if (existsSync23(
|
|
55911
|
+
if (existsSync23(join51(projectRoot, "go.mod")))
|
|
55495
55912
|
return "go";
|
|
55496
55913
|
return "unknown";
|
|
55497
55914
|
}
|
|
55498
55915
|
function detectLinter(projectRoot) {
|
|
55499
|
-
if (existsSync23(
|
|
55916
|
+
if (existsSync23(join51(projectRoot, "biome.json")) || existsSync23(join51(projectRoot, "biome.jsonc"))) {
|
|
55500
55917
|
return "biome";
|
|
55501
55918
|
}
|
|
55502
|
-
if (existsSync23(
|
|
55919
|
+
if (existsSync23(join51(projectRoot, ".eslintrc.json")) || existsSync23(join51(projectRoot, ".eslintrc.js")) || existsSync23(join51(projectRoot, "eslint.config.js"))) {
|
|
55503
55920
|
return "eslint";
|
|
55504
55921
|
}
|
|
55505
55922
|
return "unknown";
|
|
55506
55923
|
}
|
|
55507
55924
|
function detectMonorepo(projectRoot) {
|
|
55508
|
-
if (existsSync23(
|
|
55925
|
+
if (existsSync23(join51(projectRoot, "turbo.json")))
|
|
55509
55926
|
return "turborepo";
|
|
55510
|
-
if (existsSync23(
|
|
55927
|
+
if (existsSync23(join51(projectRoot, "nx.json")))
|
|
55511
55928
|
return "nx";
|
|
55512
|
-
if (existsSync23(
|
|
55929
|
+
if (existsSync23(join51(projectRoot, "pnpm-workspace.yaml")))
|
|
55513
55930
|
return "pnpm-workspaces";
|
|
55514
55931
|
const pkg = readPackageJson(projectRoot);
|
|
55515
55932
|
if (pkg?.workspaces)
|
|
@@ -55653,7 +56070,7 @@ __export(exports_init, {
|
|
|
55653
56070
|
});
|
|
55654
56071
|
import { existsSync as existsSync24 } from "fs";
|
|
55655
56072
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
55656
|
-
import { join as
|
|
56073
|
+
import { join as join52 } from "path";
|
|
55657
56074
|
function validateProjectName(name) {
|
|
55658
56075
|
if (!name)
|
|
55659
56076
|
return { valid: false, error: "name must be non-empty" };
|
|
@@ -55689,7 +56106,7 @@ async function checkInitCollision(name, currentWorkdir, currentRemote) {
|
|
|
55689
56106
|
}
|
|
55690
56107
|
async function updateGitignore(projectRoot) {
|
|
55691
56108
|
const logger = getLogger();
|
|
55692
|
-
const gitignorePath =
|
|
56109
|
+
const gitignorePath = join52(projectRoot, ".gitignore");
|
|
55693
56110
|
let existing = "";
|
|
55694
56111
|
if (existsSync24(gitignorePath)) {
|
|
55695
56112
|
existing = await Bun.file(gitignorePath).text();
|
|
@@ -55775,7 +56192,7 @@ async function initGlobal() {
|
|
|
55775
56192
|
await mkdir9(globalDir, { recursive: true });
|
|
55776
56193
|
logger.info("init", "Created global config directory", { path: globalDir });
|
|
55777
56194
|
}
|
|
55778
|
-
const configPath =
|
|
56195
|
+
const configPath = join52(globalDir, "config.json");
|
|
55779
56196
|
if (!existsSync24(configPath)) {
|
|
55780
56197
|
await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
|
|
55781
56198
|
`);
|
|
@@ -55783,14 +56200,14 @@ async function initGlobal() {
|
|
|
55783
56200
|
} else {
|
|
55784
56201
|
logger.info("init", "Global config already exists", { path: configPath });
|
|
55785
56202
|
}
|
|
55786
|
-
const constitutionPath =
|
|
56203
|
+
const constitutionPath = join52(globalDir, "constitution.md");
|
|
55787
56204
|
if (!existsSync24(constitutionPath)) {
|
|
55788
56205
|
await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
|
|
55789
56206
|
logger.info("init", "Created global constitution", { path: constitutionPath });
|
|
55790
56207
|
} else {
|
|
55791
56208
|
logger.info("init", "Global constitution already exists", { path: constitutionPath });
|
|
55792
56209
|
}
|
|
55793
|
-
const hooksDir =
|
|
56210
|
+
const hooksDir = join52(globalDir, "hooks");
|
|
55794
56211
|
if (!existsSync24(hooksDir)) {
|
|
55795
56212
|
await mkdir9(hooksDir, { recursive: true });
|
|
55796
56213
|
logger.info("init", "Created global hooks directory", { path: hooksDir });
|
|
@@ -55823,7 +56240,7 @@ async function initProject(projectRoot, options) {
|
|
|
55823
56240
|
if (detectedName && !options?.force) {
|
|
55824
56241
|
const collision = await checkInitCollision(detectedName, projectRoot, currentRemote);
|
|
55825
56242
|
if (collision.collision && collision.existing) {
|
|
55826
|
-
const configPath2 =
|
|
56243
|
+
const configPath2 = join52(projectDir, "config.json");
|
|
55827
56244
|
throw new NaxError([
|
|
55828
56245
|
`Project name collision: "${detectedName}"`,
|
|
55829
56246
|
` This project: ${projectRoot}`,
|
|
@@ -55851,7 +56268,7 @@ async function initProject(projectRoot, options) {
|
|
|
55851
56268
|
linter: stack.linter,
|
|
55852
56269
|
monorepo: stack.monorepo
|
|
55853
56270
|
});
|
|
55854
|
-
const configPath =
|
|
56271
|
+
const configPath = join52(projectDir, "config.json");
|
|
55855
56272
|
if (!existsSync24(configPath)) {
|
|
55856
56273
|
await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
|
|
55857
56274
|
`);
|
|
@@ -55860,14 +56277,14 @@ async function initProject(projectRoot, options) {
|
|
|
55860
56277
|
logger.info("init", "Project config already exists", { path: configPath });
|
|
55861
56278
|
}
|
|
55862
56279
|
await initContext(projectRoot, { ai: options?.ai, force: options?.force });
|
|
55863
|
-
const constitutionPath =
|
|
56280
|
+
const constitutionPath = join52(projectDir, "constitution.md");
|
|
55864
56281
|
if (!existsSync24(constitutionPath) || options?.force) {
|
|
55865
56282
|
await Bun.write(constitutionPath, buildConstitution(stack));
|
|
55866
56283
|
logger.info("init", "Created project constitution", { path: constitutionPath });
|
|
55867
56284
|
} else {
|
|
55868
56285
|
logger.info("init", "Project constitution already exists", { path: constitutionPath });
|
|
55869
56286
|
}
|
|
55870
|
-
const hooksDir =
|
|
56287
|
+
const hooksDir = join52(projectDir, "hooks");
|
|
55871
56288
|
if (!existsSync24(hooksDir)) {
|
|
55872
56289
|
await mkdir9(hooksDir, { recursive: true });
|
|
55873
56290
|
logger.info("init", "Created project hooks directory", { path: hooksDir });
|
|
@@ -57302,12 +57719,12 @@ var init_loader4 = __esm(() => {
|
|
|
57302
57719
|
});
|
|
57303
57720
|
|
|
57304
57721
|
// src/utils/paths.ts
|
|
57305
|
-
import { join as
|
|
57722
|
+
import { join as join64 } from "path";
|
|
57306
57723
|
function getRunsDir() {
|
|
57307
|
-
return process.env.NAX_RUNS_DIR ??
|
|
57724
|
+
return process.env.NAX_RUNS_DIR ?? join64(globalConfigDir(), "runs");
|
|
57308
57725
|
}
|
|
57309
57726
|
function getEventsRootDir() {
|
|
57310
|
-
return
|
|
57727
|
+
return join64(globalConfigDir(), "events");
|
|
57311
57728
|
}
|
|
57312
57729
|
var init_paths3 = __esm(() => {
|
|
57313
57730
|
init_paths();
|
|
@@ -57367,7 +57784,7 @@ var init_command_argv = __esm(() => {
|
|
|
57367
57784
|
});
|
|
57368
57785
|
|
|
57369
57786
|
// src/hooks/runner.ts
|
|
57370
|
-
import { join as
|
|
57787
|
+
import { join as join71 } from "path";
|
|
57371
57788
|
function createDrainDeadline2(deadlineMs) {
|
|
57372
57789
|
let timeoutId;
|
|
57373
57790
|
const promise2 = new Promise((resolve16) => {
|
|
@@ -57386,14 +57803,14 @@ async function loadHooksConfig(projectDir, globalDir) {
|
|
|
57386
57803
|
let globalHooks = { hooks: {} };
|
|
57387
57804
|
let projectHooks = { hooks: {} };
|
|
57388
57805
|
let skipGlobal = false;
|
|
57389
|
-
const projectPath =
|
|
57806
|
+
const projectPath = join71(projectDir, "hooks.json");
|
|
57390
57807
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
57391
57808
|
if (projectData) {
|
|
57392
57809
|
projectHooks = projectData;
|
|
57393
57810
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
57394
57811
|
}
|
|
57395
57812
|
if (!skipGlobal && globalDir) {
|
|
57396
|
-
const globalPath =
|
|
57813
|
+
const globalPath = join71(globalDir, "hooks.json");
|
|
57397
57814
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
57398
57815
|
if (globalData) {
|
|
57399
57816
|
globalHooks = globalData;
|
|
@@ -57563,7 +57980,7 @@ var package_default;
|
|
|
57563
57980
|
var init_package = __esm(() => {
|
|
57564
57981
|
package_default = {
|
|
57565
57982
|
name: "@nathapp/nax",
|
|
57566
|
-
version: "0.67.
|
|
57983
|
+
version: "0.67.13",
|
|
57567
57984
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
57568
57985
|
type: "module",
|
|
57569
57986
|
bin: {
|
|
@@ -57658,8 +58075,8 @@ var init_version = __esm(() => {
|
|
|
57658
58075
|
NAX_VERSION = package_default.version;
|
|
57659
58076
|
NAX_COMMIT = (() => {
|
|
57660
58077
|
try {
|
|
57661
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
57662
|
-
return "
|
|
58078
|
+
if (/^[0-9a-f]{6,10}$/.test("45ff95d3"))
|
|
58079
|
+
return "45ff95d3";
|
|
57663
58080
|
} catch {}
|
|
57664
58081
|
try {
|
|
57665
58082
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -58528,15 +58945,15 @@ var init_acceptance_loop = __esm(() => {
|
|
|
58528
58945
|
|
|
58529
58946
|
// src/session/scratch-purge.ts
|
|
58530
58947
|
import { mkdir as mkdir13, rename, rm } from "fs/promises";
|
|
58531
|
-
import { dirname as dirname12, join as
|
|
58948
|
+
import { dirname as dirname12, join as join72 } from "path";
|
|
58532
58949
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
58533
|
-
const sessionsDir =
|
|
58950
|
+
const sessionsDir = join72(projectDir, ".nax", "features", featureName, "sessions");
|
|
58534
58951
|
const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
|
|
58535
58952
|
const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
|
|
58536
58953
|
let purged = 0;
|
|
58537
58954
|
for (const sessionId of sessionIds) {
|
|
58538
|
-
const sessionDir =
|
|
58539
|
-
const descriptorPath =
|
|
58955
|
+
const sessionDir = join72(sessionsDir, sessionId);
|
|
58956
|
+
const descriptorPath = join72(sessionDir, "descriptor.json");
|
|
58540
58957
|
if (!await _scratchPurgeDeps.fileExists(descriptorPath))
|
|
58541
58958
|
continue;
|
|
58542
58959
|
let lastActivityAt;
|
|
@@ -58552,7 +58969,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
|
|
|
58552
58969
|
if (new Date(lastActivityAt).getTime() >= cutoffMs)
|
|
58553
58970
|
continue;
|
|
58554
58971
|
if (archiveInsteadOfDelete) {
|
|
58555
|
-
const archiveDest =
|
|
58972
|
+
const archiveDest = join72(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
|
|
58556
58973
|
await _scratchPurgeDeps.move(sessionDir, archiveDest);
|
|
58557
58974
|
} else {
|
|
58558
58975
|
await _scratchPurgeDeps.remove(sessionDir);
|
|
@@ -59261,12 +59678,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
|
|
|
59261
59678
|
|
|
59262
59679
|
// src/pipeline/subscribers/events-writer.ts
|
|
59263
59680
|
import { appendFile as appendFile5, mkdir as mkdir14 } from "fs/promises";
|
|
59264
|
-
import { basename as basename14, join as
|
|
59681
|
+
import { basename as basename14, join as join73 } from "path";
|
|
59265
59682
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
59266
59683
|
const logger = getSafeLogger();
|
|
59267
59684
|
const project = basename14(workdir);
|
|
59268
|
-
const eventsDir =
|
|
59269
|
-
const eventsFile =
|
|
59685
|
+
const eventsDir = join73(getEventsRootDir(), project);
|
|
59686
|
+
const eventsFile = join73(eventsDir, "events.jsonl");
|
|
59270
59687
|
let dirReady = false;
|
|
59271
59688
|
const write = (line) => {
|
|
59272
59689
|
return (async () => {
|
|
@@ -59447,12 +59864,12 @@ var init_interaction2 = __esm(() => {
|
|
|
59447
59864
|
|
|
59448
59865
|
// src/pipeline/subscribers/registry.ts
|
|
59449
59866
|
import { mkdir as mkdir15, writeFile as writeFile2 } from "fs/promises";
|
|
59450
|
-
import { basename as basename15, join as
|
|
59867
|
+
import { basename as basename15, join as join74 } from "path";
|
|
59451
59868
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
59452
59869
|
const logger = getSafeLogger();
|
|
59453
59870
|
const project = basename15(workdir);
|
|
59454
|
-
const runDir =
|
|
59455
|
-
const metaFile =
|
|
59871
|
+
const runDir = join74(getRunsDir(), `${project}-${feature}-${runId}`);
|
|
59872
|
+
const metaFile = join74(runDir, "meta.json");
|
|
59456
59873
|
const unsub = bus.on("run:started", (_ev) => {
|
|
59457
59874
|
return (async () => {
|
|
59458
59875
|
try {
|
|
@@ -59462,8 +59879,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
|
59462
59879
|
project,
|
|
59463
59880
|
feature,
|
|
59464
59881
|
workdir,
|
|
59465
|
-
statusPath:
|
|
59466
|
-
eventsDir:
|
|
59882
|
+
statusPath: join74(outputDir, "features", feature, "status.json"),
|
|
59883
|
+
eventsDir: join74(outputDir, "features", feature, "runs"),
|
|
59467
59884
|
registeredAt: new Date().toISOString()
|
|
59468
59885
|
};
|
|
59469
59886
|
await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -59709,7 +60126,7 @@ var init_types9 = __esm(() => {
|
|
|
59709
60126
|
|
|
59710
60127
|
// src/worktree/dependencies.ts
|
|
59711
60128
|
import { existsSync as existsSync32 } from "fs";
|
|
59712
|
-
import { join as
|
|
60129
|
+
import { join as join75 } from "path";
|
|
59713
60130
|
async function prepareWorktreeDependencies(options) {
|
|
59714
60131
|
const mode = options.config.execution.worktreeDependencies.mode;
|
|
59715
60132
|
const resolvedCwd = resolveDependencyCwd(options);
|
|
@@ -59723,7 +60140,7 @@ async function prepareWorktreeDependencies(options) {
|
|
|
59723
60140
|
}
|
|
59724
60141
|
}
|
|
59725
60142
|
function resolveDependencyCwd(options) {
|
|
59726
|
-
return options.storyWorkdir ?
|
|
60143
|
+
return options.storyWorkdir ? join75(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
|
|
59727
60144
|
}
|
|
59728
60145
|
function resolveInheritedDependencies(options, resolvedCwd) {
|
|
59729
60146
|
if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
|
|
@@ -59733,7 +60150,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
|
|
|
59733
60150
|
}
|
|
59734
60151
|
function hasDependencyManifests(worktreeRoot, resolvedCwd) {
|
|
59735
60152
|
const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
|
|
59736
|
-
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(
|
|
60153
|
+
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join75(directory, filename))));
|
|
59737
60154
|
}
|
|
59738
60155
|
async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
|
|
59739
60156
|
const setupCommand = config2.execution.worktreeDependencies.setupCommand;
|
|
@@ -59797,13 +60214,13 @@ __export(exports_manager, {
|
|
|
59797
60214
|
});
|
|
59798
60215
|
import { existsSync as existsSync33, symlinkSync } from "fs";
|
|
59799
60216
|
import { mkdir as mkdir16 } from "fs/promises";
|
|
59800
|
-
import { join as
|
|
60217
|
+
import { join as join76 } from "path";
|
|
59801
60218
|
|
|
59802
60219
|
class WorktreeManager {
|
|
59803
60220
|
async ensureGitExcludes(projectRoot) {
|
|
59804
60221
|
const logger = getSafeLogger();
|
|
59805
|
-
const infoDir =
|
|
59806
|
-
const excludePath =
|
|
60222
|
+
const infoDir = join76(projectRoot, ".git", "info");
|
|
60223
|
+
const excludePath = join76(infoDir, "exclude");
|
|
59807
60224
|
try {
|
|
59808
60225
|
await mkdir16(infoDir, { recursive: true });
|
|
59809
60226
|
let existing = "";
|
|
@@ -59830,7 +60247,7 @@ ${missing.join(`
|
|
|
59830
60247
|
}
|
|
59831
60248
|
async create(projectRoot, storyId) {
|
|
59832
60249
|
validateStoryId(storyId);
|
|
59833
|
-
const worktreePath =
|
|
60250
|
+
const worktreePath = join76(projectRoot, ".nax-wt", storyId);
|
|
59834
60251
|
const branchName = `nax/${storyId}`;
|
|
59835
60252
|
try {
|
|
59836
60253
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -59871,9 +60288,9 @@ ${missing.join(`
|
|
|
59871
60288
|
}
|
|
59872
60289
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
59873
60290
|
}
|
|
59874
|
-
const envSource =
|
|
60291
|
+
const envSource = join76(projectRoot, ".env");
|
|
59875
60292
|
if (existsSync33(envSource)) {
|
|
59876
|
-
const envTarget =
|
|
60293
|
+
const envTarget = join76(worktreePath, ".env");
|
|
59877
60294
|
try {
|
|
59878
60295
|
symlinkSync(envSource, envTarget, "file");
|
|
59879
60296
|
} catch (error48) {
|
|
@@ -59884,7 +60301,7 @@ ${missing.join(`
|
|
|
59884
60301
|
}
|
|
59885
60302
|
async remove(projectRoot, storyId) {
|
|
59886
60303
|
validateStoryId(storyId);
|
|
59887
|
-
const worktreePath =
|
|
60304
|
+
const worktreePath = join76(projectRoot, ".nax-wt", storyId);
|
|
59888
60305
|
const branchName = `nax/${storyId}`;
|
|
59889
60306
|
try {
|
|
59890
60307
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -60426,13 +60843,16 @@ var init_tier_outcome = __esm(() => {
|
|
|
60426
60843
|
});
|
|
60427
60844
|
|
|
60428
60845
|
// src/execution/escalation/tier-escalation.ts
|
|
60429
|
-
function buildEscalationFailure(story, currentTier, reviewFindings, cost) {
|
|
60846
|
+
function buildEscalationFailure(story, currentTier, reviewFindings, cost, pipelineReason, failureCategory) {
|
|
60430
60847
|
const stage = reviewFindings && reviewFindings.length > 0 ? "review" : "escalation";
|
|
60848
|
+
const trimmedReason = pipelineReason?.trim();
|
|
60849
|
+
const categoryPart = failureCategory ? ` [${failureCategory}]` : "";
|
|
60850
|
+
const summary = trimmedReason ? `Tier ${currentTier}${categoryPart}: ${trimmedReason}` : `Tier ${currentTier}${categoryPart} failed \u2014 no pipeline reason recorded`;
|
|
60431
60851
|
return {
|
|
60432
60852
|
attempt: (story.attempts ?? 0) + 1,
|
|
60433
60853
|
modelTier: currentTier,
|
|
60434
60854
|
stage,
|
|
60435
|
-
summary
|
|
60855
|
+
summary,
|
|
60436
60856
|
reviewFindings: reviewFindings && reviewFindings.length > 0 ? reviewFindings : undefined,
|
|
60437
60857
|
cost: cost ?? 0,
|
|
60438
60858
|
timestamp: new Date().toISOString()
|
|
@@ -60536,7 +60956,7 @@ async function handleTierEscalation(ctx) {
|
|
|
60536
60956
|
const isChangingTier = currentStoryTier !== escalatedTier;
|
|
60537
60957
|
const shouldResetAttempts = isChangingTier || shouldSwitchToTestAfter;
|
|
60538
60958
|
const escalationRecord = isChangingTier || shouldSwitchToTestAfter ? buildEscalationRecord(currentStoryTier, shouldSwitchToTestAfter ? currentStoryTier : escalatedTier, ctx.pipelineResult.reason ?? "Escalated to next retry path") : undefined;
|
|
60539
|
-
const escalationFailure = buildEscalationFailure(s, currentStoryTier, escalateReviewFindings, ctx.attemptCost);
|
|
60959
|
+
const escalationFailure = buildEscalationFailure(s, currentStoryTier, escalateReviewFindings, ctx.attemptCost, verifiedPipelineReason, escalateFailureCategory);
|
|
60540
60960
|
return {
|
|
60541
60961
|
...s,
|
|
60542
60962
|
attempts: shouldResetAttempts ? 0 : (s.attempts ?? 0) + 1,
|
|
@@ -60684,10 +61104,10 @@ var init_merge_conflict_rectify = __esm(() => {
|
|
|
60684
61104
|
});
|
|
60685
61105
|
|
|
60686
61106
|
// src/execution/pipeline-result-handler.ts
|
|
60687
|
-
import { join as
|
|
61107
|
+
import { join as join77 } from "path";
|
|
60688
61108
|
async function removeWorktreeDirectory(projectRoot, storyId) {
|
|
60689
61109
|
const logger = getSafeLogger();
|
|
60690
|
-
const worktreePath =
|
|
61110
|
+
const worktreePath = join77(projectRoot, ".nax-wt", storyId);
|
|
60691
61111
|
try {
|
|
60692
61112
|
const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
60693
61113
|
cwd: projectRoot,
|
|
@@ -60898,7 +61318,7 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
60898
61318
|
|
|
60899
61319
|
// src/execution/iteration-runner.ts
|
|
60900
61320
|
import { existsSync as existsSync34 } from "fs";
|
|
60901
|
-
import { join as
|
|
61321
|
+
import { join as join78 } from "path";
|
|
60902
61322
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
60903
61323
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
60904
61324
|
if (ctx.dryRun) {
|
|
@@ -60923,7 +61343,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
60923
61343
|
const storyStartTime = Date.now();
|
|
60924
61344
|
let effectiveWorkdir = ctx.workdir;
|
|
60925
61345
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
60926
|
-
const worktreePath =
|
|
61346
|
+
const worktreePath = join78(ctx.workdir, ".nax-wt", story.id);
|
|
60927
61347
|
const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
|
|
60928
61348
|
if (!worktreeExists) {
|
|
60929
61349
|
await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
|
|
@@ -60943,7 +61363,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
60943
61363
|
}
|
|
60944
61364
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
60945
61365
|
const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
|
|
60946
|
-
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(
|
|
61366
|
+
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join78(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
|
|
60947
61367
|
let dependencyContext;
|
|
60948
61368
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
60949
61369
|
try {
|
|
@@ -60970,7 +61390,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
60970
61390
|
};
|
|
60971
61391
|
}
|
|
60972
61392
|
}
|
|
60973
|
-
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ?
|
|
61393
|
+
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join78(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join78(ctx.workdir, story.workdir) : ctx.workdir;
|
|
60974
61394
|
const pipelineContext = {
|
|
60975
61395
|
config: effectiveConfig,
|
|
60976
61396
|
rootConfig: ctx.config,
|
|
@@ -61171,7 +61591,7 @@ __export(exports_parallel_worker, {
|
|
|
61171
61591
|
executeParallelBatch: () => executeParallelBatch,
|
|
61172
61592
|
_parallelWorkerDeps: () => _parallelWorkerDeps
|
|
61173
61593
|
});
|
|
61174
|
-
import { join as
|
|
61594
|
+
import { join as join79 } from "path";
|
|
61175
61595
|
async function executeStoryInWorktree(story, worktreePath, dependencyContext, context, routing, eventEmitter) {
|
|
61176
61596
|
const logger = getSafeLogger();
|
|
61177
61597
|
try {
|
|
@@ -61191,7 +61611,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
|
|
|
61191
61611
|
story,
|
|
61192
61612
|
stories: [story],
|
|
61193
61613
|
projectDir: context.projectDir,
|
|
61194
|
-
workdir: dependencyContext.cwd ?? (story.workdir ?
|
|
61614
|
+
workdir: dependencyContext.cwd ?? (story.workdir ? join79(worktreePath, story.workdir) : worktreePath),
|
|
61195
61615
|
worktreeDependencyContext: dependencyContext,
|
|
61196
61616
|
routing,
|
|
61197
61617
|
storyGitRef: storyGitRef ?? undefined
|
|
@@ -62020,7 +62440,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
62020
62440
|
var init_status_file = () => {};
|
|
62021
62441
|
|
|
62022
62442
|
// src/execution/status-writer.ts
|
|
62023
|
-
import { join as
|
|
62443
|
+
import { join as join80 } from "path";
|
|
62024
62444
|
|
|
62025
62445
|
class StatusWriter {
|
|
62026
62446
|
statusFile;
|
|
@@ -62139,7 +62559,7 @@ class StatusWriter {
|
|
|
62139
62559
|
if (!this._prd)
|
|
62140
62560
|
return;
|
|
62141
62561
|
const safeLogger = getSafeLogger();
|
|
62142
|
-
const featureStatusPath =
|
|
62562
|
+
const featureStatusPath = join80(featureDir, "status.json");
|
|
62143
62563
|
const write = async () => {
|
|
62144
62564
|
try {
|
|
62145
62565
|
const base = this.getSnapshot(totalCost, iterations);
|
|
@@ -62573,7 +62993,7 @@ __export(exports_run_initialization, {
|
|
|
62573
62993
|
initializeRun: () => initializeRun,
|
|
62574
62994
|
_reconcileDeps: () => _reconcileDeps
|
|
62575
62995
|
});
|
|
62576
|
-
import { join as
|
|
62996
|
+
import { join as join81 } from "path";
|
|
62577
62997
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
62578
62998
|
const logger = getSafeLogger();
|
|
62579
62999
|
let reconciledCount = 0;
|
|
@@ -62590,7 +63010,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
62590
63010
|
});
|
|
62591
63011
|
continue;
|
|
62592
63012
|
}
|
|
62593
|
-
const effectiveWorkdir = story.workdir ?
|
|
63013
|
+
const effectiveWorkdir = story.workdir ? join81(workdir, story.workdir) : workdir;
|
|
62594
63014
|
try {
|
|
62595
63015
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
62596
63016
|
if (!reviewResult.success) {
|
|
@@ -94022,7 +94442,7 @@ __export(exports_curator, {
|
|
|
94022
94442
|
});
|
|
94023
94443
|
import { readdirSync as readdirSync9 } from "fs";
|
|
94024
94444
|
import { unlink as unlink4 } from "fs/promises";
|
|
94025
|
-
import { basename as basename16, join as
|
|
94445
|
+
import { basename as basename16, join as join83 } from "path";
|
|
94026
94446
|
function getProjectKey(config2, projectDir) {
|
|
94027
94447
|
return config2.name?.trim() || basename16(projectDir);
|
|
94028
94448
|
}
|
|
@@ -94105,7 +94525,7 @@ async function curatorStatus(options) {
|
|
|
94105
94525
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
94106
94526
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94107
94527
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94108
|
-
const runsDir =
|
|
94528
|
+
const runsDir = join83(outputDir, "runs");
|
|
94109
94529
|
const runIds = listRunIds(runsDir);
|
|
94110
94530
|
let runId;
|
|
94111
94531
|
if (options.run) {
|
|
@@ -94122,8 +94542,8 @@ async function curatorStatus(options) {
|
|
|
94122
94542
|
runId = runIds[runIds.length - 1];
|
|
94123
94543
|
}
|
|
94124
94544
|
console.log(`Run: ${runId}`);
|
|
94125
|
-
const runDir =
|
|
94126
|
-
const observationsPath =
|
|
94545
|
+
const runDir = join83(runsDir, runId);
|
|
94546
|
+
const observationsPath = join83(runDir, "observations.jsonl");
|
|
94127
94547
|
const observations = await parseObservations(observationsPath);
|
|
94128
94548
|
const counts = new Map;
|
|
94129
94549
|
for (const obs of observations) {
|
|
@@ -94133,7 +94553,7 @@ async function curatorStatus(options) {
|
|
|
94133
94553
|
for (const [kind, count] of counts.entries()) {
|
|
94134
94554
|
console.log(` ${kind}: ${count}`);
|
|
94135
94555
|
}
|
|
94136
|
-
const proposalsPath =
|
|
94556
|
+
const proposalsPath = join83(runDir, "curator-proposals.md");
|
|
94137
94557
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
94138
94558
|
if (proposalText !== null) {
|
|
94139
94559
|
console.log("");
|
|
@@ -94147,8 +94567,8 @@ async function curatorCommit(options) {
|
|
|
94147
94567
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
94148
94568
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94149
94569
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94150
|
-
const runDir =
|
|
94151
|
-
const proposalsPath =
|
|
94570
|
+
const runDir = join83(outputDir, "runs", options.runId);
|
|
94571
|
+
const proposalsPath = join83(runDir, "curator-proposals.md");
|
|
94152
94572
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
94153
94573
|
if (proposalText === null) {
|
|
94154
94574
|
console.log(`curator-proposals.md not found for run ${options.runId}.`);
|
|
@@ -94164,7 +94584,7 @@ async function curatorCommit(options) {
|
|
|
94164
94584
|
const dropFileState = new Map;
|
|
94165
94585
|
const skippedDrops = new Set;
|
|
94166
94586
|
for (const drop2 of drops) {
|
|
94167
|
-
const targetPath =
|
|
94587
|
+
const targetPath = join83(resolved.projectDir, drop2.canonicalFile);
|
|
94168
94588
|
if (!dropFileState.has(targetPath)) {
|
|
94169
94589
|
const fileExists2 = await Bun.file(targetPath).exists();
|
|
94170
94590
|
const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
|
|
@@ -94198,7 +94618,7 @@ async function curatorCommit(options) {
|
|
|
94198
94618
|
if (skippedDrops.has(drop2)) {
|
|
94199
94619
|
continue;
|
|
94200
94620
|
}
|
|
94201
|
-
const targetPath =
|
|
94621
|
+
const targetPath = join83(resolved.projectDir, drop2.canonicalFile);
|
|
94202
94622
|
const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
|
|
94203
94623
|
const filtered = filterDropContent(existing, drop2.description);
|
|
94204
94624
|
await _curatorCmdDeps.writeFile(targetPath, filtered);
|
|
@@ -94207,7 +94627,7 @@ async function curatorCommit(options) {
|
|
|
94207
94627
|
}
|
|
94208
94628
|
const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
|
|
94209
94629
|
for (const add2 of adds) {
|
|
94210
|
-
const targetPath =
|
|
94630
|
+
const targetPath = join83(resolved.projectDir, add2.canonicalFile);
|
|
94211
94631
|
const content = buildAddContent(add2);
|
|
94212
94632
|
await _curatorCmdDeps.appendFile(targetPath, content);
|
|
94213
94633
|
modifiedFiles.add(targetPath);
|
|
@@ -94244,7 +94664,7 @@ async function curatorDryrun(options) {
|
|
|
94244
94664
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
94245
94665
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94246
94666
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94247
|
-
const runsDir =
|
|
94667
|
+
const runsDir = join83(outputDir, "runs");
|
|
94248
94668
|
const runIds = listRunIds(runsDir);
|
|
94249
94669
|
if (runIds.length === 0) {
|
|
94250
94670
|
console.log("No runs found.");
|
|
@@ -94255,7 +94675,7 @@ async function curatorDryrun(options) {
|
|
|
94255
94675
|
console.log(`Run ${options.run} not found in ${runsDir}.`);
|
|
94256
94676
|
return;
|
|
94257
94677
|
}
|
|
94258
|
-
const observationsPath =
|
|
94678
|
+
const observationsPath = join83(runsDir, runId, "observations.jsonl");
|
|
94259
94679
|
const observations = await parseObservations(observationsPath);
|
|
94260
94680
|
const thresholds = getThresholds(config2);
|
|
94261
94681
|
const proposals = runHeuristics(observations, thresholds);
|
|
@@ -94296,12 +94716,12 @@ async function curatorGc(options) {
|
|
|
94296
94716
|
await _curatorCmdDeps.writeFile(rollupPath, newContent);
|
|
94297
94717
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94298
94718
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94299
|
-
const perRunsDir =
|
|
94719
|
+
const perRunsDir = join83(outputDir, "runs");
|
|
94300
94720
|
for (const runId of uniqueRunIds) {
|
|
94301
94721
|
if (!keepSet.has(runId)) {
|
|
94302
|
-
const runDir =
|
|
94303
|
-
await _curatorCmdDeps.removeFile(
|
|
94304
|
-
await _curatorCmdDeps.removeFile(
|
|
94722
|
+
const runDir = join83(perRunsDir, runId);
|
|
94723
|
+
await _curatorCmdDeps.removeFile(join83(runDir, "observations.jsonl"));
|
|
94724
|
+
await _curatorCmdDeps.removeFile(join83(runDir, "curator-proposals.md"));
|
|
94305
94725
|
}
|
|
94306
94726
|
}
|
|
94307
94727
|
console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
|
|
@@ -94346,7 +94766,7 @@ var init_curator2 = __esm(() => {
|
|
|
94346
94766
|
init_source();
|
|
94347
94767
|
import { existsSync as existsSync37, mkdirSync as mkdirSync7 } from "fs";
|
|
94348
94768
|
import { homedir as homedir3 } from "os";
|
|
94349
|
-
import { basename as basename17, join as
|
|
94769
|
+
import { basename as basename17, join as join84 } from "path";
|
|
94350
94770
|
|
|
94351
94771
|
// node_modules/commander/esm.mjs
|
|
94352
94772
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -94370,12 +94790,12 @@ init_errors();
|
|
|
94370
94790
|
init_operations();
|
|
94371
94791
|
|
|
94372
94792
|
// src/plan/strategies/context-builder.ts
|
|
94373
|
-
import { join as
|
|
94793
|
+
import { join as join37 } from "path";
|
|
94374
94794
|
init_config();
|
|
94375
94795
|
init_errors();
|
|
94376
94796
|
init_interaction();
|
|
94377
94797
|
async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
94378
|
-
const naxDir =
|
|
94798
|
+
const naxDir = join37(workdir, ".nax");
|
|
94379
94799
|
if (!deps.existsSync(naxDir)) {
|
|
94380
94800
|
throw new NaxError(`.nax directory not found. Run 'nax init' first in ${workdir}`, "PLAN_CONTEXT_NO_NAX_DIR", {
|
|
94381
94801
|
stage: "plan",
|
|
@@ -94383,8 +94803,8 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
94383
94803
|
});
|
|
94384
94804
|
}
|
|
94385
94805
|
validateFeatureName(options.feature);
|
|
94386
|
-
const outputDir =
|
|
94387
|
-
const outputPath =
|
|
94806
|
+
const outputDir = join37(naxDir, "features", options.feature);
|
|
94807
|
+
const outputPath = join37(outputDir, "prd.json");
|
|
94388
94808
|
const [specContent, sourceRoots, pkg] = await Promise.all([
|
|
94389
94809
|
deps.readFile(options.from),
|
|
94390
94810
|
deps.scanSourceRoots(workdir),
|
|
@@ -94399,7 +94819,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
94399
94819
|
...new Set(sourceRoots.map((root) => root.path).filter((path7) => path7 !== ".").map((path7) => path7.startsWith("/") ? path7.replace(`${workdir}/`, "") : path7))
|
|
94400
94820
|
];
|
|
94401
94821
|
const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
|
|
94402
|
-
const packageJson = await deps.readPackageJsonAt(
|
|
94822
|
+
const packageJson = await deps.readPackageJsonAt(join37(workdir, relativePath, "package.json"));
|
|
94403
94823
|
return buildPackageSummary(relativePath, packageJson);
|
|
94404
94824
|
}));
|
|
94405
94825
|
const projectName = detectProjectName(workdir, pkg);
|
|
@@ -95000,7 +95420,7 @@ init_interaction();
|
|
|
95000
95420
|
init_prd();
|
|
95001
95421
|
init_runtime();
|
|
95002
95422
|
import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
|
|
95003
|
-
import { basename as basename7, join as
|
|
95423
|
+
import { basename as basename7, join as join42, resolve as resolve14 } from "path";
|
|
95004
95424
|
var _statusFeaturesDeps = {
|
|
95005
95425
|
projectOutputDir,
|
|
95006
95426
|
loadConfig
|
|
@@ -95014,7 +95434,7 @@ function isPidAlive(pid) {
|
|
|
95014
95434
|
}
|
|
95015
95435
|
}
|
|
95016
95436
|
async function loadStatusFile(featureDir) {
|
|
95017
|
-
const statusPath =
|
|
95437
|
+
const statusPath = join42(featureDir, "status.json");
|
|
95018
95438
|
if (!existsSync17(statusPath)) {
|
|
95019
95439
|
return null;
|
|
95020
95440
|
}
|
|
@@ -95029,7 +95449,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
95029
95449
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
95030
95450
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
95031
95451
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
95032
|
-
const statusPath =
|
|
95452
|
+
const statusPath = join42(outputDir, "status.json");
|
|
95033
95453
|
if (!existsSync17(statusPath)) {
|
|
95034
95454
|
return null;
|
|
95035
95455
|
}
|
|
@@ -95041,7 +95461,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
95041
95461
|
}
|
|
95042
95462
|
}
|
|
95043
95463
|
async function getFeatureSummary(featureName, featureDir) {
|
|
95044
|
-
const prdPath =
|
|
95464
|
+
const prdPath = join42(featureDir, "prd.json");
|
|
95045
95465
|
if (!existsSync17(prdPath)) {
|
|
95046
95466
|
return {
|
|
95047
95467
|
name: featureName,
|
|
@@ -95084,7 +95504,7 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
95084
95504
|
};
|
|
95085
95505
|
}
|
|
95086
95506
|
}
|
|
95087
|
-
const runsDir =
|
|
95507
|
+
const runsDir = join42(featureDir, "runs");
|
|
95088
95508
|
if (existsSync17(runsDir)) {
|
|
95089
95509
|
const runs = readdirSync3(runsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl") && e.name !== "latest.jsonl").map((e) => e.name).sort().reverse();
|
|
95090
95510
|
if (runs.length > 0) {
|
|
@@ -95098,7 +95518,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
95098
95518
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
95099
95519
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
95100
95520
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
95101
|
-
const featuresDir =
|
|
95521
|
+
const featuresDir = join42(outputDir, "features");
|
|
95102
95522
|
if (!existsSync17(featuresDir)) {
|
|
95103
95523
|
console.log(source_default.dim("No features found."));
|
|
95104
95524
|
return;
|
|
@@ -95139,7 +95559,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
95139
95559
|
console.log();
|
|
95140
95560
|
}
|
|
95141
95561
|
}
|
|
95142
|
-
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name,
|
|
95562
|
+
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join42(featuresDir, name))));
|
|
95143
95563
|
console.log(source_default.bold(`\uD83D\uDCCA Features
|
|
95144
95564
|
`));
|
|
95145
95565
|
const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
|
|
@@ -95165,7 +95585,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
95165
95585
|
console.log();
|
|
95166
95586
|
}
|
|
95167
95587
|
async function displayFeatureDetails(featureName, featureDir) {
|
|
95168
|
-
const prdPath =
|
|
95588
|
+
const prdPath = join42(featureDir, "prd.json");
|
|
95169
95589
|
if (!existsSync17(prdPath)) {
|
|
95170
95590
|
console.log(source_default.bold(`
|
|
95171
95591
|
\uD83D\uDCCA ${featureName}
|
|
@@ -95311,7 +95731,7 @@ async function displayFeatureStatus(options = {}) {
|
|
|
95311
95731
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
95312
95732
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
95313
95733
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
95314
|
-
featureDir =
|
|
95734
|
+
featureDir = join42(outputDir, "features", options.feature);
|
|
95315
95735
|
} else {
|
|
95316
95736
|
const resolved = resolveProject({ feature: options.feature });
|
|
95317
95737
|
if (!resolved.featureDir) {
|
|
@@ -95331,7 +95751,7 @@ init_errors();
|
|
|
95331
95751
|
init_logger2();
|
|
95332
95752
|
init_runtime();
|
|
95333
95753
|
import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
|
|
95334
|
-
import { basename as basename8, join as
|
|
95754
|
+
import { basename as basename8, join as join43 } from "path";
|
|
95335
95755
|
async function resolveOutputDir2(workdir, override) {
|
|
95336
95756
|
if (override)
|
|
95337
95757
|
return override;
|
|
@@ -95355,7 +95775,7 @@ async function runsListCommand(options) {
|
|
|
95355
95775
|
const logger = getLogger();
|
|
95356
95776
|
const { feature, workdir } = options;
|
|
95357
95777
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
95358
|
-
const runsDir =
|
|
95778
|
+
const runsDir = join43(outputDir, "features", feature, "runs");
|
|
95359
95779
|
if (!existsSync18(runsDir)) {
|
|
95360
95780
|
logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
|
|
95361
95781
|
return;
|
|
@@ -95367,7 +95787,7 @@ async function runsListCommand(options) {
|
|
|
95367
95787
|
}
|
|
95368
95788
|
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
95369
95789
|
for (const file3 of files.sort().reverse()) {
|
|
95370
|
-
const logPath =
|
|
95790
|
+
const logPath = join43(runsDir, file3);
|
|
95371
95791
|
const entries = await parseRunLog(logPath);
|
|
95372
95792
|
const startEvent = entries.find((e) => e.message === "run.start");
|
|
95373
95793
|
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
@@ -95394,7 +95814,7 @@ async function runsShowCommand(options) {
|
|
|
95394
95814
|
const logger = getLogger();
|
|
95395
95815
|
const { runId, feature, workdir } = options;
|
|
95396
95816
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
95397
|
-
const logPath =
|
|
95817
|
+
const logPath = join43(outputDir, "features", feature, "runs", `${runId}.jsonl`);
|
|
95398
95818
|
if (!existsSync18(logPath)) {
|
|
95399
95819
|
logger.error("cli", "Run not found", { runId, feature, logPath });
|
|
95400
95820
|
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
@@ -95507,7 +95927,7 @@ init_logger2();
|
|
|
95507
95927
|
init_prd();
|
|
95508
95928
|
init_runtime();
|
|
95509
95929
|
import { existsSync as existsSync25, readdirSync as readdirSync5 } from "fs";
|
|
95510
|
-
import { basename as basename12, join as
|
|
95930
|
+
import { basename as basename12, join as join57 } from "path";
|
|
95511
95931
|
|
|
95512
95932
|
// src/cli/diagnose-analysis.ts
|
|
95513
95933
|
function detectFailurePattern(story, _prd, status) {
|
|
@@ -95710,7 +96130,7 @@ function isProcessAlive2(pid) {
|
|
|
95710
96130
|
}
|
|
95711
96131
|
}
|
|
95712
96132
|
async function loadStatusFile2(outputDir) {
|
|
95713
|
-
const statusPath =
|
|
96133
|
+
const statusPath = join57(outputDir, "status.json");
|
|
95714
96134
|
if (!existsSync25(statusPath))
|
|
95715
96135
|
return null;
|
|
95716
96136
|
try {
|
|
@@ -95738,7 +96158,7 @@ async function countCommitsSince(workdir, since) {
|
|
|
95738
96158
|
}
|
|
95739
96159
|
}
|
|
95740
96160
|
async function checkLock(workdir) {
|
|
95741
|
-
const lockFile = Bun.file(
|
|
96161
|
+
const lockFile = Bun.file(join57(workdir, "nax.lock"));
|
|
95742
96162
|
if (!await lockFile.exists())
|
|
95743
96163
|
return { lockPresent: false };
|
|
95744
96164
|
try {
|
|
@@ -95756,8 +96176,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
95756
96176
|
const logger = getLogger();
|
|
95757
96177
|
const workdir = options.workdir ?? process.cwd();
|
|
95758
96178
|
const naxSubdir = findProjectDir(workdir);
|
|
95759
|
-
let projectDir = naxSubdir ?
|
|
95760
|
-
if (!projectDir && existsSync25(
|
|
96179
|
+
let projectDir = naxSubdir ? join57(naxSubdir, "..") : null;
|
|
96180
|
+
if (!projectDir && existsSync25(join57(workdir, ".nax"))) {
|
|
95761
96181
|
projectDir = workdir;
|
|
95762
96182
|
}
|
|
95763
96183
|
if (!projectDir)
|
|
@@ -95771,7 +96191,7 @@ async function diagnoseCommand(options = {}) {
|
|
|
95771
96191
|
if (status2) {
|
|
95772
96192
|
feature = status2.run.feature;
|
|
95773
96193
|
} else {
|
|
95774
|
-
const featuresDir =
|
|
96194
|
+
const featuresDir = join57(outputDir, "features");
|
|
95775
96195
|
if (!existsSync25(featuresDir))
|
|
95776
96196
|
throw new Error("No features found in project");
|
|
95777
96197
|
const features = readdirSync5(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -95781,8 +96201,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
95781
96201
|
logger.info("diagnose", "No feature specified, using first found", { feature });
|
|
95782
96202
|
}
|
|
95783
96203
|
}
|
|
95784
|
-
const featureDir =
|
|
95785
|
-
const prdPath =
|
|
96204
|
+
const featureDir = join57(outputDir, "features", feature);
|
|
96205
|
+
const prdPath = join57(featureDir, "prd.json");
|
|
95786
96206
|
if (!existsSync25(prdPath))
|
|
95787
96207
|
throw new Error(`Feature not found: ${feature}`);
|
|
95788
96208
|
const prd = await loadPRD(prdPath);
|
|
@@ -95827,7 +96247,7 @@ init_source();
|
|
|
95827
96247
|
init_loader();
|
|
95828
96248
|
init_generator2();
|
|
95829
96249
|
import { existsSync as existsSync26 } from "fs";
|
|
95830
|
-
import { join as
|
|
96250
|
+
import { join as join58 } from "path";
|
|
95831
96251
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
95832
96252
|
async function generateCommand(options) {
|
|
95833
96253
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -95870,7 +96290,7 @@ async function generateCommand(options) {
|
|
|
95870
96290
|
return;
|
|
95871
96291
|
}
|
|
95872
96292
|
if (options.package) {
|
|
95873
|
-
const packageDir =
|
|
96293
|
+
const packageDir = join58(workdir, options.package);
|
|
95874
96294
|
if (dryRun) {
|
|
95875
96295
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
95876
96296
|
}
|
|
@@ -95890,8 +96310,8 @@ async function generateCommand(options) {
|
|
|
95890
96310
|
process.exit(1);
|
|
95891
96311
|
return;
|
|
95892
96312
|
}
|
|
95893
|
-
const contextPath = options.context ?
|
|
95894
|
-
const outputDir = options.output ?
|
|
96313
|
+
const contextPath = options.context ? join58(workdir, options.context) : join58(workdir, ".nax/context.md");
|
|
96314
|
+
const outputDir = options.output ? join58(workdir, options.output) : workdir;
|
|
95895
96315
|
const autoInject = !options.noAutoInject;
|
|
95896
96316
|
if (!existsSync26(contextPath)) {
|
|
95897
96317
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
@@ -95997,7 +96417,7 @@ async function generateCommand(options) {
|
|
|
95997
96417
|
// src/cli/config-display.ts
|
|
95998
96418
|
init_loader();
|
|
95999
96419
|
import { existsSync as existsSync28 } from "fs";
|
|
96000
|
-
import { join as
|
|
96420
|
+
import { join as join60 } from "path";
|
|
96001
96421
|
|
|
96002
96422
|
// src/cli/config-descriptions.ts
|
|
96003
96423
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -96248,7 +96668,7 @@ function deepEqual(a, b) {
|
|
|
96248
96668
|
init_defaults();
|
|
96249
96669
|
init_loader();
|
|
96250
96670
|
import { existsSync as existsSync27 } from "fs";
|
|
96251
|
-
import { join as
|
|
96671
|
+
import { join as join59 } from "path";
|
|
96252
96672
|
async function loadConfigFile(path19) {
|
|
96253
96673
|
if (!existsSync27(path19))
|
|
96254
96674
|
return null;
|
|
@@ -96270,7 +96690,7 @@ async function loadProjectConfig() {
|
|
|
96270
96690
|
const projectDir = findProjectDir();
|
|
96271
96691
|
if (!projectDir)
|
|
96272
96692
|
return null;
|
|
96273
|
-
const projectPath =
|
|
96693
|
+
const projectPath = join59(projectDir, "config.json");
|
|
96274
96694
|
return await loadConfigFile(projectPath);
|
|
96275
96695
|
}
|
|
96276
96696
|
|
|
@@ -96330,7 +96750,7 @@ async function configCommand(config2, options = {}) {
|
|
|
96330
96750
|
function determineConfigSources() {
|
|
96331
96751
|
const globalPath = globalConfigPath();
|
|
96332
96752
|
const projectDir = findProjectDir();
|
|
96333
|
-
const projectPath = projectDir ?
|
|
96753
|
+
const projectPath = projectDir ? join60(projectDir, "config.json") : null;
|
|
96334
96754
|
return {
|
|
96335
96755
|
global: fileExists(globalPath) ? globalPath : null,
|
|
96336
96756
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
@@ -96479,15 +96899,15 @@ init_paths();
|
|
|
96479
96899
|
init_profile();
|
|
96480
96900
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
96481
96901
|
import { readdirSync as readdirSync6 } from "fs";
|
|
96482
|
-
import { join as
|
|
96902
|
+
import { join as join61 } from "path";
|
|
96483
96903
|
var _profileCLIDeps = {
|
|
96484
96904
|
env: process.env
|
|
96485
96905
|
};
|
|
96486
96906
|
var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
|
|
96487
96907
|
var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
|
|
96488
96908
|
async function profileListCommand(startDir) {
|
|
96489
|
-
const globalProfilesDir =
|
|
96490
|
-
const projectProfilesDir =
|
|
96909
|
+
const globalProfilesDir = join61(globalConfigDir(), "profiles");
|
|
96910
|
+
const projectProfilesDir = join61(projectConfigDir(startDir), "profiles");
|
|
96491
96911
|
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
96492
96912
|
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
96493
96913
|
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
@@ -96546,7 +96966,7 @@ function maskProfileValues(obj) {
|
|
|
96546
96966
|
return result;
|
|
96547
96967
|
}
|
|
96548
96968
|
async function profileUseCommand(profileName, startDir) {
|
|
96549
|
-
const configPath =
|
|
96969
|
+
const configPath = join61(projectConfigDir(startDir), "config.json");
|
|
96550
96970
|
const configFile = Bun.file(configPath);
|
|
96551
96971
|
let existing = {};
|
|
96552
96972
|
if (await configFile.exists()) {
|
|
@@ -96565,8 +96985,8 @@ async function profileCurrentCommand(startDir) {
|
|
|
96565
96985
|
return resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
96566
96986
|
}
|
|
96567
96987
|
async function profileCreateCommand(profileName, startDir) {
|
|
96568
|
-
const profilesDir =
|
|
96569
|
-
const profilePath =
|
|
96988
|
+
const profilesDir = join61(projectConfigDir(startDir), "profiles");
|
|
96989
|
+
const profilePath = join61(profilesDir, `${profileName}.json`);
|
|
96570
96990
|
const profileFile = Bun.file(profilePath);
|
|
96571
96991
|
if (await profileFile.exists()) {
|
|
96572
96992
|
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
@@ -96688,7 +97108,7 @@ async function contextInspectCommand(options) {
|
|
|
96688
97108
|
init_canonical_loader();
|
|
96689
97109
|
init_errors();
|
|
96690
97110
|
import { mkdir as mkdir12 } from "fs/promises";
|
|
96691
|
-
import { basename as basename13, join as
|
|
97111
|
+
import { basename as basename13, join as join62 } from "path";
|
|
96692
97112
|
var _rulesCLIDeps = {
|
|
96693
97113
|
readFile: async (path19) => Bun.file(path19).text(),
|
|
96694
97114
|
writeFile: async (path19, content) => {
|
|
@@ -96697,7 +97117,7 @@ var _rulesCLIDeps = {
|
|
|
96697
97117
|
fileExists: async (path19) => Bun.file(path19).exists(),
|
|
96698
97118
|
globInDir: (dir) => {
|
|
96699
97119
|
try {
|
|
96700
|
-
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) =>
|
|
97120
|
+
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join62(dir, f));
|
|
96701
97121
|
} catch {
|
|
96702
97122
|
return [];
|
|
96703
97123
|
}
|
|
@@ -96746,7 +97166,7 @@ ${r.content}`).join(`
|
|
|
96746
97166
|
`);
|
|
96747
97167
|
const shimContent = `${header + body}
|
|
96748
97168
|
`;
|
|
96749
|
-
const shimPath =
|
|
97169
|
+
const shimPath = join62(workdir, shimFileName);
|
|
96750
97170
|
if (options.dryRun) {
|
|
96751
97171
|
console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
|
|
96752
97172
|
return;
|
|
@@ -96775,14 +97195,14 @@ function neutralizeContent(content) {
|
|
|
96775
97195
|
}
|
|
96776
97196
|
async function collectMigrationSources(workdir) {
|
|
96777
97197
|
const sources = [];
|
|
96778
|
-
const claudeMdPath =
|
|
97198
|
+
const claudeMdPath = join62(workdir, "CLAUDE.md");
|
|
96779
97199
|
if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
|
|
96780
97200
|
const content = await _rulesCLIDeps.readFile(claudeMdPath);
|
|
96781
97201
|
if (content.trim()) {
|
|
96782
97202
|
sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
|
|
96783
97203
|
}
|
|
96784
97204
|
}
|
|
96785
|
-
const rulesDir =
|
|
97205
|
+
const rulesDir = join62(workdir, ".claude", "rules");
|
|
96786
97206
|
const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
|
|
96787
97207
|
for (const filePath of ruleFiles) {
|
|
96788
97208
|
try {
|
|
@@ -96802,7 +97222,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96802
97222
|
console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
|
|
96803
97223
|
return;
|
|
96804
97224
|
}
|
|
96805
|
-
const targetDir =
|
|
97225
|
+
const targetDir = join62(workdir, CANONICAL_RULES_DIR);
|
|
96806
97226
|
if (!options.dryRun) {
|
|
96807
97227
|
try {
|
|
96808
97228
|
await _rulesCLIDeps.mkdir(targetDir);
|
|
@@ -96813,7 +97233,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96813
97233
|
let written = 0;
|
|
96814
97234
|
let skipped = 0;
|
|
96815
97235
|
for (const { sourcePath, targetFileName, content } of sources) {
|
|
96816
|
-
const targetPath =
|
|
97236
|
+
const targetPath = join62(targetDir, targetFileName);
|
|
96817
97237
|
if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
|
|
96818
97238
|
console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
|
|
96819
97239
|
skipped++;
|
|
@@ -96852,7 +97272,7 @@ function collectCanonicalRuleRoots(workdir) {
|
|
|
96852
97272
|
const packageRel = normalized.slice(0, idx);
|
|
96853
97273
|
if (!packageRel)
|
|
96854
97274
|
continue;
|
|
96855
|
-
roots.add(
|
|
97275
|
+
roots.add(join62(workdir, packageRel));
|
|
96856
97276
|
}
|
|
96857
97277
|
return [...roots].sort();
|
|
96858
97278
|
}
|
|
@@ -96874,7 +97294,7 @@ init_logger2();
|
|
|
96874
97294
|
init_detect2();
|
|
96875
97295
|
init_workspace();
|
|
96876
97296
|
init_common();
|
|
96877
|
-
import { join as
|
|
97297
|
+
import { join as join63 } from "path";
|
|
96878
97298
|
function resolveEffective(detected, configPatterns) {
|
|
96879
97299
|
if (configPatterns !== undefined)
|
|
96880
97300
|
return "config";
|
|
@@ -96959,7 +97379,7 @@ async function detectCommand(options) {
|
|
|
96959
97379
|
const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96960
97380
|
const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
|
|
96961
97381
|
const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96962
|
-
const pkgConfigPath =
|
|
97382
|
+
const pkgConfigPath = join63(workdir, ".nax", "mono", dir, "config.json");
|
|
96963
97383
|
const pkgRaw = await loadRawConfig(pkgConfigPath);
|
|
96964
97384
|
const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
|
|
96965
97385
|
const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
|
|
@@ -97013,13 +97433,13 @@ async function detectCommand(options) {
|
|
|
97013
97433
|
if (rootDetected.confidence === "empty") {
|
|
97014
97434
|
console.log(source_default.yellow(" root: skipped (empty detection)"));
|
|
97015
97435
|
} else {
|
|
97016
|
-
const rootConfigPath =
|
|
97436
|
+
const rootConfigPath = join63(workdir, ".nax", "config.json");
|
|
97017
97437
|
try {
|
|
97018
97438
|
const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
|
|
97019
97439
|
if (status === "skipped") {
|
|
97020
97440
|
console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
|
|
97021
97441
|
} else {
|
|
97022
|
-
console.log(source_default.green(` root: ${status} \u2192 ${
|
|
97442
|
+
console.log(source_default.green(` root: ${status} \u2192 ${join63(".nax", "config.json")}`));
|
|
97023
97443
|
}
|
|
97024
97444
|
} catch (err) {
|
|
97025
97445
|
console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
|
|
@@ -97032,13 +97452,13 @@ async function detectCommand(options) {
|
|
|
97032
97452
|
console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
|
|
97033
97453
|
continue;
|
|
97034
97454
|
}
|
|
97035
|
-
const pkgConfigPath =
|
|
97455
|
+
const pkgConfigPath = join63(workdir, ".nax", "mono", dir, "config.json");
|
|
97036
97456
|
try {
|
|
97037
97457
|
const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
|
|
97038
97458
|
if (status === "skipped") {
|
|
97039
97459
|
console.log(source_default.dim(` ${dir}: skipped (already set)`));
|
|
97040
97460
|
} else {
|
|
97041
|
-
console.log(source_default.green(` ${dir}: ${status} \u2192 ${
|
|
97461
|
+
console.log(source_default.green(` ${dir}: ${status} \u2192 ${join63(".nax", "mono", dir, "config.json")}`));
|
|
97042
97462
|
}
|
|
97043
97463
|
} catch (err) {
|
|
97044
97464
|
console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
|
|
@@ -97061,19 +97481,19 @@ async function diagnose(options) {
|
|
|
97061
97481
|
// src/commands/logs.ts
|
|
97062
97482
|
init_common();
|
|
97063
97483
|
import { existsSync as existsSync30 } from "fs";
|
|
97064
|
-
import { join as
|
|
97484
|
+
import { join as join67 } from "path";
|
|
97065
97485
|
|
|
97066
97486
|
// src/commands/logs-formatter.ts
|
|
97067
97487
|
init_source();
|
|
97068
97488
|
init_formatter();
|
|
97069
97489
|
import { readdirSync as readdirSync8 } from "fs";
|
|
97070
|
-
import { join as
|
|
97490
|
+
import { join as join66 } from "path";
|
|
97071
97491
|
|
|
97072
97492
|
// src/commands/logs-reader.ts
|
|
97073
97493
|
init_paths3();
|
|
97074
97494
|
import { existsSync as existsSync29, readdirSync as readdirSync7 } from "fs";
|
|
97075
97495
|
import { readdir as readdir4 } from "fs/promises";
|
|
97076
|
-
import { join as
|
|
97496
|
+
import { join as join65 } from "path";
|
|
97077
97497
|
var _logsReaderDeps = {
|
|
97078
97498
|
getRunsDir
|
|
97079
97499
|
};
|
|
@@ -97087,7 +97507,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
97087
97507
|
}
|
|
97088
97508
|
let matched = null;
|
|
97089
97509
|
for (const entry of entries) {
|
|
97090
|
-
const metaPath =
|
|
97510
|
+
const metaPath = join65(runsDir, entry, "meta.json");
|
|
97091
97511
|
try {
|
|
97092
97512
|
const meta3 = await Bun.file(metaPath).json();
|
|
97093
97513
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -97109,14 +97529,14 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
97109
97529
|
return null;
|
|
97110
97530
|
}
|
|
97111
97531
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
97112
|
-
return
|
|
97532
|
+
return join65(matched.eventsDir, specificFile ?? files[0]);
|
|
97113
97533
|
}
|
|
97114
97534
|
async function selectRunFile(runsDir) {
|
|
97115
97535
|
const files = readdirSync7(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
97116
97536
|
if (files.length === 0) {
|
|
97117
97537
|
return null;
|
|
97118
97538
|
}
|
|
97119
|
-
return
|
|
97539
|
+
return join65(runsDir, files[0]);
|
|
97120
97540
|
}
|
|
97121
97541
|
async function extractRunSummary(filePath) {
|
|
97122
97542
|
const file3 = Bun.file(filePath);
|
|
@@ -97202,7 +97622,7 @@ Runs:
|
|
|
97202
97622
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
97203
97623
|
console.log(source_default.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
97204
97624
|
for (const file3 of files) {
|
|
97205
|
-
const filePath =
|
|
97625
|
+
const filePath = join66(runsDir, file3);
|
|
97206
97626
|
const summary = await extractRunSummary(filePath);
|
|
97207
97627
|
const timestamp = file3.replace(".jsonl", "");
|
|
97208
97628
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -97316,7 +97736,7 @@ async function logsCommand(options) {
|
|
|
97316
97736
|
return;
|
|
97317
97737
|
}
|
|
97318
97738
|
const resolved = resolveProject({ dir: options.dir });
|
|
97319
|
-
const naxDir =
|
|
97739
|
+
const naxDir = join67(resolved.projectDir, ".nax");
|
|
97320
97740
|
const configPath = resolved.configPath;
|
|
97321
97741
|
const configFile = Bun.file(configPath);
|
|
97322
97742
|
const config2 = await configFile.json();
|
|
@@ -97324,8 +97744,8 @@ async function logsCommand(options) {
|
|
|
97324
97744
|
if (!featureName) {
|
|
97325
97745
|
throw new Error("No feature specified in config.json");
|
|
97326
97746
|
}
|
|
97327
|
-
const featureDir =
|
|
97328
|
-
const runsDir =
|
|
97747
|
+
const featureDir = join67(naxDir, "features", featureName);
|
|
97748
|
+
const runsDir = join67(featureDir, "runs");
|
|
97329
97749
|
if (!existsSync30(runsDir)) {
|
|
97330
97750
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
97331
97751
|
}
|
|
@@ -97351,7 +97771,7 @@ init_prd();
|
|
|
97351
97771
|
init_precheck();
|
|
97352
97772
|
init_common();
|
|
97353
97773
|
import { existsSync as existsSync31 } from "fs";
|
|
97354
|
-
import { join as
|
|
97774
|
+
import { join as join68 } from "path";
|
|
97355
97775
|
async function precheckCommand(options) {
|
|
97356
97776
|
const resolved = resolveProject({
|
|
97357
97777
|
dir: options.dir,
|
|
@@ -97373,9 +97793,9 @@ async function precheckCommand(options) {
|
|
|
97373
97793
|
process.exit(1);
|
|
97374
97794
|
}
|
|
97375
97795
|
}
|
|
97376
|
-
const naxDir =
|
|
97377
|
-
const featureDir =
|
|
97378
|
-
const prdPath =
|
|
97796
|
+
const naxDir = join68(resolved.projectDir, ".nax");
|
|
97797
|
+
const featureDir = join68(naxDir, "features", featureName);
|
|
97798
|
+
const prdPath = join68(featureDir, "prd.json");
|
|
97379
97799
|
if (!existsSync31(featureDir)) {
|
|
97380
97800
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
97381
97801
|
process.exit(1);
|
|
@@ -97398,7 +97818,7 @@ async function precheckCommand(options) {
|
|
|
97398
97818
|
init_source();
|
|
97399
97819
|
init_paths3();
|
|
97400
97820
|
import { readdir as readdir5 } from "fs/promises";
|
|
97401
|
-
import { join as
|
|
97821
|
+
import { join as join69 } from "path";
|
|
97402
97822
|
var DEFAULT_LIMIT = 20;
|
|
97403
97823
|
var _runsCmdDeps = {
|
|
97404
97824
|
getRunsDir
|
|
@@ -97453,7 +97873,7 @@ async function runsCommand(options = {}) {
|
|
|
97453
97873
|
}
|
|
97454
97874
|
const rows = [];
|
|
97455
97875
|
for (const entry of entries) {
|
|
97456
|
-
const metaPath =
|
|
97876
|
+
const metaPath = join69(runsDir, entry, "meta.json");
|
|
97457
97877
|
let meta3;
|
|
97458
97878
|
try {
|
|
97459
97879
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -97530,7 +97950,7 @@ async function runsCommand(options = {}) {
|
|
|
97530
97950
|
|
|
97531
97951
|
// src/commands/unlock.ts
|
|
97532
97952
|
init_source();
|
|
97533
|
-
import { join as
|
|
97953
|
+
import { join as join70 } from "path";
|
|
97534
97954
|
function isProcessAlive3(pid) {
|
|
97535
97955
|
try {
|
|
97536
97956
|
process.kill(pid, 0);
|
|
@@ -97545,7 +97965,7 @@ function formatLockAge(ageMs) {
|
|
|
97545
97965
|
}
|
|
97546
97966
|
async function unlockCommand(options) {
|
|
97547
97967
|
const workdir = options.dir ?? process.cwd();
|
|
97548
|
-
const lockPath =
|
|
97968
|
+
const lockPath = join70(workdir, "nax.lock");
|
|
97549
97969
|
const lockFile = Bun.file(lockPath);
|
|
97550
97970
|
const exists = await lockFile.exists();
|
|
97551
97971
|
if (!exists) {
|
|
@@ -105618,7 +106038,7 @@ Next: nax generate --package ${options.package}`));
|
|
|
105618
106038
|
}
|
|
105619
106039
|
return;
|
|
105620
106040
|
}
|
|
105621
|
-
const naxDir =
|
|
106041
|
+
const naxDir = join84(workdir, ".nax");
|
|
105622
106042
|
if (existsSync37(naxDir) && !options.force) {
|
|
105623
106043
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
105624
106044
|
return;
|
|
@@ -105647,11 +106067,11 @@ Next: nax generate --package ${options.package}`));
|
|
|
105647
106067
|
}
|
|
105648
106068
|
}
|
|
105649
106069
|
}
|
|
105650
|
-
mkdirSync7(
|
|
105651
|
-
mkdirSync7(
|
|
106070
|
+
mkdirSync7(join84(naxDir, "features"), { recursive: true });
|
|
106071
|
+
mkdirSync7(join84(naxDir, "hooks"), { recursive: true });
|
|
105652
106072
|
const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
|
|
105653
|
-
await Bun.write(
|
|
105654
|
-
await Bun.write(
|
|
106073
|
+
await Bun.write(join84(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
|
|
106074
|
+
await Bun.write(join84(naxDir, "hooks.json"), JSON.stringify({
|
|
105655
106075
|
hooks: {
|
|
105656
106076
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
105657
106077
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -105659,12 +106079,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
105659
106079
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
105660
106080
|
}
|
|
105661
106081
|
}, null, 2));
|
|
105662
|
-
await Bun.write(
|
|
106082
|
+
await Bun.write(join84(naxDir, ".gitignore"), `# nax temp files
|
|
105663
106083
|
*.tmp
|
|
105664
106084
|
.paused.json
|
|
105665
106085
|
.nax-verifier-verdict.json
|
|
105666
106086
|
`);
|
|
105667
|
-
await Bun.write(
|
|
106087
|
+
await Bun.write(join84(naxDir, "context.md"), `# Project Context
|
|
105668
106088
|
|
|
105669
106089
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
105670
106090
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -105794,8 +106214,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105794
106214
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105795
106215
|
process.exit(1);
|
|
105796
106216
|
}
|
|
105797
|
-
const featureDir =
|
|
105798
|
-
const prdPath =
|
|
106217
|
+
const featureDir = join84(naxDir, "features", options.feature);
|
|
106218
|
+
const prdPath = join84(featureDir, "prd.json");
|
|
105799
106219
|
if (options.plan && options.from) {
|
|
105800
106220
|
if (existsSync37(prdPath) && !options.force) {
|
|
105801
106221
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
@@ -105817,10 +106237,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105817
106237
|
}
|
|
105818
106238
|
}
|
|
105819
106239
|
try {
|
|
105820
|
-
const planLogDir =
|
|
106240
|
+
const planLogDir = join84(featureDir, "plan");
|
|
105821
106241
|
mkdirSync7(planLogDir, { recursive: true });
|
|
105822
106242
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105823
|
-
const planLogPath =
|
|
106243
|
+
const planLogPath = join84(planLogDir, `${planLogId}.jsonl`);
|
|
105824
106244
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
105825
106245
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
105826
106246
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -105866,10 +106286,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105866
106286
|
resetLogger();
|
|
105867
106287
|
const projectKey = config2.name?.trim() || basename17(workdir);
|
|
105868
106288
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
105869
|
-
const runsDir =
|
|
106289
|
+
const runsDir = join84(outputDir, "features", options.feature, "runs");
|
|
105870
106290
|
mkdirSync7(runsDir, { recursive: true });
|
|
105871
106291
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105872
|
-
const logFilePath =
|
|
106292
|
+
const logFilePath = join84(runsDir, `${runId}.jsonl`);
|
|
105873
106293
|
const isTTY = process.stdout.isTTY ?? false;
|
|
105874
106294
|
const headlessFlag = options.headless ?? false;
|
|
105875
106295
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -105886,7 +106306,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105886
106306
|
config2.agent.default = options.agent;
|
|
105887
106307
|
}
|
|
105888
106308
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
105889
|
-
const globalNaxDir =
|
|
106309
|
+
const globalNaxDir = join84(homedir3(), ".nax");
|
|
105890
106310
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
105891
106311
|
const eventEmitter = new PipelineEventEmitter;
|
|
105892
106312
|
let tuiInstance;
|
|
@@ -105909,7 +106329,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105909
106329
|
} else {
|
|
105910
106330
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
105911
106331
|
}
|
|
105912
|
-
const statusFilePath =
|
|
106332
|
+
const statusFilePath = join84(outputDir, "status.json");
|
|
105913
106333
|
let parallel;
|
|
105914
106334
|
if (options.parallel !== undefined) {
|
|
105915
106335
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -105935,7 +106355,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105935
106355
|
headless: useHeadless,
|
|
105936
106356
|
skipPrecheck: options.skipPrecheck ?? false
|
|
105937
106357
|
});
|
|
105938
|
-
const latestSymlink =
|
|
106358
|
+
const latestSymlink = join84(runsDir, "latest.jsonl");
|
|
105939
106359
|
try {
|
|
105940
106360
|
if (existsSync37(latestSymlink)) {
|
|
105941
106361
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
@@ -105996,9 +106416,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
105996
106416
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105997
106417
|
process.exit(1);
|
|
105998
106418
|
}
|
|
105999
|
-
const featureDir =
|
|
106419
|
+
const featureDir = join84(naxDir, "features", name);
|
|
106000
106420
|
mkdirSync7(featureDir, { recursive: true });
|
|
106001
|
-
await Bun.write(
|
|
106421
|
+
await Bun.write(join84(featureDir, "spec.md"), `# Feature: ${name}
|
|
106002
106422
|
|
|
106003
106423
|
## Overview
|
|
106004
106424
|
|
|
@@ -106031,7 +106451,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
106031
106451
|
|
|
106032
106452
|
<!-- What this feature explicitly does NOT cover. -->
|
|
106033
106453
|
`);
|
|
106034
|
-
await Bun.write(
|
|
106454
|
+
await Bun.write(join84(featureDir, "progress.txt"), `# Progress: ${name}
|
|
106035
106455
|
|
|
106036
106456
|
Created: ${new Date().toISOString()}
|
|
106037
106457
|
|
|
@@ -106057,7 +106477,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
106057
106477
|
console.error(source_default.red("nax not initialized."));
|
|
106058
106478
|
process.exit(1);
|
|
106059
106479
|
}
|
|
106060
|
-
const featuresDir =
|
|
106480
|
+
const featuresDir = join84(naxDir, "features");
|
|
106061
106481
|
if (!existsSync37(featuresDir)) {
|
|
106062
106482
|
console.log(source_default.dim("No features yet."));
|
|
106063
106483
|
return;
|
|
@@ -106072,7 +106492,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
106072
106492
|
Features:
|
|
106073
106493
|
`));
|
|
106074
106494
|
for (const name of entries) {
|
|
106075
|
-
const prdPath =
|
|
106495
|
+
const prdPath = join84(featuresDir, name, "prd.json");
|
|
106076
106496
|
if (existsSync37(prdPath)) {
|
|
106077
106497
|
const prd = await loadPRD(prdPath);
|
|
106078
106498
|
const c = countStories(prd);
|
|
@@ -106107,10 +106527,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
106107
106527
|
cliOverrides.profile = options.profile;
|
|
106108
106528
|
}
|
|
106109
106529
|
const config2 = await loadConfig(workdir, cliOverrides);
|
|
106110
|
-
const featureLogDir =
|
|
106530
|
+
const featureLogDir = join84(naxDir, "features", options.feature, "plan");
|
|
106111
106531
|
mkdirSync7(featureLogDir, { recursive: true });
|
|
106112
106532
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
106113
|
-
const planLogPath =
|
|
106533
|
+
const planLogPath = join84(featureLogDir, `${planLogId}.jsonl`);
|
|
106114
106534
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
106115
106535
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
106116
106536
|
try {
|