@nathapp/nax 0.67.11 → 0.67.12
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 +514 -347
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -35772,6 +35772,16 @@ function parseTestEditDeclarations(output) {
|
|
|
35772
35772
|
}
|
|
35773
35773
|
return result;
|
|
35774
35774
|
}
|
|
35775
|
+
function normaliseWs(s) {
|
|
35776
|
+
return s.replace(/\s+/g, " ").replace(/\s*([(),<>])\s*/g, "$1").replace(/\s*:\s*/g, ": ").trim();
|
|
35777
|
+
}
|
|
35778
|
+
function validatePrdQuote(prdQuote, story) {
|
|
35779
|
+
if (!prdQuote.trim())
|
|
35780
|
+
return false;
|
|
35781
|
+
const needle = normaliseWs(prdQuote);
|
|
35782
|
+
const haystack = normaliseWs([story.description, ...story.acceptanceCriteria].join(" "));
|
|
35783
|
+
return haystack.includes(needle);
|
|
35784
|
+
}
|
|
35775
35785
|
var REASON_RE;
|
|
35776
35786
|
var init_test_edit_declaration = __esm(() => {
|
|
35777
35787
|
REASON_RE = /^TEST_EDIT_REASON:\s*(prd_contract|lint_only|sibling_scope|mock_structure)\s*$/m;
|
|
@@ -37896,7 +37906,7 @@ var init__finding_to_check = __esm(() => {
|
|
|
37896
37906
|
});
|
|
37897
37907
|
|
|
37898
37908
|
// src/operations/autofix-implementer-strategy.ts
|
|
37899
|
-
function makeAutofixImplementerStrategy(story, config2) {
|
|
37909
|
+
function makeAutofixImplementerStrategy(story, config2, sink) {
|
|
37900
37910
|
return {
|
|
37901
37911
|
name: "autofix-implementer",
|
|
37902
37912
|
appliesTo: (f) => f.fixTarget === "source" && IMPLEMENTER_SOURCES.has(f.source),
|
|
@@ -37905,10 +37915,19 @@ function makeAutofixImplementerStrategy(story, config2) {
|
|
|
37905
37915
|
failedChecks: findingsToFailedChecks(findings),
|
|
37906
37916
|
story
|
|
37907
37917
|
}),
|
|
37908
|
-
extractApplied: (output) =>
|
|
37909
|
-
|
|
37910
|
-
|
|
37911
|
-
|
|
37918
|
+
extractApplied: (output) => {
|
|
37919
|
+
for (const decl of output.testEditDeclarations) {
|
|
37920
|
+
if (decl.reason === "mock_structure" && decl.files && decl.reasonDetail) {
|
|
37921
|
+
sink.mockHandoffs.push({ files: decl.files, reasonDetail: decl.reasonDetail });
|
|
37922
|
+
} else if (decl.reason !== "mock_structure") {
|
|
37923
|
+
sink.testEdits.push(decl);
|
|
37924
|
+
}
|
|
37925
|
+
}
|
|
37926
|
+
return {
|
|
37927
|
+
summary: output.unresolvedReason ?? "",
|
|
37928
|
+
unresolved: output.unresolvedReason
|
|
37929
|
+
};
|
|
37930
|
+
},
|
|
37912
37931
|
maxAttempts: config2.execution.rectification.maxAttemptsPerStrategy,
|
|
37913
37932
|
coRun: "co-run-sequential"
|
|
37914
37933
|
};
|
|
@@ -37921,16 +37940,42 @@ var init_autofix_implementer_strategy = __esm(() => {
|
|
|
37921
37940
|
});
|
|
37922
37941
|
|
|
37923
37942
|
// src/operations/autofix-test-writer-strategy.ts
|
|
37924
|
-
function makeAutofixTestWriterStrategy(story, config2) {
|
|
37943
|
+
function makeAutofixTestWriterStrategy(story, config2, sink) {
|
|
37925
37944
|
return {
|
|
37926
37945
|
name: "autofix-test-writer",
|
|
37927
|
-
appliesTo: (f) => f.fixTarget === "test" || f.source === "adversarial-review",
|
|
37946
|
+
appliesTo: (f) => f.fixTarget === "test" || f.source === "adversarial-review" || sink.mockHandoffs.length > 0,
|
|
37928
37947
|
fixOp: testWriterRectifyOp,
|
|
37929
|
-
buildInput: (findings, _prior, _cycleCtx) =>
|
|
37930
|
-
|
|
37931
|
-
|
|
37932
|
-
|
|
37933
|
-
|
|
37948
|
+
buildInput: (findings, _prior, _cycleCtx) => {
|
|
37949
|
+
if (sink.mockHandoffs.length > 0) {
|
|
37950
|
+
const handoffs = sink.mockHandoffs.splice(0);
|
|
37951
|
+
const seenFiles = new Set;
|
|
37952
|
+
const handoffFiles = [];
|
|
37953
|
+
for (const h of handoffs) {
|
|
37954
|
+
for (const f of h.files) {
|
|
37955
|
+
if (!seenFiles.has(f)) {
|
|
37956
|
+
seenFiles.add(f);
|
|
37957
|
+
handoffFiles.push(f);
|
|
37958
|
+
}
|
|
37959
|
+
}
|
|
37960
|
+
}
|
|
37961
|
+
const handoffReason = handoffs.map((h) => h.reasonDetail).join(`
|
|
37962
|
+
---
|
|
37963
|
+
`);
|
|
37964
|
+
return {
|
|
37965
|
+
failedChecks: findingsToFailedChecks(findings),
|
|
37966
|
+
story,
|
|
37967
|
+
mode: "mock-restructure",
|
|
37968
|
+
blockingThreshold: config2.review?.blockingThreshold,
|
|
37969
|
+
handoffReason,
|
|
37970
|
+
handoffFiles
|
|
37971
|
+
};
|
|
37972
|
+
}
|
|
37973
|
+
return {
|
|
37974
|
+
failedChecks: findingsToFailedChecks(findings),
|
|
37975
|
+
story,
|
|
37976
|
+
blockingThreshold: config2.review?.blockingThreshold
|
|
37977
|
+
};
|
|
37978
|
+
},
|
|
37934
37979
|
maxAttempts: config2.execution.rectification.maxAttemptsPerStrategy,
|
|
37935
37980
|
coRun: "co-run-sequential"
|
|
37936
37981
|
};
|
|
@@ -37940,6 +37985,100 @@ var init_autofix_test_writer_strategy = __esm(() => {
|
|
|
37940
37985
|
init_autofix_test_writer();
|
|
37941
37986
|
});
|
|
37942
37987
|
|
|
37988
|
+
// src/operations/apply-test-edit-declarations.ts
|
|
37989
|
+
function applyTestEditDeclarations(findings, declarations, story, invalidMockStructure) {
|
|
37990
|
+
let result = [...findings];
|
|
37991
|
+
const advisories = [];
|
|
37992
|
+
for (const d of declarations) {
|
|
37993
|
+
if (d.reason === "prd_contract") {
|
|
37994
|
+
const prdQuote = d.prdQuote ?? "";
|
|
37995
|
+
const valid = validatePrdQuote(prdQuote, story);
|
|
37996
|
+
if (valid) {
|
|
37997
|
+
result = result.map((f) => {
|
|
37998
|
+
if (f.file === d.file && f.fixTarget === "source") {
|
|
37999
|
+
return {
|
|
38000
|
+
...f,
|
|
38001
|
+
fixTarget: "test",
|
|
38002
|
+
meta: {
|
|
38003
|
+
...f.meta,
|
|
38004
|
+
prdContractDeclaration: d
|
|
38005
|
+
}
|
|
38006
|
+
};
|
|
38007
|
+
}
|
|
38008
|
+
return f;
|
|
38009
|
+
});
|
|
38010
|
+
} else {
|
|
38011
|
+
advisories.push({
|
|
38012
|
+
source: "autofix",
|
|
38013
|
+
severity: "warning",
|
|
38014
|
+
category: "prd_quote_mismatch",
|
|
38015
|
+
message: `PRD quote not found verbatim in story text for file: ${d.file}`,
|
|
38016
|
+
file: d.file,
|
|
38017
|
+
fixTarget: "source"
|
|
38018
|
+
});
|
|
38019
|
+
}
|
|
38020
|
+
}
|
|
38021
|
+
}
|
|
38022
|
+
if (invalidMockStructure && invalidMockStructure.length > 0) {
|
|
38023
|
+
for (const d of invalidMockStructure) {
|
|
38024
|
+
const fileList = (d.files ?? [d.file]).join(", ");
|
|
38025
|
+
advisories.push({
|
|
38026
|
+
source: "autofix",
|
|
38027
|
+
severity: "warning",
|
|
38028
|
+
category: "mock_structure_invalid_files",
|
|
38029
|
+
message: `Mock structure handoff references file that does not exist or is not a test file: ${fileList}`,
|
|
38030
|
+
fixTarget: "source"
|
|
38031
|
+
});
|
|
38032
|
+
}
|
|
38033
|
+
}
|
|
38034
|
+
return [...result, ...advisories];
|
|
38035
|
+
}
|
|
38036
|
+
var init_apply_test_edit_declarations = __esm(() => {
|
|
38037
|
+
init_test_edit_declaration();
|
|
38038
|
+
});
|
|
38039
|
+
|
|
38040
|
+
// src/operations/validate-mock-structure-files.ts
|
|
38041
|
+
import { join as join23 } from "path";
|
|
38042
|
+
async function validateMockStructureFiles(declarations, resolvedTestPatterns, packageDir, deps) {
|
|
38043
|
+
const fileExists = deps?.fileExists ?? defaultFileExists;
|
|
38044
|
+
const valid = [];
|
|
38045
|
+
const invalid = [];
|
|
38046
|
+
for (const d of declarations) {
|
|
38047
|
+
if (d.reason !== "mock_structure") {
|
|
38048
|
+
valid.push(d);
|
|
38049
|
+
continue;
|
|
38050
|
+
}
|
|
38051
|
+
const files = d.files ?? [d.file];
|
|
38052
|
+
let allValid = true;
|
|
38053
|
+
for (const file3 of files) {
|
|
38054
|
+
const absolutePath = join23(packageDir, file3);
|
|
38055
|
+
const exists = await fileExists(absolutePath);
|
|
38056
|
+
if (!exists) {
|
|
38057
|
+
allValid = false;
|
|
38058
|
+
break;
|
|
38059
|
+
}
|
|
38060
|
+
const matchesPattern = resolvedTestPatterns.regex.some((re) => re.test(file3));
|
|
38061
|
+
if (!matchesPattern) {
|
|
38062
|
+
allValid = false;
|
|
38063
|
+
break;
|
|
38064
|
+
}
|
|
38065
|
+
}
|
|
38066
|
+
if (allValid) {
|
|
38067
|
+
valid.push(d);
|
|
38068
|
+
} else {
|
|
38069
|
+
invalid.push(d);
|
|
38070
|
+
}
|
|
38071
|
+
}
|
|
38072
|
+
return { valid, invalid };
|
|
38073
|
+
}
|
|
38074
|
+
var defaultFileExists = (p) => Bun.file(p).exists();
|
|
38075
|
+
var init_validate_mock_structure_files = () => {};
|
|
38076
|
+
|
|
38077
|
+
// src/operations/declaration-sink.ts
|
|
38078
|
+
function makeDeclarationSink() {
|
|
38079
|
+
return { testEdits: [], mockHandoffs: [] };
|
|
38080
|
+
}
|
|
38081
|
+
|
|
37943
38082
|
// src/operations/mechanical-lintfix-strategy.ts
|
|
37944
38083
|
function shellQuotePath2(path5) {
|
|
37945
38084
|
return `'${path5.replaceAll("'", `'\\''`)}'`;
|
|
@@ -38420,6 +38559,8 @@ var init_operations = __esm(() => {
|
|
|
38420
38559
|
init_full_suite_rectify();
|
|
38421
38560
|
init_autofix_implementer_strategy();
|
|
38422
38561
|
init_autofix_test_writer_strategy();
|
|
38562
|
+
init_apply_test_edit_declarations();
|
|
38563
|
+
init_validate_mock_structure_files();
|
|
38423
38564
|
init__finding_to_check();
|
|
38424
38565
|
init_mechanical_lintfix_strategy();
|
|
38425
38566
|
init_mechanical_formatfix_strategy();
|
|
@@ -39070,7 +39211,7 @@ var init_lint_parsing = __esm(() => {
|
|
|
39070
39211
|
});
|
|
39071
39212
|
|
|
39072
39213
|
// src/review/scoped-lint.ts
|
|
39073
|
-
import { join as
|
|
39214
|
+
import { join as join24, relative as relative10 } from "path";
|
|
39074
39215
|
function shellQuotePath4(path5) {
|
|
39075
39216
|
return `'${path5.replaceAll("'", "'\\''")}'`;
|
|
39076
39217
|
}
|
|
@@ -39118,7 +39259,7 @@ function uniqueFiles(files) {
|
|
|
39118
39259
|
async function filterFilesToScope(files, workdir, projectDir, activePackageDir) {
|
|
39119
39260
|
const inScope = [];
|
|
39120
39261
|
for (const relPath of files) {
|
|
39121
|
-
const absPath =
|
|
39262
|
+
const absPath = join24(workdir, relPath);
|
|
39122
39263
|
const exists = await _scopedLintDeps.fileExists(absPath);
|
|
39123
39264
|
if (!exists)
|
|
39124
39265
|
continue;
|
|
@@ -40384,9 +40525,34 @@ var init_review = __esm(() => {
|
|
|
40384
40525
|
});
|
|
40385
40526
|
|
|
40386
40527
|
// src/prompts/builders/rectifier-builder-helpers.ts
|
|
40528
|
+
function buildEscapeHatch(opts) {
|
|
40529
|
+
const exceptions = [EXCEPTION_1_LINT_ONLY, EXCEPTION_2_PRD_CONTRACT, EXCEPTION_3_SIBLING_SCOPE];
|
|
40530
|
+
if (opts.includeMockHandoff)
|
|
40531
|
+
exceptions.push(EXCEPTION_4_MOCK_HANDOFF);
|
|
40532
|
+
const count = exceptions.length;
|
|
40533
|
+
const countWord = ["zero", "one", "two", "three", "four"][count];
|
|
40534
|
+
return `
|
|
40535
|
+
If two findings in this list contradict each other and you cannot satisfy both, do not guess.
|
|
40536
|
+
Emit fixes for defects you can resolve, then output a line in this exact format:
|
|
40537
|
+
UNRESOLVED: <brief explanation of which findings conflicted and why they cannot both be satisfied>
|
|
40538
|
+
|
|
40539
|
+
Before emitting UNRESOLVED, confirm none of Exceptions 1\u2013${count} apply.
|
|
40540
|
+
|
|
40541
|
+
## Test-file edit exceptions
|
|
40542
|
+
|
|
40543
|
+
The "do not modify test files" rule has ${countWord} narrow escape valves. Each requires a
|
|
40544
|
+
declaration in your output. Outside these ${countWord} cases the rule is absolute.
|
|
40545
|
+
|
|
40546
|
+
${exceptions.join(`
|
|
40547
|
+
|
|
40548
|
+
`)}`;
|
|
40549
|
+
}
|
|
40550
|
+
function exceptionCountWord(story) {
|
|
40551
|
+
return THREE_SESSION_STRATEGIES.has(story.routing?.testStrategy ?? "") ? "four" : "three";
|
|
40552
|
+
}
|
|
40387
40553
|
function escapeHatchFor(story) {
|
|
40388
40554
|
const isTdd = THREE_SESSION_STRATEGIES.has(story.routing?.testStrategy ?? "");
|
|
40389
|
-
return
|
|
40555
|
+
return buildEscapeHatch({ includeMockHandoff: isTdd });
|
|
40390
40556
|
}
|
|
40391
40557
|
function noTestIsolationBlock(story) {
|
|
40392
40558
|
if (story.routing?.testStrategy !== "no-test")
|
|
@@ -40456,7 +40622,7 @@ ${errors3}
|
|
|
40456
40622
|
2. Only fix findings that are actually valid problems
|
|
40457
40623
|
3. Do NOT add keys, functions, or imports that already exist \u2014 check first
|
|
40458
40624
|
|
|
40459
|
-
Do NOT change test files or test behavior \u2014 see the
|
|
40625
|
+
Do NOT change test files or test behavior \u2014 see the ${exceptionCountWord(story)} narrow exceptions appended below.
|
|
40460
40626
|
Do NOT add new features \u2014 only fix valid issues.
|
|
40461
40627
|
Commit your fixes when done.${scopeConstraint}${noTestIsolationBlock(story)}${escapeHatchFor(story)}`;
|
|
40462
40628
|
}
|
|
@@ -40518,21 +40684,11 @@ The following quality checks failed after implementation:
|
|
|
40518
40684
|
|
|
40519
40685
|
${errors3}
|
|
40520
40686
|
|
|
40521
|
-
Fix all errors listed above that are within this story's scope \u2014 see the
|
|
40687
|
+
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
40688
|
Do NOT add new features \u2014 only fix the quality check errors.
|
|
40523
40689
|
After fixing, re-run the failing check(s) to verify they pass, then commit your changes.${scopeConstraint}${noTestIsolationBlock(story)}${escapeHatchFor(story)}`;
|
|
40524
40690
|
}
|
|
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
|
|
40691
|
+
var EXCEPTION_1_LINT_ONLY = `### Exception 1 \u2014 Lint-only edit
|
|
40536
40692
|
|
|
40537
40693
|
You MAY edit a test file ONLY when ALL of the following hold:
|
|
40538
40694
|
- The failing check is \`lint\` \u2014 not \`test\`, \`typecheck\`, \`semantic\`, or \`adversarial\`.
|
|
@@ -40548,9 +40704,7 @@ TEST_EDIT_REASON: lint_only
|
|
|
40548
40704
|
FILE: <test file path>
|
|
40549
40705
|
FINDING: <lint rule or message verbatim>
|
|
40550
40706
|
CHANGE: <before line> \u2192 <after line>
|
|
40551
|
-
|
|
40552
|
-
|
|
40553
|
-
### Exception 2 \u2014 PRD-contract mismatch
|
|
40707
|
+
\`\`\``, EXCEPTION_2_PRD_CONTRACT = `### Exception 2 \u2014 PRD-contract mismatch
|
|
40554
40708
|
|
|
40555
40709
|
You MAY correct a test's argument arity, type, or return-handling ONLY when the test's
|
|
40556
40710
|
call contradicts a literal interface signature stated in this story's description or
|
|
@@ -40566,9 +40720,7 @@ TEST_AFTER: <corrected call line>
|
|
|
40566
40720
|
\`\`\`
|
|
40567
40721
|
|
|
40568
40722
|
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
|
|
40723
|
+
signatures that directly contradict a quoted PRD interface.`, EXCEPTION_3_SIBLING_SCOPE = `### Exception 3 \u2014 Unrelated sibling spillover
|
|
40572
40724
|
|
|
40573
40725
|
When a lint or typecheck error is outside this story's intended scope, do NOT edit that
|
|
40574
40726
|
file. If the smallest package-local fix is required to satisfy this story's acceptance
|
|
@@ -40578,46 +40730,37 @@ TEST_EDIT_REASON: sibling_scope
|
|
|
40578
40730
|
SIBLING_FILE: <file path>
|
|
40579
40731
|
FINDING: <error summary>
|
|
40580
40732
|
\`\`\`
|
|
40581
|
-
and continue. Sibling-scope failures do not block your story
|
|
40582
|
-
|
|
40583
|
-
### Exception 4 \u2014 Mock-structure handoff
|
|
40733
|
+
and continue. Sibling-scope failures do not block your story.`, EXCEPTION_4_MOCK_HANDOFF = `### Exception 4 \u2014 Mock-structure handoff
|
|
40584
40734
|
|
|
40585
40735
|
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.
|
|
40736
|
+
that does NOT fit Exception 2. Two cases qualify:
|
|
40588
40737
|
|
|
40589
|
-
|
|
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
|
-
\`\`\`
|
|
40738
|
+
(a) Existing mocks are wrong \u2014 mocks reference primitives the new code bypasses,
|
|
40739
|
+
or assertion topology must change to match a new dispatch shape.
|
|
40595
40740
|
|
|
40596
|
-
|
|
40597
|
-
-
|
|
40598
|
-
-
|
|
40599
|
-
-
|
|
40600
|
-
|
|
40601
|
-
|
|
40602
|
-
Use ONLY when the only path to satisfy the ACs requires a structural test rewrite
|
|
40603
|
-
that does NOT fit Exception 2. Examples: mocks reference primitives the new code
|
|
40604
|
-
bypasses; assertion topology must change to match a new dispatch shape.
|
|
40741
|
+
(b) Required test-infrastructure does not yet exist and must be introduced \u2014
|
|
40742
|
+
e.g. in-process fake servers, network-level request interception, hermetic
|
|
40743
|
+
fixture-backed HTTP, or equivalent. Applies whenever the AC describes a
|
|
40744
|
+
hermetic/fixture-backed test surface that the current test setup cannot
|
|
40745
|
+
satisfy without new infrastructure.
|
|
40605
40746
|
|
|
40606
40747
|
Declare with:
|
|
40607
40748
|
\`\`\`
|
|
40608
40749
|
TEST_EDIT_REASON: mock_structure
|
|
40609
40750
|
FILES: <comma-separated test file paths>
|
|
40610
|
-
REASON: <one paragraph: which mock is wrong vs which dispatch the new code uses
|
|
40751
|
+
REASON: <one paragraph: which mock is wrong vs which dispatch the new code uses,
|
|
40752
|
+
or what infrastructure must be introduced>
|
|
40611
40753
|
\`\`\`
|
|
40612
40754
|
|
|
40613
40755
|
Rules:
|
|
40614
40756
|
- Do NOT make any edits yourself; the test-writer will fulfill.
|
|
40615
40757
|
- 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;
|
|
40758
|
+
- 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
40759
|
var init_rectifier_builder_helpers = __esm(() => {
|
|
40618
40760
|
init_review();
|
|
40619
40761
|
init_sections2();
|
|
40620
40762
|
THREE_SESSION_STRATEGIES = new Set(["three-session-tdd", "three-session-tdd-lite"]);
|
|
40763
|
+
CONTRADICTION_ESCAPE_HATCH = buildEscapeHatch({ includeMockHandoff: false });
|
|
40621
40764
|
});
|
|
40622
40765
|
|
|
40623
40766
|
// src/prompts/builders/rectifier-builder.ts
|
|
@@ -40698,15 +40841,16 @@ function renderPrioritizedFailures(failedChecks, opts) {
|
|
|
40698
40841
|
}
|
|
40699
40842
|
|
|
40700
40843
|
class RectifierPromptBuilder {
|
|
40701
|
-
static firstAttemptDelta(failedChecks, maxAttempts, guardrailLevel) {
|
|
40844
|
+
static firstAttemptDelta(failedChecks, maxAttempts, guardrailLevel, story) {
|
|
40702
40845
|
const parts = [];
|
|
40703
40846
|
const attemptWord = maxAttempts === 1 ? "1 attempt" : `${maxAttempts} attempts`;
|
|
40847
|
+
const exCount = story ? exceptionCountWord(story) : "three";
|
|
40704
40848
|
parts.push(`Review failed after your implementation. Fix the following issues (${attemptWord} available before escalation):
|
|
40705
40849
|
`);
|
|
40706
40850
|
parts.push(renderPrioritizedFailures(failedChecks));
|
|
40707
40851
|
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);
|
|
40852
|
+
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.`);
|
|
40853
|
+
parts.push(story ? escapeHatchFor(story) : CONTRADICTION_ESCAPE_HATCH);
|
|
40710
40854
|
const guardrails = buildBehavioralGuardrailsSection("implementer", guardrailLevel ?? "lite");
|
|
40711
40855
|
if (guardrails) {
|
|
40712
40856
|
parts.push(`
|
|
@@ -40716,7 +40860,7 @@ ${guardrails}`);
|
|
|
40716
40860
|
return parts.join(`
|
|
40717
40861
|
`);
|
|
40718
40862
|
}
|
|
40719
|
-
static continuation(failedChecks, attempt, rethinkAtAttempt, urgencyAtAttempt, guardrailLevel) {
|
|
40863
|
+
static continuation(failedChecks, attempt, rethinkAtAttempt, urgencyAtAttempt, guardrailLevel, story) {
|
|
40720
40864
|
const parts = [];
|
|
40721
40865
|
parts.push(`Your previous fix attempt did not resolve all issues. Here are the remaining failures:
|
|
40722
40866
|
`);
|
|
@@ -40729,7 +40873,7 @@ ${guardrails}`);
|
|
|
40729
40873
|
if (attempt >= urgencyAtAttempt) {
|
|
40730
40874
|
parts.push("\n**URGENT: This is your final attempt.** If you cannot fix all issues, emit `UNRESOLVED: <reason>` to escalate.\n");
|
|
40731
40875
|
}
|
|
40732
|
-
parts.push(CONTRADICTION_ESCAPE_HATCH);
|
|
40876
|
+
parts.push(story ? escapeHatchFor(story) : CONTRADICTION_ESCAPE_HATCH);
|
|
40733
40877
|
const guardrails = buildBehavioralGuardrailsSection("implementer", guardrailLevel ?? "lite");
|
|
40734
40878
|
if (guardrails) {
|
|
40735
40879
|
parts.push(`
|
|
@@ -40851,7 +40995,7 @@ ${importantNote}
|
|
|
40851
40995
|
|
|
40852
40996
|
Commit your fixes when done.${scopeConstraint}`;
|
|
40853
40997
|
}
|
|
40854
|
-
static noOpReprompt(failedChecks, noOpCount, maxNoOpReprompts, opts) {
|
|
40998
|
+
static noOpReprompt(failedChecks, noOpCount, maxNoOpReprompts, opts, story) {
|
|
40855
40999
|
const parts = [];
|
|
40856
41000
|
parts.push(`**Your previous turn produced no committed file changes.**
|
|
40857
41001
|
|
|
@@ -40892,7 +41036,7 @@ ${output}
|
|
|
40892
41036
|
`);
|
|
40893
41037
|
}
|
|
40894
41038
|
}
|
|
40895
|
-
parts.push(CONTRADICTION_ESCAPE_HATCH);
|
|
41039
|
+
parts.push(story ? escapeHatchFor(story) : CONTRADICTION_ESCAPE_HATCH);
|
|
40896
41040
|
return parts.join("");
|
|
40897
41041
|
}
|
|
40898
41042
|
static escalated(failures, story, priorAttempts, originalTier, targetTier, config2, testCommand, testScopedTemplate) {
|
|
@@ -40968,11 +41112,11 @@ ${testCommands}
|
|
|
40968
41112
|
6. Ensure ALL tests pass before completing.
|
|
40969
41113
|
|
|
40970
41114
|
**IMPORTANT:**
|
|
40971
|
-
- Do NOT modify test files \u2014 see the
|
|
41115
|
+
- 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
41116
|
- Do NOT loosen assertions to mask implementation bugs.
|
|
40973
41117
|
- Focus on fixing the source code to meet the test requirements.
|
|
40974
41118
|
- 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
|
-
`;
|
|
41119
|
+
${escapeHatchFor(story)}`;
|
|
40976
41120
|
}
|
|
40977
41121
|
static reviewRectification(failedChecks, story, opts) {
|
|
40978
41122
|
const scopeConstraint = story.workdir ? `
|
|
@@ -41029,7 +41173,7 @@ ${llmSection}
|
|
|
41029
41173
|
**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
41174
|
|
|
41031
41175
|
Do NOT add new features \u2014 only fix the identified issues.
|
|
41032
|
-
Commit your fixes when done.${scopeConstraint}${
|
|
41176
|
+
Commit your fixes when done.${scopeConstraint}${escapeHatchFor(story)}`;
|
|
41033
41177
|
}
|
|
41034
41178
|
static dialogueAwareRectification(failedChecks, story, opts) {
|
|
41035
41179
|
const scopeConstraint = story.workdir ? `
|
|
@@ -41068,9 +41212,9 @@ ${errors3}${reasoningSection}${historySection}
|
|
|
41068
41212
|
2. Only fix findings that are actually valid problems
|
|
41069
41213
|
3. Do NOT add keys, functions, or imports that already exist \u2014 check first
|
|
41070
41214
|
|
|
41071
|
-
Do NOT change test files or test behavior \u2014 see the
|
|
41215
|
+
Do NOT change test files or test behavior \u2014 see the ${exceptionCountWord(story)} narrow exceptions appended below.
|
|
41072
41216
|
Do NOT add new features \u2014 only fix valid issues.
|
|
41073
|
-
Commit your fixes when done.${scopeConstraint}${
|
|
41217
|
+
Commit your fixes when done.${scopeConstraint}${escapeHatchFor(story)}`;
|
|
41074
41218
|
}
|
|
41075
41219
|
static swapHandoff(basePrompt, pushMarkdown) {
|
|
41076
41220
|
const trimmed = pushMarkdown?.trim();
|
|
@@ -41162,9 +41306,10 @@ Tests are failing. Fix the source so all tests pass \u2014 not just the ones lis
|
|
|
41162
41306
|
4. Do not declare done until step 3 shows 0 failures.
|
|
41163
41307
|
|
|
41164
41308
|
**IMPORTANT:**
|
|
41165
|
-
- Do NOT modify test files \u2014 see the
|
|
41309
|
+
- 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
41310
|
- Do NOT loosen assertions to mask implementation bugs.
|
|
41167
41311
|
- Focus on fixing the source code to meet the test requirements.`);
|
|
41312
|
+
parts.push(escapeHatchFor(opts.story));
|
|
41168
41313
|
return parts.join("");
|
|
41169
41314
|
}
|
|
41170
41315
|
static failingTestContext(findings) {
|
|
@@ -42498,7 +42643,7 @@ var init_call = __esm(() => {
|
|
|
42498
42643
|
|
|
42499
42644
|
// src/runtime/cost-aggregator.ts
|
|
42500
42645
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
42501
|
-
import { join as
|
|
42646
|
+
import { join as join25 } from "path";
|
|
42502
42647
|
function makeCorrelationId() {
|
|
42503
42648
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
42504
42649
|
}
|
|
@@ -42689,7 +42834,7 @@ class CostAggregator {
|
|
|
42689
42834
|
if (events.length === 0 && errors3.length === 0)
|
|
42690
42835
|
return;
|
|
42691
42836
|
mkdirSync2(this._drainDir, { recursive: true });
|
|
42692
|
-
const path5 =
|
|
42837
|
+
const path5 = join25(this._drainDir, `${this._runId}.jsonl`);
|
|
42693
42838
|
const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
|
|
42694
42839
|
await _costAggDeps.write(path5, `${sorted.map((e) => JSON.stringify(e)).join(`
|
|
42695
42840
|
`)}
|
|
@@ -42729,7 +42874,7 @@ var init_cost_aggregator = __esm(() => {
|
|
|
42729
42874
|
// src/runtime/prompt-auditor.ts
|
|
42730
42875
|
import { appendFileSync } from "fs";
|
|
42731
42876
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
42732
|
-
import { join as
|
|
42877
|
+
import { join as join26 } from "path";
|
|
42733
42878
|
function createNoOpPromptAuditor() {
|
|
42734
42879
|
return {
|
|
42735
42880
|
record() {},
|
|
@@ -42795,8 +42940,8 @@ class PromptAuditor {
|
|
|
42795
42940
|
_jsonlPath;
|
|
42796
42941
|
_featureDir;
|
|
42797
42942
|
constructor(runId, flushDir, featureName) {
|
|
42798
|
-
this._featureDir =
|
|
42799
|
-
this._jsonlPath =
|
|
42943
|
+
this._featureDir = join26(flushDir, featureName);
|
|
42944
|
+
this._jsonlPath = join26(this._featureDir, `${runId}.jsonl`);
|
|
42800
42945
|
}
|
|
42801
42946
|
record(entry) {
|
|
42802
42947
|
this._enqueue(entry);
|
|
@@ -42845,7 +42990,7 @@ class PromptAuditor {
|
|
|
42845
42990
|
const auditEntry = entry;
|
|
42846
42991
|
const filename = deriveTxtFilename(auditEntry);
|
|
42847
42992
|
try {
|
|
42848
|
-
await _promptAuditorDeps.write(
|
|
42993
|
+
await _promptAuditorDeps.write(join26(this._featureDir, filename), buildTxtContent(auditEntry));
|
|
42849
42994
|
} catch (err) {
|
|
42850
42995
|
throw tagAuditError(err, "txt");
|
|
42851
42996
|
}
|
|
@@ -43933,7 +44078,7 @@ var init_pid_registry = __esm(() => {
|
|
|
43933
44078
|
// src/session/manager-deps.ts
|
|
43934
44079
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
43935
44080
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
43936
|
-
import { isAbsolute as isAbsolute9, join as
|
|
44081
|
+
import { isAbsolute as isAbsolute9, join as join27, relative as relative12, sep as sep3 } from "path";
|
|
43937
44082
|
function resolveProjectDirFromScratchDir(scratchDir) {
|
|
43938
44083
|
const marker = `${sep3}.nax${sep3}features${sep3}`;
|
|
43939
44084
|
const markerIdx = scratchDir.lastIndexOf(marker);
|
|
@@ -43954,7 +44099,7 @@ var init_manager_deps = __esm(() => {
|
|
|
43954
44099
|
now: () => new Date().toISOString(),
|
|
43955
44100
|
nowMs: () => Date.now(),
|
|
43956
44101
|
uuid: () => randomUUID3(),
|
|
43957
|
-
sessionScratchDir: (projectDir, featureName, sessionId) =>
|
|
44102
|
+
sessionScratchDir: (projectDir, featureName, sessionId) => join27(projectDir, ".nax", "features", featureName, "sessions", sessionId),
|
|
43958
44103
|
writeDescriptor: async (scratchDir, descriptor, projectDir) => {
|
|
43959
44104
|
await mkdir5(scratchDir, { recursive: true });
|
|
43960
44105
|
const { handle: _handle, ...persistable } = descriptor;
|
|
@@ -43965,7 +44110,7 @@ var init_manager_deps = __esm(() => {
|
|
|
43965
44110
|
persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
|
|
43966
44111
|
}
|
|
43967
44112
|
}
|
|
43968
|
-
await Bun.write(
|
|
44113
|
+
await Bun.write(join27(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
|
|
43969
44114
|
}
|
|
43970
44115
|
};
|
|
43971
44116
|
});
|
|
@@ -44716,7 +44861,7 @@ __export(exports_runtime, {
|
|
|
44716
44861
|
CostAggregator: () => CostAggregator,
|
|
44717
44862
|
AgentStreamEventBus: () => AgentStreamEventBus
|
|
44718
44863
|
});
|
|
44719
|
-
import { basename as basename5, join as
|
|
44864
|
+
import { basename as basename5, join as join28 } from "path";
|
|
44720
44865
|
function createRuntime(config2, workdir, opts) {
|
|
44721
44866
|
const runId = crypto.randomUUID();
|
|
44722
44867
|
const controller = new AbortController;
|
|
@@ -44732,10 +44877,10 @@ function createRuntime(config2, workdir, opts) {
|
|
|
44732
44877
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
44733
44878
|
const globalDir = globalOutputDir();
|
|
44734
44879
|
const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
|
|
44735
|
-
const costDir =
|
|
44880
|
+
const costDir = join28(outputDir, "cost");
|
|
44736
44881
|
const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
|
|
44737
44882
|
const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
|
|
44738
|
-
const auditDir = config2.agent?.promptAudit?.dir ??
|
|
44883
|
+
const auditDir = config2.agent?.promptAudit?.dir ?? join28(outputDir, "prompt-audit");
|
|
44739
44884
|
let promptAuditor;
|
|
44740
44885
|
if (opts?.promptAuditor) {
|
|
44741
44886
|
promptAuditor = opts.promptAuditor;
|
|
@@ -44886,9 +45031,9 @@ async function allSettledBounded(tasks, limit) {
|
|
|
44886
45031
|
|
|
44887
45032
|
// src/context/injector.ts
|
|
44888
45033
|
import { existsSync as existsSync8 } from "fs";
|
|
44889
|
-
import { join as
|
|
45034
|
+
import { join as join29 } from "path";
|
|
44890
45035
|
async function detectNode(workdir) {
|
|
44891
|
-
const pkgPath =
|
|
45036
|
+
const pkgPath = join29(workdir, "package.json");
|
|
44892
45037
|
if (!existsSync8(pkgPath))
|
|
44893
45038
|
return null;
|
|
44894
45039
|
try {
|
|
@@ -44905,7 +45050,7 @@ async function detectNode(workdir) {
|
|
|
44905
45050
|
}
|
|
44906
45051
|
}
|
|
44907
45052
|
async function detectGo(workdir) {
|
|
44908
|
-
const goMod =
|
|
45053
|
+
const goMod = join29(workdir, "go.mod");
|
|
44909
45054
|
if (!existsSync8(goMod))
|
|
44910
45055
|
return null;
|
|
44911
45056
|
try {
|
|
@@ -44929,7 +45074,7 @@ async function detectGo(workdir) {
|
|
|
44929
45074
|
}
|
|
44930
45075
|
}
|
|
44931
45076
|
async function detectRust(workdir) {
|
|
44932
|
-
const cargoPath =
|
|
45077
|
+
const cargoPath = join29(workdir, "Cargo.toml");
|
|
44933
45078
|
if (!existsSync8(cargoPath))
|
|
44934
45079
|
return null;
|
|
44935
45080
|
try {
|
|
@@ -44945,8 +45090,8 @@ async function detectRust(workdir) {
|
|
|
44945
45090
|
}
|
|
44946
45091
|
}
|
|
44947
45092
|
async function detectPython(workdir) {
|
|
44948
|
-
const pyproject =
|
|
44949
|
-
const requirements =
|
|
45093
|
+
const pyproject = join29(workdir, "pyproject.toml");
|
|
45094
|
+
const requirements = join29(workdir, "requirements.txt");
|
|
44950
45095
|
if (!existsSync8(pyproject) && !existsSync8(requirements))
|
|
44951
45096
|
return null;
|
|
44952
45097
|
try {
|
|
@@ -44965,7 +45110,7 @@ async function detectPython(workdir) {
|
|
|
44965
45110
|
}
|
|
44966
45111
|
}
|
|
44967
45112
|
async function detectPhp(workdir) {
|
|
44968
|
-
const composerPath =
|
|
45113
|
+
const composerPath = join29(workdir, "composer.json");
|
|
44969
45114
|
if (!existsSync8(composerPath))
|
|
44970
45115
|
return null;
|
|
44971
45116
|
try {
|
|
@@ -44978,7 +45123,7 @@ async function detectPhp(workdir) {
|
|
|
44978
45123
|
}
|
|
44979
45124
|
}
|
|
44980
45125
|
async function detectRuby(workdir) {
|
|
44981
|
-
const gemfile =
|
|
45126
|
+
const gemfile = join29(workdir, "Gemfile");
|
|
44982
45127
|
if (!existsSync8(gemfile))
|
|
44983
45128
|
return null;
|
|
44984
45129
|
try {
|
|
@@ -44990,9 +45135,9 @@ async function detectRuby(workdir) {
|
|
|
44990
45135
|
}
|
|
44991
45136
|
}
|
|
44992
45137
|
async function detectJvm(workdir) {
|
|
44993
|
-
const pom =
|
|
44994
|
-
const gradle =
|
|
44995
|
-
const gradleKts =
|
|
45138
|
+
const pom = join29(workdir, "pom.xml");
|
|
45139
|
+
const gradle = join29(workdir, "build.gradle");
|
|
45140
|
+
const gradleKts = join29(workdir, "build.gradle.kts");
|
|
44996
45141
|
if (!existsSync8(pom) && !existsSync8(gradle) && !existsSync8(gradleKts))
|
|
44997
45142
|
return null;
|
|
44998
45143
|
try {
|
|
@@ -45000,7 +45145,7 @@ async function detectJvm(workdir) {
|
|
|
45000
45145
|
const content2 = await Bun.file(pom).text();
|
|
45001
45146
|
const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
45002
45147
|
const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
|
|
45003
|
-
const lang2 = existsSync8(
|
|
45148
|
+
const lang2 = existsSync8(join29(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
|
|
45004
45149
|
return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
|
|
45005
45150
|
}
|
|
45006
45151
|
const gradleFile = existsSync8(gradleKts) ? gradleKts : gradle;
|
|
@@ -45254,7 +45399,7 @@ var init_windsurf = __esm(() => {
|
|
|
45254
45399
|
|
|
45255
45400
|
// src/context/generator.ts
|
|
45256
45401
|
import { existsSync as existsSync9 } from "fs";
|
|
45257
|
-
import { join as
|
|
45402
|
+
import { join as join30, relative as relative13 } from "path";
|
|
45258
45403
|
async function loadContextContent(options, config2) {
|
|
45259
45404
|
if (!_generatorDeps.existsSync(options.contextPath)) {
|
|
45260
45405
|
throw new Error(`Context file not found: ${options.contextPath}`);
|
|
@@ -45272,7 +45417,7 @@ async function generateFor(agent, options, config2) {
|
|
|
45272
45417
|
try {
|
|
45273
45418
|
const context = await loadContextContent(options, config2);
|
|
45274
45419
|
const content = generator.generate(context);
|
|
45275
|
-
const outputPath =
|
|
45420
|
+
const outputPath = join30(options.outputDir, generator.outputFile);
|
|
45276
45421
|
validateFilePath(outputPath, options.outputDir);
|
|
45277
45422
|
if (!options.dryRun) {
|
|
45278
45423
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -45290,7 +45435,7 @@ async function generateAll(options, config2, agentFilter) {
|
|
|
45290
45435
|
for (const [agentKey, generator] of entries) {
|
|
45291
45436
|
try {
|
|
45292
45437
|
const content = generator.generate(context);
|
|
45293
|
-
const outputPath =
|
|
45438
|
+
const outputPath = join30(options.outputDir, generator.outputFile);
|
|
45294
45439
|
validateFilePath(outputPath, options.outputDir);
|
|
45295
45440
|
if (!options.dryRun) {
|
|
45296
45441
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -45310,7 +45455,7 @@ async function discoverPackages(repoRoot) {
|
|
|
45310
45455
|
const glob = new Bun.Glob(pattern);
|
|
45311
45456
|
for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
|
|
45312
45457
|
const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
|
|
45313
|
-
const pkgAbsolute =
|
|
45458
|
+
const pkgAbsolute = join30(repoRoot, pkgRelative);
|
|
45314
45459
|
if (!seen.has(pkgAbsolute)) {
|
|
45315
45460
|
seen.add(pkgAbsolute);
|
|
45316
45461
|
packages.push(pkgAbsolute);
|
|
@@ -45342,14 +45487,14 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
45342
45487
|
}
|
|
45343
45488
|
}
|
|
45344
45489
|
}
|
|
45345
|
-
const turboPath =
|
|
45490
|
+
const turboPath = join30(repoRoot, "turbo.json");
|
|
45346
45491
|
try {
|
|
45347
45492
|
const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
|
|
45348
45493
|
if (Array.isArray(turbo.packages)) {
|
|
45349
45494
|
await resolveGlobs(turbo.packages);
|
|
45350
45495
|
}
|
|
45351
45496
|
} catch {}
|
|
45352
|
-
const pkgPath =
|
|
45497
|
+
const pkgPath = join30(repoRoot, "package.json");
|
|
45353
45498
|
try {
|
|
45354
45499
|
const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
|
|
45355
45500
|
const ws = pkg.workspaces;
|
|
@@ -45357,7 +45502,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
45357
45502
|
if (patterns.length > 0)
|
|
45358
45503
|
await resolveGlobs(patterns);
|
|
45359
45504
|
} catch {}
|
|
45360
|
-
const pnpmPath =
|
|
45505
|
+
const pnpmPath = join30(repoRoot, "pnpm-workspace.yaml");
|
|
45361
45506
|
try {
|
|
45362
45507
|
const raw = await _generatorDeps.readTextFile(pnpmPath);
|
|
45363
45508
|
const lines = raw.split(`
|
|
@@ -45383,7 +45528,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
45383
45528
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
45384
45529
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
45385
45530
|
const relativePkgPath = relative13(resolvedRepoRoot, packageDir);
|
|
45386
|
-
const contextPath =
|
|
45531
|
+
const contextPath = join30(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
45387
45532
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
45388
45533
|
return [
|
|
45389
45534
|
{
|
|
@@ -45451,7 +45596,7 @@ var init_generator2 = __esm(() => {
|
|
|
45451
45596
|
});
|
|
45452
45597
|
|
|
45453
45598
|
// src/analyze/scanner.ts
|
|
45454
|
-
import { join as
|
|
45599
|
+
import { join as join31 } from "path";
|
|
45455
45600
|
function resolveFrameworkAndRunner(language, pkg) {
|
|
45456
45601
|
if (language === "go")
|
|
45457
45602
|
return { framework: "", testRunner: "go-test" };
|
|
@@ -45473,7 +45618,7 @@ async function scanSourceRoots(workdir) {
|
|
|
45473
45618
|
});
|
|
45474
45619
|
try {
|
|
45475
45620
|
const language = await deps.detectLanguage(workdir);
|
|
45476
|
-
const pkg = await deps.readPackageJson(
|
|
45621
|
+
const pkg = await deps.readPackageJson(join31(workdir, "package.json"));
|
|
45477
45622
|
const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
|
|
45478
45623
|
return [{ path: ".", language, framework, testRunner }];
|
|
45479
45624
|
} catch {
|
|
@@ -45491,9 +45636,9 @@ async function scanSourceRoots(workdir) {
|
|
|
45491
45636
|
packages = packages.slice(0, MAX_SOURCE_ROOTS);
|
|
45492
45637
|
}
|
|
45493
45638
|
return Promise.all(packages.map(async (pkgPath) => {
|
|
45494
|
-
const pkgDir = pkgPath === "." ? workdir :
|
|
45639
|
+
const pkgDir = pkgPath === "." ? workdir : join31(workdir, pkgPath);
|
|
45495
45640
|
const language = await deps.detectLanguage(pkgDir);
|
|
45496
|
-
const pkg = await deps.readPackageJson(
|
|
45641
|
+
const pkg = await deps.readPackageJson(join31(pkgDir, "package.json"));
|
|
45497
45642
|
const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
|
|
45498
45643
|
return { path: pkgPath, language, framework, testRunner };
|
|
45499
45644
|
}));
|
|
@@ -45526,7 +45671,7 @@ var init_analyze = __esm(() => {
|
|
|
45526
45671
|
});
|
|
45527
45672
|
|
|
45528
45673
|
// src/debate/pre-phase/grounder.ts
|
|
45529
|
-
import { join as
|
|
45674
|
+
import { join as join32 } from "path";
|
|
45530
45675
|
async function buildCodebaseContext(workdir) {
|
|
45531
45676
|
const roots = await _grounderDeps.scanSourceRoots(workdir);
|
|
45532
45677
|
return buildSourceRootsSection(normalizeRoots(workdir, roots));
|
|
@@ -45538,7 +45683,7 @@ function normalizeRoots(workdir, roots) {
|
|
|
45538
45683
|
}));
|
|
45539
45684
|
}
|
|
45540
45685
|
async function writeManifestArtifact(ctx, manifest) {
|
|
45541
|
-
const manifestPath =
|
|
45686
|
+
const manifestPath = join32(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
|
|
45542
45687
|
await _grounderDeps.write(manifestPath, JSON.stringify(manifest, null, 2));
|
|
45543
45688
|
}
|
|
45544
45689
|
var _grounderDeps, grounderStrategy = async (ctx) => {
|
|
@@ -45899,7 +46044,7 @@ function formatSpecDeltas(blockers, manifest) {
|
|
|
45899
46044
|
|
|
45900
46045
|
// src/debate/verifiers/checks.ts
|
|
45901
46046
|
import { existsSync as defaultExistsSync } from "fs";
|
|
45902
|
-
import { join as
|
|
46047
|
+
import { join as join33 } from "path";
|
|
45903
46048
|
function checkFilesExist(prd, workdir, deps) {
|
|
45904
46049
|
const existsSync10 = deps?.existsSync ?? defaultExistsSync;
|
|
45905
46050
|
const findings = [];
|
|
@@ -45909,7 +46054,7 @@ function checkFilesExist(prd, workdir, deps) {
|
|
|
45909
46054
|
for (const entry of story.contextFiles) {
|
|
45910
46055
|
const filePath = typeof entry === "string" ? entry : entry.path;
|
|
45911
46056
|
const factId = typeof entry === "string" ? undefined : entry.factId;
|
|
45912
|
-
const absPath =
|
|
46057
|
+
const absPath = join33(workdir, filePath);
|
|
45913
46058
|
if (existsSync10(absPath))
|
|
45914
46059
|
continue;
|
|
45915
46060
|
if (factId) {
|
|
@@ -46020,7 +46165,7 @@ var init_checks3 = () => {};
|
|
|
46020
46165
|
|
|
46021
46166
|
// src/debate/verifiers/plan-checklist.ts
|
|
46022
46167
|
import { existsSync as existsSync10 } from "fs";
|
|
46023
|
-
import { join as
|
|
46168
|
+
import { join as join34 } from "path";
|
|
46024
46169
|
function parsePrd(output) {
|
|
46025
46170
|
if (!output)
|
|
46026
46171
|
return null;
|
|
@@ -46031,7 +46176,7 @@ function parsePrd(output) {
|
|
|
46031
46176
|
}
|
|
46032
46177
|
}
|
|
46033
46178
|
async function loadManifest(ctx) {
|
|
46034
|
-
const manifestPath =
|
|
46179
|
+
const manifestPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
|
|
46035
46180
|
const raw = await _planChecklistDeps.readFile(manifestPath);
|
|
46036
46181
|
if (!raw)
|
|
46037
46182
|
return null;
|
|
@@ -46044,7 +46189,7 @@ async function loadManifest(ctx) {
|
|
|
46044
46189
|
}
|
|
46045
46190
|
}
|
|
46046
46191
|
async function emitSpecDeltas(ctx, blockers, manifest) {
|
|
46047
|
-
const artifactPath =
|
|
46192
|
+
const artifactPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
|
|
46048
46193
|
const content = formatSpecDeltas(blockers, manifest ?? { repoFacts: [], specClaims: [], gaps: [] });
|
|
46049
46194
|
await _planChecklistDeps.write(artifactPath, content);
|
|
46050
46195
|
return artifactPath;
|
|
@@ -46395,7 +46540,7 @@ var init_runner_plan_helpers = __esm(() => {
|
|
|
46395
46540
|
});
|
|
46396
46541
|
|
|
46397
46542
|
// src/debate/runner-plan.ts
|
|
46398
|
-
import { join as
|
|
46543
|
+
import { join as join35 } from "path";
|
|
46399
46544
|
async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
46400
46545
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
46401
46546
|
const config2 = ctx.stageConfig;
|
|
@@ -46454,7 +46599,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
|
46454
46599
|
sessionMode: ctx.stageConfig.sessionMode ?? "one-shot",
|
|
46455
46600
|
proposers: ctx.stageConfig.proposers
|
|
46456
46601
|
});
|
|
46457
|
-
const outputPaths = resolved.map((_, i) =>
|
|
46602
|
+
const outputPaths = resolved.map((_, i) => join35(opts.outputDir, `prd-debate-${i}.json`));
|
|
46458
46603
|
const successful = [];
|
|
46459
46604
|
let rebuttalList;
|
|
46460
46605
|
if (selectorKind === "verifier-pick") {
|
|
@@ -48702,9 +48847,9 @@ function validateFeatureName(feature) {
|
|
|
48702
48847
|
|
|
48703
48848
|
// src/plan/critic.ts
|
|
48704
48849
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
48705
|
-
import { dirname as dirname7, join as
|
|
48850
|
+
import { dirname as dirname7, join as join38 } from "path";
|
|
48706
48851
|
async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
|
|
48707
|
-
const path7 =
|
|
48852
|
+
const path7 = join38(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
|
|
48708
48853
|
await mkdir6(dirname7(path7), { recursive: true });
|
|
48709
48854
|
await Bun.write(path7, formatSpecDeltas(findings, manifest));
|
|
48710
48855
|
return path7;
|
|
@@ -49917,9 +50062,9 @@ __export(exports_plan_decompose, {
|
|
|
49917
50062
|
runReplanLoop: () => runReplanLoop,
|
|
49918
50063
|
planDecomposeCommand: () => planDecomposeCommand
|
|
49919
50064
|
});
|
|
49920
|
-
import { join as
|
|
50065
|
+
import { join as join39 } from "path";
|
|
49921
50066
|
async function planDecomposeCommand(workdir, config2, options) {
|
|
49922
|
-
const prdPath =
|
|
50067
|
+
const prdPath = join39(workdir, ".nax", "features", options.feature, "prd.json");
|
|
49923
50068
|
if (!_planDeps.existsSync(prdPath)) {
|
|
49924
50069
|
throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
|
|
49925
50070
|
stage: "decompose",
|
|
@@ -50093,7 +50238,7 @@ var init_plan_decompose = __esm(() => {
|
|
|
50093
50238
|
|
|
50094
50239
|
// src/cli/plan-runtime.ts
|
|
50095
50240
|
import { existsSync as existsSync15 } from "fs";
|
|
50096
|
-
import { join as
|
|
50241
|
+
import { join as join40 } from "path";
|
|
50097
50242
|
function isRuntimeWithAgentManager(value) {
|
|
50098
50243
|
return typeof value === "object" && value !== null && "agentManager" in value;
|
|
50099
50244
|
}
|
|
@@ -50145,7 +50290,7 @@ var init_plan_runtime = __esm(() => {
|
|
|
50145
50290
|
writeFile: (path7, content) => Bun.write(path7, content).then(() => {}),
|
|
50146
50291
|
scanSourceRoots: (workdir) => scanSourceRoots(workdir),
|
|
50147
50292
|
createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
|
|
50148
|
-
readPackageJson: (workdir) => Bun.file(
|
|
50293
|
+
readPackageJson: (workdir) => Bun.file(join40(workdir, "package.json")).json().catch(() => null),
|
|
50149
50294
|
spawnSync: (cmd, opts) => {
|
|
50150
50295
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
|
|
50151
50296
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
@@ -50530,7 +50675,7 @@ var init_metrics = __esm(() => {
|
|
|
50530
50675
|
|
|
50531
50676
|
// src/commands/common.ts
|
|
50532
50677
|
import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync3 } from "fs";
|
|
50533
|
-
import { join as
|
|
50678
|
+
import { join as join41, resolve as resolve13 } from "path";
|
|
50534
50679
|
function resolveProject(options = {}) {
|
|
50535
50680
|
const { dir, feature } = options;
|
|
50536
50681
|
let projectRoot;
|
|
@@ -50538,12 +50683,12 @@ function resolveProject(options = {}) {
|
|
|
50538
50683
|
let configPath;
|
|
50539
50684
|
if (dir) {
|
|
50540
50685
|
projectRoot = realpathSync3(resolve13(dir));
|
|
50541
|
-
naxDir =
|
|
50686
|
+
naxDir = join41(projectRoot, ".nax");
|
|
50542
50687
|
if (!existsSync16(naxDir)) {
|
|
50543
50688
|
throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
|
|
50544
50689
|
Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
|
|
50545
50690
|
}
|
|
50546
|
-
configPath =
|
|
50691
|
+
configPath = join41(naxDir, "config.json");
|
|
50547
50692
|
if (!existsSync16(configPath)) {
|
|
50548
50693
|
throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
|
|
50549
50694
|
Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
@@ -50551,17 +50696,17 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
|
50551
50696
|
} else {
|
|
50552
50697
|
const found = findProjectRoot(process.cwd());
|
|
50553
50698
|
if (!found) {
|
|
50554
|
-
const cwdNaxDir =
|
|
50699
|
+
const cwdNaxDir = join41(process.cwd(), ".nax");
|
|
50555
50700
|
if (existsSync16(cwdNaxDir)) {
|
|
50556
|
-
const cwdConfigPath =
|
|
50701
|
+
const cwdConfigPath = join41(cwdNaxDir, "config.json");
|
|
50557
50702
|
throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
|
|
50558
50703
|
Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
|
|
50559
50704
|
}
|
|
50560
50705
|
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
50706
|
}
|
|
50562
50707
|
projectRoot = found;
|
|
50563
|
-
naxDir =
|
|
50564
|
-
configPath =
|
|
50708
|
+
naxDir = join41(projectRoot, ".nax");
|
|
50709
|
+
configPath = join41(naxDir, "config.json");
|
|
50565
50710
|
}
|
|
50566
50711
|
let featureDir;
|
|
50567
50712
|
if (feature) {
|
|
@@ -50570,8 +50715,8 @@ Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, co
|
|
|
50570
50715
|
} catch (error48) {
|
|
50571
50716
|
throw new NaxError(error48.message, "FEATURE_INVALID", { feature });
|
|
50572
50717
|
}
|
|
50573
|
-
const featuresDir =
|
|
50574
|
-
featureDir =
|
|
50718
|
+
const featuresDir = join41(naxDir, "features");
|
|
50719
|
+
featureDir = join41(featuresDir, feature);
|
|
50575
50720
|
if (!existsSync16(featureDir)) {
|
|
50576
50721
|
const availableFeatures = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
|
|
50577
50722
|
const availableMsg = availableFeatures.length > 0 ? `
|
|
@@ -50604,7 +50749,7 @@ async function resolveProjectAsync(options = {}) {
|
|
|
50604
50749
|
}
|
|
50605
50750
|
const isPlainName = !dir.includes("/") && !dir.includes("\\");
|
|
50606
50751
|
if (isPlainName) {
|
|
50607
|
-
const registryIdentityPath =
|
|
50752
|
+
const registryIdentityPath = join41(globalConfigDir(), dir, ".identity");
|
|
50608
50753
|
const identityFile = Bun.file(registryIdentityPath);
|
|
50609
50754
|
if (await identityFile.exists()) {
|
|
50610
50755
|
try {
|
|
@@ -50636,12 +50781,12 @@ function findProjectRoot(startDir) {
|
|
|
50636
50781
|
let current = resolve13(startDir);
|
|
50637
50782
|
let depth = 0;
|
|
50638
50783
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
50639
|
-
const naxDir =
|
|
50640
|
-
const configPath =
|
|
50784
|
+
const naxDir = join41(current, ".nax");
|
|
50785
|
+
const configPath = join41(naxDir, "config.json");
|
|
50641
50786
|
if (existsSync16(configPath)) {
|
|
50642
50787
|
return realpathSync3(current);
|
|
50643
50788
|
}
|
|
50644
|
-
const parent =
|
|
50789
|
+
const parent = join41(current, "..");
|
|
50645
50790
|
if (parent === current) {
|
|
50646
50791
|
break;
|
|
50647
50792
|
}
|
|
@@ -51800,10 +51945,10 @@ var init_effectiveness = __esm(() => {
|
|
|
51800
51945
|
|
|
51801
51946
|
// src/execution/progress.ts
|
|
51802
51947
|
import { appendFile as appendFile3, mkdir as mkdir7 } from "fs/promises";
|
|
51803
|
-
import { join as
|
|
51948
|
+
import { join as join44 } from "path";
|
|
51804
51949
|
async function appendProgress(featureDir, storyId, status, message) {
|
|
51805
51950
|
await mkdir7(featureDir, { recursive: true });
|
|
51806
|
-
const progressPath =
|
|
51951
|
+
const progressPath = join44(featureDir, "progress.txt");
|
|
51807
51952
|
const timestamp = new Date().toISOString();
|
|
51808
51953
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
51809
51954
|
`;
|
|
@@ -51992,7 +52137,7 @@ var init_completion = __esm(() => {
|
|
|
51992
52137
|
|
|
51993
52138
|
// src/constitution/loader.ts
|
|
51994
52139
|
import { existsSync as existsSync19 } from "fs";
|
|
51995
|
-
import { join as
|
|
52140
|
+
import { join as join45 } from "path";
|
|
51996
52141
|
function truncateToTokens(text, maxTokens) {
|
|
51997
52142
|
const maxChars = maxTokens * 3;
|
|
51998
52143
|
if (text.length <= maxChars) {
|
|
@@ -52014,7 +52159,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
52014
52159
|
}
|
|
52015
52160
|
let combinedContent = "";
|
|
52016
52161
|
if (!config2.skipGlobal) {
|
|
52017
|
-
const globalPath =
|
|
52162
|
+
const globalPath = join45(globalConfigDir(), config2.path);
|
|
52018
52163
|
if (existsSync19(globalPath)) {
|
|
52019
52164
|
const validatedPath = validateFilePath(globalPath, globalConfigDir());
|
|
52020
52165
|
const globalFile = Bun.file(validatedPath);
|
|
@@ -52024,7 +52169,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
52024
52169
|
}
|
|
52025
52170
|
}
|
|
52026
52171
|
}
|
|
52027
|
-
const projectPath =
|
|
52172
|
+
const projectPath = join45(projectDir, config2.path);
|
|
52028
52173
|
if (existsSync19(projectPath)) {
|
|
52029
52174
|
const validatedPath = validateFilePath(projectPath, projectDir);
|
|
52030
52175
|
const projectFile = Bun.file(validatedPath);
|
|
@@ -52847,7 +52992,7 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
|
52847
52992
|
continue;
|
|
52848
52993
|
findings.push(...extractPhaseFindings(phaseOutputs[phase.slot.op.name]));
|
|
52849
52994
|
}
|
|
52850
|
-
return findings;
|
|
52995
|
+
return rectification2.postValidate ? await rectification2.postValidate(findings, _validateCtx) : findings;
|
|
52851
52996
|
}
|
|
52852
52997
|
};
|
|
52853
52998
|
const cycleResult = await _storyOrchestratorDeps.runFixCycle(cycle, ctx, "story-orchestrator-rectification", { callOp: wrappedCallOp });
|
|
@@ -52861,7 +53006,8 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
|
52861
53006
|
"max-attempts-total",
|
|
52862
53007
|
"max-attempts-per-strategy",
|
|
52863
53008
|
"bail-when",
|
|
52864
|
-
"no-strategy"
|
|
53009
|
+
"no-strategy",
|
|
53010
|
+
"agent-gave-up"
|
|
52865
53011
|
]);
|
|
52866
53012
|
if (exhaustedReasons.has(cycleResult.exitReason) && cycleResult.finalFindings.length > 0) {
|
|
52867
53013
|
return { rectificationExhausted: true, unfixedFindings: cycleResult.finalFindings };
|
|
@@ -53046,6 +53192,7 @@ var init_story_orchestrator = __esm(() => {
|
|
|
53046
53192
|
});
|
|
53047
53193
|
|
|
53048
53194
|
// src/execution/build-plan-for-strategy.ts
|
|
53195
|
+
import { join as join46 } from "path";
|
|
53049
53196
|
function isThreeSessionStrategy(strategy) {
|
|
53050
53197
|
return THREE_SESSION_STRATEGIES2.has(strategy);
|
|
53051
53198
|
}
|
|
@@ -53057,7 +53204,7 @@ function isFreshRun(story) {
|
|
|
53057
53204
|
const hasReviewEscalation = (story.priorFailures ?? []).some((f) => f.stage === "review");
|
|
53058
53205
|
return !hasAttempts && !hasReviewEscalation;
|
|
53059
53206
|
}
|
|
53060
|
-
function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
53207
|
+
async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
53061
53208
|
const isThreeSession = isThreeSessionStrategy(testStrategy);
|
|
53062
53209
|
const freshRun = isFreshRun(story);
|
|
53063
53210
|
const builder = new StoryOrchestratorBuilder;
|
|
@@ -53092,6 +53239,9 @@ function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
53092
53239
|
builder.addAdversarialReview(inputs.adversarialReview);
|
|
53093
53240
|
}
|
|
53094
53241
|
if (shouldRunRectification(config2) && inputs.rectification) {
|
|
53242
|
+
const sink = makeDeclarationSink();
|
|
53243
|
+
const packageDir = join46(ctx.packageDir, story.workdir ?? "");
|
|
53244
|
+
const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
|
|
53095
53245
|
const strategies = [];
|
|
53096
53246
|
if (config2.quality.commands.lintFix || config2.quality.commands.lintFixScoped) {
|
|
53097
53247
|
strategies.push(makeMechanicalLintFixStrategy());
|
|
@@ -53103,12 +53253,28 @@ function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
53103
53253
|
strategies.push(makeFullSuiteRectifyStrategy(story, config2));
|
|
53104
53254
|
}
|
|
53105
53255
|
if (config2.quality.autofix?.enabled !== false) {
|
|
53106
|
-
strategies.push(makeAutofixImplementerStrategy(story, config2));
|
|
53107
|
-
strategies.push(makeAutofixTestWriterStrategy(story, config2));
|
|
53108
|
-
}
|
|
53256
|
+
strategies.push(makeAutofixImplementerStrategy(story, config2, sink));
|
|
53257
|
+
strategies.push(makeAutofixTestWriterStrategy(story, config2, sink));
|
|
53258
|
+
}
|
|
53259
|
+
const postValidate = async (findings, _validateCtx) => {
|
|
53260
|
+
if (sink.testEdits.length === 0 && sink.mockHandoffs.length === 0)
|
|
53261
|
+
return findings;
|
|
53262
|
+
const pendingMock = sink.mockHandoffs.map((h) => ({
|
|
53263
|
+
reason: "mock_structure",
|
|
53264
|
+
file: h.files[0] ?? "",
|
|
53265
|
+
files: h.files,
|
|
53266
|
+
reasonDetail: h.reasonDetail
|
|
53267
|
+
}));
|
|
53268
|
+
const { valid, invalid } = await validateMockStructureFiles(pendingMock, resolvedTestPatterns, packageDir);
|
|
53269
|
+
sink.mockHandoffs = valid.map((d) => ({ files: d.files ?? [], reasonDetail: d.reasonDetail ?? "" }));
|
|
53270
|
+
const allDeclarations = [...sink.testEdits, ...valid];
|
|
53271
|
+
sink.testEdits = [];
|
|
53272
|
+
return applyTestEditDeclarations(findings, allDeclarations, story, invalid);
|
|
53273
|
+
};
|
|
53109
53274
|
const rectOpts = {
|
|
53110
53275
|
...inputs.rectification,
|
|
53111
|
-
strategies: [...strategies, ...inputs.rectification.strategies]
|
|
53276
|
+
strategies: [...strategies, ...inputs.rectification.strategies],
|
|
53277
|
+
postValidate
|
|
53112
53278
|
};
|
|
53113
53279
|
builder.addRectification(rectOpts);
|
|
53114
53280
|
}
|
|
@@ -53119,6 +53285,7 @@ var init_build_plan_for_strategy = __esm(() => {
|
|
|
53119
53285
|
init_operations();
|
|
53120
53286
|
init_execution_gates();
|
|
53121
53287
|
init_full_suite_rectify();
|
|
53288
|
+
init_test_runners();
|
|
53122
53289
|
init_story_orchestrator();
|
|
53123
53290
|
THREE_SESSION_STRATEGIES2 = new Set(["three-session-tdd", "three-session-tdd-lite"]);
|
|
53124
53291
|
});
|
|
@@ -53837,7 +54004,7 @@ var init_execution = __esm(() => {
|
|
|
53837
54004
|
} : null;
|
|
53838
54005
|
const initialRef = tddMode ? await _executionDeps.captureGitRef(ctx.workdir) ?? "HEAD" : null;
|
|
53839
54006
|
const inputs = await _executionDeps.assemblePlanInputsFromCtx(ctx);
|
|
53840
|
-
const plan = buildPlanForStrategy(callCtx, ctx.story, ctx.config, ctx.routing.testStrategy, inputs);
|
|
54007
|
+
const plan = await buildPlanForStrategy(callCtx, ctx.story, ctx.config, ctx.routing.testStrategy, inputs);
|
|
53841
54008
|
let planResult;
|
|
53842
54009
|
try {
|
|
53843
54010
|
planResult = await plan.run();
|
|
@@ -54784,7 +54951,7 @@ function buildFrontmatter(story, ctx, role) {
|
|
|
54784
54951
|
}
|
|
54785
54952
|
|
|
54786
54953
|
// src/cli/prompts-tdd.ts
|
|
54787
|
-
import { join as
|
|
54954
|
+
import { join as join47 } from "path";
|
|
54788
54955
|
async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
|
|
54789
54956
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
|
|
54790
54957
|
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 +54970,7 @@ ${frontmatter}---
|
|
|
54803
54970
|
|
|
54804
54971
|
${session.prompt}`;
|
|
54805
54972
|
if (outputDir) {
|
|
54806
|
-
const promptFile =
|
|
54973
|
+
const promptFile = join47(outputDir, `${story.id}.${session.role}.md`);
|
|
54807
54974
|
await Bun.write(promptFile, fullOutput);
|
|
54808
54975
|
logger.info("cli", "Written TDD prompt file", {
|
|
54809
54976
|
storyId: story.id,
|
|
@@ -54819,7 +54986,7 @@ ${"=".repeat(80)}`);
|
|
|
54819
54986
|
}
|
|
54820
54987
|
}
|
|
54821
54988
|
if (outputDir && ctx.contextMarkdown) {
|
|
54822
|
-
const contextFile =
|
|
54989
|
+
const contextFile = join47(outputDir, `${story.id}.context.md`);
|
|
54823
54990
|
const frontmatter = buildFrontmatter(story, ctx);
|
|
54824
54991
|
const contextOutput = `---
|
|
54825
54992
|
${frontmatter}---
|
|
@@ -54834,16 +55001,16 @@ var init_prompts_tdd = __esm(() => {
|
|
|
54834
55001
|
|
|
54835
55002
|
// src/cli/prompts-main.ts
|
|
54836
55003
|
import { existsSync as existsSync20, mkdirSync as mkdirSync3 } from "fs";
|
|
54837
|
-
import { join as
|
|
55004
|
+
import { join as join48 } from "path";
|
|
54838
55005
|
async function promptsCommand(options) {
|
|
54839
55006
|
const logger = getLogger();
|
|
54840
55007
|
const { feature, workdir, config: config2, storyId, outputDir } = options;
|
|
54841
|
-
const naxDir =
|
|
55008
|
+
const naxDir = join48(workdir, ".nax");
|
|
54842
55009
|
if (!existsSync20(naxDir)) {
|
|
54843
55010
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
54844
55011
|
}
|
|
54845
|
-
const featureDir =
|
|
54846
|
-
const prdPath =
|
|
55012
|
+
const featureDir = join48(naxDir, "features", feature);
|
|
55013
|
+
const prdPath = join48(featureDir, "prd.json");
|
|
54847
55014
|
if (!existsSync20(prdPath)) {
|
|
54848
55015
|
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
54849
55016
|
}
|
|
@@ -54910,10 +55077,10 @@ ${frontmatter}---
|
|
|
54910
55077
|
|
|
54911
55078
|
${ctx.prompt}`;
|
|
54912
55079
|
if (outputDir) {
|
|
54913
|
-
const promptFile =
|
|
55080
|
+
const promptFile = join48(outputDir, `${story.id}.prompt.md`);
|
|
54914
55081
|
await Bun.write(promptFile, fullOutput);
|
|
54915
55082
|
if (ctx.contextMarkdown) {
|
|
54916
|
-
const contextFile =
|
|
55083
|
+
const contextFile = join48(outputDir, `${story.id}.context.md`);
|
|
54917
55084
|
const contextOutput = `---
|
|
54918
55085
|
${frontmatter}---
|
|
54919
55086
|
|
|
@@ -54949,12 +55116,12 @@ var init_prompts_main = __esm(() => {
|
|
|
54949
55116
|
|
|
54950
55117
|
// src/cli/prompts-init.ts
|
|
54951
55118
|
import { existsSync as existsSync21, mkdirSync as mkdirSync4 } from "fs";
|
|
54952
|
-
import { join as
|
|
55119
|
+
import { join as join49 } from "path";
|
|
54953
55120
|
async function promptsInitCommand(options) {
|
|
54954
55121
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
54955
|
-
const templatesDir =
|
|
55122
|
+
const templatesDir = join49(workdir, ".nax", "templates");
|
|
54956
55123
|
mkdirSync4(templatesDir, { recursive: true });
|
|
54957
|
-
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(
|
|
55124
|
+
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join49(templatesDir, f)));
|
|
54958
55125
|
if (existingFiles.length > 0 && !force) {
|
|
54959
55126
|
_promptsInitDeps.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
54960
55127
|
Pass --force to overwrite existing templates.`);
|
|
@@ -54962,7 +55129,7 @@ async function promptsInitCommand(options) {
|
|
|
54962
55129
|
}
|
|
54963
55130
|
const written = [];
|
|
54964
55131
|
for (const template of TEMPLATE_ROLES) {
|
|
54965
|
-
const filePath =
|
|
55132
|
+
const filePath = join49(templatesDir, template.file);
|
|
54966
55133
|
const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
|
|
54967
55134
|
const content = TEMPLATE_HEADER + roleBody;
|
|
54968
55135
|
await Bun.write(filePath, content);
|
|
@@ -54978,7 +55145,7 @@ async function promptsInitCommand(options) {
|
|
|
54978
55145
|
return written;
|
|
54979
55146
|
}
|
|
54980
55147
|
async function autoWirePromptsConfig(workdir) {
|
|
54981
|
-
const configPath =
|
|
55148
|
+
const configPath = join49(workdir, "nax.config.json");
|
|
54982
55149
|
if (!existsSync21(configPath)) {
|
|
54983
55150
|
const exampleConfig = JSON.stringify({
|
|
54984
55151
|
prompts: {
|
|
@@ -55148,7 +55315,7 @@ __export(exports_init_context, {
|
|
|
55148
55315
|
});
|
|
55149
55316
|
import { existsSync as existsSync22 } from "fs";
|
|
55150
55317
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
55151
|
-
import { basename as basename9, join as
|
|
55318
|
+
import { basename as basename9, join as join50 } from "path";
|
|
55152
55319
|
async function findFiles(dir, maxFiles = 200) {
|
|
55153
55320
|
try {
|
|
55154
55321
|
const proc = Bun.spawnSync([
|
|
@@ -55176,7 +55343,7 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
55176
55343
|
return [];
|
|
55177
55344
|
}
|
|
55178
55345
|
async function readPackageManifest(projectRoot) {
|
|
55179
|
-
const packageJsonPath =
|
|
55346
|
+
const packageJsonPath = join50(projectRoot, "package.json");
|
|
55180
55347
|
if (!existsSync22(packageJsonPath)) {
|
|
55181
55348
|
return null;
|
|
55182
55349
|
}
|
|
@@ -55194,7 +55361,7 @@ async function readPackageManifest(projectRoot) {
|
|
|
55194
55361
|
}
|
|
55195
55362
|
}
|
|
55196
55363
|
async function readReadmeSnippet(projectRoot) {
|
|
55197
|
-
const readmePath =
|
|
55364
|
+
const readmePath = join50(projectRoot, "README.md");
|
|
55198
55365
|
if (!existsSync22(readmePath)) {
|
|
55199
55366
|
return null;
|
|
55200
55367
|
}
|
|
@@ -55212,7 +55379,7 @@ async function detectEntryPoints(projectRoot) {
|
|
|
55212
55379
|
const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
|
|
55213
55380
|
const found = [];
|
|
55214
55381
|
for (const candidate of candidates) {
|
|
55215
|
-
const path14 =
|
|
55382
|
+
const path14 = join50(projectRoot, candidate);
|
|
55216
55383
|
if (existsSync22(path14)) {
|
|
55217
55384
|
found.push(candidate);
|
|
55218
55385
|
}
|
|
@@ -55223,7 +55390,7 @@ async function detectConfigFiles(projectRoot) {
|
|
|
55223
55390
|
const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
|
|
55224
55391
|
const found = [];
|
|
55225
55392
|
for (const candidate of candidates) {
|
|
55226
|
-
const path14 =
|
|
55393
|
+
const path14 = join50(projectRoot, candidate);
|
|
55227
55394
|
if (existsSync22(path14)) {
|
|
55228
55395
|
found.push(candidate);
|
|
55229
55396
|
}
|
|
@@ -55384,8 +55551,8 @@ function generatePackageContextTemplate(packagePath) {
|
|
|
55384
55551
|
}
|
|
55385
55552
|
async function initPackage(repoRoot, packagePath, force = false) {
|
|
55386
55553
|
const logger = getLogger();
|
|
55387
|
-
const naxDir =
|
|
55388
|
-
const contextPath =
|
|
55554
|
+
const naxDir = join50(repoRoot, ".nax", "mono", packagePath);
|
|
55555
|
+
const contextPath = join50(naxDir, "context.md");
|
|
55389
55556
|
if (existsSync22(contextPath) && !force) {
|
|
55390
55557
|
logger.info("init", "Package context.md already exists (use --force to overwrite)", { path: contextPath });
|
|
55391
55558
|
return;
|
|
@@ -55399,8 +55566,8 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
55399
55566
|
}
|
|
55400
55567
|
async function initContext(projectRoot, options = {}) {
|
|
55401
55568
|
const logger = getLogger();
|
|
55402
|
-
const naxDir =
|
|
55403
|
-
const contextPath =
|
|
55569
|
+
const naxDir = join50(projectRoot, ".nax");
|
|
55570
|
+
const contextPath = join50(naxDir, "context.md");
|
|
55404
55571
|
if (existsSync22(contextPath) && !options.force) {
|
|
55405
55572
|
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", { path: contextPath });
|
|
55406
55573
|
return;
|
|
@@ -55430,9 +55597,9 @@ var init_init_context = __esm(() => {
|
|
|
55430
55597
|
|
|
55431
55598
|
// src/cli/init-detect.ts
|
|
55432
55599
|
import { existsSync as existsSync23, readFileSync } from "fs";
|
|
55433
|
-
import { join as
|
|
55600
|
+
import { join as join51 } from "path";
|
|
55434
55601
|
function readPackageJson(projectRoot) {
|
|
55435
|
-
const pkgPath =
|
|
55602
|
+
const pkgPath = join51(projectRoot, "package.json");
|
|
55436
55603
|
if (!existsSync23(pkgPath))
|
|
55437
55604
|
return;
|
|
55438
55605
|
try {
|
|
@@ -55475,41 +55642,41 @@ function detectStack(projectRoot) {
|
|
|
55475
55642
|
};
|
|
55476
55643
|
}
|
|
55477
55644
|
function detectRuntime(projectRoot) {
|
|
55478
|
-
if (existsSync23(
|
|
55645
|
+
if (existsSync23(join51(projectRoot, "bun.lockb")) || existsSync23(join51(projectRoot, "bunfig.toml"))) {
|
|
55479
55646
|
return "bun";
|
|
55480
55647
|
}
|
|
55481
|
-
if (existsSync23(
|
|
55648
|
+
if (existsSync23(join51(projectRoot, "package-lock.json")) || existsSync23(join51(projectRoot, "yarn.lock")) || existsSync23(join51(projectRoot, "pnpm-lock.yaml"))) {
|
|
55482
55649
|
return "node";
|
|
55483
55650
|
}
|
|
55484
55651
|
return "unknown";
|
|
55485
55652
|
}
|
|
55486
55653
|
function detectLanguage2(projectRoot) {
|
|
55487
|
-
if (existsSync23(
|
|
55654
|
+
if (existsSync23(join51(projectRoot, "tsconfig.json")))
|
|
55488
55655
|
return "typescript";
|
|
55489
|
-
if (existsSync23(
|
|
55656
|
+
if (existsSync23(join51(projectRoot, "pyproject.toml")) || existsSync23(join51(projectRoot, "setup.py"))) {
|
|
55490
55657
|
return "python";
|
|
55491
55658
|
}
|
|
55492
|
-
if (existsSync23(
|
|
55659
|
+
if (existsSync23(join51(projectRoot, "Cargo.toml")))
|
|
55493
55660
|
return "rust";
|
|
55494
|
-
if (existsSync23(
|
|
55661
|
+
if (existsSync23(join51(projectRoot, "go.mod")))
|
|
55495
55662
|
return "go";
|
|
55496
55663
|
return "unknown";
|
|
55497
55664
|
}
|
|
55498
55665
|
function detectLinter(projectRoot) {
|
|
55499
|
-
if (existsSync23(
|
|
55666
|
+
if (existsSync23(join51(projectRoot, "biome.json")) || existsSync23(join51(projectRoot, "biome.jsonc"))) {
|
|
55500
55667
|
return "biome";
|
|
55501
55668
|
}
|
|
55502
|
-
if (existsSync23(
|
|
55669
|
+
if (existsSync23(join51(projectRoot, ".eslintrc.json")) || existsSync23(join51(projectRoot, ".eslintrc.js")) || existsSync23(join51(projectRoot, "eslint.config.js"))) {
|
|
55503
55670
|
return "eslint";
|
|
55504
55671
|
}
|
|
55505
55672
|
return "unknown";
|
|
55506
55673
|
}
|
|
55507
55674
|
function detectMonorepo(projectRoot) {
|
|
55508
|
-
if (existsSync23(
|
|
55675
|
+
if (existsSync23(join51(projectRoot, "turbo.json")))
|
|
55509
55676
|
return "turborepo";
|
|
55510
|
-
if (existsSync23(
|
|
55677
|
+
if (existsSync23(join51(projectRoot, "nx.json")))
|
|
55511
55678
|
return "nx";
|
|
55512
|
-
if (existsSync23(
|
|
55679
|
+
if (existsSync23(join51(projectRoot, "pnpm-workspace.yaml")))
|
|
55513
55680
|
return "pnpm-workspaces";
|
|
55514
55681
|
const pkg = readPackageJson(projectRoot);
|
|
55515
55682
|
if (pkg?.workspaces)
|
|
@@ -55653,7 +55820,7 @@ __export(exports_init, {
|
|
|
55653
55820
|
});
|
|
55654
55821
|
import { existsSync as existsSync24 } from "fs";
|
|
55655
55822
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
55656
|
-
import { join as
|
|
55823
|
+
import { join as join52 } from "path";
|
|
55657
55824
|
function validateProjectName(name) {
|
|
55658
55825
|
if (!name)
|
|
55659
55826
|
return { valid: false, error: "name must be non-empty" };
|
|
@@ -55689,7 +55856,7 @@ async function checkInitCollision(name, currentWorkdir, currentRemote) {
|
|
|
55689
55856
|
}
|
|
55690
55857
|
async function updateGitignore(projectRoot) {
|
|
55691
55858
|
const logger = getLogger();
|
|
55692
|
-
const gitignorePath =
|
|
55859
|
+
const gitignorePath = join52(projectRoot, ".gitignore");
|
|
55693
55860
|
let existing = "";
|
|
55694
55861
|
if (existsSync24(gitignorePath)) {
|
|
55695
55862
|
existing = await Bun.file(gitignorePath).text();
|
|
@@ -55775,7 +55942,7 @@ async function initGlobal() {
|
|
|
55775
55942
|
await mkdir9(globalDir, { recursive: true });
|
|
55776
55943
|
logger.info("init", "Created global config directory", { path: globalDir });
|
|
55777
55944
|
}
|
|
55778
|
-
const configPath =
|
|
55945
|
+
const configPath = join52(globalDir, "config.json");
|
|
55779
55946
|
if (!existsSync24(configPath)) {
|
|
55780
55947
|
await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
|
|
55781
55948
|
`);
|
|
@@ -55783,14 +55950,14 @@ async function initGlobal() {
|
|
|
55783
55950
|
} else {
|
|
55784
55951
|
logger.info("init", "Global config already exists", { path: configPath });
|
|
55785
55952
|
}
|
|
55786
|
-
const constitutionPath =
|
|
55953
|
+
const constitutionPath = join52(globalDir, "constitution.md");
|
|
55787
55954
|
if (!existsSync24(constitutionPath)) {
|
|
55788
55955
|
await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
|
|
55789
55956
|
logger.info("init", "Created global constitution", { path: constitutionPath });
|
|
55790
55957
|
} else {
|
|
55791
55958
|
logger.info("init", "Global constitution already exists", { path: constitutionPath });
|
|
55792
55959
|
}
|
|
55793
|
-
const hooksDir =
|
|
55960
|
+
const hooksDir = join52(globalDir, "hooks");
|
|
55794
55961
|
if (!existsSync24(hooksDir)) {
|
|
55795
55962
|
await mkdir9(hooksDir, { recursive: true });
|
|
55796
55963
|
logger.info("init", "Created global hooks directory", { path: hooksDir });
|
|
@@ -55823,7 +55990,7 @@ async function initProject(projectRoot, options) {
|
|
|
55823
55990
|
if (detectedName && !options?.force) {
|
|
55824
55991
|
const collision = await checkInitCollision(detectedName, projectRoot, currentRemote);
|
|
55825
55992
|
if (collision.collision && collision.existing) {
|
|
55826
|
-
const configPath2 =
|
|
55993
|
+
const configPath2 = join52(projectDir, "config.json");
|
|
55827
55994
|
throw new NaxError([
|
|
55828
55995
|
`Project name collision: "${detectedName}"`,
|
|
55829
55996
|
` This project: ${projectRoot}`,
|
|
@@ -55851,7 +56018,7 @@ async function initProject(projectRoot, options) {
|
|
|
55851
56018
|
linter: stack.linter,
|
|
55852
56019
|
monorepo: stack.monorepo
|
|
55853
56020
|
});
|
|
55854
|
-
const configPath =
|
|
56021
|
+
const configPath = join52(projectDir, "config.json");
|
|
55855
56022
|
if (!existsSync24(configPath)) {
|
|
55856
56023
|
await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
|
|
55857
56024
|
`);
|
|
@@ -55860,14 +56027,14 @@ async function initProject(projectRoot, options) {
|
|
|
55860
56027
|
logger.info("init", "Project config already exists", { path: configPath });
|
|
55861
56028
|
}
|
|
55862
56029
|
await initContext(projectRoot, { ai: options?.ai, force: options?.force });
|
|
55863
|
-
const constitutionPath =
|
|
56030
|
+
const constitutionPath = join52(projectDir, "constitution.md");
|
|
55864
56031
|
if (!existsSync24(constitutionPath) || options?.force) {
|
|
55865
56032
|
await Bun.write(constitutionPath, buildConstitution(stack));
|
|
55866
56033
|
logger.info("init", "Created project constitution", { path: constitutionPath });
|
|
55867
56034
|
} else {
|
|
55868
56035
|
logger.info("init", "Project constitution already exists", { path: constitutionPath });
|
|
55869
56036
|
}
|
|
55870
|
-
const hooksDir =
|
|
56037
|
+
const hooksDir = join52(projectDir, "hooks");
|
|
55871
56038
|
if (!existsSync24(hooksDir)) {
|
|
55872
56039
|
await mkdir9(hooksDir, { recursive: true });
|
|
55873
56040
|
logger.info("init", "Created project hooks directory", { path: hooksDir });
|
|
@@ -57302,12 +57469,12 @@ var init_loader4 = __esm(() => {
|
|
|
57302
57469
|
});
|
|
57303
57470
|
|
|
57304
57471
|
// src/utils/paths.ts
|
|
57305
|
-
import { join as
|
|
57472
|
+
import { join as join64 } from "path";
|
|
57306
57473
|
function getRunsDir() {
|
|
57307
|
-
return process.env.NAX_RUNS_DIR ??
|
|
57474
|
+
return process.env.NAX_RUNS_DIR ?? join64(globalConfigDir(), "runs");
|
|
57308
57475
|
}
|
|
57309
57476
|
function getEventsRootDir() {
|
|
57310
|
-
return
|
|
57477
|
+
return join64(globalConfigDir(), "events");
|
|
57311
57478
|
}
|
|
57312
57479
|
var init_paths3 = __esm(() => {
|
|
57313
57480
|
init_paths();
|
|
@@ -57367,7 +57534,7 @@ var init_command_argv = __esm(() => {
|
|
|
57367
57534
|
});
|
|
57368
57535
|
|
|
57369
57536
|
// src/hooks/runner.ts
|
|
57370
|
-
import { join as
|
|
57537
|
+
import { join as join71 } from "path";
|
|
57371
57538
|
function createDrainDeadline2(deadlineMs) {
|
|
57372
57539
|
let timeoutId;
|
|
57373
57540
|
const promise2 = new Promise((resolve16) => {
|
|
@@ -57386,14 +57553,14 @@ async function loadHooksConfig(projectDir, globalDir) {
|
|
|
57386
57553
|
let globalHooks = { hooks: {} };
|
|
57387
57554
|
let projectHooks = { hooks: {} };
|
|
57388
57555
|
let skipGlobal = false;
|
|
57389
|
-
const projectPath =
|
|
57556
|
+
const projectPath = join71(projectDir, "hooks.json");
|
|
57390
57557
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
57391
57558
|
if (projectData) {
|
|
57392
57559
|
projectHooks = projectData;
|
|
57393
57560
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
57394
57561
|
}
|
|
57395
57562
|
if (!skipGlobal && globalDir) {
|
|
57396
|
-
const globalPath =
|
|
57563
|
+
const globalPath = join71(globalDir, "hooks.json");
|
|
57397
57564
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
57398
57565
|
if (globalData) {
|
|
57399
57566
|
globalHooks = globalData;
|
|
@@ -57563,7 +57730,7 @@ var package_default;
|
|
|
57563
57730
|
var init_package = __esm(() => {
|
|
57564
57731
|
package_default = {
|
|
57565
57732
|
name: "@nathapp/nax",
|
|
57566
|
-
version: "0.67.
|
|
57733
|
+
version: "0.67.12",
|
|
57567
57734
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
57568
57735
|
type: "module",
|
|
57569
57736
|
bin: {
|
|
@@ -57658,8 +57825,8 @@ var init_version = __esm(() => {
|
|
|
57658
57825
|
NAX_VERSION = package_default.version;
|
|
57659
57826
|
NAX_COMMIT = (() => {
|
|
57660
57827
|
try {
|
|
57661
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
57662
|
-
return "
|
|
57828
|
+
if (/^[0-9a-f]{6,10}$/.test("c747dea2"))
|
|
57829
|
+
return "c747dea2";
|
|
57663
57830
|
} catch {}
|
|
57664
57831
|
try {
|
|
57665
57832
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -58528,15 +58695,15 @@ var init_acceptance_loop = __esm(() => {
|
|
|
58528
58695
|
|
|
58529
58696
|
// src/session/scratch-purge.ts
|
|
58530
58697
|
import { mkdir as mkdir13, rename, rm } from "fs/promises";
|
|
58531
|
-
import { dirname as dirname12, join as
|
|
58698
|
+
import { dirname as dirname12, join as join72 } from "path";
|
|
58532
58699
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
58533
|
-
const sessionsDir =
|
|
58700
|
+
const sessionsDir = join72(projectDir, ".nax", "features", featureName, "sessions");
|
|
58534
58701
|
const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
|
|
58535
58702
|
const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
|
|
58536
58703
|
let purged = 0;
|
|
58537
58704
|
for (const sessionId of sessionIds) {
|
|
58538
|
-
const sessionDir =
|
|
58539
|
-
const descriptorPath =
|
|
58705
|
+
const sessionDir = join72(sessionsDir, sessionId);
|
|
58706
|
+
const descriptorPath = join72(sessionDir, "descriptor.json");
|
|
58540
58707
|
if (!await _scratchPurgeDeps.fileExists(descriptorPath))
|
|
58541
58708
|
continue;
|
|
58542
58709
|
let lastActivityAt;
|
|
@@ -58552,7 +58719,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
|
|
|
58552
58719
|
if (new Date(lastActivityAt).getTime() >= cutoffMs)
|
|
58553
58720
|
continue;
|
|
58554
58721
|
if (archiveInsteadOfDelete) {
|
|
58555
|
-
const archiveDest =
|
|
58722
|
+
const archiveDest = join72(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
|
|
58556
58723
|
await _scratchPurgeDeps.move(sessionDir, archiveDest);
|
|
58557
58724
|
} else {
|
|
58558
58725
|
await _scratchPurgeDeps.remove(sessionDir);
|
|
@@ -59261,12 +59428,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
|
|
|
59261
59428
|
|
|
59262
59429
|
// src/pipeline/subscribers/events-writer.ts
|
|
59263
59430
|
import { appendFile as appendFile5, mkdir as mkdir14 } from "fs/promises";
|
|
59264
|
-
import { basename as basename14, join as
|
|
59431
|
+
import { basename as basename14, join as join73 } from "path";
|
|
59265
59432
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
59266
59433
|
const logger = getSafeLogger();
|
|
59267
59434
|
const project = basename14(workdir);
|
|
59268
|
-
const eventsDir =
|
|
59269
|
-
const eventsFile =
|
|
59435
|
+
const eventsDir = join73(getEventsRootDir(), project);
|
|
59436
|
+
const eventsFile = join73(eventsDir, "events.jsonl");
|
|
59270
59437
|
let dirReady = false;
|
|
59271
59438
|
const write = (line) => {
|
|
59272
59439
|
return (async () => {
|
|
@@ -59447,12 +59614,12 @@ var init_interaction2 = __esm(() => {
|
|
|
59447
59614
|
|
|
59448
59615
|
// src/pipeline/subscribers/registry.ts
|
|
59449
59616
|
import { mkdir as mkdir15, writeFile as writeFile2 } from "fs/promises";
|
|
59450
|
-
import { basename as basename15, join as
|
|
59617
|
+
import { basename as basename15, join as join74 } from "path";
|
|
59451
59618
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
59452
59619
|
const logger = getSafeLogger();
|
|
59453
59620
|
const project = basename15(workdir);
|
|
59454
|
-
const runDir =
|
|
59455
|
-
const metaFile =
|
|
59621
|
+
const runDir = join74(getRunsDir(), `${project}-${feature}-${runId}`);
|
|
59622
|
+
const metaFile = join74(runDir, "meta.json");
|
|
59456
59623
|
const unsub = bus.on("run:started", (_ev) => {
|
|
59457
59624
|
return (async () => {
|
|
59458
59625
|
try {
|
|
@@ -59462,8 +59629,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
|
59462
59629
|
project,
|
|
59463
59630
|
feature,
|
|
59464
59631
|
workdir,
|
|
59465
|
-
statusPath:
|
|
59466
|
-
eventsDir:
|
|
59632
|
+
statusPath: join74(outputDir, "features", feature, "status.json"),
|
|
59633
|
+
eventsDir: join74(outputDir, "features", feature, "runs"),
|
|
59467
59634
|
registeredAt: new Date().toISOString()
|
|
59468
59635
|
};
|
|
59469
59636
|
await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -59709,7 +59876,7 @@ var init_types9 = __esm(() => {
|
|
|
59709
59876
|
|
|
59710
59877
|
// src/worktree/dependencies.ts
|
|
59711
59878
|
import { existsSync as existsSync32 } from "fs";
|
|
59712
|
-
import { join as
|
|
59879
|
+
import { join as join75 } from "path";
|
|
59713
59880
|
async function prepareWorktreeDependencies(options) {
|
|
59714
59881
|
const mode = options.config.execution.worktreeDependencies.mode;
|
|
59715
59882
|
const resolvedCwd = resolveDependencyCwd(options);
|
|
@@ -59723,7 +59890,7 @@ async function prepareWorktreeDependencies(options) {
|
|
|
59723
59890
|
}
|
|
59724
59891
|
}
|
|
59725
59892
|
function resolveDependencyCwd(options) {
|
|
59726
|
-
return options.storyWorkdir ?
|
|
59893
|
+
return options.storyWorkdir ? join75(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
|
|
59727
59894
|
}
|
|
59728
59895
|
function resolveInheritedDependencies(options, resolvedCwd) {
|
|
59729
59896
|
if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
|
|
@@ -59733,7 +59900,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
|
|
|
59733
59900
|
}
|
|
59734
59901
|
function hasDependencyManifests(worktreeRoot, resolvedCwd) {
|
|
59735
59902
|
const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
|
|
59736
|
-
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(
|
|
59903
|
+
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join75(directory, filename))));
|
|
59737
59904
|
}
|
|
59738
59905
|
async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
|
|
59739
59906
|
const setupCommand = config2.execution.worktreeDependencies.setupCommand;
|
|
@@ -59797,13 +59964,13 @@ __export(exports_manager, {
|
|
|
59797
59964
|
});
|
|
59798
59965
|
import { existsSync as existsSync33, symlinkSync } from "fs";
|
|
59799
59966
|
import { mkdir as mkdir16 } from "fs/promises";
|
|
59800
|
-
import { join as
|
|
59967
|
+
import { join as join76 } from "path";
|
|
59801
59968
|
|
|
59802
59969
|
class WorktreeManager {
|
|
59803
59970
|
async ensureGitExcludes(projectRoot) {
|
|
59804
59971
|
const logger = getSafeLogger();
|
|
59805
|
-
const infoDir =
|
|
59806
|
-
const excludePath =
|
|
59972
|
+
const infoDir = join76(projectRoot, ".git", "info");
|
|
59973
|
+
const excludePath = join76(infoDir, "exclude");
|
|
59807
59974
|
try {
|
|
59808
59975
|
await mkdir16(infoDir, { recursive: true });
|
|
59809
59976
|
let existing = "";
|
|
@@ -59830,7 +59997,7 @@ ${missing.join(`
|
|
|
59830
59997
|
}
|
|
59831
59998
|
async create(projectRoot, storyId) {
|
|
59832
59999
|
validateStoryId(storyId);
|
|
59833
|
-
const worktreePath =
|
|
60000
|
+
const worktreePath = join76(projectRoot, ".nax-wt", storyId);
|
|
59834
60001
|
const branchName = `nax/${storyId}`;
|
|
59835
60002
|
try {
|
|
59836
60003
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -59871,9 +60038,9 @@ ${missing.join(`
|
|
|
59871
60038
|
}
|
|
59872
60039
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
59873
60040
|
}
|
|
59874
|
-
const envSource =
|
|
60041
|
+
const envSource = join76(projectRoot, ".env");
|
|
59875
60042
|
if (existsSync33(envSource)) {
|
|
59876
|
-
const envTarget =
|
|
60043
|
+
const envTarget = join76(worktreePath, ".env");
|
|
59877
60044
|
try {
|
|
59878
60045
|
symlinkSync(envSource, envTarget, "file");
|
|
59879
60046
|
} catch (error48) {
|
|
@@ -59884,7 +60051,7 @@ ${missing.join(`
|
|
|
59884
60051
|
}
|
|
59885
60052
|
async remove(projectRoot, storyId) {
|
|
59886
60053
|
validateStoryId(storyId);
|
|
59887
|
-
const worktreePath =
|
|
60054
|
+
const worktreePath = join76(projectRoot, ".nax-wt", storyId);
|
|
59888
60055
|
const branchName = `nax/${storyId}`;
|
|
59889
60056
|
try {
|
|
59890
60057
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -60684,10 +60851,10 @@ var init_merge_conflict_rectify = __esm(() => {
|
|
|
60684
60851
|
});
|
|
60685
60852
|
|
|
60686
60853
|
// src/execution/pipeline-result-handler.ts
|
|
60687
|
-
import { join as
|
|
60854
|
+
import { join as join77 } from "path";
|
|
60688
60855
|
async function removeWorktreeDirectory(projectRoot, storyId) {
|
|
60689
60856
|
const logger = getSafeLogger();
|
|
60690
|
-
const worktreePath =
|
|
60857
|
+
const worktreePath = join77(projectRoot, ".nax-wt", storyId);
|
|
60691
60858
|
try {
|
|
60692
60859
|
const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
60693
60860
|
cwd: projectRoot,
|
|
@@ -60898,7 +61065,7 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
60898
61065
|
|
|
60899
61066
|
// src/execution/iteration-runner.ts
|
|
60900
61067
|
import { existsSync as existsSync34 } from "fs";
|
|
60901
|
-
import { join as
|
|
61068
|
+
import { join as join78 } from "path";
|
|
60902
61069
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
60903
61070
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
60904
61071
|
if (ctx.dryRun) {
|
|
@@ -60923,7 +61090,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
60923
61090
|
const storyStartTime = Date.now();
|
|
60924
61091
|
let effectiveWorkdir = ctx.workdir;
|
|
60925
61092
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
60926
|
-
const worktreePath =
|
|
61093
|
+
const worktreePath = join78(ctx.workdir, ".nax-wt", story.id);
|
|
60927
61094
|
const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
|
|
60928
61095
|
if (!worktreeExists) {
|
|
60929
61096
|
await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
|
|
@@ -60943,7 +61110,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
60943
61110
|
}
|
|
60944
61111
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
60945
61112
|
const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
|
|
60946
|
-
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(
|
|
61113
|
+
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join78(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
|
|
60947
61114
|
let dependencyContext;
|
|
60948
61115
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
60949
61116
|
try {
|
|
@@ -60970,7 +61137,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
60970
61137
|
};
|
|
60971
61138
|
}
|
|
60972
61139
|
}
|
|
60973
|
-
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ?
|
|
61140
|
+
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
61141
|
const pipelineContext = {
|
|
60975
61142
|
config: effectiveConfig,
|
|
60976
61143
|
rootConfig: ctx.config,
|
|
@@ -61171,7 +61338,7 @@ __export(exports_parallel_worker, {
|
|
|
61171
61338
|
executeParallelBatch: () => executeParallelBatch,
|
|
61172
61339
|
_parallelWorkerDeps: () => _parallelWorkerDeps
|
|
61173
61340
|
});
|
|
61174
|
-
import { join as
|
|
61341
|
+
import { join as join79 } from "path";
|
|
61175
61342
|
async function executeStoryInWorktree(story, worktreePath, dependencyContext, context, routing, eventEmitter) {
|
|
61176
61343
|
const logger = getSafeLogger();
|
|
61177
61344
|
try {
|
|
@@ -61191,7 +61358,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
|
|
|
61191
61358
|
story,
|
|
61192
61359
|
stories: [story],
|
|
61193
61360
|
projectDir: context.projectDir,
|
|
61194
|
-
workdir: dependencyContext.cwd ?? (story.workdir ?
|
|
61361
|
+
workdir: dependencyContext.cwd ?? (story.workdir ? join79(worktreePath, story.workdir) : worktreePath),
|
|
61195
61362
|
worktreeDependencyContext: dependencyContext,
|
|
61196
61363
|
routing,
|
|
61197
61364
|
storyGitRef: storyGitRef ?? undefined
|
|
@@ -62020,7 +62187,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
62020
62187
|
var init_status_file = () => {};
|
|
62021
62188
|
|
|
62022
62189
|
// src/execution/status-writer.ts
|
|
62023
|
-
import { join as
|
|
62190
|
+
import { join as join80 } from "path";
|
|
62024
62191
|
|
|
62025
62192
|
class StatusWriter {
|
|
62026
62193
|
statusFile;
|
|
@@ -62139,7 +62306,7 @@ class StatusWriter {
|
|
|
62139
62306
|
if (!this._prd)
|
|
62140
62307
|
return;
|
|
62141
62308
|
const safeLogger = getSafeLogger();
|
|
62142
|
-
const featureStatusPath =
|
|
62309
|
+
const featureStatusPath = join80(featureDir, "status.json");
|
|
62143
62310
|
const write = async () => {
|
|
62144
62311
|
try {
|
|
62145
62312
|
const base = this.getSnapshot(totalCost, iterations);
|
|
@@ -62573,7 +62740,7 @@ __export(exports_run_initialization, {
|
|
|
62573
62740
|
initializeRun: () => initializeRun,
|
|
62574
62741
|
_reconcileDeps: () => _reconcileDeps
|
|
62575
62742
|
});
|
|
62576
|
-
import { join as
|
|
62743
|
+
import { join as join81 } from "path";
|
|
62577
62744
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
62578
62745
|
const logger = getSafeLogger();
|
|
62579
62746
|
let reconciledCount = 0;
|
|
@@ -62590,7 +62757,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
62590
62757
|
});
|
|
62591
62758
|
continue;
|
|
62592
62759
|
}
|
|
62593
|
-
const effectiveWorkdir = story.workdir ?
|
|
62760
|
+
const effectiveWorkdir = story.workdir ? join81(workdir, story.workdir) : workdir;
|
|
62594
62761
|
try {
|
|
62595
62762
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
62596
62763
|
if (!reviewResult.success) {
|
|
@@ -94022,7 +94189,7 @@ __export(exports_curator, {
|
|
|
94022
94189
|
});
|
|
94023
94190
|
import { readdirSync as readdirSync9 } from "fs";
|
|
94024
94191
|
import { unlink as unlink4 } from "fs/promises";
|
|
94025
|
-
import { basename as basename16, join as
|
|
94192
|
+
import { basename as basename16, join as join83 } from "path";
|
|
94026
94193
|
function getProjectKey(config2, projectDir) {
|
|
94027
94194
|
return config2.name?.trim() || basename16(projectDir);
|
|
94028
94195
|
}
|
|
@@ -94105,7 +94272,7 @@ async function curatorStatus(options) {
|
|
|
94105
94272
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
94106
94273
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94107
94274
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94108
|
-
const runsDir =
|
|
94275
|
+
const runsDir = join83(outputDir, "runs");
|
|
94109
94276
|
const runIds = listRunIds(runsDir);
|
|
94110
94277
|
let runId;
|
|
94111
94278
|
if (options.run) {
|
|
@@ -94122,8 +94289,8 @@ async function curatorStatus(options) {
|
|
|
94122
94289
|
runId = runIds[runIds.length - 1];
|
|
94123
94290
|
}
|
|
94124
94291
|
console.log(`Run: ${runId}`);
|
|
94125
|
-
const runDir =
|
|
94126
|
-
const observationsPath =
|
|
94292
|
+
const runDir = join83(runsDir, runId);
|
|
94293
|
+
const observationsPath = join83(runDir, "observations.jsonl");
|
|
94127
94294
|
const observations = await parseObservations(observationsPath);
|
|
94128
94295
|
const counts = new Map;
|
|
94129
94296
|
for (const obs of observations) {
|
|
@@ -94133,7 +94300,7 @@ async function curatorStatus(options) {
|
|
|
94133
94300
|
for (const [kind, count] of counts.entries()) {
|
|
94134
94301
|
console.log(` ${kind}: ${count}`);
|
|
94135
94302
|
}
|
|
94136
|
-
const proposalsPath =
|
|
94303
|
+
const proposalsPath = join83(runDir, "curator-proposals.md");
|
|
94137
94304
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
94138
94305
|
if (proposalText !== null) {
|
|
94139
94306
|
console.log("");
|
|
@@ -94147,8 +94314,8 @@ async function curatorCommit(options) {
|
|
|
94147
94314
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
94148
94315
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94149
94316
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94150
|
-
const runDir =
|
|
94151
|
-
const proposalsPath =
|
|
94317
|
+
const runDir = join83(outputDir, "runs", options.runId);
|
|
94318
|
+
const proposalsPath = join83(runDir, "curator-proposals.md");
|
|
94152
94319
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
94153
94320
|
if (proposalText === null) {
|
|
94154
94321
|
console.log(`curator-proposals.md not found for run ${options.runId}.`);
|
|
@@ -94164,7 +94331,7 @@ async function curatorCommit(options) {
|
|
|
94164
94331
|
const dropFileState = new Map;
|
|
94165
94332
|
const skippedDrops = new Set;
|
|
94166
94333
|
for (const drop2 of drops) {
|
|
94167
|
-
const targetPath =
|
|
94334
|
+
const targetPath = join83(resolved.projectDir, drop2.canonicalFile);
|
|
94168
94335
|
if (!dropFileState.has(targetPath)) {
|
|
94169
94336
|
const fileExists2 = await Bun.file(targetPath).exists();
|
|
94170
94337
|
const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
|
|
@@ -94198,7 +94365,7 @@ async function curatorCommit(options) {
|
|
|
94198
94365
|
if (skippedDrops.has(drop2)) {
|
|
94199
94366
|
continue;
|
|
94200
94367
|
}
|
|
94201
|
-
const targetPath =
|
|
94368
|
+
const targetPath = join83(resolved.projectDir, drop2.canonicalFile);
|
|
94202
94369
|
const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
|
|
94203
94370
|
const filtered = filterDropContent(existing, drop2.description);
|
|
94204
94371
|
await _curatorCmdDeps.writeFile(targetPath, filtered);
|
|
@@ -94207,7 +94374,7 @@ async function curatorCommit(options) {
|
|
|
94207
94374
|
}
|
|
94208
94375
|
const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
|
|
94209
94376
|
for (const add2 of adds) {
|
|
94210
|
-
const targetPath =
|
|
94377
|
+
const targetPath = join83(resolved.projectDir, add2.canonicalFile);
|
|
94211
94378
|
const content = buildAddContent(add2);
|
|
94212
94379
|
await _curatorCmdDeps.appendFile(targetPath, content);
|
|
94213
94380
|
modifiedFiles.add(targetPath);
|
|
@@ -94244,7 +94411,7 @@ async function curatorDryrun(options) {
|
|
|
94244
94411
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
94245
94412
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94246
94413
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94247
|
-
const runsDir =
|
|
94414
|
+
const runsDir = join83(outputDir, "runs");
|
|
94248
94415
|
const runIds = listRunIds(runsDir);
|
|
94249
94416
|
if (runIds.length === 0) {
|
|
94250
94417
|
console.log("No runs found.");
|
|
@@ -94255,7 +94422,7 @@ async function curatorDryrun(options) {
|
|
|
94255
94422
|
console.log(`Run ${options.run} not found in ${runsDir}.`);
|
|
94256
94423
|
return;
|
|
94257
94424
|
}
|
|
94258
|
-
const observationsPath =
|
|
94425
|
+
const observationsPath = join83(runsDir, runId, "observations.jsonl");
|
|
94259
94426
|
const observations = await parseObservations(observationsPath);
|
|
94260
94427
|
const thresholds = getThresholds(config2);
|
|
94261
94428
|
const proposals = runHeuristics(observations, thresholds);
|
|
@@ -94296,12 +94463,12 @@ async function curatorGc(options) {
|
|
|
94296
94463
|
await _curatorCmdDeps.writeFile(rollupPath, newContent);
|
|
94297
94464
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94298
94465
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94299
|
-
const perRunsDir =
|
|
94466
|
+
const perRunsDir = join83(outputDir, "runs");
|
|
94300
94467
|
for (const runId of uniqueRunIds) {
|
|
94301
94468
|
if (!keepSet.has(runId)) {
|
|
94302
|
-
const runDir =
|
|
94303
|
-
await _curatorCmdDeps.removeFile(
|
|
94304
|
-
await _curatorCmdDeps.removeFile(
|
|
94469
|
+
const runDir = join83(perRunsDir, runId);
|
|
94470
|
+
await _curatorCmdDeps.removeFile(join83(runDir, "observations.jsonl"));
|
|
94471
|
+
await _curatorCmdDeps.removeFile(join83(runDir, "curator-proposals.md"));
|
|
94305
94472
|
}
|
|
94306
94473
|
}
|
|
94307
94474
|
console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
|
|
@@ -94346,7 +94513,7 @@ var init_curator2 = __esm(() => {
|
|
|
94346
94513
|
init_source();
|
|
94347
94514
|
import { existsSync as existsSync37, mkdirSync as mkdirSync7 } from "fs";
|
|
94348
94515
|
import { homedir as homedir3 } from "os";
|
|
94349
|
-
import { basename as basename17, join as
|
|
94516
|
+
import { basename as basename17, join as join84 } from "path";
|
|
94350
94517
|
|
|
94351
94518
|
// node_modules/commander/esm.mjs
|
|
94352
94519
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -94370,12 +94537,12 @@ init_errors();
|
|
|
94370
94537
|
init_operations();
|
|
94371
94538
|
|
|
94372
94539
|
// src/plan/strategies/context-builder.ts
|
|
94373
|
-
import { join as
|
|
94540
|
+
import { join as join37 } from "path";
|
|
94374
94541
|
init_config();
|
|
94375
94542
|
init_errors();
|
|
94376
94543
|
init_interaction();
|
|
94377
94544
|
async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
94378
|
-
const naxDir =
|
|
94545
|
+
const naxDir = join37(workdir, ".nax");
|
|
94379
94546
|
if (!deps.existsSync(naxDir)) {
|
|
94380
94547
|
throw new NaxError(`.nax directory not found. Run 'nax init' first in ${workdir}`, "PLAN_CONTEXT_NO_NAX_DIR", {
|
|
94381
94548
|
stage: "plan",
|
|
@@ -94383,8 +94550,8 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
94383
94550
|
});
|
|
94384
94551
|
}
|
|
94385
94552
|
validateFeatureName(options.feature);
|
|
94386
|
-
const outputDir =
|
|
94387
|
-
const outputPath =
|
|
94553
|
+
const outputDir = join37(naxDir, "features", options.feature);
|
|
94554
|
+
const outputPath = join37(outputDir, "prd.json");
|
|
94388
94555
|
const [specContent, sourceRoots, pkg] = await Promise.all([
|
|
94389
94556
|
deps.readFile(options.from),
|
|
94390
94557
|
deps.scanSourceRoots(workdir),
|
|
@@ -94399,7 +94566,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
94399
94566
|
...new Set(sourceRoots.map((root) => root.path).filter((path7) => path7 !== ".").map((path7) => path7.startsWith("/") ? path7.replace(`${workdir}/`, "") : path7))
|
|
94400
94567
|
];
|
|
94401
94568
|
const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
|
|
94402
|
-
const packageJson = await deps.readPackageJsonAt(
|
|
94569
|
+
const packageJson = await deps.readPackageJsonAt(join37(workdir, relativePath, "package.json"));
|
|
94403
94570
|
return buildPackageSummary(relativePath, packageJson);
|
|
94404
94571
|
}));
|
|
94405
94572
|
const projectName = detectProjectName(workdir, pkg);
|
|
@@ -95000,7 +95167,7 @@ init_interaction();
|
|
|
95000
95167
|
init_prd();
|
|
95001
95168
|
init_runtime();
|
|
95002
95169
|
import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
|
|
95003
|
-
import { basename as basename7, join as
|
|
95170
|
+
import { basename as basename7, join as join42, resolve as resolve14 } from "path";
|
|
95004
95171
|
var _statusFeaturesDeps = {
|
|
95005
95172
|
projectOutputDir,
|
|
95006
95173
|
loadConfig
|
|
@@ -95014,7 +95181,7 @@ function isPidAlive(pid) {
|
|
|
95014
95181
|
}
|
|
95015
95182
|
}
|
|
95016
95183
|
async function loadStatusFile(featureDir) {
|
|
95017
|
-
const statusPath =
|
|
95184
|
+
const statusPath = join42(featureDir, "status.json");
|
|
95018
95185
|
if (!existsSync17(statusPath)) {
|
|
95019
95186
|
return null;
|
|
95020
95187
|
}
|
|
@@ -95029,7 +95196,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
95029
95196
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
95030
95197
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
95031
95198
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
95032
|
-
const statusPath =
|
|
95199
|
+
const statusPath = join42(outputDir, "status.json");
|
|
95033
95200
|
if (!existsSync17(statusPath)) {
|
|
95034
95201
|
return null;
|
|
95035
95202
|
}
|
|
@@ -95041,7 +95208,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
95041
95208
|
}
|
|
95042
95209
|
}
|
|
95043
95210
|
async function getFeatureSummary(featureName, featureDir) {
|
|
95044
|
-
const prdPath =
|
|
95211
|
+
const prdPath = join42(featureDir, "prd.json");
|
|
95045
95212
|
if (!existsSync17(prdPath)) {
|
|
95046
95213
|
return {
|
|
95047
95214
|
name: featureName,
|
|
@@ -95084,7 +95251,7 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
95084
95251
|
};
|
|
95085
95252
|
}
|
|
95086
95253
|
}
|
|
95087
|
-
const runsDir =
|
|
95254
|
+
const runsDir = join42(featureDir, "runs");
|
|
95088
95255
|
if (existsSync17(runsDir)) {
|
|
95089
95256
|
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
95257
|
if (runs.length > 0) {
|
|
@@ -95098,7 +95265,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
95098
95265
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
95099
95266
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
95100
95267
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
95101
|
-
const featuresDir =
|
|
95268
|
+
const featuresDir = join42(outputDir, "features");
|
|
95102
95269
|
if (!existsSync17(featuresDir)) {
|
|
95103
95270
|
console.log(source_default.dim("No features found."));
|
|
95104
95271
|
return;
|
|
@@ -95139,7 +95306,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
95139
95306
|
console.log();
|
|
95140
95307
|
}
|
|
95141
95308
|
}
|
|
95142
|
-
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name,
|
|
95309
|
+
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join42(featuresDir, name))));
|
|
95143
95310
|
console.log(source_default.bold(`\uD83D\uDCCA Features
|
|
95144
95311
|
`));
|
|
95145
95312
|
const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
|
|
@@ -95165,7 +95332,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
95165
95332
|
console.log();
|
|
95166
95333
|
}
|
|
95167
95334
|
async function displayFeatureDetails(featureName, featureDir) {
|
|
95168
|
-
const prdPath =
|
|
95335
|
+
const prdPath = join42(featureDir, "prd.json");
|
|
95169
95336
|
if (!existsSync17(prdPath)) {
|
|
95170
95337
|
console.log(source_default.bold(`
|
|
95171
95338
|
\uD83D\uDCCA ${featureName}
|
|
@@ -95311,7 +95478,7 @@ async function displayFeatureStatus(options = {}) {
|
|
|
95311
95478
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
95312
95479
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
95313
95480
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
95314
|
-
featureDir =
|
|
95481
|
+
featureDir = join42(outputDir, "features", options.feature);
|
|
95315
95482
|
} else {
|
|
95316
95483
|
const resolved = resolveProject({ feature: options.feature });
|
|
95317
95484
|
if (!resolved.featureDir) {
|
|
@@ -95331,7 +95498,7 @@ init_errors();
|
|
|
95331
95498
|
init_logger2();
|
|
95332
95499
|
init_runtime();
|
|
95333
95500
|
import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
|
|
95334
|
-
import { basename as basename8, join as
|
|
95501
|
+
import { basename as basename8, join as join43 } from "path";
|
|
95335
95502
|
async function resolveOutputDir2(workdir, override) {
|
|
95336
95503
|
if (override)
|
|
95337
95504
|
return override;
|
|
@@ -95355,7 +95522,7 @@ async function runsListCommand(options) {
|
|
|
95355
95522
|
const logger = getLogger();
|
|
95356
95523
|
const { feature, workdir } = options;
|
|
95357
95524
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
95358
|
-
const runsDir =
|
|
95525
|
+
const runsDir = join43(outputDir, "features", feature, "runs");
|
|
95359
95526
|
if (!existsSync18(runsDir)) {
|
|
95360
95527
|
logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
|
|
95361
95528
|
return;
|
|
@@ -95367,7 +95534,7 @@ async function runsListCommand(options) {
|
|
|
95367
95534
|
}
|
|
95368
95535
|
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
95369
95536
|
for (const file3 of files.sort().reverse()) {
|
|
95370
|
-
const logPath =
|
|
95537
|
+
const logPath = join43(runsDir, file3);
|
|
95371
95538
|
const entries = await parseRunLog(logPath);
|
|
95372
95539
|
const startEvent = entries.find((e) => e.message === "run.start");
|
|
95373
95540
|
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
@@ -95394,7 +95561,7 @@ async function runsShowCommand(options) {
|
|
|
95394
95561
|
const logger = getLogger();
|
|
95395
95562
|
const { runId, feature, workdir } = options;
|
|
95396
95563
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
95397
|
-
const logPath =
|
|
95564
|
+
const logPath = join43(outputDir, "features", feature, "runs", `${runId}.jsonl`);
|
|
95398
95565
|
if (!existsSync18(logPath)) {
|
|
95399
95566
|
logger.error("cli", "Run not found", { runId, feature, logPath });
|
|
95400
95567
|
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
@@ -95507,7 +95674,7 @@ init_logger2();
|
|
|
95507
95674
|
init_prd();
|
|
95508
95675
|
init_runtime();
|
|
95509
95676
|
import { existsSync as existsSync25, readdirSync as readdirSync5 } from "fs";
|
|
95510
|
-
import { basename as basename12, join as
|
|
95677
|
+
import { basename as basename12, join as join57 } from "path";
|
|
95511
95678
|
|
|
95512
95679
|
// src/cli/diagnose-analysis.ts
|
|
95513
95680
|
function detectFailurePattern(story, _prd, status) {
|
|
@@ -95710,7 +95877,7 @@ function isProcessAlive2(pid) {
|
|
|
95710
95877
|
}
|
|
95711
95878
|
}
|
|
95712
95879
|
async function loadStatusFile2(outputDir) {
|
|
95713
|
-
const statusPath =
|
|
95880
|
+
const statusPath = join57(outputDir, "status.json");
|
|
95714
95881
|
if (!existsSync25(statusPath))
|
|
95715
95882
|
return null;
|
|
95716
95883
|
try {
|
|
@@ -95738,7 +95905,7 @@ async function countCommitsSince(workdir, since) {
|
|
|
95738
95905
|
}
|
|
95739
95906
|
}
|
|
95740
95907
|
async function checkLock(workdir) {
|
|
95741
|
-
const lockFile = Bun.file(
|
|
95908
|
+
const lockFile = Bun.file(join57(workdir, "nax.lock"));
|
|
95742
95909
|
if (!await lockFile.exists())
|
|
95743
95910
|
return { lockPresent: false };
|
|
95744
95911
|
try {
|
|
@@ -95756,8 +95923,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
95756
95923
|
const logger = getLogger();
|
|
95757
95924
|
const workdir = options.workdir ?? process.cwd();
|
|
95758
95925
|
const naxSubdir = findProjectDir(workdir);
|
|
95759
|
-
let projectDir = naxSubdir ?
|
|
95760
|
-
if (!projectDir && existsSync25(
|
|
95926
|
+
let projectDir = naxSubdir ? join57(naxSubdir, "..") : null;
|
|
95927
|
+
if (!projectDir && existsSync25(join57(workdir, ".nax"))) {
|
|
95761
95928
|
projectDir = workdir;
|
|
95762
95929
|
}
|
|
95763
95930
|
if (!projectDir)
|
|
@@ -95771,7 +95938,7 @@ async function diagnoseCommand(options = {}) {
|
|
|
95771
95938
|
if (status2) {
|
|
95772
95939
|
feature = status2.run.feature;
|
|
95773
95940
|
} else {
|
|
95774
|
-
const featuresDir =
|
|
95941
|
+
const featuresDir = join57(outputDir, "features");
|
|
95775
95942
|
if (!existsSync25(featuresDir))
|
|
95776
95943
|
throw new Error("No features found in project");
|
|
95777
95944
|
const features = readdirSync5(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -95781,8 +95948,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
95781
95948
|
logger.info("diagnose", "No feature specified, using first found", { feature });
|
|
95782
95949
|
}
|
|
95783
95950
|
}
|
|
95784
|
-
const featureDir =
|
|
95785
|
-
const prdPath =
|
|
95951
|
+
const featureDir = join57(outputDir, "features", feature);
|
|
95952
|
+
const prdPath = join57(featureDir, "prd.json");
|
|
95786
95953
|
if (!existsSync25(prdPath))
|
|
95787
95954
|
throw new Error(`Feature not found: ${feature}`);
|
|
95788
95955
|
const prd = await loadPRD(prdPath);
|
|
@@ -95827,7 +95994,7 @@ init_source();
|
|
|
95827
95994
|
init_loader();
|
|
95828
95995
|
init_generator2();
|
|
95829
95996
|
import { existsSync as existsSync26 } from "fs";
|
|
95830
|
-
import { join as
|
|
95997
|
+
import { join as join58 } from "path";
|
|
95831
95998
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
95832
95999
|
async function generateCommand(options) {
|
|
95833
96000
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -95870,7 +96037,7 @@ async function generateCommand(options) {
|
|
|
95870
96037
|
return;
|
|
95871
96038
|
}
|
|
95872
96039
|
if (options.package) {
|
|
95873
|
-
const packageDir =
|
|
96040
|
+
const packageDir = join58(workdir, options.package);
|
|
95874
96041
|
if (dryRun) {
|
|
95875
96042
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
95876
96043
|
}
|
|
@@ -95890,8 +96057,8 @@ async function generateCommand(options) {
|
|
|
95890
96057
|
process.exit(1);
|
|
95891
96058
|
return;
|
|
95892
96059
|
}
|
|
95893
|
-
const contextPath = options.context ?
|
|
95894
|
-
const outputDir = options.output ?
|
|
96060
|
+
const contextPath = options.context ? join58(workdir, options.context) : join58(workdir, ".nax/context.md");
|
|
96061
|
+
const outputDir = options.output ? join58(workdir, options.output) : workdir;
|
|
95895
96062
|
const autoInject = !options.noAutoInject;
|
|
95896
96063
|
if (!existsSync26(contextPath)) {
|
|
95897
96064
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
@@ -95997,7 +96164,7 @@ async function generateCommand(options) {
|
|
|
95997
96164
|
// src/cli/config-display.ts
|
|
95998
96165
|
init_loader();
|
|
95999
96166
|
import { existsSync as existsSync28 } from "fs";
|
|
96000
|
-
import { join as
|
|
96167
|
+
import { join as join60 } from "path";
|
|
96001
96168
|
|
|
96002
96169
|
// src/cli/config-descriptions.ts
|
|
96003
96170
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -96248,7 +96415,7 @@ function deepEqual(a, b) {
|
|
|
96248
96415
|
init_defaults();
|
|
96249
96416
|
init_loader();
|
|
96250
96417
|
import { existsSync as existsSync27 } from "fs";
|
|
96251
|
-
import { join as
|
|
96418
|
+
import { join as join59 } from "path";
|
|
96252
96419
|
async function loadConfigFile(path19) {
|
|
96253
96420
|
if (!existsSync27(path19))
|
|
96254
96421
|
return null;
|
|
@@ -96270,7 +96437,7 @@ async function loadProjectConfig() {
|
|
|
96270
96437
|
const projectDir = findProjectDir();
|
|
96271
96438
|
if (!projectDir)
|
|
96272
96439
|
return null;
|
|
96273
|
-
const projectPath =
|
|
96440
|
+
const projectPath = join59(projectDir, "config.json");
|
|
96274
96441
|
return await loadConfigFile(projectPath);
|
|
96275
96442
|
}
|
|
96276
96443
|
|
|
@@ -96330,7 +96497,7 @@ async function configCommand(config2, options = {}) {
|
|
|
96330
96497
|
function determineConfigSources() {
|
|
96331
96498
|
const globalPath = globalConfigPath();
|
|
96332
96499
|
const projectDir = findProjectDir();
|
|
96333
|
-
const projectPath = projectDir ?
|
|
96500
|
+
const projectPath = projectDir ? join60(projectDir, "config.json") : null;
|
|
96334
96501
|
return {
|
|
96335
96502
|
global: fileExists(globalPath) ? globalPath : null,
|
|
96336
96503
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
@@ -96479,15 +96646,15 @@ init_paths();
|
|
|
96479
96646
|
init_profile();
|
|
96480
96647
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
96481
96648
|
import { readdirSync as readdirSync6 } from "fs";
|
|
96482
|
-
import { join as
|
|
96649
|
+
import { join as join61 } from "path";
|
|
96483
96650
|
var _profileCLIDeps = {
|
|
96484
96651
|
env: process.env
|
|
96485
96652
|
};
|
|
96486
96653
|
var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
|
|
96487
96654
|
var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
|
|
96488
96655
|
async function profileListCommand(startDir) {
|
|
96489
|
-
const globalProfilesDir =
|
|
96490
|
-
const projectProfilesDir =
|
|
96656
|
+
const globalProfilesDir = join61(globalConfigDir(), "profiles");
|
|
96657
|
+
const projectProfilesDir = join61(projectConfigDir(startDir), "profiles");
|
|
96491
96658
|
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
96492
96659
|
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
96493
96660
|
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
@@ -96546,7 +96713,7 @@ function maskProfileValues(obj) {
|
|
|
96546
96713
|
return result;
|
|
96547
96714
|
}
|
|
96548
96715
|
async function profileUseCommand(profileName, startDir) {
|
|
96549
|
-
const configPath =
|
|
96716
|
+
const configPath = join61(projectConfigDir(startDir), "config.json");
|
|
96550
96717
|
const configFile = Bun.file(configPath);
|
|
96551
96718
|
let existing = {};
|
|
96552
96719
|
if (await configFile.exists()) {
|
|
@@ -96565,8 +96732,8 @@ async function profileCurrentCommand(startDir) {
|
|
|
96565
96732
|
return resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
96566
96733
|
}
|
|
96567
96734
|
async function profileCreateCommand(profileName, startDir) {
|
|
96568
|
-
const profilesDir =
|
|
96569
|
-
const profilePath =
|
|
96735
|
+
const profilesDir = join61(projectConfigDir(startDir), "profiles");
|
|
96736
|
+
const profilePath = join61(profilesDir, `${profileName}.json`);
|
|
96570
96737
|
const profileFile = Bun.file(profilePath);
|
|
96571
96738
|
if (await profileFile.exists()) {
|
|
96572
96739
|
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
@@ -96688,7 +96855,7 @@ async function contextInspectCommand(options) {
|
|
|
96688
96855
|
init_canonical_loader();
|
|
96689
96856
|
init_errors();
|
|
96690
96857
|
import { mkdir as mkdir12 } from "fs/promises";
|
|
96691
|
-
import { basename as basename13, join as
|
|
96858
|
+
import { basename as basename13, join as join62 } from "path";
|
|
96692
96859
|
var _rulesCLIDeps = {
|
|
96693
96860
|
readFile: async (path19) => Bun.file(path19).text(),
|
|
96694
96861
|
writeFile: async (path19, content) => {
|
|
@@ -96697,7 +96864,7 @@ var _rulesCLIDeps = {
|
|
|
96697
96864
|
fileExists: async (path19) => Bun.file(path19).exists(),
|
|
96698
96865
|
globInDir: (dir) => {
|
|
96699
96866
|
try {
|
|
96700
|
-
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) =>
|
|
96867
|
+
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join62(dir, f));
|
|
96701
96868
|
} catch {
|
|
96702
96869
|
return [];
|
|
96703
96870
|
}
|
|
@@ -96746,7 +96913,7 @@ ${r.content}`).join(`
|
|
|
96746
96913
|
`);
|
|
96747
96914
|
const shimContent = `${header + body}
|
|
96748
96915
|
`;
|
|
96749
|
-
const shimPath =
|
|
96916
|
+
const shimPath = join62(workdir, shimFileName);
|
|
96750
96917
|
if (options.dryRun) {
|
|
96751
96918
|
console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
|
|
96752
96919
|
return;
|
|
@@ -96775,14 +96942,14 @@ function neutralizeContent(content) {
|
|
|
96775
96942
|
}
|
|
96776
96943
|
async function collectMigrationSources(workdir) {
|
|
96777
96944
|
const sources = [];
|
|
96778
|
-
const claudeMdPath =
|
|
96945
|
+
const claudeMdPath = join62(workdir, "CLAUDE.md");
|
|
96779
96946
|
if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
|
|
96780
96947
|
const content = await _rulesCLIDeps.readFile(claudeMdPath);
|
|
96781
96948
|
if (content.trim()) {
|
|
96782
96949
|
sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
|
|
96783
96950
|
}
|
|
96784
96951
|
}
|
|
96785
|
-
const rulesDir =
|
|
96952
|
+
const rulesDir = join62(workdir, ".claude", "rules");
|
|
96786
96953
|
const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
|
|
96787
96954
|
for (const filePath of ruleFiles) {
|
|
96788
96955
|
try {
|
|
@@ -96802,7 +96969,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96802
96969
|
console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
|
|
96803
96970
|
return;
|
|
96804
96971
|
}
|
|
96805
|
-
const targetDir =
|
|
96972
|
+
const targetDir = join62(workdir, CANONICAL_RULES_DIR);
|
|
96806
96973
|
if (!options.dryRun) {
|
|
96807
96974
|
try {
|
|
96808
96975
|
await _rulesCLIDeps.mkdir(targetDir);
|
|
@@ -96813,7 +96980,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96813
96980
|
let written = 0;
|
|
96814
96981
|
let skipped = 0;
|
|
96815
96982
|
for (const { sourcePath, targetFileName, content } of sources) {
|
|
96816
|
-
const targetPath =
|
|
96983
|
+
const targetPath = join62(targetDir, targetFileName);
|
|
96817
96984
|
if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
|
|
96818
96985
|
console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
|
|
96819
96986
|
skipped++;
|
|
@@ -96852,7 +97019,7 @@ function collectCanonicalRuleRoots(workdir) {
|
|
|
96852
97019
|
const packageRel = normalized.slice(0, idx);
|
|
96853
97020
|
if (!packageRel)
|
|
96854
97021
|
continue;
|
|
96855
|
-
roots.add(
|
|
97022
|
+
roots.add(join62(workdir, packageRel));
|
|
96856
97023
|
}
|
|
96857
97024
|
return [...roots].sort();
|
|
96858
97025
|
}
|
|
@@ -96874,7 +97041,7 @@ init_logger2();
|
|
|
96874
97041
|
init_detect2();
|
|
96875
97042
|
init_workspace();
|
|
96876
97043
|
init_common();
|
|
96877
|
-
import { join as
|
|
97044
|
+
import { join as join63 } from "path";
|
|
96878
97045
|
function resolveEffective(detected, configPatterns) {
|
|
96879
97046
|
if (configPatterns !== undefined)
|
|
96880
97047
|
return "config";
|
|
@@ -96959,7 +97126,7 @@ async function detectCommand(options) {
|
|
|
96959
97126
|
const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96960
97127
|
const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
|
|
96961
97128
|
const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96962
|
-
const pkgConfigPath =
|
|
97129
|
+
const pkgConfigPath = join63(workdir, ".nax", "mono", dir, "config.json");
|
|
96963
97130
|
const pkgRaw = await loadRawConfig(pkgConfigPath);
|
|
96964
97131
|
const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
|
|
96965
97132
|
const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
|
|
@@ -97013,13 +97180,13 @@ async function detectCommand(options) {
|
|
|
97013
97180
|
if (rootDetected.confidence === "empty") {
|
|
97014
97181
|
console.log(source_default.yellow(" root: skipped (empty detection)"));
|
|
97015
97182
|
} else {
|
|
97016
|
-
const rootConfigPath =
|
|
97183
|
+
const rootConfigPath = join63(workdir, ".nax", "config.json");
|
|
97017
97184
|
try {
|
|
97018
97185
|
const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
|
|
97019
97186
|
if (status === "skipped") {
|
|
97020
97187
|
console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
|
|
97021
97188
|
} else {
|
|
97022
|
-
console.log(source_default.green(` root: ${status} \u2192 ${
|
|
97189
|
+
console.log(source_default.green(` root: ${status} \u2192 ${join63(".nax", "config.json")}`));
|
|
97023
97190
|
}
|
|
97024
97191
|
} catch (err) {
|
|
97025
97192
|
console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
|
|
@@ -97032,13 +97199,13 @@ async function detectCommand(options) {
|
|
|
97032
97199
|
console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
|
|
97033
97200
|
continue;
|
|
97034
97201
|
}
|
|
97035
|
-
const pkgConfigPath =
|
|
97202
|
+
const pkgConfigPath = join63(workdir, ".nax", "mono", dir, "config.json");
|
|
97036
97203
|
try {
|
|
97037
97204
|
const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
|
|
97038
97205
|
if (status === "skipped") {
|
|
97039
97206
|
console.log(source_default.dim(` ${dir}: skipped (already set)`));
|
|
97040
97207
|
} else {
|
|
97041
|
-
console.log(source_default.green(` ${dir}: ${status} \u2192 ${
|
|
97208
|
+
console.log(source_default.green(` ${dir}: ${status} \u2192 ${join63(".nax", "mono", dir, "config.json")}`));
|
|
97042
97209
|
}
|
|
97043
97210
|
} catch (err) {
|
|
97044
97211
|
console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
|
|
@@ -97061,19 +97228,19 @@ async function diagnose(options) {
|
|
|
97061
97228
|
// src/commands/logs.ts
|
|
97062
97229
|
init_common();
|
|
97063
97230
|
import { existsSync as existsSync30 } from "fs";
|
|
97064
|
-
import { join as
|
|
97231
|
+
import { join as join67 } from "path";
|
|
97065
97232
|
|
|
97066
97233
|
// src/commands/logs-formatter.ts
|
|
97067
97234
|
init_source();
|
|
97068
97235
|
init_formatter();
|
|
97069
97236
|
import { readdirSync as readdirSync8 } from "fs";
|
|
97070
|
-
import { join as
|
|
97237
|
+
import { join as join66 } from "path";
|
|
97071
97238
|
|
|
97072
97239
|
// src/commands/logs-reader.ts
|
|
97073
97240
|
init_paths3();
|
|
97074
97241
|
import { existsSync as existsSync29, readdirSync as readdirSync7 } from "fs";
|
|
97075
97242
|
import { readdir as readdir4 } from "fs/promises";
|
|
97076
|
-
import { join as
|
|
97243
|
+
import { join as join65 } from "path";
|
|
97077
97244
|
var _logsReaderDeps = {
|
|
97078
97245
|
getRunsDir
|
|
97079
97246
|
};
|
|
@@ -97087,7 +97254,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
97087
97254
|
}
|
|
97088
97255
|
let matched = null;
|
|
97089
97256
|
for (const entry of entries) {
|
|
97090
|
-
const metaPath =
|
|
97257
|
+
const metaPath = join65(runsDir, entry, "meta.json");
|
|
97091
97258
|
try {
|
|
97092
97259
|
const meta3 = await Bun.file(metaPath).json();
|
|
97093
97260
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -97109,14 +97276,14 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
97109
97276
|
return null;
|
|
97110
97277
|
}
|
|
97111
97278
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
97112
|
-
return
|
|
97279
|
+
return join65(matched.eventsDir, specificFile ?? files[0]);
|
|
97113
97280
|
}
|
|
97114
97281
|
async function selectRunFile(runsDir) {
|
|
97115
97282
|
const files = readdirSync7(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
97116
97283
|
if (files.length === 0) {
|
|
97117
97284
|
return null;
|
|
97118
97285
|
}
|
|
97119
|
-
return
|
|
97286
|
+
return join65(runsDir, files[0]);
|
|
97120
97287
|
}
|
|
97121
97288
|
async function extractRunSummary(filePath) {
|
|
97122
97289
|
const file3 = Bun.file(filePath);
|
|
@@ -97202,7 +97369,7 @@ Runs:
|
|
|
97202
97369
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
97203
97370
|
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
97371
|
for (const file3 of files) {
|
|
97205
|
-
const filePath =
|
|
97372
|
+
const filePath = join66(runsDir, file3);
|
|
97206
97373
|
const summary = await extractRunSummary(filePath);
|
|
97207
97374
|
const timestamp = file3.replace(".jsonl", "");
|
|
97208
97375
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -97316,7 +97483,7 @@ async function logsCommand(options) {
|
|
|
97316
97483
|
return;
|
|
97317
97484
|
}
|
|
97318
97485
|
const resolved = resolveProject({ dir: options.dir });
|
|
97319
|
-
const naxDir =
|
|
97486
|
+
const naxDir = join67(resolved.projectDir, ".nax");
|
|
97320
97487
|
const configPath = resolved.configPath;
|
|
97321
97488
|
const configFile = Bun.file(configPath);
|
|
97322
97489
|
const config2 = await configFile.json();
|
|
@@ -97324,8 +97491,8 @@ async function logsCommand(options) {
|
|
|
97324
97491
|
if (!featureName) {
|
|
97325
97492
|
throw new Error("No feature specified in config.json");
|
|
97326
97493
|
}
|
|
97327
|
-
const featureDir =
|
|
97328
|
-
const runsDir =
|
|
97494
|
+
const featureDir = join67(naxDir, "features", featureName);
|
|
97495
|
+
const runsDir = join67(featureDir, "runs");
|
|
97329
97496
|
if (!existsSync30(runsDir)) {
|
|
97330
97497
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
97331
97498
|
}
|
|
@@ -97351,7 +97518,7 @@ init_prd();
|
|
|
97351
97518
|
init_precheck();
|
|
97352
97519
|
init_common();
|
|
97353
97520
|
import { existsSync as existsSync31 } from "fs";
|
|
97354
|
-
import { join as
|
|
97521
|
+
import { join as join68 } from "path";
|
|
97355
97522
|
async function precheckCommand(options) {
|
|
97356
97523
|
const resolved = resolveProject({
|
|
97357
97524
|
dir: options.dir,
|
|
@@ -97373,9 +97540,9 @@ async function precheckCommand(options) {
|
|
|
97373
97540
|
process.exit(1);
|
|
97374
97541
|
}
|
|
97375
97542
|
}
|
|
97376
|
-
const naxDir =
|
|
97377
|
-
const featureDir =
|
|
97378
|
-
const prdPath =
|
|
97543
|
+
const naxDir = join68(resolved.projectDir, ".nax");
|
|
97544
|
+
const featureDir = join68(naxDir, "features", featureName);
|
|
97545
|
+
const prdPath = join68(featureDir, "prd.json");
|
|
97379
97546
|
if (!existsSync31(featureDir)) {
|
|
97380
97547
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
97381
97548
|
process.exit(1);
|
|
@@ -97398,7 +97565,7 @@ async function precheckCommand(options) {
|
|
|
97398
97565
|
init_source();
|
|
97399
97566
|
init_paths3();
|
|
97400
97567
|
import { readdir as readdir5 } from "fs/promises";
|
|
97401
|
-
import { join as
|
|
97568
|
+
import { join as join69 } from "path";
|
|
97402
97569
|
var DEFAULT_LIMIT = 20;
|
|
97403
97570
|
var _runsCmdDeps = {
|
|
97404
97571
|
getRunsDir
|
|
@@ -97453,7 +97620,7 @@ async function runsCommand(options = {}) {
|
|
|
97453
97620
|
}
|
|
97454
97621
|
const rows = [];
|
|
97455
97622
|
for (const entry of entries) {
|
|
97456
|
-
const metaPath =
|
|
97623
|
+
const metaPath = join69(runsDir, entry, "meta.json");
|
|
97457
97624
|
let meta3;
|
|
97458
97625
|
try {
|
|
97459
97626
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -97530,7 +97697,7 @@ async function runsCommand(options = {}) {
|
|
|
97530
97697
|
|
|
97531
97698
|
// src/commands/unlock.ts
|
|
97532
97699
|
init_source();
|
|
97533
|
-
import { join as
|
|
97700
|
+
import { join as join70 } from "path";
|
|
97534
97701
|
function isProcessAlive3(pid) {
|
|
97535
97702
|
try {
|
|
97536
97703
|
process.kill(pid, 0);
|
|
@@ -97545,7 +97712,7 @@ function formatLockAge(ageMs) {
|
|
|
97545
97712
|
}
|
|
97546
97713
|
async function unlockCommand(options) {
|
|
97547
97714
|
const workdir = options.dir ?? process.cwd();
|
|
97548
|
-
const lockPath =
|
|
97715
|
+
const lockPath = join70(workdir, "nax.lock");
|
|
97549
97716
|
const lockFile = Bun.file(lockPath);
|
|
97550
97717
|
const exists = await lockFile.exists();
|
|
97551
97718
|
if (!exists) {
|
|
@@ -105618,7 +105785,7 @@ Next: nax generate --package ${options.package}`));
|
|
|
105618
105785
|
}
|
|
105619
105786
|
return;
|
|
105620
105787
|
}
|
|
105621
|
-
const naxDir =
|
|
105788
|
+
const naxDir = join84(workdir, ".nax");
|
|
105622
105789
|
if (existsSync37(naxDir) && !options.force) {
|
|
105623
105790
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
105624
105791
|
return;
|
|
@@ -105647,11 +105814,11 @@ Next: nax generate --package ${options.package}`));
|
|
|
105647
105814
|
}
|
|
105648
105815
|
}
|
|
105649
105816
|
}
|
|
105650
|
-
mkdirSync7(
|
|
105651
|
-
mkdirSync7(
|
|
105817
|
+
mkdirSync7(join84(naxDir, "features"), { recursive: true });
|
|
105818
|
+
mkdirSync7(join84(naxDir, "hooks"), { recursive: true });
|
|
105652
105819
|
const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
|
|
105653
|
-
await Bun.write(
|
|
105654
|
-
await Bun.write(
|
|
105820
|
+
await Bun.write(join84(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
|
|
105821
|
+
await Bun.write(join84(naxDir, "hooks.json"), JSON.stringify({
|
|
105655
105822
|
hooks: {
|
|
105656
105823
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
105657
105824
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -105659,12 +105826,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
105659
105826
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
105660
105827
|
}
|
|
105661
105828
|
}, null, 2));
|
|
105662
|
-
await Bun.write(
|
|
105829
|
+
await Bun.write(join84(naxDir, ".gitignore"), `# nax temp files
|
|
105663
105830
|
*.tmp
|
|
105664
105831
|
.paused.json
|
|
105665
105832
|
.nax-verifier-verdict.json
|
|
105666
105833
|
`);
|
|
105667
|
-
await Bun.write(
|
|
105834
|
+
await Bun.write(join84(naxDir, "context.md"), `# Project Context
|
|
105668
105835
|
|
|
105669
105836
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
105670
105837
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -105794,8 +105961,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105794
105961
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105795
105962
|
process.exit(1);
|
|
105796
105963
|
}
|
|
105797
|
-
const featureDir =
|
|
105798
|
-
const prdPath =
|
|
105964
|
+
const featureDir = join84(naxDir, "features", options.feature);
|
|
105965
|
+
const prdPath = join84(featureDir, "prd.json");
|
|
105799
105966
|
if (options.plan && options.from) {
|
|
105800
105967
|
if (existsSync37(prdPath) && !options.force) {
|
|
105801
105968
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
@@ -105817,10 +105984,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105817
105984
|
}
|
|
105818
105985
|
}
|
|
105819
105986
|
try {
|
|
105820
|
-
const planLogDir =
|
|
105987
|
+
const planLogDir = join84(featureDir, "plan");
|
|
105821
105988
|
mkdirSync7(planLogDir, { recursive: true });
|
|
105822
105989
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105823
|
-
const planLogPath =
|
|
105990
|
+
const planLogPath = join84(planLogDir, `${planLogId}.jsonl`);
|
|
105824
105991
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
105825
105992
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
105826
105993
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -105866,10 +106033,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105866
106033
|
resetLogger();
|
|
105867
106034
|
const projectKey = config2.name?.trim() || basename17(workdir);
|
|
105868
106035
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
105869
|
-
const runsDir =
|
|
106036
|
+
const runsDir = join84(outputDir, "features", options.feature, "runs");
|
|
105870
106037
|
mkdirSync7(runsDir, { recursive: true });
|
|
105871
106038
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105872
|
-
const logFilePath =
|
|
106039
|
+
const logFilePath = join84(runsDir, `${runId}.jsonl`);
|
|
105873
106040
|
const isTTY = process.stdout.isTTY ?? false;
|
|
105874
106041
|
const headlessFlag = options.headless ?? false;
|
|
105875
106042
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -105886,7 +106053,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105886
106053
|
config2.agent.default = options.agent;
|
|
105887
106054
|
}
|
|
105888
106055
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
105889
|
-
const globalNaxDir =
|
|
106056
|
+
const globalNaxDir = join84(homedir3(), ".nax");
|
|
105890
106057
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
105891
106058
|
const eventEmitter = new PipelineEventEmitter;
|
|
105892
106059
|
let tuiInstance;
|
|
@@ -105909,7 +106076,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105909
106076
|
} else {
|
|
105910
106077
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
105911
106078
|
}
|
|
105912
|
-
const statusFilePath =
|
|
106079
|
+
const statusFilePath = join84(outputDir, "status.json");
|
|
105913
106080
|
let parallel;
|
|
105914
106081
|
if (options.parallel !== undefined) {
|
|
105915
106082
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -105935,7 +106102,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105935
106102
|
headless: useHeadless,
|
|
105936
106103
|
skipPrecheck: options.skipPrecheck ?? false
|
|
105937
106104
|
});
|
|
105938
|
-
const latestSymlink =
|
|
106105
|
+
const latestSymlink = join84(runsDir, "latest.jsonl");
|
|
105939
106106
|
try {
|
|
105940
106107
|
if (existsSync37(latestSymlink)) {
|
|
105941
106108
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
@@ -105996,9 +106163,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
105996
106163
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105997
106164
|
process.exit(1);
|
|
105998
106165
|
}
|
|
105999
|
-
const featureDir =
|
|
106166
|
+
const featureDir = join84(naxDir, "features", name);
|
|
106000
106167
|
mkdirSync7(featureDir, { recursive: true });
|
|
106001
|
-
await Bun.write(
|
|
106168
|
+
await Bun.write(join84(featureDir, "spec.md"), `# Feature: ${name}
|
|
106002
106169
|
|
|
106003
106170
|
## Overview
|
|
106004
106171
|
|
|
@@ -106031,7 +106198,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
106031
106198
|
|
|
106032
106199
|
<!-- What this feature explicitly does NOT cover. -->
|
|
106033
106200
|
`);
|
|
106034
|
-
await Bun.write(
|
|
106201
|
+
await Bun.write(join84(featureDir, "progress.txt"), `# Progress: ${name}
|
|
106035
106202
|
|
|
106036
106203
|
Created: ${new Date().toISOString()}
|
|
106037
106204
|
|
|
@@ -106057,7 +106224,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
106057
106224
|
console.error(source_default.red("nax not initialized."));
|
|
106058
106225
|
process.exit(1);
|
|
106059
106226
|
}
|
|
106060
|
-
const featuresDir =
|
|
106227
|
+
const featuresDir = join84(naxDir, "features");
|
|
106061
106228
|
if (!existsSync37(featuresDir)) {
|
|
106062
106229
|
console.log(source_default.dim("No features yet."));
|
|
106063
106230
|
return;
|
|
@@ -106072,7 +106239,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
106072
106239
|
Features:
|
|
106073
106240
|
`));
|
|
106074
106241
|
for (const name of entries) {
|
|
106075
|
-
const prdPath =
|
|
106242
|
+
const prdPath = join84(featuresDir, name, "prd.json");
|
|
106076
106243
|
if (existsSync37(prdPath)) {
|
|
106077
106244
|
const prd = await loadPRD(prdPath);
|
|
106078
106245
|
const c = countStories(prd);
|
|
@@ -106107,10 +106274,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
106107
106274
|
cliOverrides.profile = options.profile;
|
|
106108
106275
|
}
|
|
106109
106276
|
const config2 = await loadConfig(workdir, cliOverrides);
|
|
106110
|
-
const featureLogDir =
|
|
106277
|
+
const featureLogDir = join84(naxDir, "features", options.feature, "plan");
|
|
106111
106278
|
mkdirSync7(featureLogDir, { recursive: true });
|
|
106112
106279
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
106113
|
-
const planLogPath =
|
|
106280
|
+
const planLogPath = join84(featureLogDir, `${planLogId}.jsonl`);
|
|
106114
106281
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
106115
106282
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
106116
106283
|
try {
|