@nathapp/nax 0.82.1 → 0.82.2
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/README.md +75 -42
- package/dist/nax.js +922 -573
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -2668,7 +2668,7 @@ var package_default;
|
|
|
2668
2668
|
var init_package = __esm(() => {
|
|
2669
2669
|
package_default = {
|
|
2670
2670
|
name: "@nathapp/nax",
|
|
2671
|
-
version: "0.82.
|
|
2671
|
+
version: "0.82.2",
|
|
2672
2672
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
2673
2673
|
type: "module",
|
|
2674
2674
|
bin: {
|
|
@@ -18398,10 +18398,26 @@ function isNaxConfigFile(root, resolved) {
|
|
|
18398
18398
|
return segments.length === 2 || segments.length >= 4 && segments[1] === "mono";
|
|
18399
18399
|
}
|
|
18400
18400
|
function isNaxOwnedWritePath(rel) {
|
|
18401
|
+
return naxOwnedKind(rel) !== undefined;
|
|
18402
|
+
}
|
|
18403
|
+
function naxOwnedKind(rel) {
|
|
18401
18404
|
const segments = rel.split("/");
|
|
18402
18405
|
if (segments.length === 1 && QUEUE_CONTROL_FILES.has(segments[0] ?? ""))
|
|
18403
|
-
return
|
|
18404
|
-
|
|
18406
|
+
return "queue";
|
|
18407
|
+
if (segments[0] === ".nax" && segments[1] === "features" && segments[segments.length - 1] === "prd.json") {
|
|
18408
|
+
return "prd";
|
|
18409
|
+
}
|
|
18410
|
+
return;
|
|
18411
|
+
}
|
|
18412
|
+
function naxOwnedBashRefusal(tool, kind, hit, verb) {
|
|
18413
|
+
switch (kind) {
|
|
18414
|
+
case "prd":
|
|
18415
|
+
return `${tool} command ${verb} "${hit}", which holds this story's acceptance criteria. ` + "nax updates it itself during the run, so it shows as modified. " + "Bash commands naming it are refused, reads included -- leave it as is. " + "To view it, use the `Read` tool.";
|
|
18416
|
+
case "queue":
|
|
18417
|
+
return `${tool} command ${verb} "${hit}", which is nax's run-control queue. ` + "Bash commands naming it are refused, reads included -- change the run through the queue command.";
|
|
18418
|
+
case "config":
|
|
18419
|
+
return `${tool} command ${verb} "${hit}", which is nax configuration. ` + "Bash commands naming it are refused, reads included -- nax configuration is not changed from inside a run.";
|
|
18420
|
+
}
|
|
18405
18421
|
}
|
|
18406
18422
|
function naxOwnedWriteRefusal(tool, rel, exemptRel) {
|
|
18407
18423
|
if (!NAX_OWNED_WRITE_TOOLS.has(tool))
|
|
@@ -19566,7 +19582,7 @@ function capitalize(text) {
|
|
|
19566
19582
|
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
19567
19583
|
}
|
|
19568
19584
|
function escalateDescription(shell, patterns) {
|
|
19569
|
-
return `Run one shell command string under ${shell}. ${PREFER_STRUCTURED_TOOLS_SENTENCE}` + `${capitalize(describeGrants(patterns))}.
|
|
19585
|
+
return `Run one shell command string under ${shell}. ${PREFER_STRUCTURED_TOOLS_SENTENCE}` + `${capitalize(describeGrants(patterns))}. Every command, granted or not, is checked first: paths ` + "and redirect targets must stay inside the repository root, and `.git/` access, denied flags, unexpanded " + "`$VAR`, glob or brace characters, `~`, a bare `cd`, and a command matching a deny rule are " + "refused without asking. For a command using a construct that cannot be analysed (e.g. command or process " + "substitution, backticks, here-documents, subshells, `2>&1`, `#` comments, an option-shaped `cd`), these " + "checks cover the part before that construct. A command outside the granted forms, or one using a construct " + "that cannot be analysed, is not refused: it is sent to a human for approval (unless an identical command " + "was already approved and remembered) and, if they allow it, runs exactly as written; it is refused if they " + "deny it or do not answer in time, so prefer the granted forms. Each segment of a `&&`/`||`/`;`/`|` chain is " + "checked separately. " + BACKGROUND_PROCESSES_KILLED_SENTENCE;
|
|
19570
19586
|
}
|
|
19571
19587
|
function rawDescription(shell, containment = RAW_UNCONTAINED) {
|
|
19572
19588
|
return `Run one shell command string under ${shell}. ${PREFER_STRUCTURED_TOOLS_SENTENCE}` + "This stage runs under raw mode (ADR-030): pipes, redirects, command substitution ($(...), backticks), " + "process substitution, here-documents and subshells all work here -- nothing is refused for being unparseable, " + "and Bash allow/deny/ask rules configured for this stage are NOT consulted. " + containment + "The only refusal is a command the lexer CAN parse that names or redirects into one of the exact file " + "paths nax owns -- .nax/config.json, .nax/mono/*/config.json, " + ".nax/features/**/prd.json, or the root queue-control files -- change those through nax rather than by " + "writing them directly. That screen is advisory, not a boundary: it matches exact file paths only, so a " + "command using command substitution, a directory target (cp x .nax/), a glob, a nested shell (sh -c '...'), " + "tar -C or dd of=, or a symlink alias all skip it; use the sandbox for a boundary. " + BACKGROUND_PROCESSES_KILLED_SENTENCE;
|
|
@@ -19779,6 +19795,46 @@ var init_delete = __esm(() => {
|
|
|
19779
19795
|
};
|
|
19780
19796
|
});
|
|
19781
19797
|
|
|
19798
|
+
// src/tools/edit-region.ts
|
|
19799
|
+
function countNewlines(text) {
|
|
19800
|
+
let count = 0;
|
|
19801
|
+
for (let i = 0;i < text.length; i += 1) {
|
|
19802
|
+
if (text[i] === `
|
|
19803
|
+
`)
|
|
19804
|
+
count += 1;
|
|
19805
|
+
}
|
|
19806
|
+
return count;
|
|
19807
|
+
}
|
|
19808
|
+
function composeEditRegion({ updated, matchIndex, newStringLength }) {
|
|
19809
|
+
const lines = splitModelLines(updated);
|
|
19810
|
+
const total = lines.length;
|
|
19811
|
+
if (total === 0)
|
|
19812
|
+
return "[file is now empty]";
|
|
19813
|
+
const start = Math.min(total, 1 + countNewlines(updated.slice(0, matchIndex)));
|
|
19814
|
+
const end = newStringLength > 0 ? Math.min(total, 1 + countNewlines(updated.slice(0, matchIndex + newStringLength - 1))) : start;
|
|
19815
|
+
const a = Math.max(1, start - CONTEXT_LINES);
|
|
19816
|
+
const b = Math.min(total, end + CONTEXT_LINES);
|
|
19817
|
+
const header = `[lines ${a}-${b} of ${total}]`;
|
|
19818
|
+
const selected = end - start + 1 > MAX_REGION_LINES_SHOWN ? [
|
|
19819
|
+
...lines.slice(a - 1, start + 2),
|
|
19820
|
+
`[... lines ${start + 3}-${end - 3} not shown ...]`,
|
|
19821
|
+
...lines.slice(end - 3, b)
|
|
19822
|
+
] : lines.slice(a - 1, b);
|
|
19823
|
+
let view = [header, ...selected].join(`
|
|
19824
|
+
`);
|
|
19825
|
+
while (view.endsWith(`
|
|
19826
|
+
`))
|
|
19827
|
+
view = view.slice(0, -1);
|
|
19828
|
+
return view;
|
|
19829
|
+
}
|
|
19830
|
+
function replaceUniqueLiteral(source, oldString, newString, matchIndex) {
|
|
19831
|
+
return source.slice(0, matchIndex) + newString + source.slice(matchIndex + oldString.length);
|
|
19832
|
+
}
|
|
19833
|
+
var CONTEXT_LINES = 3, MAX_REGION_LINES_SHOWN = 8;
|
|
19834
|
+
var init_edit_region = __esm(() => {
|
|
19835
|
+
init_truncate();
|
|
19836
|
+
});
|
|
19837
|
+
|
|
19782
19838
|
// src/tools/edit.ts
|
|
19783
19839
|
import { readFile, stat, writeFile } from "fs/promises";
|
|
19784
19840
|
function countOccurrences(haystack, needle) {
|
|
@@ -19792,11 +19848,17 @@ function countOccurrences(haystack, needle) {
|
|
|
19792
19848
|
}
|
|
19793
19849
|
return count;
|
|
19794
19850
|
}
|
|
19795
|
-
var editTool;
|
|
19851
|
+
var _editDeps, editTool;
|
|
19796
19852
|
var init_edit = __esm(() => {
|
|
19853
|
+
init_edit_region();
|
|
19854
|
+
_editDeps = {
|
|
19855
|
+
stat: (path) => stat(path),
|
|
19856
|
+
readFile: (path, encoding) => readFile(path, encoding),
|
|
19857
|
+
writeFile: (path, data, encoding) => writeFile(path, data, encoding)
|
|
19858
|
+
};
|
|
19797
19859
|
editTool = {
|
|
19798
19860
|
name: "Edit",
|
|
19799
|
-
description: "Replace one exact occurrence of old_string with new_string in a repository file. Fails if the match is absent or ambiguous.",
|
|
19861
|
+
description: "Replace one exact occurrence of old_string with new_string in a repository file. Fails if the match is absent or ambiguous. On success the result shows the edited lines with up to 3 lines of context and their line range, so you do not need to Read the file again to check the edit.",
|
|
19800
19862
|
inputSchema: {
|
|
19801
19863
|
type: "object",
|
|
19802
19864
|
properties: {
|
|
@@ -19817,7 +19879,7 @@ var init_edit = __esm(() => {
|
|
|
19817
19879
|
return { content: "old_string and new_string must be strings", isError: true };
|
|
19818
19880
|
}
|
|
19819
19881
|
try {
|
|
19820
|
-
const { size } = await stat(target);
|
|
19882
|
+
const { size } = await _editDeps.stat(target);
|
|
19821
19883
|
if (size > ctx.maxFileBytes) {
|
|
19822
19884
|
return {
|
|
19823
19885
|
content: `the file is ${size} bytes, which exceeds the ${ctx.maxFileBytes}-byte file ceiling -- refusing to edit ${target}`,
|
|
@@ -19829,7 +19891,7 @@ var init_edit = __esm(() => {
|
|
|
19829
19891
|
}
|
|
19830
19892
|
let source;
|
|
19831
19893
|
try {
|
|
19832
|
-
source = await readFile(target, "utf8");
|
|
19894
|
+
source = await _editDeps.readFile(target, "utf8");
|
|
19833
19895
|
} catch (err) {
|
|
19834
19896
|
return { content: err instanceof Error ? err.message : String(err), isError: true };
|
|
19835
19897
|
}
|
|
@@ -19844,8 +19906,12 @@ var init_edit = __esm(() => {
|
|
|
19844
19906
|
};
|
|
19845
19907
|
}
|
|
19846
19908
|
try {
|
|
19847
|
-
|
|
19848
|
-
|
|
19909
|
+
const matchIndex = source.indexOf(oldString);
|
|
19910
|
+
const updated = replaceUniqueLiteral(source, oldString, newString, matchIndex);
|
|
19911
|
+
await _editDeps.writeFile(target, updated, "utf8");
|
|
19912
|
+
const region = composeEditRegion({ updated, matchIndex, newStringLength: newString.length });
|
|
19913
|
+
return { content: `edited ${target}
|
|
19914
|
+
${region}` };
|
|
19849
19915
|
} catch (err) {
|
|
19850
19916
|
return { content: err instanceof Error ? err.message : String(err), isError: true };
|
|
19851
19917
|
}
|
|
@@ -20927,9 +20993,6 @@ var ASK_NO_CHANNEL_REASON = "matched an ask rule requiring human approval; no ap
|
|
|
20927
20993
|
var init_ask = () => {};
|
|
20928
20994
|
|
|
20929
20995
|
// src/permissions/bash-lex.ts
|
|
20930
|
-
function refused(construct) {
|
|
20931
|
-
return { kind: "refused", construct };
|
|
20932
|
-
}
|
|
20933
20996
|
function doubleQuoteEnd(command, from) {
|
|
20934
20997
|
for (let i = from;i < command.length; i += 1) {
|
|
20935
20998
|
const char = command[i];
|
|
@@ -20944,7 +21007,7 @@ function doubleQuoteEnd(command, from) {
|
|
|
20944
21007
|
}
|
|
20945
21008
|
function lexBashCommand(command) {
|
|
20946
21009
|
if (command.trim() === "")
|
|
20947
|
-
return refused
|
|
21010
|
+
return { kind: "refused", construct: "an empty command", prefix: [] };
|
|
20948
21011
|
const segments = [];
|
|
20949
21012
|
let tokens = [];
|
|
20950
21013
|
let redirects = [];
|
|
@@ -20952,6 +21015,10 @@ function lexBashCommand(command) {
|
|
|
20952
21015
|
let opaque = false;
|
|
20953
21016
|
let started = false;
|
|
20954
21017
|
let pendingRedirect;
|
|
21018
|
+
function refusedHere(construct) {
|
|
21019
|
+
const prefix = tokens.length === 0 && redirects.length === 0 ? [...segments] : [...segments, { tokens, redirects }];
|
|
21020
|
+
return { kind: "refused", construct, prefix };
|
|
21021
|
+
}
|
|
20955
21022
|
function flushWord() {
|
|
20956
21023
|
if (!started)
|
|
20957
21024
|
return;
|
|
@@ -20983,7 +21050,7 @@ function lexBashCommand(command) {
|
|
|
20983
21050
|
if (char === "'") {
|
|
20984
21051
|
const end = command.indexOf("'", i + 1);
|
|
20985
21052
|
if (end === -1)
|
|
20986
|
-
return
|
|
21053
|
+
return refusedHere("an unbalanced single quote");
|
|
20987
21054
|
word += command.slice(i + 1, end);
|
|
20988
21055
|
started = true;
|
|
20989
21056
|
i = end + 1;
|
|
@@ -20992,12 +21059,12 @@ function lexBashCommand(command) {
|
|
|
20992
21059
|
if (char === '"') {
|
|
20993
21060
|
const end = doubleQuoteEnd(command, i + 1);
|
|
20994
21061
|
if (end === -1)
|
|
20995
|
-
return
|
|
21062
|
+
return refusedHere("an unbalanced double quote");
|
|
20996
21063
|
const inner = command.slice(i + 1, end);
|
|
20997
21064
|
if (inner.includes("$("))
|
|
20998
|
-
return
|
|
21065
|
+
return refusedHere("a command substitution `$(...)`");
|
|
20999
21066
|
if (inner.includes("`"))
|
|
21000
|
-
return
|
|
21067
|
+
return refusedHere("a backtick command substitution");
|
|
21001
21068
|
if (inner.includes("$"))
|
|
21002
21069
|
opaque = true;
|
|
21003
21070
|
word += inner;
|
|
@@ -21007,32 +21074,32 @@ function lexBashCommand(command) {
|
|
|
21007
21074
|
}
|
|
21008
21075
|
if (char === "\\") {
|
|
21009
21076
|
if (next === undefined)
|
|
21010
|
-
return
|
|
21077
|
+
return refusedHere("a trailing backslash");
|
|
21011
21078
|
word += next;
|
|
21012
21079
|
started = true;
|
|
21013
21080
|
i += 2;
|
|
21014
21081
|
continue;
|
|
21015
21082
|
}
|
|
21016
21083
|
if (char === "(" || char === ")")
|
|
21017
|
-
return
|
|
21084
|
+
return refusedHere("a subshell `( ... )`");
|
|
21018
21085
|
if (char === "!" && !started)
|
|
21019
|
-
return
|
|
21086
|
+
return refusedHere("a `!` negation");
|
|
21020
21087
|
if (char === "#" && !started)
|
|
21021
|
-
return
|
|
21088
|
+
return refusedHere("a `#` comment");
|
|
21022
21089
|
if (char === "$" && next === "(")
|
|
21023
|
-
return
|
|
21090
|
+
return refusedHere("a command substitution `$(...)`");
|
|
21024
21091
|
if (char === "`")
|
|
21025
|
-
return
|
|
21092
|
+
return refusedHere("a backtick command substitution");
|
|
21026
21093
|
if ((char === "<" || char === ">") && next === "(") {
|
|
21027
|
-
return
|
|
21094
|
+
return refusedHere("a process substitution `<(...)` / `>(...)`");
|
|
21028
21095
|
}
|
|
21029
21096
|
if (char === "<" && next === "<")
|
|
21030
|
-
return
|
|
21097
|
+
return refusedHere("a here-document `<<`");
|
|
21031
21098
|
if ((char === "<" || char === ">") && next === "&") {
|
|
21032
|
-
return
|
|
21099
|
+
return refusedHere("file-descriptor duplication (`2>&1`)");
|
|
21033
21100
|
}
|
|
21034
21101
|
if (char === "&" && next === ">")
|
|
21035
|
-
return
|
|
21102
|
+
return refusedHere("the `&>` redirection form");
|
|
21036
21103
|
if (char === "$") {
|
|
21037
21104
|
opaque = true;
|
|
21038
21105
|
word += char;
|
|
@@ -21049,21 +21116,21 @@ function lexBashCommand(command) {
|
|
|
21049
21116
|
` || char === ";") {
|
|
21050
21117
|
const error49 = flushSegment(";");
|
|
21051
21118
|
if (error49 !== undefined)
|
|
21052
|
-
return
|
|
21119
|
+
return refusedHere(error49);
|
|
21053
21120
|
i += 1;
|
|
21054
21121
|
continue;
|
|
21055
21122
|
}
|
|
21056
21123
|
if (char === "&" && next === "&" || char === "|" && next === "|") {
|
|
21057
21124
|
const error49 = flushSegment(char === "&" ? "&&" : "||");
|
|
21058
21125
|
if (error49 !== undefined)
|
|
21059
|
-
return
|
|
21126
|
+
return refusedHere(error49);
|
|
21060
21127
|
i += 2;
|
|
21061
21128
|
continue;
|
|
21062
21129
|
}
|
|
21063
21130
|
if (char === "|" || char === "&") {
|
|
21064
21131
|
const error49 = flushSegment(char);
|
|
21065
21132
|
if (error49 !== undefined)
|
|
21066
|
-
return
|
|
21133
|
+
return refusedHere(error49);
|
|
21067
21134
|
i += 1;
|
|
21068
21135
|
continue;
|
|
21069
21136
|
}
|
|
@@ -21088,7 +21155,7 @@ function lexBashCommand(command) {
|
|
|
21088
21155
|
}
|
|
21089
21156
|
const error48 = flushSegment();
|
|
21090
21157
|
if (error48 !== undefined)
|
|
21091
|
-
return
|
|
21158
|
+
return refusedHere(error48);
|
|
21092
21159
|
return { kind: "ok", segments };
|
|
21093
21160
|
}
|
|
21094
21161
|
|
|
@@ -21219,7 +21286,9 @@ function checkPayload(args, segment, cwd) {
|
|
|
21219
21286
|
case "no-target":
|
|
21220
21287
|
return { refusal: deny("`cd` with no target is refused") };
|
|
21221
21288
|
case "option-shaped":
|
|
21222
|
-
return {
|
|
21289
|
+
return {
|
|
21290
|
+
refusal: deny(`cd target "${cdResult.text}" is option-shaped, and this gate does not model it`, false, true)
|
|
21291
|
+
};
|
|
21223
21292
|
case "opaque":
|
|
21224
21293
|
case "unresolved":
|
|
21225
21294
|
return { refusal: deny(`cd target "${cdResult.text}" is not inside the permitted root`, true) };
|
|
@@ -21256,29 +21325,30 @@ function checkBashCommand(args) {
|
|
|
21256
21325
|
if (command.trim() === "")
|
|
21257
21326
|
return deny(`"command" must not be empty`);
|
|
21258
21327
|
const lexed = lexBashCommand(command);
|
|
21259
|
-
|
|
21260
|
-
|
|
21261
|
-
}
|
|
21262
|
-
for (const segment of lexed.segments) {
|
|
21328
|
+
const segments = lexed.kind === "ok" ? lexed.segments : lexed.prefix;
|
|
21329
|
+
for (const segment of segments) {
|
|
21263
21330
|
if (args.denyEntry !== undefined && matchesSegment(args.denyEntry, segment)) {
|
|
21264
21331
|
return deny(`${tool} segment "${render(segment)}" is denied for this stage by rule ${ruleExpr(tool, args.denyEntry, segment)}`);
|
|
21265
21332
|
}
|
|
21266
21333
|
}
|
|
21267
|
-
for (const segment of lexed.segments) {
|
|
21268
|
-
if (args.grant.unconditional || matchesSegment(args.grant, segment))
|
|
21269
|
-
continue;
|
|
21270
|
-
const granted = args.grant.raw.filter((pattern) => pattern !== "*").join(", ");
|
|
21271
|
-
const alternatives = granted === "" ? "no command forms are granted for this stage" : `granted forms: ${granted}`;
|
|
21272
|
-
return deny(`${tool} is not granted "${render(segment)}" -- ${alternatives}`, false, true);
|
|
21273
|
-
}
|
|
21274
21334
|
let cwd = [args.initialPath];
|
|
21275
|
-
for (const segment of
|
|
21335
|
+
for (const segment of segments) {
|
|
21276
21336
|
const result = checkPayload(args, segment, cwd);
|
|
21277
21337
|
if (result.refusal !== undefined)
|
|
21278
21338
|
return result.refusal;
|
|
21279
21339
|
cwd = nextWorkingDirectories(segment, cwd, result.cdTargets);
|
|
21280
21340
|
}
|
|
21281
|
-
|
|
21341
|
+
if (lexed.kind === "refused") {
|
|
21342
|
+
return deny(`command contains ${lexed.construct}, which cannot be analysed and is therefore refused -- ` + "rewrite it without that construct, or use a structured tool", false, true);
|
|
21343
|
+
}
|
|
21344
|
+
for (const segment of segments) {
|
|
21345
|
+
if (args.grant.unconditional || matchesSegment(args.grant, segment))
|
|
21346
|
+
continue;
|
|
21347
|
+
const granted = args.grant.raw.filter((pattern) => pattern !== "*").join(", ");
|
|
21348
|
+
const alternatives = granted === "" ? "no command forms are granted for this stage" : `granted forms: ${granted}`;
|
|
21349
|
+
return deny(`${tool} is not granted "${render(segment)}" -- ${alternatives}`, false, true);
|
|
21350
|
+
}
|
|
21351
|
+
for (const segment of segments) {
|
|
21282
21352
|
if (args.askEntry !== undefined && matchesSegment(args.askEntry, segment)) {
|
|
21283
21353
|
return { kind: "ask", rule: ruleExpr(tool, args.askEntry, segment) };
|
|
21284
21354
|
}
|
|
@@ -21299,15 +21369,16 @@ function protectedHit(args, candidate, cwd) {
|
|
|
21299
21369
|
for (const directory of cwd) {
|
|
21300
21370
|
const lexical = realOrRaw(resolve10(directory, candidate));
|
|
21301
21371
|
if (isNaxConfigFile(args.root, lexical))
|
|
21302
|
-
return candidate;
|
|
21372
|
+
return { kind: "config", hit: candidate };
|
|
21303
21373
|
const resolved = args.resolvePath(candidate, directory);
|
|
21304
21374
|
if (resolved === null)
|
|
21305
21375
|
continue;
|
|
21306
21376
|
const rel = relative5(args.root, resolved).split(sep4).join("/");
|
|
21307
21377
|
if (rel.startsWith(".."))
|
|
21308
21378
|
continue;
|
|
21309
|
-
|
|
21310
|
-
|
|
21379
|
+
const kind = naxOwnedKind(rel);
|
|
21380
|
+
if (kind !== undefined)
|
|
21381
|
+
return { kind, hit: candidate };
|
|
21311
21382
|
}
|
|
21312
21383
|
return;
|
|
21313
21384
|
}
|
|
@@ -21327,7 +21398,7 @@ function screenRawBashCommand(args) {
|
|
|
21327
21398
|
continue;
|
|
21328
21399
|
const hit = protectedHit(args, token.text, cwd);
|
|
21329
21400
|
if (hit !== undefined) {
|
|
21330
|
-
return deny2(
|
|
21401
|
+
return deny2(naxOwnedBashRefusal(tool, hit.kind, hit.hit, "names"));
|
|
21331
21402
|
}
|
|
21332
21403
|
}
|
|
21333
21404
|
for (const redirect of segment.redirects) {
|
|
@@ -21335,7 +21406,7 @@ function screenRawBashCommand(args) {
|
|
|
21335
21406
|
continue;
|
|
21336
21407
|
const hit = protectedHit(args, redirect.target, cwd);
|
|
21337
21408
|
if (hit !== undefined) {
|
|
21338
|
-
return deny2(
|
|
21409
|
+
return deny2(naxOwnedBashRefusal(tool, hit.kind, hit.hit, "redirects into"));
|
|
21339
21410
|
}
|
|
21340
21411
|
}
|
|
21341
21412
|
const cdResult = cdTargetsFor(segment, cwd, args.resolvePath);
|
|
@@ -22593,6 +22664,47 @@ var init_provider_advertise = __esm(() => {
|
|
|
22593
22664
|
init_provider_types();
|
|
22594
22665
|
});
|
|
22595
22666
|
|
|
22667
|
+
// src/tools/read-continuation.ts
|
|
22668
|
+
function limitStopFooter(input) {
|
|
22669
|
+
const { nextOffset, totalIsFloor, endLine, totalLines } = input;
|
|
22670
|
+
const remaining = Math.max(0, totalLines - endLine);
|
|
22671
|
+
const countLabel = totalIsFloor ? `${remaining}+` : `${remaining}`;
|
|
22672
|
+
return `[${countLabel} more lines in file. Use offset=${nextOffset} to continue.]`;
|
|
22673
|
+
}
|
|
22674
|
+
function shouldAppendLimitStopFooter(hasLimit, endLine, totalLines) {
|
|
22675
|
+
return hasLimit && endLine < totalLines;
|
|
22676
|
+
}
|
|
22677
|
+
function capFooter(input) {
|
|
22678
|
+
const { firstLine: firstLine2, lastLine, totalLabel } = input;
|
|
22679
|
+
return `[Showing lines ${firstLine2}-${lastLine} of ${totalLabel}. Use offset=${lastLine + 1} to continue.]`;
|
|
22680
|
+
}
|
|
22681
|
+
function applyCapCut(input) {
|
|
22682
|
+
const { header, lines, firstLine: firstLine2, totalLabel, limitStopFooter: limitStopFooter2, unshapedBody, maxBytes, maxLines } = input;
|
|
22683
|
+
const candidate = limitStopFooter2 === "" ? unshapedBody : `${unshapedBody}
|
|
22684
|
+
${limitStopFooter2}`;
|
|
22685
|
+
if (splitModelLines(candidate).length <= maxLines && Buffer.byteLength(candidate, "utf8") <= maxBytes) {
|
|
22686
|
+
return { content: candidate };
|
|
22687
|
+
}
|
|
22688
|
+
for (let k = Math.min(lines.length, maxLines - 2);k >= 1; k -= 1) {
|
|
22689
|
+
if (1 + k + 1 > maxLines)
|
|
22690
|
+
continue;
|
|
22691
|
+
const lastLine = firstLine2 + k - 1;
|
|
22692
|
+
const footer = capFooter({ firstLine: firstLine2, lastLine, totalLabel });
|
|
22693
|
+
const bodyJoined = lines.slice(0, k).join(`
|
|
22694
|
+
`);
|
|
22695
|
+
const cut = `${header}
|
|
22696
|
+
${bodyJoined}
|
|
22697
|
+
${footer}`;
|
|
22698
|
+
if (Buffer.byteLength(cut, "utf8") <= maxBytes) {
|
|
22699
|
+
return { content: cut };
|
|
22700
|
+
}
|
|
22701
|
+
}
|
|
22702
|
+
return { content: candidate };
|
|
22703
|
+
}
|
|
22704
|
+
var init_read_continuation = __esm(() => {
|
|
22705
|
+
init_truncate();
|
|
22706
|
+
});
|
|
22707
|
+
|
|
22596
22708
|
// src/tools/read.ts
|
|
22597
22709
|
function parsePositiveInt(value, field) {
|
|
22598
22710
|
if (typeof value !== "number" || !Number.isInteger(value))
|
|
@@ -22612,11 +22724,12 @@ function countLines(prefix) {
|
|
|
22612
22724
|
var UNSUPPORTED_RANGE_ALIASES, readTool;
|
|
22613
22725
|
var init_read = __esm(() => {
|
|
22614
22726
|
init_bounded_io();
|
|
22727
|
+
init_read_continuation();
|
|
22615
22728
|
init_truncate();
|
|
22616
22729
|
UNSUPPORTED_RANGE_ALIASES = ["start_line", "end_line", "start", "end", "line", "lineEnd", "size"];
|
|
22617
22730
|
readTool = {
|
|
22618
22731
|
name: "Read",
|
|
22619
|
-
description: "Read a UTF-8 text file from the repository. Paths are relative to the repository root. " + "Optionally pass offset (1-based line number to start from) and/or limit (maximum number of " + "lines to return) to read a slice instead of the whole file.",
|
|
22732
|
+
description: "Read a UTF-8 text file from the repository. Paths are relative to the repository root. " + "Optionally pass offset (1-based line number to start from) and/or limit (maximum number of " + "lines to return) to read a slice instead of the whole file. " + "Use Read to examine files instead of cat, sed, head, tail or awk in Bash. " + "A read that stops before the end of the file ends with a line naming the offset to continue from. " + "For a large file, read the part you need with offset/limit; when you need the whole file, continue with offset until complete. " + `Output is capped at ${MODEL_MAX_LINES} lines or ${MODEL_MAX_BYTES} bytes.`,
|
|
22620
22733
|
inputSchema: {
|
|
22621
22734
|
type: "object",
|
|
22622
22735
|
properties: {
|
|
@@ -22646,9 +22759,21 @@ var init_read = __esm(() => {
|
|
|
22646
22759
|
const prefix = await readPrefix(target, readCeiling);
|
|
22647
22760
|
const bounded2 = Buffer.byteLength(prefix, "utf8") > readCeiling;
|
|
22648
22761
|
const lineCount = countLines(prefix);
|
|
22649
|
-
const
|
|
22650
|
-
|
|
22651
|
-
|
|
22762
|
+
const totalLabel2 = bounded2 ? `${lineCount}+` : `${lineCount}`;
|
|
22763
|
+
const header = `[${totalLabel2} lines]`;
|
|
22764
|
+
const unshaped = prefix === "" ? header : `${header}
|
|
22765
|
+
${prefix}`;
|
|
22766
|
+
const result2 = applyCapCut({
|
|
22767
|
+
header,
|
|
22768
|
+
lines: splitModelLines(prefix),
|
|
22769
|
+
firstLine: 1,
|
|
22770
|
+
totalLabel: totalLabel2,
|
|
22771
|
+
limitStopFooter: "",
|
|
22772
|
+
unshapedBody: unshaped,
|
|
22773
|
+
maxBytes: ctx.maxBytes,
|
|
22774
|
+
maxLines: MODEL_MAX_LINES
|
|
22775
|
+
});
|
|
22776
|
+
return { content: result2.content };
|
|
22652
22777
|
}
|
|
22653
22778
|
let offset = 1;
|
|
22654
22779
|
if (hasOffset) {
|
|
@@ -22666,25 +22791,38 @@ ${prefix}` };
|
|
|
22666
22791
|
}
|
|
22667
22792
|
const body = await readPrefix(target, ctx.maxFileBytes);
|
|
22668
22793
|
const bounded = Buffer.byteLength(body, "utf8") > ctx.maxFileBytes;
|
|
22669
|
-
const
|
|
22670
|
-
`);
|
|
22671
|
-
const lines = trailingNewline ? body.slice(0, -1).split(`
|
|
22672
|
-
`) : body.split(`
|
|
22673
|
-
`);
|
|
22674
|
-
const totalLines = lines.length;
|
|
22794
|
+
const totalLines = countLines(body);
|
|
22675
22795
|
const totalLabel = bounded ? `${totalLines}+` : `${totalLines}`;
|
|
22676
22796
|
if (offset > totalLines) {
|
|
22677
22797
|
return {
|
|
22678
22798
|
content: bounded ? `offset ${offset} is past the first ${totalLines} lines, which is all that could be read within the ${ctx.maxFileBytes}-byte ceiling` : `offset ${offset} is past the end of the file -- it has ${totalLines} lines`
|
|
22679
22799
|
};
|
|
22680
22800
|
}
|
|
22801
|
+
const lines = splitModelLines(body);
|
|
22681
22802
|
const startIndex = offset - 1;
|
|
22682
22803
|
const endLine = limit === undefined ? totalLines : Math.min(startIndex + limit, totalLines);
|
|
22683
22804
|
const selected = lines.slice(startIndex, endLine).join(`
|
|
22684
22805
|
`);
|
|
22685
|
-
const
|
|
22686
|
-
|
|
22687
|
-
|
|
22806
|
+
const headerLine = `[lines ${offset}-${endLine} of ${totalLabel}]`;
|
|
22807
|
+
const limitStop = shouldAppendLimitStopFooter(limit !== undefined, endLine, totalLines) ? limitStopFooter({
|
|
22808
|
+
nextOffset: endLine + 1,
|
|
22809
|
+
totalIsFloor: bounded,
|
|
22810
|
+
endLine,
|
|
22811
|
+
totalLines
|
|
22812
|
+
}) : "";
|
|
22813
|
+
const unshapedBody = `${headerLine}
|
|
22814
|
+
${selected}`;
|
|
22815
|
+
const result = applyCapCut({
|
|
22816
|
+
header: headerLine,
|
|
22817
|
+
lines: lines.slice(startIndex, endLine),
|
|
22818
|
+
firstLine: offset,
|
|
22819
|
+
totalLabel,
|
|
22820
|
+
limitStopFooter: limitStop,
|
|
22821
|
+
unshapedBody,
|
|
22822
|
+
maxBytes: ctx.maxBytes,
|
|
22823
|
+
maxLines: MODEL_MAX_LINES
|
|
22824
|
+
});
|
|
22825
|
+
return { content: result.content };
|
|
22688
22826
|
} catch (err) {
|
|
22689
22827
|
return { content: err instanceof Error ? err.message : String(err), isError: true };
|
|
22690
22828
|
}
|
|
@@ -24199,7 +24337,7 @@ var init_scratchpad = __esm(() => {
|
|
|
24199
24337
|
init_truncate();
|
|
24200
24338
|
scratchpadWriteTool = {
|
|
24201
24339
|
name: "ScratchpadWrite",
|
|
24202
|
-
description: "Write a throwaway file to your scratchpad at .nax/scratchpad/. Use it for notes to yourself, command output you want to re-read, or
|
|
24340
|
+
description: "Write a throwaway file to your scratchpad at .nax/scratchpad/. Use it for notes to yourself, command output you want to re-read, intermediate lists, or a probe script to run against the project's code. It is never committed and is wiped when a run finishes (a failed run's scratchpad is retained for inspection until the next run starts and clears it). Paths are relative to the scratchpad and cannot reach the repository.",
|
|
24203
24341
|
inputSchema: {
|
|
24204
24342
|
type: "object",
|
|
24205
24343
|
properties: {
|
|
@@ -24441,33 +24579,97 @@ var init_spill = __esm(() => {
|
|
|
24441
24579
|
// src/tools/tool-audit.ts
|
|
24442
24580
|
import { mkdir as mkdir7, writeFile as writeFile5 } from "fs/promises";
|
|
24443
24581
|
import { join as join15 } from "path";
|
|
24582
|
+
function registerToolAuditSink(runId, sink) {
|
|
24583
|
+
const sinks = openToolAuditSinks.get(runId);
|
|
24584
|
+
if (sinks === undefined) {
|
|
24585
|
+
openToolAuditSinks.set(runId, new Set([sink]));
|
|
24586
|
+
return;
|
|
24587
|
+
}
|
|
24588
|
+
sinks.add(sink);
|
|
24589
|
+
}
|
|
24590
|
+
function unregisterToolAuditSink(runId, sink) {
|
|
24591
|
+
const sinks = openToolAuditSinks.get(runId);
|
|
24592
|
+
if (sinks === undefined)
|
|
24593
|
+
return;
|
|
24594
|
+
sinks.delete(sink);
|
|
24595
|
+
if (sinks.size === 0)
|
|
24596
|
+
openToolAuditSinks.delete(runId);
|
|
24597
|
+
}
|
|
24598
|
+
async function flushOpenToolAuditSinks(runId) {
|
|
24599
|
+
const sinks = openToolAuditSinks.get(runId);
|
|
24600
|
+
if (sinks === undefined)
|
|
24601
|
+
return;
|
|
24602
|
+
openToolAuditSinks.delete(runId);
|
|
24603
|
+
await Promise.all([...sinks].map(async (sink) => {
|
|
24604
|
+
try {
|
|
24605
|
+
await sink.flushPartial();
|
|
24606
|
+
} catch (error48) {
|
|
24607
|
+
getSafeLogger()?.warn("tools", "tool-audit partial flush failed", { runId, error: errorMessage(error48) });
|
|
24608
|
+
}
|
|
24609
|
+
}));
|
|
24610
|
+
}
|
|
24444
24611
|
function createNoOpToolAuditSink() {
|
|
24445
24612
|
return { record() {}, async flush() {} };
|
|
24446
24613
|
}
|
|
24614
|
+
function nextFileStamp() {
|
|
24615
|
+
const now = Date.now();
|
|
24616
|
+
lastFileStamp = now > lastFileStamp ? now : lastFileStamp + 1;
|
|
24617
|
+
return lastFileStamp;
|
|
24618
|
+
}
|
|
24447
24619
|
function createToolAuditSink(opts) {
|
|
24448
24620
|
const calls = [];
|
|
24449
|
-
|
|
24621
|
+
const runId = opts.header?.runId;
|
|
24622
|
+
let partialFlushed = false;
|
|
24623
|
+
const write = async (partial2) => {
|
|
24624
|
+
if (calls.length === 0)
|
|
24625
|
+
return;
|
|
24626
|
+
await mkdir7(opts.dir, { recursive: true });
|
|
24627
|
+
const body = JSON.stringify({
|
|
24628
|
+
schemaVersion: TOOL_AUDIT_SCHEMA_VERSION,
|
|
24629
|
+
...opts.header ?? {},
|
|
24630
|
+
sessionName: opts.sessionName,
|
|
24631
|
+
...partial2 ? { partial: true } : {},
|
|
24632
|
+
calls: redactRowStrings(calls)
|
|
24633
|
+
}, null, 2);
|
|
24634
|
+
const prefix = runId !== undefined ? `${runId}-` : "";
|
|
24635
|
+
await writeFile5(join15(opts.dir, `${prefix}${nextFileStamp()}-${opts.sessionName}.json`), body);
|
|
24636
|
+
};
|
|
24637
|
+
const sink = {
|
|
24450
24638
|
record(entry) {
|
|
24639
|
+
if (partialFlushed) {
|
|
24640
|
+
getSafeLogger()?.warn("tools", "tool-audit row recorded after the partial flush was dropped", {
|
|
24641
|
+
runId,
|
|
24642
|
+
tool: entry.tool
|
|
24643
|
+
});
|
|
24644
|
+
return;
|
|
24645
|
+
}
|
|
24451
24646
|
calls.push(entry);
|
|
24452
24647
|
},
|
|
24648
|
+
async flushPartial() {
|
|
24649
|
+
if (partialFlushed)
|
|
24650
|
+
return;
|
|
24651
|
+
await write(true);
|
|
24652
|
+
partialFlushed = true;
|
|
24653
|
+
if (runId !== undefined)
|
|
24654
|
+
unregisterToolAuditSink(runId, sink);
|
|
24655
|
+
},
|
|
24453
24656
|
async flush() {
|
|
24454
|
-
if (
|
|
24657
|
+
if (partialFlushed)
|
|
24455
24658
|
return;
|
|
24456
|
-
|
|
24457
|
-
|
|
24458
|
-
|
|
24459
|
-
...opts.header ?? {},
|
|
24460
|
-
sessionName: opts.sessionName,
|
|
24461
|
-
calls: redactRowStrings(calls)
|
|
24462
|
-
}, null, 2);
|
|
24463
|
-
const prefix = opts.header?.runId !== undefined ? `${opts.header.runId}-` : "";
|
|
24464
|
-
await writeFile5(join15(opts.dir, `${prefix}${Date.now()}-${opts.sessionName}.json`), body);
|
|
24659
|
+
if (runId !== undefined)
|
|
24660
|
+
unregisterToolAuditSink(runId, sink);
|
|
24661
|
+
await write(false);
|
|
24465
24662
|
}
|
|
24466
24663
|
};
|
|
24664
|
+
if (runId !== undefined)
|
|
24665
|
+
registerToolAuditSink(runId, sink);
|
|
24666
|
+
return sink;
|
|
24467
24667
|
}
|
|
24468
|
-
var TOOL_AUDIT_SCHEMA_VERSION = 1;
|
|
24668
|
+
var openToolAuditSinks, TOOL_AUDIT_SCHEMA_VERSION = 1, lastFileStamp = 0;
|
|
24469
24669
|
var init_tool_audit = __esm(() => {
|
|
24670
|
+
init_logger2();
|
|
24470
24671
|
init_permissions();
|
|
24672
|
+
openToolAuditSinks = new Map;
|
|
24471
24673
|
});
|
|
24472
24674
|
|
|
24473
24675
|
// src/tools/write.ts
|
|
@@ -24677,6 +24879,7 @@ function createCodingToolRuntime(opts) {
|
|
|
24677
24879
|
tool: policyIdentity,
|
|
24678
24880
|
stage: opts.pipelineStage ?? "unknown",
|
|
24679
24881
|
rule: verdict.rule ?? verdict.reason,
|
|
24882
|
+
...verdict.rule !== undefined ? { matchedRule: verdict.rule } : {},
|
|
24680
24883
|
summary: ask.summary,
|
|
24681
24884
|
...ask.unshowable ? { unshowable: true } : {},
|
|
24682
24885
|
...typeof input[tool.scope.commandField ?? ""] === "string" ? { command: input[tool.scope.commandField] } : {},
|
|
@@ -25605,7 +25808,9 @@ var init_config_guards = __esm(() => {
|
|
|
25605
25808
|
"review.gateLLMChecksOnMechanicalPass": "LLM review checks are sequenced by the story orchestrator; this key never had an effect after #1859",
|
|
25606
25809
|
debate: "the multi-agent debate subsystem was removed; a config that enabled debate.stages.plan without setting plan.mode was resolving to the debate plan strategy and now plans with `single` \u2014 set plan.mode explicitly to `single` or `refine`",
|
|
25607
25810
|
"plan.citationThreshold": "this key only fed the removed pipeline plan mode",
|
|
25608
|
-
"plan.criticModel": "this key only fed the removed pipeline plan mode"
|
|
25811
|
+
"plan.criticModel": "this key only fed the removed pipeline plan mode",
|
|
25812
|
+
"quality.autofix.enforceTestWriterIsolation": "the mock-structure handoff path this key guarded was removed in #1084; the key has had no effect since",
|
|
25813
|
+
"prompts.overrides.single-session": "the single-session prompt role is not used by any run since fb3cfad3e; test-after stories use the tdd-simple role, so override tdd-simple instead"
|
|
25609
25814
|
};
|
|
25610
25815
|
});
|
|
25611
25816
|
|
|
@@ -26775,12 +26980,10 @@ var init_schemas_execution = __esm(() => {
|
|
|
26775
26980
|
}).default({ format: "auto" }),
|
|
26776
26981
|
autofix: exports_external.object({
|
|
26777
26982
|
enabled: exports_external.boolean().default(true),
|
|
26778
|
-
maxAttempts: exports_external.number().int().min(1).default(3)
|
|
26779
|
-
enforceTestWriterIsolation: exports_external.boolean().default(true)
|
|
26983
|
+
maxAttempts: exports_external.number().int().min(1).default(3)
|
|
26780
26984
|
}).default({
|
|
26781
26985
|
enabled: true,
|
|
26782
|
-
maxAttempts: 3
|
|
26783
|
-
enforceTestWriterIsolation: true
|
|
26986
|
+
maxAttempts: 3
|
|
26784
26987
|
}),
|
|
26785
26988
|
forceExit: exports_external.boolean().default(false),
|
|
26786
26989
|
detectOpenHandles: exports_external.boolean().default(true),
|
|
@@ -26847,7 +27050,7 @@ var init_schemas_execution = __esm(() => {
|
|
|
26847
27050
|
});
|
|
26848
27051
|
|
|
26849
27052
|
// src/config/schemas-infra.ts
|
|
26850
|
-
var PlanConfigSchema, AcceptanceFixConfigSchema, AcceptanceConfigSchema, LlmRoutingConfigSchema, AgentRoutingProfileSchema, AgentRoutingConfigSchema, RoutingConfigSchema, OptimizerConfigSchema, PluginConfigEntrySchema, HooksConfigSchema, InteractionConfigSchema, StorySizeGateConfigSchema, PromptAuditConfigSchema, UsageAuditConfigSchema, FallbackTargetSchema, AgentFallbackConfigSchema, DEFAULT_AGENT_IDLE_WATCHDOG_CONFIG, AgentIdleWatchdogConfigSchema, DEFAULT_AGENT_SPIN_BREAKER_CONFIG, AgentSpinBreakerConfigSchema, AgentAcpConfigSchema, AgentNativeTransportRetryConfigSchema, AgentNativeConfigSchema, AgentTimeoutRetryConfigSchema, DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG, AgentConfigSchema, PrecheckConfigSchema, PromptsConfigSchema, ProjectProfileSchema, VALID_AGENT_TYPES, GenerateConfigSchema, CuratorThresholdsSchema, CuratorRetentionConfigSchema, CuratorConfigSchema;
|
|
27053
|
+
var PlanConfigSchema, AcceptanceFixConfigSchema, AcceptanceConfigSchema, LlmRoutingConfigSchema, AgentRoutingProfileSchema, AgentRoutingConfigSchema, RoutingConfigSchema, OptimizerConfigSchema, PluginConfigEntrySchema, HooksConfigSchema, InteractionConfigSchema, StorySizeGateConfigSchema, PromptAuditConfigSchema, UsageAuditConfigSchema, FallbackTargetSchema, AgentFallbackConfigSchema, DEFAULT_AGENT_IDLE_WATCHDOG_CONFIG, AgentIdleWatchdogConfigSchema, DEFAULT_AGENT_SPIN_BREAKER_CONFIG, AgentSpinBreakerConfigSchema, AgentAcpConfigSchema, AgentNativeTransportRetryConfigSchema, AgentNativeConfigSchema, AgentTimeoutRetryConfigSchema, DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG, AgentConfigSchema, PrecheckConfigSchema, PROMPT_OVERRIDE_ROLES, PromptsConfigSchema, ProjectProfileSchema, VALID_AGENT_TYPES, GenerateConfigSchema, CuratorThresholdsSchema, CuratorRetentionConfigSchema, CuratorConfigSchema;
|
|
26851
27054
|
var init_schemas_infra = __esm(() => {
|
|
26852
27055
|
init_zod();
|
|
26853
27056
|
init_agent_defaults();
|
|
@@ -27098,10 +27301,19 @@ var init_schemas_infra = __esm(() => {
|
|
|
27098
27301
|
PrecheckConfigSchema = exports_external.object({
|
|
27099
27302
|
storySizeGate: StorySizeGateConfigSchema
|
|
27100
27303
|
});
|
|
27304
|
+
PROMPT_OVERRIDE_ROLES = ["no-test", "test-writer", "implementer", "verifier", "tdd-simple"];
|
|
27101
27305
|
PromptsConfigSchema = exports_external.object({
|
|
27102
|
-
overrides: exports_external.record(exports_external.string().
|
|
27103
|
-
|
|
27104
|
-
|
|
27306
|
+
overrides: exports_external.record(exports_external.string(), exports_external.string().min(1, "Override path must be non-empty")).superRefine((overrides, ctx) => {
|
|
27307
|
+
for (const key of Object.keys(overrides)) {
|
|
27308
|
+
if (!PROMPT_OVERRIDE_ROLES.includes(key)) {
|
|
27309
|
+
ctx.addIssue({
|
|
27310
|
+
code: "custom",
|
|
27311
|
+
message: `Role must be one of: ${PROMPT_OVERRIDE_ROLES.join(", ")}`,
|
|
27312
|
+
path: [key]
|
|
27313
|
+
});
|
|
27314
|
+
}
|
|
27315
|
+
}
|
|
27316
|
+
}).optional(),
|
|
27105
27317
|
behavioralGuardrails: exports_external.enum(["off", "lite", "strict"]).default("lite")
|
|
27106
27318
|
});
|
|
27107
27319
|
ProjectProfileSchema = exports_external.object({
|
|
@@ -27477,7 +27689,7 @@ var init_schemas3 = __esm(() => {
|
|
|
27477
27689
|
agents: { enabled: true, strategy: "off", profiles: [] }
|
|
27478
27690
|
}),
|
|
27479
27691
|
execution: ExecutionConfigSchema.default({
|
|
27480
|
-
maxIterations:
|
|
27692
|
+
maxIterations: 20,
|
|
27481
27693
|
iterationDelayMs: 2000,
|
|
27482
27694
|
costLimit: 30,
|
|
27483
27695
|
sessionTimeoutSeconds: 3600,
|
|
@@ -27522,8 +27734,7 @@ var init_schemas3 = __esm(() => {
|
|
|
27522
27734
|
},
|
|
27523
27735
|
autofix: {
|
|
27524
27736
|
enabled: true,
|
|
27525
|
-
maxAttempts: 3
|
|
27526
|
-
enforceTestWriterIsolation: true
|
|
27737
|
+
maxAttempts: 3
|
|
27527
27738
|
},
|
|
27528
27739
|
forceExit: false,
|
|
27529
27740
|
detectOpenHandles: true,
|
|
@@ -40501,23 +40712,6 @@ function lintDiagnosticToFinding(d, workdir, tool) {
|
|
|
40501
40712
|
var init_lint = __esm(() => {
|
|
40502
40713
|
init_path_utils();
|
|
40503
40714
|
});
|
|
40504
|
-
// src/findings/adapters/semantic-review.ts
|
|
40505
|
-
function reviewFindingToFinding(f) {
|
|
40506
|
-
return {
|
|
40507
|
-
source: "semantic-review",
|
|
40508
|
-
severity: f.severity,
|
|
40509
|
-
category: f.category ?? "",
|
|
40510
|
-
rule: f.ruleId,
|
|
40511
|
-
file: f.file,
|
|
40512
|
-
line: f.line,
|
|
40513
|
-
column: f.column,
|
|
40514
|
-
endLine: f.endLine,
|
|
40515
|
-
endColumn: f.endColumn,
|
|
40516
|
-
message: f.message,
|
|
40517
|
-
fixTarget: "source"
|
|
40518
|
-
};
|
|
40519
|
-
}
|
|
40520
|
-
|
|
40521
40715
|
// src/findings/adapters/test-failure.ts
|
|
40522
40716
|
function testFailureToFinding(failure) {
|
|
40523
40717
|
const frames = (failure.stackTrace ?? []).slice(0, MAX_FRAMES_IN_MESSAGE);
|
|
@@ -41362,7 +41556,7 @@ function buildBehavioralGuardrailsSection(role, level, _variant, _isolation) {
|
|
|
41362
41556
|
if (role === "test-writer") {
|
|
41363
41557
|
return buildTestWriterGuardrails(level);
|
|
41364
41558
|
}
|
|
41365
|
-
if (role === "
|
|
41559
|
+
if (role === "tdd-simple" || role === "batch") {
|
|
41366
41560
|
return buildCombinedGuardrails(level);
|
|
41367
41561
|
}
|
|
41368
41562
|
return buildImplementerGuardrails(level);
|
|
@@ -41486,7 +41680,7 @@ ${body}`;
|
|
|
41486
41680
|
}
|
|
41487
41681
|
var HERMETIC_ROLES, LANGUAGE_GUIDANCE;
|
|
41488
41682
|
var init_hermetic = __esm(() => {
|
|
41489
|
-
HERMETIC_ROLES = new Set(["test-writer", "implementer", "tdd-simple", "batch"
|
|
41683
|
+
HERMETIC_ROLES = new Set(["test-writer", "implementer", "tdd-simple", "batch"]);
|
|
41490
41684
|
LANGUAGE_GUIDANCE = {
|
|
41491
41685
|
go: "Define interfaces for external dependencies. Use constructor injection. Test with interface mocks \u2014 no real I/O in tests.",
|
|
41492
41686
|
rust: "Use trait objects or generics for external deps. Mock with the mockall crate. Use #[cfg(test)] modules.",
|
|
@@ -41682,11 +41876,6 @@ isolation scope: Implement source code in src/ to make tests pass. Do not modify
|
|
|
41682
41876
|
return `${header}
|
|
41683
41877
|
|
|
41684
41878
|
isolation scope: Read-only TDD integrity inspection. Review story-scoped test results and test-file modifications. Do NOT apply source or test fixes. You MAY write only the verdict file (.nax-verifier-verdict.json).${footer}`;
|
|
41685
|
-
}
|
|
41686
|
-
if (role === "single-session") {
|
|
41687
|
-
return `${header}
|
|
41688
|
-
|
|
41689
|
-
isolation scope: Create test files in test/ directory, then implement source code in src/ to make tests pass. Both directories are in scope for this session.${footer}`;
|
|
41690
41879
|
}
|
|
41691
41880
|
return `${header}
|
|
41692
41881
|
|
|
@@ -41727,7 +41916,11 @@ freely. Every other path under \`.nax/\` stays off limits.
|
|
|
41727
41916
|
- A test under \`.nax/\` is NOT a reason to skip writing source-tree tests. \`.nax/\` is generated
|
|
41728
41917
|
scaffolding, not real coverage of the package's code.
|
|
41729
41918
|
- A source-tree test is NOT a reason to remove a test under \`.nax/\`. The two serve different
|
|
41730
|
-
purposes and must coexist
|
|
41919
|
+
purposes and must coexist.
|
|
41920
|
+
|
|
41921
|
+
nax updates \`.nax/features/<feature>/prd.json\` itself during a run, so it shows as modified in
|
|
41922
|
+
\`git status\` \u2014 do not diff or revert it: this story's criteria are already in this prompt, and if you
|
|
41923
|
+
need its contents, read it with the \`Read\` tool; shell commands that name it may be refused.`;
|
|
41731
41924
|
}
|
|
41732
41925
|
|
|
41733
41926
|
// src/prompts/sections/out-of-scope.ts
|
|
@@ -41891,25 +42084,6 @@ Instructions:
|
|
|
41891
42084
|
- Do NOT perform semantic acceptance review; semantic/adversarial review stages own acceptance criteria and broad code-quality findings
|
|
41892
42085
|
- Write a detailed verdict with reasoning
|
|
41893
42086
|
- Goal: verify story-scoped tests pass and test integrity was preserved`;
|
|
41894
|
-
}
|
|
41895
|
-
if (role === "single-session") {
|
|
41896
|
-
return `# Role: Single-Session
|
|
41897
|
-
|
|
41898
|
-
Your task: write tests AND implement the feature in one session.
|
|
41899
|
-
|
|
41900
|
-
Workflow:
|
|
41901
|
-
1. Read the acceptance criteria. For each AC, plan one success-path test and one boundary/failure test.
|
|
41902
|
-
2. Create test files in the location the project uses for tests. Cover every AC.
|
|
41903
|
-
3. Run the tests to confirm they fail with ASSERTION failures \u2014 NOT import errors or compile errors. A test that errors before reaching its assertion does not prove the behavior is missing.
|
|
41904
|
-
4. Implement source code in the package's source location to make the tests pass.
|
|
41905
|
-
5. After each meaningful change, re-run only the scoped test files \u2014 never the full suite.
|
|
41906
|
-
6. When all scoped tests pass, stage and commit ALL changed files: \`${gitCommitInstruction(commitMsg)}\`.
|
|
41907
|
-
|
|
41908
|
-
Rules:
|
|
41909
|
-
- Each test name describes ONE behavior; use AC IDs when available.
|
|
41910
|
-
- Assert on observable outputs.
|
|
41911
|
-
- ${frameworkHint}
|
|
41912
|
-
- Goal: every AC has at least one passing test; all changes committed.`;
|
|
41913
42087
|
}
|
|
41914
42088
|
if (role === "batch") {
|
|
41915
42089
|
const verifyCmdLine = testCmd ? ` - Re-run only the scoped test files after each meaningful change: ${testCmd}` : " - Re-run only the scoped test files after each meaningful change";
|
|
@@ -41969,6 +42143,11 @@ or is read by another step. A failed run's scratchpad is retained for inspection
|
|
|
41969
42143
|
before failing outlive that run, and are cleared at the next run's start. Treat every file as disposable,
|
|
41970
42144
|
and overwrite freely.
|
|
41971
42145
|
|
|
42146
|
+
To try a snippet that imports project code or dependencies, write it under \`${dir}\` with
|
|
42147
|
+
\`ScratchpadWrite\` and run it from there: relative imports resolve from \`${dir}\`, so prefer the
|
|
42148
|
+
project's package names or path aliases where it has them. A script written outside the repository,
|
|
42149
|
+
such as in \`/tmp\`, cannot resolve the project's modules, and the file tools cannot write there.
|
|
42150
|
+
|
|
41972
42151
|
\`${dir}\` is the one directory under \`.nax/\` you may write to. Every other path under \`.nax/\`
|
|
41973
42152
|
must still never be moved, renamed, or deleted.`;
|
|
41974
42153
|
}
|
|
@@ -42199,7 +42378,7 @@ An adversarial reviewer will audit your tests after implementation and BLOCK the
|
|
|
42199
42378
|
}
|
|
42200
42379
|
var AUTHORING_ROLES;
|
|
42201
42380
|
var init_test_quality = __esm(() => {
|
|
42202
|
-
AUTHORING_ROLES = new Set(["test-writer", "
|
|
42381
|
+
AUTHORING_ROLES = new Set(["test-writer", "tdd-simple", "batch"]);
|
|
42203
42382
|
});
|
|
42204
42383
|
|
|
42205
42384
|
// src/prompts/sections/verdict.ts
|
|
@@ -42399,7 +42578,7 @@ ACCEPTANCE TEST FILE: ${p.acceptanceTestPath}
|
|
|
42399
42578
|
|
|
42400
42579
|
SOURCE FILES (auto-detected from imports, up to ${p.maxFileLines} lines each):
|
|
42401
42580
|
${p.sourceFilesSection}
|
|
42402
|
-
|
|
42581
|
+
|
|
42403
42582
|
Respond with ONLY a JSON object in this exact format (no markdown, no extra text):
|
|
42404
42583
|
${responseSchema}`;
|
|
42405
42584
|
}
|
|
@@ -42478,16 +42657,10 @@ ${f.content}
|
|
|
42478
42657
|
\`\`\``).join(`
|
|
42479
42658
|
|
|
42480
42659
|
`) : "(No source files could be resolved from imports)";
|
|
42481
|
-
const verdictSection = p.semanticVerdicts && p.semanticVerdicts.length > 0 ? `
|
|
42482
|
-
SEMANTIC VERDICTS:
|
|
42483
|
-
${p.semanticVerdicts.map((v) => `- ${v.storyId}: ${v.passed ? "likely test bug (semantic review confirmed AC implementation)" : "unconfirmed"}`).join(`
|
|
42484
|
-
`)}
|
|
42485
|
-
` : "";
|
|
42486
42660
|
return this.buildDiagnosisPromptTemplate({
|
|
42487
42661
|
truncatedOutput,
|
|
42488
42662
|
acceptanceTestPath: p.acceptanceTestPath ?? "(path unavailable \u2014 inspect test output for file references)",
|
|
42489
42663
|
sourceFilesSection,
|
|
42490
|
-
verdictSection,
|
|
42491
42664
|
maxFileLines: MAX_FILE_LINES
|
|
42492
42665
|
});
|
|
42493
42666
|
}
|
|
@@ -53204,8 +53377,8 @@ var init_version = __esm(() => {
|
|
|
53204
53377
|
NAX_AI_VERSION = CATALOG_VERSION;
|
|
53205
53378
|
NAX_COMMIT = (() => {
|
|
53206
53379
|
try {
|
|
53207
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
53208
|
-
return "
|
|
53380
|
+
if (/^[0-9a-f]{6,10}$/.test("7c21771a"))
|
|
53381
|
+
return "7c21771a";
|
|
53209
53382
|
} catch {}
|
|
53210
53383
|
try {
|
|
53211
53384
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -53764,7 +53937,7 @@ function attachCostSubscriber(bus, aggregator, runId, projectKey) {
|
|
|
53764
53937
|
offCompleted();
|
|
53765
53938
|
};
|
|
53766
53939
|
}
|
|
53767
|
-
var _costSubscriberDeps, COST_ROW_SCHEMA_VERSION =
|
|
53940
|
+
var _costSubscriberDeps, COST_ROW_SCHEMA_VERSION = 7;
|
|
53768
53941
|
var init_cost2 = __esm(() => {
|
|
53769
53942
|
init_agents();
|
|
53770
53943
|
init_version();
|
|
@@ -53886,8 +54059,33 @@ function toUsageEntry(event, runId) {
|
|
|
53886
54059
|
costUsd: event.costUsd
|
|
53887
54060
|
};
|
|
53888
54061
|
}
|
|
53889
|
-
function
|
|
53890
|
-
|
|
54062
|
+
function toOneShotEntry(event, runId) {
|
|
54063
|
+
if (event.kind !== "complete")
|
|
54064
|
+
return null;
|
|
54065
|
+
const tu = event.tokenUsage;
|
|
54066
|
+
const wireExact = event.exactCostUsd;
|
|
54067
|
+
const costUsd = typeof wireExact === "number" && Number.isFinite(wireExact) ? wireExact : event.estimatedCostUsd;
|
|
54068
|
+
if (!tu && (costUsd ?? 0) === 0)
|
|
54069
|
+
return null;
|
|
54070
|
+
return {
|
|
54071
|
+
ts: event.timestamp,
|
|
54072
|
+
runId,
|
|
54073
|
+
scopeId: event.scopeId,
|
|
54074
|
+
streamCallId: event.callId ?? "one-shot",
|
|
54075
|
+
sessionName: event.sessionName,
|
|
54076
|
+
storyId: event.storyId,
|
|
54077
|
+
stage: event.stage,
|
|
54078
|
+
agentName: event.agentName,
|
|
54079
|
+
cadence: "one-shot",
|
|
54080
|
+
input: tu?.inputTokens,
|
|
54081
|
+
output: tu?.outputTokens,
|
|
54082
|
+
cacheRead: tu?.cacheReadInputTokens,
|
|
54083
|
+
cacheWrite: tu?.cacheCreationInputTokens,
|
|
54084
|
+
costUsd
|
|
54085
|
+
};
|
|
54086
|
+
}
|
|
54087
|
+
function attachUsageAuditSubscriber(bus, dispatchEvents, auditor, runId) {
|
|
54088
|
+
const offStream = bus.onAgentStream((event) => {
|
|
53891
54089
|
switch (event.kind) {
|
|
53892
54090
|
case "agent.usage_update":
|
|
53893
54091
|
auditor.record(toUsageEntry(event, runId));
|
|
@@ -53896,6 +54094,15 @@ function attachUsageAuditSubscriber(bus, auditor, runId) {
|
|
|
53896
54094
|
break;
|
|
53897
54095
|
}
|
|
53898
54096
|
});
|
|
54097
|
+
const offDispatch = dispatchEvents.onDispatch((event) => {
|
|
54098
|
+
const entry = toOneShotEntry(event, runId);
|
|
54099
|
+
if (entry)
|
|
54100
|
+
auditor.record(entry);
|
|
54101
|
+
});
|
|
54102
|
+
return () => {
|
|
54103
|
+
offStream();
|
|
54104
|
+
offDispatch();
|
|
54105
|
+
};
|
|
53899
54106
|
}
|
|
53900
54107
|
|
|
53901
54108
|
// src/runtime/middleware/index.ts
|
|
@@ -53907,15 +54114,255 @@ var init_middleware = __esm(() => {
|
|
|
53907
54114
|
init_logging();
|
|
53908
54115
|
});
|
|
53909
54116
|
|
|
54117
|
+
// src/runtime/usage-auditor.ts
|
|
54118
|
+
import { appendFileSync as appendFileSync2 } from "fs";
|
|
54119
|
+
import { mkdir as mkdir16 } from "fs/promises";
|
|
54120
|
+
import { join as join45 } from "path";
|
|
54121
|
+
function createNoOpUsageAuditor() {
|
|
54122
|
+
return {
|
|
54123
|
+
record() {},
|
|
54124
|
+
async flush() {}
|
|
54125
|
+
};
|
|
54126
|
+
}
|
|
54127
|
+
function deriveSessionRole(sessionName) {
|
|
54128
|
+
for (const role of ROLES_BY_DESCENDING_LENGTH) {
|
|
54129
|
+
if (sessionName === role || sessionName.endsWith(`-${role}`))
|
|
54130
|
+
return role;
|
|
54131
|
+
}
|
|
54132
|
+
return;
|
|
54133
|
+
}
|
|
54134
|
+
|
|
54135
|
+
class UsageAuditor {
|
|
54136
|
+
_queue = Promise.resolve();
|
|
54137
|
+
_dirCreated = false;
|
|
54138
|
+
_dir;
|
|
54139
|
+
_jsonlPath;
|
|
54140
|
+
constructor(runId, dir) {
|
|
54141
|
+
this._dir = dir;
|
|
54142
|
+
this._jsonlPath = join45(dir, `${runId}.jsonl`);
|
|
54143
|
+
}
|
|
54144
|
+
record(entry) {
|
|
54145
|
+
this._queue = this._queue.then(() => this._writeEntry(entry)).catch((err) => {
|
|
54146
|
+
const sysErr = err;
|
|
54147
|
+
getSafeLogger()?.warn("audit", "usage-audit write failed", {
|
|
54148
|
+
path: this._jsonlPath,
|
|
54149
|
+
error: errorMessage(err),
|
|
54150
|
+
code: sysErr?.code,
|
|
54151
|
+
errno: sysErr?.errno,
|
|
54152
|
+
syscall: sysErr?.syscall,
|
|
54153
|
+
ts: entry.ts,
|
|
54154
|
+
storyId: entry.storyId,
|
|
54155
|
+
sessionName: entry.sessionName,
|
|
54156
|
+
agentName: entry.agentName,
|
|
54157
|
+
stage: entry.stage,
|
|
54158
|
+
streamCallId: entry.streamCallId
|
|
54159
|
+
});
|
|
54160
|
+
});
|
|
54161
|
+
}
|
|
54162
|
+
async _writeEntry(entry) {
|
|
54163
|
+
if (!this._dirCreated) {
|
|
54164
|
+
await mkdir16(this._dir, { recursive: true });
|
|
54165
|
+
this._dirCreated = true;
|
|
54166
|
+
}
|
|
54167
|
+
const row = { ...entry, sessionRole: deriveSessionRole(entry.sessionName) };
|
|
54168
|
+
await _usageAuditorDeps.appendLine(this._jsonlPath, `${JSON.stringify(row)}
|
|
54169
|
+
`);
|
|
54170
|
+
}
|
|
54171
|
+
async flush() {
|
|
54172
|
+
await this._queue;
|
|
54173
|
+
}
|
|
54174
|
+
}
|
|
54175
|
+
var _usageAuditorDeps, ROLES_BY_DESCENDING_LENGTH;
|
|
54176
|
+
var init_usage_auditor = __esm(() => {
|
|
54177
|
+
init_logger2();
|
|
54178
|
+
init_session_role();
|
|
54179
|
+
_usageAuditorDeps = {
|
|
54180
|
+
appendLine: async (path6, data) => {
|
|
54181
|
+
appendFileSync2(path6, data, "utf8");
|
|
54182
|
+
}
|
|
54183
|
+
};
|
|
54184
|
+
ROLES_BY_DESCENDING_LENGTH = [...KNOWN_SESSION_ROLES].sort((a, b) => b.length - a.length);
|
|
54185
|
+
});
|
|
54186
|
+
|
|
54187
|
+
// src/runtime/in-flight-usage.ts
|
|
54188
|
+
function hasSpend(entry) {
|
|
54189
|
+
const { input, output, cacheRead, cacheWrite } = entry.tokens;
|
|
54190
|
+
return entry.costUsd > 0 || input + output + cacheRead + cacheWrite > 0;
|
|
54191
|
+
}
|
|
54192
|
+
function attachInFlightUsageTracker(streamBus, dispatchEvents) {
|
|
54193
|
+
const entries = new Map;
|
|
54194
|
+
const latestEndedBySession = new Map;
|
|
54195
|
+
let endedSeq = 0;
|
|
54196
|
+
const entryFor = (event) => {
|
|
54197
|
+
let entry = entries.get(event.callId);
|
|
54198
|
+
if (entry === undefined) {
|
|
54199
|
+
entry = {
|
|
54200
|
+
streamCallId: event.callId,
|
|
54201
|
+
agentName: event.agentName,
|
|
54202
|
+
model: "unknown",
|
|
54203
|
+
sessionName: event.sessionName,
|
|
54204
|
+
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
54205
|
+
costUsd: 0,
|
|
54206
|
+
roundTrips: 0
|
|
54207
|
+
};
|
|
54208
|
+
entries.set(event.callId, entry);
|
|
54209
|
+
}
|
|
54210
|
+
entry.agentName = event.agentName;
|
|
54211
|
+
entry.sessionName = event.sessionName;
|
|
54212
|
+
if (event.storyId !== undefined)
|
|
54213
|
+
entry.storyId = event.storyId;
|
|
54214
|
+
if (event.stage !== undefined)
|
|
54215
|
+
entry.stage = event.stage;
|
|
54216
|
+
if (event.scopeId !== undefined)
|
|
54217
|
+
entry.scopeId = event.scopeId;
|
|
54218
|
+
return entry;
|
|
54219
|
+
};
|
|
54220
|
+
const onUsageUpdate = (event) => {
|
|
54221
|
+
if (event.cadence !== "round-trip")
|
|
54222
|
+
return;
|
|
54223
|
+
const entry = entryFor(event);
|
|
54224
|
+
entry.tokens.input += event.inputTokens ?? 0;
|
|
54225
|
+
entry.tokens.output += event.outputTokens ?? 0;
|
|
54226
|
+
entry.tokens.cacheRead += event.cacheRead ?? 0;
|
|
54227
|
+
entry.tokens.cacheWrite += event.cacheWrite ?? 0;
|
|
54228
|
+
entry.costUsd += event.costUsd ?? 0;
|
|
54229
|
+
if (event.roundTrip !== undefined)
|
|
54230
|
+
entry.roundTrips += 1;
|
|
54231
|
+
};
|
|
54232
|
+
const onCallStarted = (event) => {
|
|
54233
|
+
const entry = entryFor(event);
|
|
54234
|
+
entry.model = event.model === "" ? "unknown" : event.model;
|
|
54235
|
+
};
|
|
54236
|
+
const forget = (callId) => {
|
|
54237
|
+
entries.delete(callId);
|
|
54238
|
+
for (const [sessionName, remembered] of latestEndedBySession) {
|
|
54239
|
+
if (remembered === callId)
|
|
54240
|
+
latestEndedBySession.delete(sessionName);
|
|
54241
|
+
}
|
|
54242
|
+
};
|
|
54243
|
+
const onCallEnded = (event) => {
|
|
54244
|
+
latestEndedBySession.set(event.sessionName, event.callId);
|
|
54245
|
+
if (event.status === "success" || event.status === "timeout") {
|
|
54246
|
+
forget(event.callId);
|
|
54247
|
+
return;
|
|
54248
|
+
}
|
|
54249
|
+
const existing2 = entries.get(event.callId);
|
|
54250
|
+
if (existing2 === undefined)
|
|
54251
|
+
return;
|
|
54252
|
+
const entry = entryFor(event);
|
|
54253
|
+
entry.endedStatus = event.status;
|
|
54254
|
+
endedSeq += 1;
|
|
54255
|
+
entry.endedSeq = endedSeq;
|
|
54256
|
+
};
|
|
54257
|
+
const onDispatch = (event) => {
|
|
54258
|
+
if (event.kind !== "session-turn")
|
|
54259
|
+
return;
|
|
54260
|
+
const callId = latestEndedBySession.get(event.sessionName);
|
|
54261
|
+
if (callId === undefined)
|
|
54262
|
+
return;
|
|
54263
|
+
const entry = entries.get(callId);
|
|
54264
|
+
if (entry !== undefined && entry.endedStatus === "cancelled")
|
|
54265
|
+
forget(callId);
|
|
54266
|
+
};
|
|
54267
|
+
const matchesScope = (entry, event) => {
|
|
54268
|
+
if (entry.scopeId === undefined)
|
|
54269
|
+
return false;
|
|
54270
|
+
return entry.scopeId === event.scopeId || entry.scopeId === event.callId;
|
|
54271
|
+
};
|
|
54272
|
+
const onDispatchError = (event) => {
|
|
54273
|
+
const hasUsage = event.tokenUsage !== undefined;
|
|
54274
|
+
const costUsd = event.exactCostUsd ?? event.estimatedCostUsd ?? 0;
|
|
54275
|
+
if (!hasUsage && !(Number.isFinite(costUsd) && costUsd > 0))
|
|
54276
|
+
return;
|
|
54277
|
+
let target;
|
|
54278
|
+
for (const entry of entries.values()) {
|
|
54279
|
+
if (entry.endedStatus === undefined || !matchesScope(entry, event))
|
|
54280
|
+
continue;
|
|
54281
|
+
if (target === undefined || (entry.endedSeq ?? 0) > (target.endedSeq ?? 0))
|
|
54282
|
+
target = entry;
|
|
54283
|
+
}
|
|
54284
|
+
if (target !== undefined)
|
|
54285
|
+
forget(target.streamCallId);
|
|
54286
|
+
};
|
|
54287
|
+
const onStream = (event) => {
|
|
54288
|
+
if (event.kind === "agent.usage_update")
|
|
54289
|
+
onUsageUpdate(event);
|
|
54290
|
+
else if (event.kind === "agent.call_started")
|
|
54291
|
+
onCallStarted(event);
|
|
54292
|
+
else if (event.kind === "agent.call_ended")
|
|
54293
|
+
onCallEnded(event);
|
|
54294
|
+
};
|
|
54295
|
+
const residuals = () => {
|
|
54296
|
+
const out = [];
|
|
54297
|
+
for (const entry of entries.values()) {
|
|
54298
|
+
if (!hasSpend(entry))
|
|
54299
|
+
continue;
|
|
54300
|
+
out.push({
|
|
54301
|
+
streamCallId: entry.streamCallId,
|
|
54302
|
+
agentName: entry.agentName,
|
|
54303
|
+
model: entry.model,
|
|
54304
|
+
sessionName: entry.sessionName,
|
|
54305
|
+
...entry.storyId !== undefined ? { storyId: entry.storyId } : {},
|
|
54306
|
+
...entry.stage !== undefined ? { stage: entry.stage } : {},
|
|
54307
|
+
...entry.scopeId !== undefined ? { scopeId: entry.scopeId } : {},
|
|
54308
|
+
tokens: { ...entry.tokens },
|
|
54309
|
+
costUsd: entry.costUsd,
|
|
54310
|
+
roundTrips: entry.roundTrips
|
|
54311
|
+
});
|
|
54312
|
+
}
|
|
54313
|
+
return out;
|
|
54314
|
+
};
|
|
54315
|
+
const offStream = streamBus.onAgentStream(onStream);
|
|
54316
|
+
const offDispatch = dispatchEvents.onDispatch(onDispatch);
|
|
54317
|
+
const offError = dispatchEvents.onDispatchError(onDispatchError);
|
|
54318
|
+
return {
|
|
54319
|
+
tracker: { residuals },
|
|
54320
|
+
off: () => {
|
|
54321
|
+
offStream();
|
|
54322
|
+
offDispatch();
|
|
54323
|
+
offError();
|
|
54324
|
+
}
|
|
54325
|
+
};
|
|
54326
|
+
}
|
|
54327
|
+
function toPartialCostEvent(residual, runId, projectKey) {
|
|
54328
|
+
const sessionRole = deriveSessionRole(residual.sessionName);
|
|
54329
|
+
return {
|
|
54330
|
+
ts: Date.now(),
|
|
54331
|
+
runId,
|
|
54332
|
+
...projectKey !== undefined ? { projectKey } : {},
|
|
54333
|
+
schemaVersion: COST_ROW_SCHEMA_VERSION,
|
|
54334
|
+
partial: true,
|
|
54335
|
+
agentName: residual.agentName,
|
|
54336
|
+
model: residual.model,
|
|
54337
|
+
...sessionRole !== undefined ? { sessionRole } : {},
|
|
54338
|
+
...residual.stage !== undefined ? { stage: residual.stage } : {},
|
|
54339
|
+
...residual.storyId !== undefined ? { storyId: residual.storyId } : {},
|
|
54340
|
+
...residual.scopeId !== undefined ? { scopeId: residual.scopeId } : {},
|
|
54341
|
+
callId: residual.streamCallId,
|
|
54342
|
+
tokens: { ...residual.tokens },
|
|
54343
|
+
roundTrips: residual.roundTrips,
|
|
54344
|
+
roundTripUnit: "model-call",
|
|
54345
|
+
costUsd: residual.costUsd,
|
|
54346
|
+
estimatedCostUsd: residual.costUsd,
|
|
54347
|
+
exactCostUsd: residual.costUsd,
|
|
54348
|
+
confidence: "estimated",
|
|
54349
|
+
durationMs: 0
|
|
54350
|
+
};
|
|
54351
|
+
}
|
|
54352
|
+
var init_in_flight_usage = __esm(() => {
|
|
54353
|
+
init_middleware();
|
|
54354
|
+
init_usage_auditor();
|
|
54355
|
+
});
|
|
54356
|
+
|
|
53910
54357
|
// src/runtime/packages.ts
|
|
53911
|
-
import { isAbsolute as isAbsolute15, join as
|
|
54358
|
+
import { isAbsolute as isAbsolute15, join as join46, relative as relative17 } from "path";
|
|
53912
54359
|
function packageWorkdir(view) {
|
|
53913
54360
|
const { packageDir, repoRoot } = view;
|
|
53914
54361
|
if (!packageDir)
|
|
53915
54362
|
return repoRoot;
|
|
53916
54363
|
if (!repoRoot || isAbsolute15(packageDir))
|
|
53917
54364
|
return packageDir;
|
|
53918
|
-
return
|
|
54365
|
+
return join46(repoRoot, packageDir);
|
|
53919
54366
|
}
|
|
53920
54367
|
function createPackageView(config2, packageDir, repoRoot, hasOverride, overlay) {
|
|
53921
54368
|
const memo = new Map;
|
|
@@ -54028,7 +54475,7 @@ function storyExecRoot(view) {
|
|
|
54028
54475
|
const segments = packageDir.split("/");
|
|
54029
54476
|
if (segments[0] !== ".nax-wt" || segments.length < 2)
|
|
54030
54477
|
return repoRoot;
|
|
54031
|
-
return
|
|
54478
|
+
return join46(repoRoot, segments[0], segments[1]);
|
|
54032
54479
|
}
|
|
54033
54480
|
var _packagesDeps;
|
|
54034
54481
|
var init_packages = __esm(() => {
|
|
@@ -54161,9 +54608,9 @@ var init_paths2 = __esm(() => {
|
|
|
54161
54608
|
});
|
|
54162
54609
|
|
|
54163
54610
|
// src/runtime/prompt-auditor.ts
|
|
54164
|
-
import { appendFileSync as
|
|
54165
|
-
import { mkdir as
|
|
54166
|
-
import { join as
|
|
54611
|
+
import { appendFileSync as appendFileSync3 } from "fs";
|
|
54612
|
+
import { mkdir as mkdir17 } from "fs/promises";
|
|
54613
|
+
import { join as join47 } from "path";
|
|
54167
54614
|
function createNoOpPromptAuditor() {
|
|
54168
54615
|
return {
|
|
54169
54616
|
record() {},
|
|
@@ -54243,8 +54690,8 @@ class PromptAuditor {
|
|
|
54243
54690
|
_featureDir;
|
|
54244
54691
|
_turnOrdinals = new Map;
|
|
54245
54692
|
constructor(runId, flushDir, featureName) {
|
|
54246
|
-
this._featureDir =
|
|
54247
|
-
this._jsonlPath =
|
|
54693
|
+
this._featureDir = join47(flushDir, featureName);
|
|
54694
|
+
this._jsonlPath = join47(this._featureDir, `${runId}.jsonl`);
|
|
54248
54695
|
}
|
|
54249
54696
|
record(entry) {
|
|
54250
54697
|
this._enqueue(entry.callType === "run" ? { ...entry, turn: this._nextTurn(entry) } : entry);
|
|
@@ -54282,7 +54729,7 @@ class PromptAuditor {
|
|
|
54282
54729
|
async _writeEntry(entry) {
|
|
54283
54730
|
if (!this._dirCreated) {
|
|
54284
54731
|
try {
|
|
54285
|
-
await
|
|
54732
|
+
await mkdir17(this._featureDir, { recursive: true });
|
|
54286
54733
|
} catch (err) {
|
|
54287
54734
|
throw tagAuditError(err, "jsonl");
|
|
54288
54735
|
}
|
|
@@ -54299,7 +54746,7 @@ class PromptAuditor {
|
|
|
54299
54746
|
return;
|
|
54300
54747
|
const filename = deriveTxtFilename(entry);
|
|
54301
54748
|
try {
|
|
54302
|
-
await _promptAuditorDeps.write(
|
|
54749
|
+
await _promptAuditorDeps.write(join47(this._featureDir, filename), buildTxtContent(safeEntry));
|
|
54303
54750
|
} catch (err) {
|
|
54304
54751
|
throw tagAuditError(err, "txt");
|
|
54305
54752
|
}
|
|
@@ -54313,80 +54760,10 @@ var init_prompt_auditor = __esm(() => {
|
|
|
54313
54760
|
init_logger2();
|
|
54314
54761
|
_promptAuditorDeps = {
|
|
54315
54762
|
write: (path7, data) => Bun.write(path7, data),
|
|
54316
|
-
appendLine: async (path7, data) => {
|
|
54317
|
-
appendFileSync2(path7, data, "utf8");
|
|
54318
|
-
}
|
|
54319
|
-
};
|
|
54320
|
-
});
|
|
54321
|
-
|
|
54322
|
-
// src/runtime/usage-auditor.ts
|
|
54323
|
-
import { appendFileSync as appendFileSync3 } from "fs";
|
|
54324
|
-
import { mkdir as mkdir17 } from "fs/promises";
|
|
54325
|
-
import { join as join47 } from "path";
|
|
54326
|
-
function createNoOpUsageAuditor() {
|
|
54327
|
-
return {
|
|
54328
|
-
record() {},
|
|
54329
|
-
async flush() {}
|
|
54330
|
-
};
|
|
54331
|
-
}
|
|
54332
|
-
function deriveSessionRole(sessionName) {
|
|
54333
|
-
for (const role of ROLES_BY_DESCENDING_LENGTH) {
|
|
54334
|
-
if (sessionName === role || sessionName.endsWith(`-${role}`))
|
|
54335
|
-
return role;
|
|
54336
|
-
}
|
|
54337
|
-
return;
|
|
54338
|
-
}
|
|
54339
|
-
|
|
54340
|
-
class UsageAuditor {
|
|
54341
|
-
_queue = Promise.resolve();
|
|
54342
|
-
_dirCreated = false;
|
|
54343
|
-
_dir;
|
|
54344
|
-
_jsonlPath;
|
|
54345
|
-
constructor(runId, dir) {
|
|
54346
|
-
this._dir = dir;
|
|
54347
|
-
this._jsonlPath = join47(dir, `${runId}.jsonl`);
|
|
54348
|
-
}
|
|
54349
|
-
record(entry) {
|
|
54350
|
-
this._queue = this._queue.then(() => this._writeEntry(entry)).catch((err) => {
|
|
54351
|
-
const sysErr = err;
|
|
54352
|
-
getSafeLogger()?.warn("audit", "usage-audit write failed", {
|
|
54353
|
-
path: this._jsonlPath,
|
|
54354
|
-
error: errorMessage(err),
|
|
54355
|
-
code: sysErr?.code,
|
|
54356
|
-
errno: sysErr?.errno,
|
|
54357
|
-
syscall: sysErr?.syscall,
|
|
54358
|
-
ts: entry.ts,
|
|
54359
|
-
storyId: entry.storyId,
|
|
54360
|
-
sessionName: entry.sessionName,
|
|
54361
|
-
agentName: entry.agentName,
|
|
54362
|
-
stage: entry.stage,
|
|
54363
|
-
streamCallId: entry.streamCallId
|
|
54364
|
-
});
|
|
54365
|
-
});
|
|
54366
|
-
}
|
|
54367
|
-
async _writeEntry(entry) {
|
|
54368
|
-
if (!this._dirCreated) {
|
|
54369
|
-
await mkdir17(this._dir, { recursive: true });
|
|
54370
|
-
this._dirCreated = true;
|
|
54371
|
-
}
|
|
54372
|
-
const row = { ...entry, sessionRole: deriveSessionRole(entry.sessionName) };
|
|
54373
|
-
await _usageAuditorDeps.appendLine(this._jsonlPath, `${JSON.stringify(row)}
|
|
54374
|
-
`);
|
|
54375
|
-
}
|
|
54376
|
-
async flush() {
|
|
54377
|
-
await this._queue;
|
|
54378
|
-
}
|
|
54379
|
-
}
|
|
54380
|
-
var _usageAuditorDeps, ROLES_BY_DESCENDING_LENGTH;
|
|
54381
|
-
var init_usage_auditor = __esm(() => {
|
|
54382
|
-
init_logger2();
|
|
54383
|
-
init_session_role();
|
|
54384
|
-
_usageAuditorDeps = {
|
|
54385
54763
|
appendLine: async (path7, data) => {
|
|
54386
54764
|
appendFileSync3(path7, data, "utf8");
|
|
54387
54765
|
}
|
|
54388
54766
|
};
|
|
54389
|
-
ROLES_BY_DESCENDING_LENGTH = [...KNOWN_SESSION_ROLES].sort((a, b) => b.length - a.length);
|
|
54390
54767
|
});
|
|
54391
54768
|
|
|
54392
54769
|
// src/agents/factory.ts
|
|
@@ -66183,6 +66560,7 @@ __export(exports_runtime, {
|
|
|
66183
66560
|
_usageAuditorDeps: () => _usageAuditorDeps,
|
|
66184
66561
|
attachAgentIdleWatchdog: () => attachAgentIdleWatchdog,
|
|
66185
66562
|
attachAgentStreamLogging: () => attachAgentStreamLogging,
|
|
66563
|
+
attachInFlightUsageTracker: () => attachInFlightUsageTracker,
|
|
66186
66564
|
attachUsageAuditSubscriber: () => attachUsageAuditSubscriber,
|
|
66187
66565
|
claimProjectIdentity: () => claimProjectIdentity,
|
|
66188
66566
|
createNoOpCostAggregator: () => createNoOpCostAggregator,
|
|
@@ -66205,6 +66583,7 @@ __export(exports_runtime, {
|
|
|
66205
66583
|
spinTerminalNotice: () => spinTerminalNotice,
|
|
66206
66584
|
storyExecRoot: () => storyExecRoot,
|
|
66207
66585
|
storySpendUsd: () => storySpendUsd,
|
|
66586
|
+
toPartialCostEvent: () => toPartialCostEvent,
|
|
66208
66587
|
totalSpendUsd: () => totalSpendUsd,
|
|
66209
66588
|
writeProjectIdentity: () => writeProjectIdentity
|
|
66210
66589
|
});
|
|
@@ -66296,9 +66675,10 @@ function createRuntime(config2, workdir, opts) {
|
|
|
66296
66675
|
const offCost = attachCostSubscriber(dispatchEvents, costAggregator, runId, getProjectKey(config2, workdir));
|
|
66297
66676
|
const offAudit = attachAuditSubscriber(dispatchEvents, promptAuditor, runId);
|
|
66298
66677
|
const offReviewAudit = attachReviewAuditSubscriber(dispatchEvents, reviewAuditor, runId);
|
|
66299
|
-
const offUsageAudit = attachUsageAuditSubscriber(agentStreamEvents, usageAuditor, runId);
|
|
66678
|
+
const offUsageAudit = attachUsageAuditSubscriber(agentStreamEvents, dispatchEvents, usageAuditor, runId);
|
|
66300
66679
|
const offAgentStreamLogging = attachAgentStreamLogging(agentStreamEvents, runId);
|
|
66301
66680
|
const offWatchdog = attachAgentIdleWatchdog(agentStreamEvents, watchdogControllerRegistry, config2);
|
|
66681
|
+
const { tracker: inFlightTracker, off: offInFlightUsage } = attachInFlightUsageTracker(agentStreamEvents, dispatchEvents);
|
|
66302
66682
|
const packages = createPackageRegistry(configLoader, workdir);
|
|
66303
66683
|
const logger = getLogger();
|
|
66304
66684
|
const quarantineMemo = createQuarantineMemo();
|
|
@@ -66366,6 +66746,7 @@ function createRuntime(config2, workdir, opts) {
|
|
|
66366
66746
|
offUsageAudit();
|
|
66367
66747
|
offAgentStreamLogging();
|
|
66368
66748
|
offWatchdog();
|
|
66749
|
+
offInFlightUsage();
|
|
66369
66750
|
if (opts?.parentSignal && parentAbortHandler) {
|
|
66370
66751
|
opts.parentSignal.removeEventListener("abort", parentAbortHandler);
|
|
66371
66752
|
}
|
|
@@ -66375,6 +66756,10 @@ function createRuntime(config2, workdir, opts) {
|
|
|
66375
66756
|
sessionManager.close();
|
|
66376
66757
|
await mcpPool.close();
|
|
66377
66758
|
await writeMcpRollup(outputDir, buildMcpRollup({ runId, events: mcpPool.events(), withheld: mcpWithheld })).catch((error48) => logger.warn("runtime", "mcp rollup write failed", { error: String(error48) }));
|
|
66759
|
+
for (const residual of inFlightTracker.residuals()) {
|
|
66760
|
+
costAggregator.record(toPartialCostEvent(residual, runId, projectKey));
|
|
66761
|
+
}
|
|
66762
|
+
await flushOpenToolAuditSinks(runId);
|
|
66378
66763
|
const results = await Promise.allSettled([
|
|
66379
66764
|
promptAuditor.flush(),
|
|
66380
66765
|
usageAuditor.flush(),
|
|
@@ -66394,6 +66779,7 @@ var init_runtime2 = __esm(() => {
|
|
|
66394
66779
|
init_agent_stream_events();
|
|
66395
66780
|
init_cost_aggregator();
|
|
66396
66781
|
init_dispatch_events();
|
|
66782
|
+
init_in_flight_usage();
|
|
66397
66783
|
init_middleware();
|
|
66398
66784
|
init_packages();
|
|
66399
66785
|
init_paths2();
|
|
@@ -66402,6 +66788,7 @@ var init_runtime2 = __esm(() => {
|
|
|
66402
66788
|
init_session_role();
|
|
66403
66789
|
init_spin_breaker();
|
|
66404
66790
|
init_usage_auditor();
|
|
66791
|
+
init_tools();
|
|
66405
66792
|
init_factory();
|
|
66406
66793
|
init_manager2();
|
|
66407
66794
|
init_config();
|
|
@@ -66415,6 +66802,7 @@ var init_runtime2 = __esm(() => {
|
|
|
66415
66802
|
init_agent_stream_events();
|
|
66416
66803
|
init_cost_aggregator();
|
|
66417
66804
|
init_dispatch_events();
|
|
66805
|
+
init_in_flight_usage();
|
|
66418
66806
|
init_middleware();
|
|
66419
66807
|
init_packages();
|
|
66420
66808
|
init_paths2();
|
|
@@ -66826,7 +67214,6 @@ var init_feature_context_filter = __esm(() => {
|
|
|
66826
67214
|
implementer: ["all", "implementer"],
|
|
66827
67215
|
"test-writer": ["all", "test-writer"],
|
|
66828
67216
|
verifier: ["all", "verifier"],
|
|
66829
|
-
"single-session": ["all", "implementer", "test-writer"],
|
|
66830
67217
|
"tdd-simple": ["all", "implementer", "test-writer"],
|
|
66831
67218
|
"no-test": ["all", "implementer"],
|
|
66832
67219
|
batch: ["all", "implementer", "test-writer"]
|
|
@@ -68073,8 +68460,7 @@ var init_acceptance_diagnose = __esm(() => {
|
|
|
68073
68460
|
testOutput: input.testOutput,
|
|
68074
68461
|
testFileContent: input.testFileContent,
|
|
68075
68462
|
acceptanceTestPath: input.acceptanceTestPath,
|
|
68076
|
-
sourceFiles: input.sourceFiles
|
|
68077
|
-
semanticVerdicts: input.semanticVerdicts
|
|
68463
|
+
sourceFiles: input.sourceFiles
|
|
68078
68464
|
});
|
|
68079
68465
|
return {
|
|
68080
68466
|
role: { id: "role", content: "", overridable: false },
|
|
@@ -79097,7 +79483,7 @@ var init_config_descriptions = __esm(() => {
|
|
|
79097
79483
|
"routing.llm.mode": "Routing mode: one-shot | per-story | hybrid",
|
|
79098
79484
|
"routing.llm.timeoutMs": "Timeout for LLM routing call in milliseconds",
|
|
79099
79485
|
execution: "Execution limits and timeouts",
|
|
79100
|
-
"execution.maxIterations": "Max iterations per feature run
|
|
79486
|
+
"execution.maxIterations": "Max iterations per feature run \u2014 each story attempt, parallel batch and the final completion pass counts one",
|
|
79101
79487
|
"execution.iterationDelayMs": "Delay between iterations in milliseconds",
|
|
79102
79488
|
"execution.costLimit": "Max cost in USD before pausing execution (override per run with `nax run --max-cost`)",
|
|
79103
79489
|
"execution.sessionTimeoutSeconds": "Timeout per agent coding session in seconds",
|
|
@@ -79253,7 +79639,6 @@ var init_config_descriptions = __esm(() => {
|
|
|
79253
79639
|
"prompts.overrides.test-writer": 'Path to custom test-writer prompt (e.g., ".nax/prompts/test-writer.md")',
|
|
79254
79640
|
"prompts.overrides.implementer": 'Path to custom implementer prompt (e.g., ".nax/prompts/implementer.md")',
|
|
79255
79641
|
"prompts.overrides.verifier": 'Path to custom verifier prompt (e.g., ".nax/prompts/verifier.md")',
|
|
79256
|
-
"prompts.overrides.single-session": 'Path to custom single-session prompt (e.g., ".nax/prompts/single-session.md")',
|
|
79257
79642
|
agent: "Agent protocol configuration (ACP-003)",
|
|
79258
79643
|
"agent.protocol": "Protocol for agent communication: 'acp' (default) | 'native' | 'hybrid'",
|
|
79259
79644
|
"agent.native.catalogOverrides": "Native only. Explicit catalog entries for model ids newer than the bundled pi-ai snapshot, plus optional provider-wide baseUrl/headers.",
|
|
@@ -79556,7 +79941,7 @@ function displayConfigWithDescriptions(obj, path10, sources, indent = 0) {
|
|
|
79556
79941
|
if (description) {
|
|
79557
79942
|
console.log(`${indentStr}# prompts.overrides: ${description}`);
|
|
79558
79943
|
}
|
|
79559
|
-
const roles = ["test-writer", "implementer", "verifier"
|
|
79944
|
+
const roles = ["test-writer", "implementer", "verifier"];
|
|
79560
79945
|
console.log(`${indentStr}overrides:`);
|
|
79561
79946
|
for (const role of roles) {
|
|
79562
79947
|
const roleDesc = FIELD_DESCRIPTIONS[`prompts.overrides.${role}`];
|
|
@@ -79601,7 +79986,7 @@ function displayConfigWithDescriptions(obj, path10, sources, indent = 0) {
|
|
|
79601
79986
|
if (description) {
|
|
79602
79987
|
console.log(`# prompts.overrides: ${description}`);
|
|
79603
79988
|
}
|
|
79604
|
-
const roles = ["test-writer", "implementer", "verifier"
|
|
79989
|
+
const roles = ["test-writer", "implementer", "verifier"];
|
|
79605
79990
|
console.log("overrides:");
|
|
79606
79991
|
for (const role of roles) {
|
|
79607
79992
|
const roleDesc = FIELD_DESCRIPTIONS[`prompts.overrides.${role}`];
|
|
@@ -80630,75 +81015,6 @@ var init_hardening = __esm(() => {
|
|
|
80630
81015
|
};
|
|
80631
81016
|
});
|
|
80632
81017
|
|
|
80633
|
-
// src/acceptance/semantic-verdict.ts
|
|
80634
|
-
import path11 from "path";
|
|
80635
|
-
async function persistSemanticVerdict(featureDir2, storyId, verdict) {
|
|
80636
|
-
const dir = path11.join(featureDir2, "semantic-verdicts");
|
|
80637
|
-
await _semanticVerdictDeps.mkdirp(dir);
|
|
80638
|
-
const filePath = path11.join(dir, `${storyId}.json`);
|
|
80639
|
-
await _semanticVerdictDeps.writeFile(filePath, JSON.stringify(verdict, null, 2));
|
|
80640
|
-
}
|
|
80641
|
-
function migrateSemanticVerdict(verdict) {
|
|
80642
|
-
if (!verdict.findings?.length)
|
|
80643
|
-
return verdict;
|
|
80644
|
-
const first = verdict.findings[0];
|
|
80645
|
-
if ("source" in first)
|
|
80646
|
-
return verdict;
|
|
80647
|
-
return {
|
|
80648
|
-
...verdict,
|
|
80649
|
-
findings: verdict.findings.map((f) => reviewFindingToFinding(f))
|
|
80650
|
-
};
|
|
80651
|
-
}
|
|
80652
|
-
async function loadSemanticVerdicts(featureDir2) {
|
|
80653
|
-
const dir = path11.join(featureDir2, "semantic-verdicts");
|
|
80654
|
-
let files;
|
|
80655
|
-
try {
|
|
80656
|
-
files = await _semanticVerdictDeps.readdir(dir);
|
|
80657
|
-
} catch (err) {
|
|
80658
|
-
if (err.code === "ENOENT")
|
|
80659
|
-
return [];
|
|
80660
|
-
throw err;
|
|
80661
|
-
}
|
|
80662
|
-
const results = [];
|
|
80663
|
-
for (const file2 of files) {
|
|
80664
|
-
if (!file2.endsWith(".json"))
|
|
80665
|
-
continue;
|
|
80666
|
-
const filePath = path11.join(dir, file2);
|
|
80667
|
-
const content = await _semanticVerdictDeps.readFile(filePath);
|
|
80668
|
-
try {
|
|
80669
|
-
const parsed = JSON.parse(content);
|
|
80670
|
-
results.push(migrateSemanticVerdict(parsed));
|
|
80671
|
-
} catch {
|
|
80672
|
-
_semanticVerdictDeps.logDebug(`Skipping invalid JSON in semantic-verdicts/${file2}`);
|
|
80673
|
-
}
|
|
80674
|
-
}
|
|
80675
|
-
return results;
|
|
80676
|
-
}
|
|
80677
|
-
var _semanticVerdictDeps;
|
|
80678
|
-
var init_semantic_verdict = __esm(() => {
|
|
80679
|
-
init_findings();
|
|
80680
|
-
init_logger2();
|
|
80681
|
-
_semanticVerdictDeps = {
|
|
80682
|
-
mkdirp: async (dir) => {
|
|
80683
|
-
const { mkdir: mkdir21 } = await import("fs/promises");
|
|
80684
|
-
await mkdir21(dir, { recursive: true });
|
|
80685
|
-
},
|
|
80686
|
-
writeFile: async (filePath, content) => {
|
|
80687
|
-
await Bun.write(filePath, content);
|
|
80688
|
-
},
|
|
80689
|
-
readdir: async (dir) => {
|
|
80690
|
-
const { readdir: readdir7 } = await import("fs/promises");
|
|
80691
|
-
return readdir7(dir);
|
|
80692
|
-
},
|
|
80693
|
-
readFile: async (filePath) => {
|
|
80694
|
-
return Bun.file(filePath).text();
|
|
80695
|
-
},
|
|
80696
|
-
logDebug: (msg) => {
|
|
80697
|
-
getLogger()?.debug("semantic-verdict", msg);
|
|
80698
|
-
}
|
|
80699
|
-
};
|
|
80700
|
-
});
|
|
80701
|
-
|
|
80702
81018
|
// src/acceptance/index.ts
|
|
80703
81019
|
var exports_acceptance = {};
|
|
80704
81020
|
__export(exports_acceptance, {
|
|
@@ -80711,11 +81027,9 @@ __export(exports_acceptance, {
|
|
|
80711
81027
|
groupStoriesByPackage: () => groupStoriesByPackage,
|
|
80712
81028
|
isStubTestContent: () => isStubTestContent,
|
|
80713
81029
|
loadAcceptanceTestContent: () => loadAcceptanceTestContent,
|
|
80714
|
-
loadSemanticVerdicts: () => loadSemanticVerdicts,
|
|
80715
81030
|
loadSourceFilesForDiagnosis: () => loadSourceFilesForDiagnosis,
|
|
80716
81031
|
parseAcceptanceCriteria: () => parseAcceptanceCriteria,
|
|
80717
81032
|
parseRefinementResponse: () => parseRefinementResponse,
|
|
80718
|
-
persistSemanticVerdict: () => persistSemanticVerdict,
|
|
80719
81033
|
refinementWouldFallback: () => refinementWouldFallback,
|
|
80720
81034
|
resolveAcceptanceFeatureTestPath: () => resolveAcceptanceFeatureTestPath,
|
|
80721
81035
|
resolveSuggestedPackageFeatureTestPath: () => resolveSuggestedPackageFeatureTestPath,
|
|
@@ -80729,7 +81043,6 @@ var init_acceptance2 = __esm(() => {
|
|
|
80729
81043
|
init_generator();
|
|
80730
81044
|
init_hardening();
|
|
80731
81045
|
init_refinement();
|
|
80732
|
-
init_semantic_verdict();
|
|
80733
81046
|
init_test_path();
|
|
80734
81047
|
});
|
|
80735
81048
|
|
|
@@ -81725,11 +82038,11 @@ var init_generate = __esm(() => {
|
|
|
81725
82038
|
// src/cli/init-context.ts
|
|
81726
82039
|
import { mkdir as mkdir21, readdir as readdir7 } from "fs/promises";
|
|
81727
82040
|
import { basename as basename11, join as join74, relative as relative23, sep as sep12 } from "path";
|
|
81728
|
-
async function bunFileExists(
|
|
81729
|
-
return Bun.file(
|
|
82041
|
+
async function bunFileExists(path11) {
|
|
82042
|
+
return Bun.file(path11).exists();
|
|
81730
82043
|
}
|
|
81731
|
-
async function mkdirp(
|
|
81732
|
-
await mkdir21(
|
|
82044
|
+
async function mkdirp(path11) {
|
|
82045
|
+
await mkdir21(path11, { recursive: true });
|
|
81733
82046
|
}
|
|
81734
82047
|
async function findFiles(dir, maxFiles = 200) {
|
|
81735
82048
|
const files = [];
|
|
@@ -81792,8 +82105,8 @@ async function detectEntryPoints(projectRoot) {
|
|
|
81792
82105
|
const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
|
|
81793
82106
|
const found = [];
|
|
81794
82107
|
for (const candidate of candidates) {
|
|
81795
|
-
const
|
|
81796
|
-
if (await bunFileExists(
|
|
82108
|
+
const path11 = join74(projectRoot, candidate);
|
|
82109
|
+
if (await bunFileExists(path11)) {
|
|
81797
82110
|
found.push(candidate);
|
|
81798
82111
|
}
|
|
81799
82112
|
}
|
|
@@ -81803,8 +82116,8 @@ async function detectConfigFiles(projectRoot) {
|
|
|
81803
82116
|
const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
|
|
81804
82117
|
const found = [];
|
|
81805
82118
|
for (const candidate of candidates) {
|
|
81806
|
-
const
|
|
81807
|
-
if (await bunFileExists(
|
|
82119
|
+
const path11 = join74(projectRoot, candidate);
|
|
82120
|
+
if (await bunFileExists(path11)) {
|
|
81808
82121
|
found.push(candidate);
|
|
81809
82122
|
}
|
|
81810
82123
|
}
|
|
@@ -82633,9 +82946,9 @@ var init_scanner = __esm(() => {
|
|
|
82633
82946
|
_scannerDeps = {
|
|
82634
82947
|
discoverWorkspacePackages: (workdir) => discoverWorkspacePackages2(workdir),
|
|
82635
82948
|
detectLanguage: (pkgDir) => detectLanguage(pkgDir),
|
|
82636
|
-
readPackageJson: async (
|
|
82949
|
+
readPackageJson: async (path11) => {
|
|
82637
82950
|
try {
|
|
82638
|
-
const file2 = Bun.file(
|
|
82951
|
+
const file2 = Bun.file(path11);
|
|
82639
82952
|
if (!await file2.exists())
|
|
82640
82953
|
return null;
|
|
82641
82954
|
return await file2.json();
|
|
@@ -82656,6 +82969,7 @@ var init_analyze = __esm(() => {
|
|
|
82656
82969
|
function createHumanAskLink(opts) {
|
|
82657
82970
|
let queue = Promise.resolve();
|
|
82658
82971
|
let activeId;
|
|
82972
|
+
const canRemember = (req) => opts.onRemember !== undefined && req.command !== undefined;
|
|
82659
82973
|
const deny3 = (decidedBy) => ({
|
|
82660
82974
|
decision: "deny",
|
|
82661
82975
|
decidedBy
|
|
@@ -82793,7 +83107,7 @@ function createHumanAskLink(opts) {
|
|
|
82793
83107
|
`stage: ${req.stage}`
|
|
82794
83108
|
].join(`
|
|
82795
83109
|
`),
|
|
82796
|
-
options:
|
|
83110
|
+
options: canRemember(req) ? [ALLOW_ONCE, ALLOW_REMEMBER, DENY] : [ALLOW_ONCE, DENY],
|
|
82797
83111
|
timeout: opts.timeoutMs,
|
|
82798
83112
|
fallback: "abort",
|
|
82799
83113
|
createdAt: Date.now(),
|
|
@@ -82811,7 +83125,7 @@ function createHumanAskLink(opts) {
|
|
|
82811
83125
|
if (!PERMITS.has(action)) {
|
|
82812
83126
|
outcome = deny3("human");
|
|
82813
83127
|
} else {
|
|
82814
|
-
if (action === "allow-remember" && opts.onRemember) {
|
|
83128
|
+
if (action === "allow-remember" && canRemember(req) && opts.onRemember) {
|
|
82815
83129
|
try {
|
|
82816
83130
|
await opts.onRemember(req);
|
|
82817
83131
|
} catch (err) {
|
|
@@ -82937,7 +83251,7 @@ function createHumanAskLink(opts) {
|
|
|
82937
83251
|
async function cancelPendingAsk(link) {
|
|
82938
83252
|
await link.cancel();
|
|
82939
83253
|
}
|
|
82940
|
-
var MAX_COMMAND_CHARS = 3500, maskedFooter = (count) => `${count} secret value(s) masked; the approved command contains them`, _askLinkDeps,
|
|
83254
|
+
var MAX_COMMAND_CHARS = 3500, maskedFooter = (count) => `${count} secret value(s) masked; the approved command contains them`, _askLinkDeps, ALLOW_ONCE, ALLOW_REMEMBER, DENY, PERMITS;
|
|
82941
83255
|
var init_ask_link = __esm(() => {
|
|
82942
83256
|
init_permissions();
|
|
82943
83257
|
init_logger2();
|
|
@@ -82946,11 +83260,9 @@ var init_ask_link = __esm(() => {
|
|
|
82946
83260
|
clearTimeout: (id) => clearTimeout(id),
|
|
82947
83261
|
ASK_KEEPALIVE_MS: 60000
|
|
82948
83262
|
};
|
|
82949
|
-
|
|
82950
|
-
|
|
82951
|
-
|
|
82952
|
-
{ key: "deny", label: "Deny" }
|
|
82953
|
-
];
|
|
83263
|
+
ALLOW_ONCE = { key: "allow", label: "Allow once" };
|
|
83264
|
+
ALLOW_REMEMBER = { key: "allow-remember", label: "Allow + remember" };
|
|
83265
|
+
DENY = { key: "deny", label: "Deny" };
|
|
82954
83266
|
PERMITS = new Set(["allow", "allow-remember"]);
|
|
82955
83267
|
});
|
|
82956
83268
|
|
|
@@ -83124,10 +83436,10 @@ async function buildDispatchAskWiring(opts, deps = _dispatchAskDeps) {
|
|
|
83124
83436
|
stage: req.stage,
|
|
83125
83437
|
command: req.command ?? "",
|
|
83126
83438
|
root: req.root ?? opts.repoRoot,
|
|
83127
|
-
origin: "escalate",
|
|
83128
|
-
matchedRule: null,
|
|
83439
|
+
origin: req.matchedRule !== undefined ? "askRule" : "escalate",
|
|
83440
|
+
matchedRule: req.matchedRule ?? null,
|
|
83129
83441
|
approvedAt: new Date().toISOString(),
|
|
83130
|
-
approvedBy: "
|
|
83442
|
+
approvedBy: opts.config.interaction?.plugin ?? "unknown",
|
|
83131
83443
|
naxCommit: NAX_COMMIT
|
|
83132
83444
|
})
|
|
83133
83445
|
});
|
|
@@ -83205,6 +83517,25 @@ async function collectEffectiveRunStageModes(opts, deps = _dispatchAskDeps) {
|
|
|
83205
83517
|
})));
|
|
83206
83518
|
return collectRunStageModes([opts.rootConfig, ...opts.extraConfigs ?? [], ...packageConfigs]);
|
|
83207
83519
|
}
|
|
83520
|
+
async function buildApprovalsSeal(opts, deps = _dispatchAskDeps) {
|
|
83521
|
+
const stageModes = await collectEffectiveRunStageModes({ projectDir: opts.projectDir, rootConfig: opts.rootConfig, packageDirs: opts.packageDirs }, deps);
|
|
83522
|
+
const forgeCapable = isForgeCapable(stageModes, opts.rootConfig.execution?.sandbox?.enabled === true);
|
|
83523
|
+
if (!forgeCapable)
|
|
83524
|
+
return async () => {};
|
|
83525
|
+
const approvalsFile = approvalsPath(opts.outputDir);
|
|
83526
|
+
const runId = opts.runId;
|
|
83527
|
+
return async () => {
|
|
83528
|
+
try {
|
|
83529
|
+
await deps.prepareApprovalsStore({ approvalsFile, runId, forgeCapable: true });
|
|
83530
|
+
} catch (error48) {
|
|
83531
|
+
getSafeLogger()?.warn("permissions", "[approvals] could not update the store's taint marker", {
|
|
83532
|
+
approvalsFile,
|
|
83533
|
+
forgeCapable: true,
|
|
83534
|
+
error: error48
|
|
83535
|
+
});
|
|
83536
|
+
}
|
|
83537
|
+
};
|
|
83538
|
+
}
|
|
83208
83539
|
async function buildRunDispatchAskWiring(opts, deps = _dispatchAskDeps) {
|
|
83209
83540
|
const stageModes = await collectEffectiveRunStageModes({
|
|
83210
83541
|
projectDir: opts.projectDir,
|
|
@@ -83218,6 +83549,7 @@ var DEFAULT_APPROVAL_TIMEOUT_MS = 600000, APPROVAL_AUDIT_DIR = "approval-audit",
|
|
|
83218
83549
|
var init_dispatch_ask = __esm(() => {
|
|
83219
83550
|
init_command_safety();
|
|
83220
83551
|
init_config();
|
|
83552
|
+
init_logger2();
|
|
83221
83553
|
init_permissions();
|
|
83222
83554
|
init_version();
|
|
83223
83555
|
init_ask_link();
|
|
@@ -88334,6 +88666,11 @@ async function performTeardown(ctx) {
|
|
|
88334
88666
|
if (ctx.pidRegistry) {
|
|
88335
88667
|
await ctx.pidRegistry.killAll();
|
|
88336
88668
|
}
|
|
88669
|
+
if (ctx.sealApprovals) {
|
|
88670
|
+
await ctx.sealApprovals().catch(() => {
|
|
88671
|
+
return;
|
|
88672
|
+
});
|
|
88673
|
+
}
|
|
88337
88674
|
}
|
|
88338
88675
|
function getSignalNumber(signal) {
|
|
88339
88676
|
const signalMap = {
|
|
@@ -88490,7 +88827,7 @@ var init_crash_recovery = __esm(() => {
|
|
|
88490
88827
|
});
|
|
88491
88828
|
|
|
88492
88829
|
// src/execution/ensure-package-dirs.ts
|
|
88493
|
-
import
|
|
88830
|
+
import path11 from "path";
|
|
88494
88831
|
async function ensureStoryPackageDirs(prd, workdir, deps = _ensurePackageDirsDeps) {
|
|
88495
88832
|
const logger = getSafeLogger();
|
|
88496
88833
|
const relToStoryId = new Map;
|
|
@@ -88503,8 +88840,8 @@ async function ensureStoryPackageDirs(prd, workdir, deps = _ensurePackageDirsDep
|
|
|
88503
88840
|
}
|
|
88504
88841
|
const created = [];
|
|
88505
88842
|
for (const [rel, storyId] of relToStoryId) {
|
|
88506
|
-
const abs =
|
|
88507
|
-
const rootWithSep = workdir.endsWith(
|
|
88843
|
+
const abs = path11.resolve(workdir, rel);
|
|
88844
|
+
const rootWithSep = workdir.endsWith(path11.sep) ? workdir : workdir + path11.sep;
|
|
88508
88845
|
if (abs !== workdir && !abs.startsWith(rootWithSep)) {
|
|
88509
88846
|
logger?.warn("execution", "Skipping story workdir outside repo root", {
|
|
88510
88847
|
storyId,
|
|
@@ -88655,8 +88992,8 @@ async function verifyQuoteTriple(triple, workdir, deps = _quoteIntegrityDeps) {
|
|
|
88655
88992
|
return false;
|
|
88656
88993
|
const lines = content.split(`
|
|
88657
88994
|
`);
|
|
88658
|
-
const start = Math.max(0, triple.line - 1 -
|
|
88659
|
-
const end = Math.min(lines.length, triple.line +
|
|
88995
|
+
const start = Math.max(0, triple.line - 1 - CONTEXT_LINES2);
|
|
88996
|
+
const end = Math.min(lines.length, triple.line + CONTEXT_LINES2);
|
|
88660
88997
|
const window2 = lines.slice(start, end).join(`
|
|
88661
88998
|
`);
|
|
88662
88999
|
return normalizeWs2(window2).toLowerCase().includes(normalizeWs2(triple.quote).toLowerCase());
|
|
@@ -88681,14 +89018,14 @@ async function verifyEscalationQuotes(reason, workdir, storyId, deps = _quoteInt
|
|
|
88681
89018
|
}
|
|
88682
89019
|
return verified;
|
|
88683
89020
|
}
|
|
88684
|
-
var _quoteIntegrityDeps,
|
|
89021
|
+
var _quoteIntegrityDeps, CONTEXT_LINES2 = 3;
|
|
88685
89022
|
var init_quote_integrity = __esm(() => {
|
|
88686
89023
|
init_logger2();
|
|
88687
89024
|
init_path_security2();
|
|
88688
89025
|
_quoteIntegrityDeps = {
|
|
88689
|
-
readFile: async (
|
|
89026
|
+
readFile: async (path12) => {
|
|
88690
89027
|
try {
|
|
88691
|
-
return await Bun.file(
|
|
89028
|
+
return await Bun.file(path12).text();
|
|
88692
89029
|
} catch {
|
|
88693
89030
|
return null;
|
|
88694
89031
|
}
|
|
@@ -89128,7 +89465,7 @@ var init_escalation = __esm(() => {
|
|
|
89128
89465
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
89129
89466
|
import { rename as rename5, unlink as unlink5 } from "fs/promises";
|
|
89130
89467
|
import { hostname as hostname3 } from "os";
|
|
89131
|
-
import
|
|
89468
|
+
import path12 from "path";
|
|
89132
89469
|
function getSafeLogger3() {
|
|
89133
89470
|
try {
|
|
89134
89471
|
return getLogger();
|
|
@@ -89208,7 +89545,7 @@ async function claimReclaimableLock(lockPath, observedContent, lockData) {
|
|
|
89208
89545
|
return { action: "discard" };
|
|
89209
89546
|
}
|
|
89210
89547
|
async function acquireLock(workdir) {
|
|
89211
|
-
const lockPath =
|
|
89548
|
+
const lockPath = path12.join(workdir, "nax.lock");
|
|
89212
89549
|
const lockFile = Bun.file(lockPath);
|
|
89213
89550
|
try {
|
|
89214
89551
|
const exists2 = await lockFile.exists();
|
|
@@ -89261,7 +89598,7 @@ async function acquireLock(workdir) {
|
|
|
89261
89598
|
}
|
|
89262
89599
|
}
|
|
89263
89600
|
async function releaseLock(workdir) {
|
|
89264
|
-
const lockPath =
|
|
89601
|
+
const lockPath = path12.join(workdir, "nax.lock");
|
|
89265
89602
|
try {
|
|
89266
89603
|
await unlink5(lockPath);
|
|
89267
89604
|
} catch (error48) {
|
|
@@ -89286,7 +89623,7 @@ var init_lock2 = __esm(() => {
|
|
|
89286
89623
|
// src/execution/feature-lock.ts
|
|
89287
89624
|
import { mkdir as mkdir24, rename as rename6, unlink as unlink6 } from "fs/promises";
|
|
89288
89625
|
import { hostname as hostname4 } from "os";
|
|
89289
|
-
import
|
|
89626
|
+
import path13 from "path";
|
|
89290
89627
|
function validateFeatureId2(featureId) {
|
|
89291
89628
|
if (!featureId || featureId.length === 0) {
|
|
89292
89629
|
throw new NaxError("Feature ID cannot be empty", "INVALID_FEATURE_ID", { stage: "feature-lock" });
|
|
@@ -89316,7 +89653,7 @@ function getSafeLogger4() {
|
|
|
89316
89653
|
}
|
|
89317
89654
|
function featureLockPath(outputDir, feature) {
|
|
89318
89655
|
validateFeatureId2(feature);
|
|
89319
|
-
return
|
|
89656
|
+
return path13.join(outputDir, "features", feature, "nax.lock");
|
|
89320
89657
|
}
|
|
89321
89658
|
function lockHost() {
|
|
89322
89659
|
return _featureLockDeps.host();
|
|
@@ -89377,7 +89714,7 @@ function emptyHolder(feature) {
|
|
|
89377
89714
|
}
|
|
89378
89715
|
async function acquireFeatureLock(args) {
|
|
89379
89716
|
const lockPath = _featureLockDeps.featureLockPath(args.outputDir, args.feature);
|
|
89380
|
-
const featureDir2 =
|
|
89717
|
+
const featureDir2 = path13.dirname(lockPath);
|
|
89381
89718
|
await mkdir24(featureDir2, { recursive: true });
|
|
89382
89719
|
const lockFile = Bun.file(lockPath);
|
|
89383
89720
|
if (await lockFile.exists()) {
|
|
@@ -90834,7 +91171,7 @@ var init_acceptance3 = __esm(() => {
|
|
|
90834
91171
|
});
|
|
90835
91172
|
|
|
90836
91173
|
// src/pipeline/stages/acceptance-setup.ts
|
|
90837
|
-
import
|
|
91174
|
+
import path14 from "path";
|
|
90838
91175
|
function computeACFingerprint(criteria) {
|
|
90839
91176
|
const sorted = [...criteria].sort().join(`
|
|
90840
91177
|
`);
|
|
@@ -90844,7 +91181,7 @@ function computeACFingerprint(criteria) {
|
|
|
90844
91181
|
}
|
|
90845
91182
|
function computeAcceptanceLayoutFingerprint(workdir, groups) {
|
|
90846
91183
|
const layout = groups.map(({ testPath, stories }) => ({
|
|
90847
|
-
testPath:
|
|
91184
|
+
testPath: path14.relative(workdir, testPath).replaceAll(path14.sep, "/"),
|
|
90848
91185
|
storyIds: stories.map((story) => story.id).sort()
|
|
90849
91186
|
})).sort((a, b) => a.testPath.localeCompare(b.testPath));
|
|
90850
91187
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
@@ -90854,14 +91191,14 @@ function computeAcceptanceLayoutFingerprint(workdir, groups) {
|
|
|
90854
91191
|
async function runAcceptanceSetup(ctx, featureDir2, phaseStartTime) {
|
|
90855
91192
|
const language = ctx.config.project?.language;
|
|
90856
91193
|
const testPathConfig = ctx.config.acceptance.testPath;
|
|
90857
|
-
const metaPath =
|
|
91194
|
+
const metaPath = path14.join(featureDir2, "acceptance-meta.json");
|
|
90858
91195
|
const allCriteria = ctx.prd.userStories.filter(isInAcceptanceScope).flatMap((s) => s.acceptanceCriteria);
|
|
90859
91196
|
const featureName = ctx.prd.feature ?? ctx.prd.featureName;
|
|
90860
91197
|
const groups = await groupStoriesByPackage(ctx.prd, ctx.workdir, featureName, testPathConfig, language);
|
|
90861
91198
|
const nonFixStories = groups.flatMap((g) => g.stories);
|
|
90862
91199
|
const groupConfigs = new Map;
|
|
90863
91200
|
for (const group of groups) {
|
|
90864
|
-
const relativeWorkdir =
|
|
91201
|
+
const relativeWorkdir = path14.relative(ctx.projectDir, group.packageDir);
|
|
90865
91202
|
let config2 = ctx.config;
|
|
90866
91203
|
if (relativeWorkdir && relativeWorkdir !== ".") {
|
|
90867
91204
|
try {
|
|
@@ -90906,7 +91243,6 @@ async function runAcceptanceSetup(ctx, featureDir2, phaseStartTime) {
|
|
|
90906
91243
|
await _acceptanceSetupDeps.deleteFile(testPath);
|
|
90907
91244
|
}
|
|
90908
91245
|
}
|
|
90909
|
-
await _acceptanceSetupDeps.deleteSemanticVerdicts(featureDir2);
|
|
90910
91246
|
shouldGenerate = true;
|
|
90911
91247
|
regenerated = true;
|
|
90912
91248
|
} else {
|
|
@@ -91014,7 +91350,7 @@ async function runAcceptanceSetup(ctx, featureDir2, phaseStartTime) {
|
|
|
91014
91350
|
testable: c.testable,
|
|
91015
91351
|
storyId: c.storyId
|
|
91016
91352
|
})), null, 2);
|
|
91017
|
-
await _acceptanceSetupDeps.writeFile(
|
|
91353
|
+
await _acceptanceSetupDeps.writeFile(path14.join(featureDir2, "acceptance-refined.json"), refinedJsonContent);
|
|
91018
91354
|
}
|
|
91019
91355
|
if (sawDispatchFailure) {
|
|
91020
91356
|
getSafeLogger()?.warn("acceptance-setup", "generation dispatch failed; not recording acceptance meta so the next run regenerates", { storyId: ctx.story?.id, metaPath });
|
|
@@ -91126,21 +91462,6 @@ var init_acceptance_setup = __esm(() => {
|
|
|
91126
91462
|
throw err;
|
|
91127
91463
|
}
|
|
91128
91464
|
},
|
|
91129
|
-
deleteSemanticVerdicts: async (featureDir2) => {
|
|
91130
|
-
const dir = `${featureDir2}/semantic-verdicts`;
|
|
91131
|
-
const { readdir: readdir8, unlink: unlink7 } = await import("fs/promises");
|
|
91132
|
-
let files;
|
|
91133
|
-
try {
|
|
91134
|
-
files = await readdir8(dir);
|
|
91135
|
-
} catch (err) {
|
|
91136
|
-
if (err.code === "ENOENT")
|
|
91137
|
-
return;
|
|
91138
|
-
throw err;
|
|
91139
|
-
}
|
|
91140
|
-
for (const file2 of files) {
|
|
91141
|
-
await unlink7(`${dir}/${file2}`);
|
|
91142
|
-
}
|
|
91143
|
-
},
|
|
91144
91465
|
readMeta: async (metaPath) => {
|
|
91145
91466
|
const f = Bun.file(metaPath);
|
|
91146
91467
|
if (!await f.exists())
|
|
@@ -91345,7 +91666,6 @@ async function getDiffFilePaths(workdir, baseRef) {
|
|
|
91345
91666
|
}
|
|
91346
91667
|
var MAX_DIFF_TEXT_CHARS = 8000, HIGH_MEMORY_TELEMETRY_BYTES, STREAM_DRAIN_DEADLINE_MS = 2000, completionStage, _completionDeps;
|
|
91347
91668
|
var init_completion = __esm(() => {
|
|
91348
|
-
init_acceptance2();
|
|
91349
91669
|
init_config();
|
|
91350
91670
|
init_engine();
|
|
91351
91671
|
init_fragments();
|
|
@@ -91465,7 +91785,6 @@ var init_completion = __esm(() => {
|
|
|
91465
91785
|
};
|
|
91466
91786
|
_completionDeps = {
|
|
91467
91787
|
checkReviewGate,
|
|
91468
|
-
persistSemanticVerdict,
|
|
91469
91788
|
savePRD,
|
|
91470
91789
|
getDiffText,
|
|
91471
91790
|
getDiffFilePaths,
|
|
@@ -92241,9 +92560,9 @@ var init_prompt2 = __esm(() => {
|
|
|
92241
92560
|
});
|
|
92242
92561
|
|
|
92243
92562
|
// src/pipeline/stages/queue-check.ts
|
|
92244
|
-
import
|
|
92563
|
+
import path15 from "path";
|
|
92245
92564
|
function resolvePrdPath(ctx) {
|
|
92246
|
-
return
|
|
92565
|
+
return path15.join(ctx.featureDir ?? featureDir(ctx.workdir, "unknown"), "prd.json");
|
|
92247
92566
|
}
|
|
92248
92567
|
function logDroppedCommands(logger, ctx, queueCommands, currentIndex) {
|
|
92249
92568
|
const dropped = queueCommands.slice(currentIndex + 1);
|
|
@@ -92294,10 +92613,10 @@ async function processQueueCommands(ctx, logger, queueCommands) {
|
|
|
92294
92613
|
}
|
|
92295
92614
|
if (cmd.type === "INJECT") {
|
|
92296
92615
|
try {
|
|
92297
|
-
if (
|
|
92616
|
+
if (path15.isAbsolute(cmd.storyFile)) {
|
|
92298
92617
|
throw new NaxError(`INJECT storyFile must be a relative path within the workspace: ${cmd.storyFile}`, "INJECT_PATH_ABSOLUTE", { stage: "queue-check", storyId: ctx.story?.id ?? "unknown", storyFile: cmd.storyFile });
|
|
92299
92618
|
}
|
|
92300
|
-
const storyFilePath = validateFilePath(
|
|
92619
|
+
const storyFilePath = validateFilePath(path15.join(ctx.workdir, cmd.storyFile), ctx.workdir);
|
|
92301
92620
|
const raw = await Bun.file(storyFilePath).json();
|
|
92302
92621
|
const existingIds = new Set(ctx.prd.userStories.map((s) => s.id));
|
|
92303
92622
|
const story = validateInjectedStory(raw, existingIds);
|
|
@@ -93675,7 +93994,7 @@ var init_hooks = __esm(() => {
|
|
|
93675
93994
|
});
|
|
93676
93995
|
|
|
93677
93996
|
// src/execution/lifecycle/acceptance-helpers.ts
|
|
93678
|
-
import
|
|
93997
|
+
import path16 from "path";
|
|
93679
93998
|
function resolveAcceptanceFixTarget(acceptanceTestPaths, failedPackage, config2) {
|
|
93680
93999
|
const matchedEntry = failedPackage ? acceptanceTestPaths?.find((entry) => entry.testPath === failedPackage.testPath || entry.packageDir === failedPackage.packageDir) : undefined;
|
|
93681
94000
|
const selectedPathEntry = matchedEntry ?? acceptanceTestPaths?.[0];
|
|
@@ -93698,10 +94017,7 @@ function resolveAcceptanceFixTarget(acceptanceTestPaths, failedPackage, config2)
|
|
|
93698
94017
|
function isStubTestFile(content) {
|
|
93699
94018
|
return isStubTestContent(content);
|
|
93700
94019
|
}
|
|
93701
|
-
function isTestLevelFailure(failedACs, totalACs
|
|
93702
|
-
if (semanticVerdicts && semanticVerdicts.length > 0 && semanticVerdicts.every((v) => v.passed)) {
|
|
93703
|
-
return true;
|
|
93704
|
-
}
|
|
94020
|
+
function isTestLevelFailure(failedACs, totalACs) {
|
|
93705
94021
|
const failedCount = typeof failedACs === "number" ? failedACs : failedACs.length;
|
|
93706
94022
|
const hasACError = Array.isArray(failedACs) && failedACs.includes("AC-ERROR");
|
|
93707
94023
|
if (hasACError)
|
|
@@ -93713,7 +94029,7 @@ function isTestLevelFailure(failedACs, totalACs, semanticVerdicts) {
|
|
|
93713
94029
|
async function loadSpecContent(featureDir2) {
|
|
93714
94030
|
if (!featureDir2)
|
|
93715
94031
|
return "";
|
|
93716
|
-
const specPath =
|
|
94032
|
+
const specPath = path16.join(featureDir2, "spec.md");
|
|
93717
94033
|
const specFile = Bun.file(specPath);
|
|
93718
94034
|
return await specFile.exists() ? await specFile.text() : "";
|
|
93719
94035
|
}
|
|
@@ -93733,7 +94049,7 @@ async function loadAcceptanceTestContent2(featureDir2, testPaths, configuredTest
|
|
|
93733
94049
|
}
|
|
93734
94050
|
if (!configuredTestPath)
|
|
93735
94051
|
return [];
|
|
93736
|
-
const resolvedPath =
|
|
94052
|
+
const resolvedPath = path16.join(featureDir2, configuredTestPath);
|
|
93737
94053
|
const testFile = Bun.file(resolvedPath);
|
|
93738
94054
|
const content = await testFile.exists() ? await testFile.text() : "";
|
|
93739
94055
|
return [{ content, path: resolvedPath }];
|
|
@@ -93763,7 +94079,7 @@ async function regenerateAcceptanceTest(testPath, acceptanceContext) {
|
|
|
93763
94079
|
const { unlink: unlink7 } = await import("fs/promises");
|
|
93764
94080
|
await unlink7(testPath);
|
|
93765
94081
|
if (acceptanceContext.featureDir) {
|
|
93766
|
-
const metaPath =
|
|
94082
|
+
const metaPath = path16.join(acceptanceContext.featureDir, "acceptance-meta.json");
|
|
93767
94083
|
try {
|
|
93768
94084
|
await unlink7(metaPath);
|
|
93769
94085
|
} catch {}
|
|
@@ -93779,7 +94095,7 @@ async function regenerateAcceptanceTest(testPath, acceptanceContext) {
|
|
|
93779
94095
|
const diffOutput = await _regenerateDeps.spawnGitDiff(repoRoot, storyGitRef, pathspec);
|
|
93780
94096
|
const changedFilesRaw = diffOutput.split(`
|
|
93781
94097
|
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
93782
|
-
const packageDir = storyPkg && acceptanceContext.projectDir ?
|
|
94098
|
+
const packageDir = storyPkg && acceptanceContext.projectDir ? path16.join(acceptanceContext.projectDir, storyPkg) : undefined;
|
|
93783
94099
|
const ignoreMatchers = acceptanceContext.naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
|
|
93784
94100
|
const changedFiles2 = filterNaxInternalPaths(changedFilesRaw, ignoreMatchers);
|
|
93785
94101
|
const MAX_BYTES = 51200;
|
|
@@ -93791,7 +94107,7 @@ async function regenerateAcceptanceTest(testPath, acceptanceContext) {
|
|
|
93791
94107
|
for (const file2 of changedFiles2) {
|
|
93792
94108
|
if (totalBytes >= MAX_BYTES)
|
|
93793
94109
|
break;
|
|
93794
|
-
const filePath =
|
|
94110
|
+
const filePath = path16.join(repoRoot, file2);
|
|
93795
94111
|
try {
|
|
93796
94112
|
const fileContent = await _regenerateDeps.readFile(filePath);
|
|
93797
94113
|
const remaining = MAX_BYTES - totalBytes;
|
|
@@ -93878,7 +94194,7 @@ function fixCallCtx(ctx, packageDir, config2) {
|
|
|
93878
94194
|
}
|
|
93879
94195
|
async function resolveAcceptanceDiagnosis(opts) {
|
|
93880
94196
|
const logger = getSafeLogger();
|
|
93881
|
-
const { ctx, failures, totalACs, strategy,
|
|
94197
|
+
const { ctx, failures, totalACs, strategy, diagnosisOpts } = opts;
|
|
93882
94198
|
const storyId = diagnosisOpts.storyId;
|
|
93883
94199
|
if (strategy === "implement-only") {
|
|
93884
94200
|
logger?.info("acceptance.diagnosis", "Fast path: implement-only strategy \u2192 source_bug", { storyId });
|
|
@@ -93888,19 +94204,6 @@ async function resolveAcceptanceDiagnosis(opts) {
|
|
|
93888
94204
|
confidence: 1
|
|
93889
94205
|
};
|
|
93890
94206
|
}
|
|
93891
|
-
const SENTINELS = ["AC-ERROR", "AC-HOOK"];
|
|
93892
|
-
const hasOnlySentinels = failures.failedACs.length > 0 && failures.failedACs.every((ac) => SENTINELS.includes(ac));
|
|
93893
|
-
if (!hasOnlySentinels && semanticVerdicts.length > 0 && semanticVerdicts.every((v) => v.passed)) {
|
|
93894
|
-
logger?.info("acceptance.diagnosis", "Fast path: all semantic verdicts passed \u2192 test_bug", {
|
|
93895
|
-
storyId,
|
|
93896
|
-
verdictCount: semanticVerdicts.length
|
|
93897
|
-
});
|
|
93898
|
-
return {
|
|
93899
|
-
verdict: "test_bug",
|
|
93900
|
-
reasoning: `Semantic review confirmed all ${semanticVerdicts.length} ACs are implemented \u2014 failure is a test generation issue`,
|
|
93901
|
-
confidence: 1
|
|
93902
|
-
};
|
|
93903
|
-
}
|
|
93904
94207
|
if (isTestLevelFailure(failures.failedACs, totalACs)) {
|
|
93905
94208
|
logger?.info("acceptance.diagnosis", "Fast path: test-level failure heuristic \u2192 test_bug", {
|
|
93906
94209
|
storyId,
|
|
@@ -93922,8 +94225,7 @@ async function resolveAcceptanceDiagnosis(opts) {
|
|
|
93922
94225
|
testOutput: diagnosisOpts.testOutput,
|
|
93923
94226
|
testFileContent: diagnosisOpts.testFileContent,
|
|
93924
94227
|
acceptanceTestPath: diagnosisOpts.acceptanceTestPath,
|
|
93925
|
-
sourceFiles
|
|
93926
|
-
semanticVerdicts
|
|
94228
|
+
sourceFiles
|
|
93927
94229
|
});
|
|
93928
94230
|
}
|
|
93929
94231
|
var _diagnosisDeps;
|
|
@@ -94211,7 +94513,6 @@ async function runAcceptanceLoop(ctx) {
|
|
|
94211
94513
|
continue;
|
|
94212
94514
|
}
|
|
94213
94515
|
}
|
|
94214
|
-
const semanticVerdicts = ctx.featureDir ? await _acceptanceLoopDeps.loadSemanticVerdicts(ctx.featureDir) : [];
|
|
94215
94516
|
const totalACs = prd.userStories.filter((s) => !isLegacyFixStory(s)).flatMap((s) => s.acceptanceCriteria).length;
|
|
94216
94517
|
if (!ctx.runtime) {
|
|
94217
94518
|
logger?.error("acceptance", "Runtime not found for diagnosis", { storyId: firstStory?.id });
|
|
@@ -94234,7 +94535,6 @@ async function runAcceptanceLoop(ctx) {
|
|
|
94234
94535
|
failures: pkgFailures,
|
|
94235
94536
|
totalACs,
|
|
94236
94537
|
strategy,
|
|
94237
|
-
semanticVerdicts,
|
|
94238
94538
|
diagnosisOpts: {
|
|
94239
94539
|
testOutput: pkg.output,
|
|
94240
94540
|
testFileContent,
|
|
@@ -94280,7 +94580,6 @@ var init_acceptance_loop = __esm(() => {
|
|
|
94280
94580
|
init_acceptance_helpers();
|
|
94281
94581
|
init_acceptance_helpers();
|
|
94282
94582
|
_acceptanceLoopDeps = {
|
|
94283
|
-
loadSemanticVerdicts,
|
|
94284
94583
|
loadAcceptanceTestContent
|
|
94285
94584
|
};
|
|
94286
94585
|
_acceptanceFixCycleDeps = {
|
|
@@ -94509,7 +94808,7 @@ var init_scratchpad_wipe = __esm(() => {
|
|
|
94509
94808
|
init_logger2();
|
|
94510
94809
|
init_tools();
|
|
94511
94810
|
_scratchpadWipeDeps = {
|
|
94512
|
-
remove: (
|
|
94811
|
+
remove: (path17) => rm7(path17, { recursive: true, force: true })
|
|
94513
94812
|
};
|
|
94514
94813
|
});
|
|
94515
94814
|
|
|
@@ -94661,6 +94960,13 @@ async function cleanupRun(options) {
|
|
|
94661
94960
|
} catch (error48) {
|
|
94662
94961
|
logger?.warn("plugins", "Plugin teardown failed", { error: error48 });
|
|
94663
94962
|
}
|
|
94963
|
+
if (options.sealApprovals) {
|
|
94964
|
+
try {
|
|
94965
|
+
await options.sealApprovals();
|
|
94966
|
+
} catch (error48) {
|
|
94967
|
+
logger?.warn("permissions", "End-of-run approvals seal failed \u2014 continuing teardown", { error: error48 });
|
|
94968
|
+
}
|
|
94969
|
+
}
|
|
94664
94970
|
if (interactionChain) {
|
|
94665
94971
|
try {
|
|
94666
94972
|
await interactionChain.destroy();
|
|
@@ -97098,8 +97404,8 @@ async function defaultRun(cmd, opts) {
|
|
|
97098
97404
|
clearTimeout(timer);
|
|
97099
97405
|
}
|
|
97100
97406
|
}
|
|
97101
|
-
async function defaultReadText(
|
|
97102
|
-
const file2 = Bun.file(
|
|
97407
|
+
async function defaultReadText(path17) {
|
|
97408
|
+
const file2 = Bun.file(path17);
|
|
97103
97409
|
if (!await file2.exists())
|
|
97104
97410
|
return null;
|
|
97105
97411
|
return file2.text();
|
|
@@ -97199,10 +97505,10 @@ var init_pr = __esm(() => {
|
|
|
97199
97505
|
});
|
|
97200
97506
|
|
|
97201
97507
|
// src/forge/template.ts
|
|
97202
|
-
import * as
|
|
97508
|
+
import * as path17 from "path";
|
|
97203
97509
|
async function firstExisting(workdir, deps, paths) {
|
|
97204
97510
|
for (const relPath of paths) {
|
|
97205
|
-
const content = await deps.readText(
|
|
97511
|
+
const content = await deps.readText(path17.join(workdir, relPath));
|
|
97206
97512
|
if (content !== null)
|
|
97207
97513
|
return content;
|
|
97208
97514
|
}
|
|
@@ -97417,7 +97723,7 @@ var init_pr_body = __esm(() => {
|
|
|
97417
97723
|
});
|
|
97418
97724
|
|
|
97419
97725
|
// src/plugins/builtin/auto-pr/index.ts
|
|
97420
|
-
import * as
|
|
97726
|
+
import * as path18 from "path";
|
|
97421
97727
|
async function defaultRun2(cmd, opts) {
|
|
97422
97728
|
const argv = cmd[0] === "git" ? hardenedGitArgv(cmd) : cmd;
|
|
97423
97729
|
const proc = Bun.spawn(argv, { cwd: opts.cwd, env: gitSpawnEnv(), stdout: "pipe", stderr: "pipe" });
|
|
@@ -97443,8 +97749,8 @@ async function defaultRun2(cmd, opts) {
|
|
|
97443
97749
|
clearTimeout(timer);
|
|
97444
97750
|
}
|
|
97445
97751
|
}
|
|
97446
|
-
async function defaultReadText2(
|
|
97447
|
-
const file2 = Bun.file(
|
|
97752
|
+
async function defaultReadText2(path19) {
|
|
97753
|
+
const file2 = Bun.file(path19);
|
|
97448
97754
|
if (!await file2.exists())
|
|
97449
97755
|
return null;
|
|
97450
97756
|
return file2.text();
|
|
@@ -97474,8 +97780,8 @@ function getStorySummary(context) {
|
|
|
97474
97780
|
function relativePrdPath(workdir, prdPath) {
|
|
97475
97781
|
if (!prdPath)
|
|
97476
97782
|
return prdPath;
|
|
97477
|
-
const rel =
|
|
97478
|
-
return rel && !rel.startsWith("..") && !
|
|
97783
|
+
const rel = path18.relative(workdir, prdPath);
|
|
97784
|
+
return rel && !rel.startsWith("..") && !path18.isAbsolute(rel) ? rel : prdPath;
|
|
97479
97785
|
}
|
|
97480
97786
|
function toPrBodyContext(context) {
|
|
97481
97787
|
const summary = getStorySummary(context);
|
|
@@ -97764,7 +98070,7 @@ var init_auto_prune = __esm(() => {
|
|
|
97764
98070
|
});
|
|
97765
98071
|
|
|
97766
98072
|
// src/plugins/builtin/curator/collect.ts
|
|
97767
|
-
import * as
|
|
98073
|
+
import * as path19 from "path";
|
|
97768
98074
|
function now() {
|
|
97769
98075
|
return new Date().toISOString();
|
|
97770
98076
|
}
|
|
@@ -97822,7 +98128,7 @@ function tokenCount(story) {
|
|
|
97822
98128
|
}
|
|
97823
98129
|
async function collectFromMetrics(context) {
|
|
97824
98130
|
const observations = [];
|
|
97825
|
-
const metricsPath =
|
|
98131
|
+
const metricsPath = path19.join(context.outputDir, "metrics.json");
|
|
97826
98132
|
try {
|
|
97827
98133
|
const data = await readJsonFile(metricsPath);
|
|
97828
98134
|
const runs = Array.isArray(data) ? data : [data];
|
|
@@ -97898,11 +98204,11 @@ function findingMessage(finding) {
|
|
|
97898
98204
|
}
|
|
97899
98205
|
async function collectFromReviewAudit(context) {
|
|
97900
98206
|
const observations = [];
|
|
97901
|
-
const auditDir =
|
|
98207
|
+
const auditDir = path19.join(context.outputDir, "review-audit");
|
|
97902
98208
|
try {
|
|
97903
98209
|
const glob = new Bun.Glob("**/*.json");
|
|
97904
98210
|
for await (const file2 of glob.scan({ cwd: auditDir, absolute: false })) {
|
|
97905
|
-
const fullPath =
|
|
98211
|
+
const fullPath = path19.join(auditDir, file2);
|
|
97906
98212
|
try {
|
|
97907
98213
|
const audit = asRecord3(await readJsonFile(fullPath));
|
|
97908
98214
|
if (!audit)
|
|
@@ -97953,7 +98259,7 @@ async function collectFromContextManifests(context) {
|
|
|
97953
98259
|
try {
|
|
97954
98260
|
const glob = new Bun.Glob("*/stories/*/context-manifest-*.json");
|
|
97955
98261
|
for await (const file2 of glob.scan({ cwd: featuresRoot, absolute: false })) {
|
|
97956
|
-
const fullPath =
|
|
98262
|
+
const fullPath = path19.join(featuresRoot, file2);
|
|
97957
98263
|
try {
|
|
97958
98264
|
const parts = file2.split("/");
|
|
97959
98265
|
const featureId = parts[0] ?? context.feature;
|
|
@@ -98560,9 +98866,9 @@ function renderProposals(proposals, runId, observationCount, provenance) {
|
|
|
98560
98866
|
|
|
98561
98867
|
// src/plugins/builtin/curator/rollup.ts
|
|
98562
98868
|
import { appendFile as appendFile7, mkdir as mkdir26, writeFile as writeFile11 } from "fs/promises";
|
|
98563
|
-
import * as
|
|
98869
|
+
import * as path20 from "path";
|
|
98564
98870
|
async function appendToRollup(observations, rollupPath) {
|
|
98565
|
-
const dir =
|
|
98871
|
+
const dir = path20.dirname(rollupPath);
|
|
98566
98872
|
await mkdir26(dir, { recursive: true }).catch(() => {});
|
|
98567
98873
|
const { withPathFileLock: withPathFileLock2 } = await Promise.resolve().then(() => (init_path_file_lock(), exports_path_file_lock));
|
|
98568
98874
|
await withPathFileLock2(rollupPath, () => appendToRollupUnlocked(observations, rollupPath)).catch(() => {});
|
|
@@ -98651,7 +98957,7 @@ var init_rollup2 = __esm(() => {
|
|
|
98651
98957
|
|
|
98652
98958
|
// src/plugins/builtin/curator/index.ts
|
|
98653
98959
|
import { mkdir as mkdir27 } from "fs/promises";
|
|
98654
|
-
import * as
|
|
98960
|
+
import * as path21 from "path";
|
|
98655
98961
|
function getCuratorEnabled(context) {
|
|
98656
98962
|
const cfg = context.config;
|
|
98657
98963
|
if (!cfg)
|
|
@@ -98727,7 +99033,7 @@ var init_curator = __esm(() => {
|
|
|
98727
99033
|
const observations = await collectObservations(curatorContext);
|
|
98728
99034
|
if (context.outputDir) {
|
|
98729
99035
|
const { observationsPath, rollupPath } = resolveCuratorOutputs(curatorContext);
|
|
98730
|
-
const runDir =
|
|
99036
|
+
const runDir = path21.dirname(observationsPath);
|
|
98731
99037
|
await mkdir27(runDir, { recursive: true });
|
|
98732
99038
|
await Bun.write(observationsPath, observations.map((o) => JSON.stringify(o)).join(`
|
|
98733
99039
|
`) + (observations.length > 0 ? `
|
|
@@ -98765,7 +99071,7 @@ var init_curator = __esm(() => {
|
|
|
98765
99071
|
const windowHasObservations = window2.observations.length > 0;
|
|
98766
99072
|
const provenance = windowHasObservations ? { runCount: window2.runIds.length, observationCount: window2.observations.length } : { runCount: 1, observationCount: observations.length };
|
|
98767
99073
|
const markdown = renderProposals(proposals, context.runId, observations.length, provenance);
|
|
98768
|
-
const proposalsMdPath =
|
|
99074
|
+
const proposalsMdPath = path21.join(runDir, "curator-proposals.md");
|
|
98769
99075
|
await Bun.write(proposalsMdPath, markdown);
|
|
98770
99076
|
}
|
|
98771
99077
|
return {
|
|
@@ -99148,14 +99454,14 @@ var init_validator = __esm(() => {
|
|
|
99148
99454
|
|
|
99149
99455
|
// src/plugins/loader.ts
|
|
99150
99456
|
import * as fs from "fs/promises";
|
|
99151
|
-
import * as
|
|
99457
|
+
import * as path22 from "path";
|
|
99152
99458
|
function getSafeLogger7() {
|
|
99153
99459
|
return getSafeLogger();
|
|
99154
99460
|
}
|
|
99155
99461
|
function extractPluginName(pluginPath) {
|
|
99156
|
-
const basename13 =
|
|
99462
|
+
const basename13 = path22.basename(pluginPath);
|
|
99157
99463
|
if (basename13 === "index.ts" || basename13 === "index.js" || basename13 === "index.mjs") {
|
|
99158
|
-
return
|
|
99464
|
+
return path22.basename(path22.dirname(pluginPath));
|
|
99159
99465
|
}
|
|
99160
99466
|
return basename13.replace(/\.(ts|js|mjs)$/, "");
|
|
99161
99467
|
}
|
|
@@ -99302,7 +99608,7 @@ async function discoverPlugins(dir, isTestFileFn) {
|
|
|
99302
99608
|
try {
|
|
99303
99609
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
99304
99610
|
for (const entry of entries) {
|
|
99305
|
-
const fullPath =
|
|
99611
|
+
const fullPath = path22.join(dir, entry.name);
|
|
99306
99612
|
if (entry.isFile()) {
|
|
99307
99613
|
if (isPluginFile(entry.name, isTestFileFn)) {
|
|
99308
99614
|
discovered.push({ path: fullPath });
|
|
@@ -99310,7 +99616,7 @@ async function discoverPlugins(dir, isTestFileFn) {
|
|
|
99310
99616
|
} else if (entry.isDirectory()) {
|
|
99311
99617
|
const indexPaths = ["index.ts", "index.js", "index.mjs"];
|
|
99312
99618
|
for (const indexFile of indexPaths) {
|
|
99313
|
-
const indexPath =
|
|
99619
|
+
const indexPath = path22.join(fullPath, indexFile);
|
|
99314
99620
|
try {
|
|
99315
99621
|
await fs.access(indexPath);
|
|
99316
99622
|
discovered.push({ path: indexPath });
|
|
@@ -99335,13 +99641,13 @@ function isPluginFile(filename, isTestFileFn) {
|
|
|
99335
99641
|
return !FALLBACK_TEST_FILE_RE.test(filename);
|
|
99336
99642
|
}
|
|
99337
99643
|
function resolveModulePath(modulePath, projectRoot) {
|
|
99338
|
-
if (
|
|
99644
|
+
if (path22.isAbsolute(modulePath) || !modulePath.startsWith("./") && !modulePath.startsWith("../")) {
|
|
99339
99645
|
return modulePath;
|
|
99340
99646
|
}
|
|
99341
99647
|
if (projectRoot) {
|
|
99342
|
-
return
|
|
99648
|
+
return path22.resolve(projectRoot, modulePath);
|
|
99343
99649
|
}
|
|
99344
|
-
return
|
|
99650
|
+
return path22.resolve(modulePath);
|
|
99345
99651
|
}
|
|
99346
99652
|
async function loadAndValidatePlugin(initialModulePath, config2, allowedRoots, originalPath) {
|
|
99347
99653
|
let attemptedPath = initialModulePath;
|
|
@@ -99563,8 +99869,8 @@ var init_language_commands = __esm(() => {
|
|
|
99563
99869
|
|
|
99564
99870
|
// src/review/scoped-lint.ts
|
|
99565
99871
|
import { join as join100, relative as relative26 } from "path";
|
|
99566
|
-
function normalizePath3(
|
|
99567
|
-
return
|
|
99872
|
+
function normalizePath3(path23) {
|
|
99873
|
+
return path23.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
99568
99874
|
}
|
|
99569
99875
|
function isSupportedDerivedScopedCommand(command) {
|
|
99570
99876
|
const commands = normalizeCommandSpec(command);
|
|
@@ -99799,7 +100105,7 @@ var init_scoped_lint = __esm(() => {
|
|
|
99799
100105
|
listChangedFiles,
|
|
99800
100106
|
findPackageDir,
|
|
99801
100107
|
runLintCommand,
|
|
99802
|
-
fileExists: (
|
|
100108
|
+
fileExists: (path23) => Bun.file(path23).exists()
|
|
99803
100109
|
};
|
|
99804
100110
|
});
|
|
99805
100111
|
|
|
@@ -100260,7 +100566,7 @@ var init_paused_story_prompts = __esm(() => {
|
|
|
100260
100566
|
});
|
|
100261
100567
|
|
|
100262
100568
|
// src/execution/lifecycle/run-setup-init.ts
|
|
100263
|
-
import
|
|
100569
|
+
import path23 from "path";
|
|
100264
100570
|
async function initializeAfterLock(options) {
|
|
100265
100571
|
const logger = getSafeLogger();
|
|
100266
100572
|
const { config: config2, workdir, feature, dryRun, runtime, interactionChain, runId, agentGetFn, statusWriter, deps } = options;
|
|
@@ -100292,8 +100598,8 @@ async function initializeAfterLock(options) {
|
|
|
100292
100598
|
explicit: Object.fromEntries(explicitFields.map((f) => [f, existingProjectConfig[f]])),
|
|
100293
100599
|
detected: Object.fromEntries(autodetectedFields.map((f) => [f, detectedProfile[f]]))
|
|
100294
100600
|
});
|
|
100295
|
-
const globalPluginsDir =
|
|
100296
|
-
const projectPluginsDir =
|
|
100601
|
+
const globalPluginsDir = path23.join(globalConfigDir(), "plugins");
|
|
100602
|
+
const projectPluginsDir = path23.join(workdir, ".nax", "plugins");
|
|
100297
100603
|
const configPlugins = config2.plugins || [];
|
|
100298
100604
|
const resolvedPatterns = await resolveTestFilePatterns(config2, workdir);
|
|
100299
100605
|
const isTestFileFn = (filename) => resolvedPatterns.regex.some((re) => re.test(filename));
|
|
@@ -101252,7 +101558,7 @@ __export(exports_migrate, {
|
|
|
101252
101558
|
});
|
|
101253
101559
|
import { existsSync as existsSync30 } from "fs";
|
|
101254
101560
|
import { mkdir as mkdir28, readdir as readdir10, rename as rename9 } from "fs/promises";
|
|
101255
|
-
import
|
|
101561
|
+
import path24 from "path";
|
|
101256
101562
|
async function detectGeneratedContent(naxDir) {
|
|
101257
101563
|
if (!existsSync30(naxDir))
|
|
101258
101564
|
return [];
|
|
@@ -101265,17 +101571,17 @@ async function detectGeneratedContent(naxDir) {
|
|
|
101265
101571
|
}
|
|
101266
101572
|
for (const entry of entries) {
|
|
101267
101573
|
if (GENERATED_NAMES.has(entry)) {
|
|
101268
|
-
candidates.push({ name: entry, srcPath:
|
|
101574
|
+
candidates.push({ name: entry, srcPath: path24.join(naxDir, entry) });
|
|
101269
101575
|
}
|
|
101270
101576
|
}
|
|
101271
|
-
const featuresDir2 =
|
|
101577
|
+
const featuresDir2 = path24.join(naxDir, "features");
|
|
101272
101578
|
if (existsSync30(featuresDir2)) {
|
|
101273
101579
|
let featureDirs = [];
|
|
101274
101580
|
try {
|
|
101275
101581
|
featureDirs = await readdir10(featuresDir2);
|
|
101276
101582
|
} catch {}
|
|
101277
101583
|
for (const fid of featureDirs) {
|
|
101278
|
-
const featureDir2 =
|
|
101584
|
+
const featureDir2 = path24.join(featuresDir2, fid);
|
|
101279
101585
|
let subEntries = [];
|
|
101280
101586
|
try {
|
|
101281
101587
|
subEntries = await readdir10(featureDir2);
|
|
@@ -101285,12 +101591,12 @@ async function detectGeneratedContent(naxDir) {
|
|
|
101285
101591
|
for (const sub of subEntries) {
|
|
101286
101592
|
if (GENERATED_FEATURE_SUBNAMES.has(sub)) {
|
|
101287
101593
|
candidates.push({
|
|
101288
|
-
name:
|
|
101289
|
-
srcPath:
|
|
101594
|
+
name: path24.join("features", fid, sub),
|
|
101595
|
+
srcPath: path24.join(featureDir2, sub)
|
|
101290
101596
|
});
|
|
101291
101597
|
}
|
|
101292
101598
|
if (sub === "stories") {
|
|
101293
|
-
const storiesDir =
|
|
101599
|
+
const storiesDir = path24.join(featureDir2, "stories");
|
|
101294
101600
|
let storyDirs = [];
|
|
101295
101601
|
try {
|
|
101296
101602
|
storyDirs = await readdir10(storiesDir);
|
|
@@ -101298,7 +101604,7 @@ async function detectGeneratedContent(naxDir) {
|
|
|
101298
101604
|
continue;
|
|
101299
101605
|
}
|
|
101300
101606
|
for (const sid of storyDirs) {
|
|
101301
|
-
const storyDir =
|
|
101607
|
+
const storyDir = path24.join(storiesDir, sid);
|
|
101302
101608
|
let storyEntries = [];
|
|
101303
101609
|
try {
|
|
101304
101610
|
storyEntries = await readdir10(storyDir);
|
|
@@ -101308,8 +101614,8 @@ async function detectGeneratedContent(naxDir) {
|
|
|
101308
101614
|
for (const se of storyEntries) {
|
|
101309
101615
|
if (se.startsWith("context-manifest-") && se.endsWith(".json")) {
|
|
101310
101616
|
candidates.push({
|
|
101311
|
-
name:
|
|
101312
|
-
srcPath:
|
|
101617
|
+
name: path24.join("features", fid, "stories", sid, se),
|
|
101618
|
+
srcPath: path24.join(storyDir, se)
|
|
101313
101619
|
});
|
|
101314
101620
|
}
|
|
101315
101621
|
}
|
|
@@ -101330,15 +101636,15 @@ async function migrateCommand(options) {
|
|
|
101330
101636
|
name: options.reclaim
|
|
101331
101637
|
});
|
|
101332
101638
|
}
|
|
101333
|
-
const src =
|
|
101639
|
+
const src = path24.join(globalConfigDir(), options.reclaim);
|
|
101334
101640
|
if (!existsSync30(src)) {
|
|
101335
101641
|
throw new NaxError(`Nothing to reclaim: ~/.nax/${options.reclaim} does not exist`, "MIGRATE_RECLAIM_NOT_FOUND", {
|
|
101336
101642
|
stage: "migrate",
|
|
101337
101643
|
name: options.reclaim
|
|
101338
101644
|
});
|
|
101339
101645
|
}
|
|
101340
|
-
const archiveBase =
|
|
101341
|
-
const archiveDest =
|
|
101646
|
+
const archiveBase = path24.join(globalConfigDir(), "_archive");
|
|
101647
|
+
const archiveDest = path24.join(archiveBase, `${options.reclaim}-${Date.now()}`);
|
|
101342
101648
|
await mkdir28(archiveBase, { recursive: true });
|
|
101343
101649
|
await rename9(src, archiveDest);
|
|
101344
101650
|
logger.info("migrate", `Reclaimed: archived to ${archiveDest}`, { storyId: "_migrate" });
|
|
@@ -101378,8 +101684,8 @@ async function migrateCommand(options) {
|
|
|
101378
101684
|
logger.info("migrate", `Merged: identity for "${options.merge}" updated`, { storyId: "_migrate" });
|
|
101379
101685
|
return;
|
|
101380
101686
|
}
|
|
101381
|
-
const naxDir =
|
|
101382
|
-
const configPath =
|
|
101687
|
+
const naxDir = path24.join(options.workdir, ".nax");
|
|
101688
|
+
const configPath = path24.join(naxDir, "config.json");
|
|
101383
101689
|
if (!existsSync30(configPath)) {
|
|
101384
101690
|
throw new NaxError("No .nax/config.json found \u2014 run nax init first", "MIGRATE_NO_CONFIG", {
|
|
101385
101691
|
stage: "migrate",
|
|
@@ -101395,7 +101701,7 @@ async function migrateCommand(options) {
|
|
|
101395
101701
|
cause: e
|
|
101396
101702
|
});
|
|
101397
101703
|
}
|
|
101398
|
-
const projectKey = config2.name?.trim() ||
|
|
101704
|
+
const projectKey = config2.name?.trim() || path24.basename(options.workdir);
|
|
101399
101705
|
const destBase = projectOutputDir(projectKey, config2.outputDir);
|
|
101400
101706
|
const candidates = await detectGeneratedContent(naxDir);
|
|
101401
101707
|
if (candidates.length === 0) {
|
|
@@ -101404,7 +101710,7 @@ async function migrateCommand(options) {
|
|
|
101404
101710
|
}
|
|
101405
101711
|
if (options.dryRun) {
|
|
101406
101712
|
for (const c of candidates) {
|
|
101407
|
-
logger.info("migrate", `[dry-run] Would move: ${c.srcPath} -> ${
|
|
101713
|
+
logger.info("migrate", `[dry-run] Would move: ${c.srcPath} -> ${path24.join(destBase, c.name)}`, {
|
|
101408
101714
|
storyId: "_migrate"
|
|
101409
101715
|
});
|
|
101410
101716
|
}
|
|
@@ -101413,8 +101719,8 @@ async function migrateCommand(options) {
|
|
|
101413
101719
|
await mkdir28(destBase, { recursive: true });
|
|
101414
101720
|
let moved = 0;
|
|
101415
101721
|
for (const candidate of candidates) {
|
|
101416
|
-
const dest =
|
|
101417
|
-
await mkdir28(
|
|
101722
|
+
const dest = path24.join(destBase, candidate.name);
|
|
101723
|
+
await mkdir28(path24.dirname(dest), { recursive: true });
|
|
101418
101724
|
if (existsSync30(dest)) {
|
|
101419
101725
|
throw new NaxError(`Migration conflict: destination already exists.
|
|
101420
101726
|
Source: ${candidate.srcPath}
|
|
@@ -101444,7 +101750,7 @@ async function migrateCommand(options) {
|
|
|
101444
101750
|
moved++;
|
|
101445
101751
|
logger.info("migrate", `Moved: ${candidate.name}`, { storyId: "_migrate" });
|
|
101446
101752
|
}
|
|
101447
|
-
await Bun.write(
|
|
101753
|
+
await Bun.write(path24.join(destBase, ".migrated-from"), JSON.stringify({ from: options.workdir, migratedAt: new Date().toISOString() }, null, 2));
|
|
101448
101754
|
logger.info("migrate", `Migration complete: ${moved} entries moved`, {
|
|
101449
101755
|
storyId: "_migrate",
|
|
101450
101756
|
destBase
|
|
@@ -101815,10 +102121,10 @@ function renderReport(timeline, options = {}) {
|
|
|
101815
102121
|
// src/commands/replay.ts
|
|
101816
102122
|
import { existsSync as existsSync32 } from "fs";
|
|
101817
102123
|
import { dirname as dirname20 } from "path";
|
|
101818
|
-
async function readJsonlLenient(
|
|
101819
|
-
if (!existsSync32(
|
|
102124
|
+
async function readJsonlLenient(path25) {
|
|
102125
|
+
if (!existsSync32(path25))
|
|
101820
102126
|
return [];
|
|
101821
|
-
const content = await Bun.file(
|
|
102127
|
+
const content = await Bun.file(path25).text();
|
|
101822
102128
|
const lines = content.split(`
|
|
101823
102129
|
`);
|
|
101824
102130
|
const entries = [];
|
|
@@ -101832,11 +102138,11 @@ async function readJsonlLenient(path26) {
|
|
|
101832
102138
|
}
|
|
101833
102139
|
return entries;
|
|
101834
102140
|
}
|
|
101835
|
-
async function readJsonOrUndefined(
|
|
101836
|
-
if (!existsSync32(
|
|
102141
|
+
async function readJsonOrUndefined(path25) {
|
|
102142
|
+
if (!existsSync32(path25))
|
|
101837
102143
|
return;
|
|
101838
102144
|
try {
|
|
101839
|
-
return await Bun.file(
|
|
102145
|
+
return await Bun.file(path25).json();
|
|
101840
102146
|
} catch {
|
|
101841
102147
|
return;
|
|
101842
102148
|
}
|
|
@@ -102549,7 +102855,7 @@ __export(exports_precheck_runner, {
|
|
|
102549
102855
|
runPrecheckValidation: () => runPrecheckValidation
|
|
102550
102856
|
});
|
|
102551
102857
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
102552
|
-
import
|
|
102858
|
+
import path25 from "path";
|
|
102553
102859
|
async function runPrecheckValidation(ctx) {
|
|
102554
102860
|
const logger = getSafeLogger();
|
|
102555
102861
|
if (process.env.NAX_PRECHECK !== "1") {
|
|
@@ -102558,7 +102864,7 @@ async function runPrecheckValidation(ctx) {
|
|
|
102558
102864
|
}
|
|
102559
102865
|
logger?.info("precheck", "Running precheck validations...");
|
|
102560
102866
|
const { runPrecheck: runPrecheck3 } = await Promise.resolve().then(() => (init_precheck2(), exports_precheck));
|
|
102561
|
-
const projectKey = ctx.config.name?.trim() ||
|
|
102867
|
+
const projectKey = ctx.config.name?.trim() || path25.basename(ctx.workdir);
|
|
102562
102868
|
const outputDir = projectOutputDir(projectKey, ctx.config.outputDir);
|
|
102563
102869
|
const featureLock = ctx.featureName !== undefined ? { outputDir, feature: ctx.featureName } : undefined;
|
|
102564
102870
|
const precheckResult = await runPrecheck3(ctx.config, ctx.prd, {
|
|
@@ -102568,7 +102874,7 @@ async function runPrecheckValidation(ctx) {
|
|
|
102568
102874
|
...featureLock !== undefined ? { featureLock } : {}
|
|
102569
102875
|
});
|
|
102570
102876
|
if (ctx.logFilePath) {
|
|
102571
|
-
mkdirSync5(
|
|
102877
|
+
mkdirSync5(path25.dirname(ctx.logFilePath), { recursive: true });
|
|
102572
102878
|
const precheckLog = {
|
|
102573
102879
|
type: "precheck",
|
|
102574
102880
|
timestamp: new Date().toISOString(),
|
|
@@ -102666,7 +102972,7 @@ __export(exports_run_setup, {
|
|
|
102666
102972
|
warnInertBashStages: () => warnInertBashStages,
|
|
102667
102973
|
warnProfileMismatch: () => warnProfileMismatch
|
|
102668
102974
|
});
|
|
102669
|
-
import
|
|
102975
|
+
import path26 from "path";
|
|
102670
102976
|
async function setupRun(options) {
|
|
102671
102977
|
const logger = getSafeLogger();
|
|
102672
102978
|
warnFallbackMisconfiguration(options.config, options.agentGetFn, logger);
|
|
@@ -102731,6 +103037,7 @@ async function setupRun(options) {
|
|
|
102731
103037
|
}
|
|
102732
103038
|
await runtime.pidRegistry.cleanupStale();
|
|
102733
103039
|
let cleanupCrashHandlers;
|
|
103040
|
+
let sealApprovals;
|
|
102734
103041
|
try {
|
|
102735
103042
|
cleanupCrashHandlers = _runSetupDeps.installCrashHandlers({
|
|
102736
103043
|
statusWriter,
|
|
@@ -102748,6 +103055,9 @@ async function setupRun(options) {
|
|
|
102748
103055
|
emitError: (reason) => {
|
|
102749
103056
|
pipelineEventBus.emit({ type: "run:errored", reason, feature: options.feature });
|
|
102750
103057
|
},
|
|
103058
|
+
sealApprovals: async () => {
|
|
103059
|
+
await sealApprovals?.();
|
|
103060
|
+
},
|
|
102751
103061
|
onShutdown: async (abortSignal) => {
|
|
102752
103062
|
await closeAllRunSessions(sessionManager, options.agentGetFn, { force: true, signal: abortSignal });
|
|
102753
103063
|
await runtime.close().catch((err) => {
|
|
@@ -102760,7 +103070,7 @@ async function setupRun(options) {
|
|
|
102760
103070
|
statusWriter.setPrd(prd);
|
|
102761
103071
|
{
|
|
102762
103072
|
const { detectGeneratedContent: detectGeneratedContent2, migrateCommand: migrateCommand2 } = await Promise.resolve().then(() => (init_commands(), exports_commands));
|
|
102763
|
-
const naxDir =
|
|
103073
|
+
const naxDir = path26.join(workdir, ".nax");
|
|
102764
103074
|
const candidates = await detectGeneratedContent2(naxDir).catch(() => []);
|
|
102765
103075
|
if (candidates.length > 0) {
|
|
102766
103076
|
logger?.info("setup", "Found generated content under .nax/ \u2014 migrating to output dir", {
|
|
@@ -102787,7 +103097,7 @@ async function setupRun(options) {
|
|
|
102787
103097
|
remoteUrl = new TextDecoder().decode(gitResult.stdout).trim() || null;
|
|
102788
103098
|
}
|
|
102789
103099
|
} catch {}
|
|
102790
|
-
const projectKey = config2.name?.trim() ||
|
|
103100
|
+
const projectKey = config2.name?.trim() || path26.basename(workdir);
|
|
102791
103101
|
await claimProjectIdentity2(projectKey, workdir, remoteUrl).catch((err) => {
|
|
102792
103102
|
if (err instanceof NaxError && err.code === "RUN_NAME_COLLISION") {
|
|
102793
103103
|
throw err;
|
|
@@ -102871,6 +103181,19 @@ async function setupRun(options) {
|
|
|
102871
103181
|
sweepFeatureTranscripts: _runSetupDeps.sweepFeatureTranscripts
|
|
102872
103182
|
}
|
|
102873
103183
|
});
|
|
103184
|
+
try {
|
|
103185
|
+
sealApprovals = await _runSetupDeps.buildApprovalsSeal({
|
|
103186
|
+
projectDir: options.workdir,
|
|
103187
|
+
rootConfig: options.config,
|
|
103188
|
+
packageDirs: initResult.prd.userStories.map(storyPackageDir),
|
|
103189
|
+
outputDir: runtime.outputDir,
|
|
103190
|
+
runId: options.runId
|
|
103191
|
+
});
|
|
103192
|
+
} catch (error48) {
|
|
103193
|
+
await releaseFeatureLock({ outputDir: runtime.outputDir, feature, runId });
|
|
103194
|
+
await releaseLock(workdir);
|
|
103195
|
+
throw error48;
|
|
103196
|
+
}
|
|
102874
103197
|
return {
|
|
102875
103198
|
statusWriter,
|
|
102876
103199
|
sessionManager,
|
|
@@ -102880,7 +103203,8 @@ async function setupRun(options) {
|
|
|
102880
103203
|
storyCounts: initResult.storyCounts,
|
|
102881
103204
|
interactionChain: initResult.interactionChain,
|
|
102882
103205
|
shutdownController,
|
|
102883
|
-
runtime
|
|
103206
|
+
runtime,
|
|
103207
|
+
sealApprovals
|
|
102884
103208
|
};
|
|
102885
103209
|
} catch (error48) {
|
|
102886
103210
|
cleanupCrashHandlers?.();
|
|
@@ -102909,6 +103233,7 @@ var init_run_setup = __esm(() => {
|
|
|
102909
103233
|
init_test_runners();
|
|
102910
103234
|
init_tools();
|
|
102911
103235
|
init_git_env();
|
|
103236
|
+
init_path_frame();
|
|
102912
103237
|
init_crash_recovery();
|
|
102913
103238
|
init_feature_lock();
|
|
102914
103239
|
init_helpers();
|
|
@@ -102921,6 +103246,7 @@ var init_run_setup = __esm(() => {
|
|
|
102921
103246
|
createRuntime,
|
|
102922
103247
|
installCrashHandlers,
|
|
102923
103248
|
sweepFeatureTranscripts,
|
|
103249
|
+
buildApprovalsSeal,
|
|
102924
103250
|
acquireLock,
|
|
102925
103251
|
acquireFeatureLock
|
|
102926
103252
|
};
|
|
@@ -104177,7 +104503,7 @@ var init_queue_file_lock = __esm(() => {
|
|
|
104177
104503
|
|
|
104178
104504
|
// src/execution/queue-handler.ts
|
|
104179
104505
|
import { rename as rename10, unlink as unlink11 } from "fs/promises";
|
|
104180
|
-
import
|
|
104506
|
+
import path27 from "path";
|
|
104181
104507
|
function getSafeLogger8() {
|
|
104182
104508
|
try {
|
|
104183
104509
|
return getLogger();
|
|
@@ -104209,8 +104535,8 @@ async function claimCommandsLocked(queuePath, processingPath, logger) {
|
|
|
104209
104535
|
return result.commands;
|
|
104210
104536
|
}
|
|
104211
104537
|
async function readQueueFile(workdir) {
|
|
104212
|
-
const queuePath =
|
|
104213
|
-
const processingPath =
|
|
104538
|
+
const queuePath = path27.join(workdir, ".queue.txt");
|
|
104539
|
+
const processingPath = path27.join(workdir, ".queue.txt.processing");
|
|
104214
104540
|
const logger = getSafeLogger8();
|
|
104215
104541
|
try {
|
|
104216
104542
|
return await withQueueFileLock(queuePath, async () => {
|
|
@@ -104225,8 +104551,8 @@ async function readQueueFile(workdir) {
|
|
|
104225
104551
|
}
|
|
104226
104552
|
}
|
|
104227
104553
|
async function processQueueFile(workdir, processor) {
|
|
104228
|
-
const queuePath =
|
|
104229
|
-
const processingPath =
|
|
104554
|
+
const queuePath = path27.join(workdir, ".queue.txt");
|
|
104555
|
+
const processingPath = path27.join(workdir, ".queue.txt.processing");
|
|
104230
104556
|
const logger = getSafeLogger8();
|
|
104231
104557
|
try {
|
|
104232
104558
|
return await withQueueFileLock(queuePath, async () => {
|
|
@@ -104251,7 +104577,7 @@ async function processQueueFile(workdir, processor) {
|
|
|
104251
104577
|
}
|
|
104252
104578
|
}
|
|
104253
104579
|
async function drainQueueAtBatchBoundary(workdir, prd) {
|
|
104254
|
-
if (!await Bun.file(
|
|
104580
|
+
if (!await Bun.file(path27.join(workdir, ".queue.txt")).exists()) {
|
|
104255
104581
|
return { paused: false };
|
|
104256
104582
|
}
|
|
104257
104583
|
const logger = getSafeLogger8();
|
|
@@ -104307,10 +104633,10 @@ async function applyBatchBoundaryCommands(workdir, prd, commands, logger) {
|
|
|
104307
104633
|
}
|
|
104308
104634
|
if (cmd.type === "INJECT") {
|
|
104309
104635
|
try {
|
|
104310
|
-
if (
|
|
104636
|
+
if (path27.isAbsolute(cmd.storyFile)) {
|
|
104311
104637
|
throw new NaxError(`INJECT storyFile must be a relative path within the workspace: ${cmd.storyFile}`, "INJECT_PATH_ABSOLUTE", { stage: "queue-batch-boundary", storyFile: cmd.storyFile });
|
|
104312
104638
|
}
|
|
104313
|
-
const storyFilePath = validateFilePath(
|
|
104639
|
+
const storyFilePath = validateFilePath(path27.join(workdir, cmd.storyFile), workdir);
|
|
104314
104640
|
const raw = await Bun.file(storyFilePath).json();
|
|
104315
104641
|
const existingIds = new Set(prd.userStories.map((s) => s.id));
|
|
104316
104642
|
const story = validateInjectedStory(raw, existingIds);
|
|
@@ -104330,8 +104656,8 @@ async function applyBatchBoundaryCommands(workdir, prd, commands, logger) {
|
|
|
104330
104656
|
return { paused: false };
|
|
104331
104657
|
}
|
|
104332
104658
|
async function clearQueueFile(workdir) {
|
|
104333
|
-
const queuePath =
|
|
104334
|
-
const processingPath =
|
|
104659
|
+
const queuePath = path27.join(workdir, ".queue.txt");
|
|
104660
|
+
const processingPath = path27.join(workdir, ".queue.txt.processing");
|
|
104335
104661
|
const logger = getSafeLogger8();
|
|
104336
104662
|
try {
|
|
104337
104663
|
await withQueueFileLock(queuePath, async () => {
|
|
@@ -105750,12 +106076,12 @@ var init_body = __esm(() => {
|
|
|
105750
106076
|
// src/finish/pr/context.ts
|
|
105751
106077
|
import { readFile as readFile8 } from "fs/promises";
|
|
105752
106078
|
import { join as join113 } from "path";
|
|
105753
|
-
async function readJson(
|
|
106079
|
+
async function readJson(path28) {
|
|
105754
106080
|
let text;
|
|
105755
106081
|
try {
|
|
105756
|
-
text = await _finishPrDeps.readText(
|
|
106082
|
+
text = await _finishPrDeps.readText(path28);
|
|
105757
106083
|
} catch (error48) {
|
|
105758
|
-
_finishPrDeps.warn("[finish-pr] Failed to read PR context artifact", { path:
|
|
106084
|
+
_finishPrDeps.warn("[finish-pr] Failed to read PR context artifact", { path: path28, error: error48 });
|
|
105759
106085
|
return;
|
|
105760
106086
|
}
|
|
105761
106087
|
if (text === null)
|
|
@@ -105763,7 +106089,7 @@ async function readJson(path29) {
|
|
|
105763
106089
|
try {
|
|
105764
106090
|
return JSON.parse(text);
|
|
105765
106091
|
} catch (error48) {
|
|
105766
|
-
_finishPrDeps.warn("[finish-pr] Failed to parse PR context artifact", { path:
|
|
106092
|
+
_finishPrDeps.warn("[finish-pr] Failed to parse PR context artifact", { path: path28, error: error48 });
|
|
105767
106093
|
return;
|
|
105768
106094
|
}
|
|
105769
106095
|
}
|
|
@@ -105847,9 +106173,9 @@ var init_context5 = __esm(() => {
|
|
|
105847
106173
|
init_pr_title();
|
|
105848
106174
|
_finishPrDeps = {
|
|
105849
106175
|
run: defaultForgeDeps.run,
|
|
105850
|
-
readText: async (
|
|
106176
|
+
readText: async (path28) => {
|
|
105851
106177
|
try {
|
|
105852
|
-
return await readFile8(
|
|
106178
|
+
return await readFile8(path28, "utf8");
|
|
105853
106179
|
} catch (err) {
|
|
105854
106180
|
if (err.code === "ENOENT")
|
|
105855
106181
|
return null;
|
|
@@ -106738,7 +107064,7 @@ var init_finish = __esm(() => {
|
|
|
106738
107064
|
});
|
|
106739
107065
|
|
|
106740
107066
|
// src/execution/runner-completion.ts
|
|
106741
|
-
import
|
|
107067
|
+
import path28 from "path";
|
|
106742
107068
|
async function runCompletionPhase(options) {
|
|
106743
107069
|
const logger = getSafeLogger();
|
|
106744
107070
|
logger?.debug("execution", "Completion phase started", {
|
|
@@ -106759,7 +107085,7 @@ async function runCompletionPhase(options) {
|
|
|
106759
107085
|
const acceptanceStartTime = Date.now();
|
|
106760
107086
|
pipelineEventBus.emit({ type: "postrun:phase:started", phase: "acceptance" });
|
|
106761
107087
|
const acceptanceTestPaths = options.featureDir ? await Promise.all((await groupStoriesByPackage(options.prd, options.workdir, options.feature, options.config.acceptance.testPath, options.config.project?.language)).map(async (g) => {
|
|
106762
|
-
const relativeWorkdir =
|
|
107088
|
+
const relativeWorkdir = path28.relative(options.workdir, g.packageDir);
|
|
106763
107089
|
let groupConfig = options.config;
|
|
106764
107090
|
if (relativeWorkdir && relativeWorkdir !== ".") {
|
|
106765
107091
|
try {
|
|
@@ -106890,7 +107216,7 @@ async function runCompletionPhase(options) {
|
|
|
106890
107216
|
exitReason: options.exitReason,
|
|
106891
107217
|
runtime: options.runtime,
|
|
106892
107218
|
abortSignal: options.abortSignal,
|
|
106893
|
-
isSequential: options.parallel === undefined,
|
|
107219
|
+
isSequential: options.parallel === undefined || !(options.parallel > 1),
|
|
106894
107220
|
interactionChain: options.interactionChain
|
|
106895
107221
|
});
|
|
106896
107222
|
const { durationMs, runCompletedAt, finalCounts, reportedTotal, pluginGateFailed } = completionResult;
|
|
@@ -107496,7 +107822,7 @@ __export(exports_parallel_batch, {
|
|
|
107496
107822
|
_parallelBatchDeps: () => _parallelBatchDeps,
|
|
107497
107823
|
runParallelBatch: () => runParallelBatch
|
|
107498
107824
|
});
|
|
107499
|
-
import
|
|
107825
|
+
import path29 from "path";
|
|
107500
107826
|
async function runParallelBatch(options) {
|
|
107501
107827
|
const { stories, ctx, prd } = options;
|
|
107502
107828
|
const { workdir, config: config2, maxConcurrency, pipelineContext, eventEmitter, agentGetFn, hooks, pluginRegistry } = ctx;
|
|
@@ -107531,7 +107857,7 @@ async function runParallelBatch(options) {
|
|
|
107531
107857
|
}
|
|
107532
107858
|
worktreePaths.set(story.id, storyWorktreePath(workdir, worktreeId));
|
|
107533
107859
|
}
|
|
107534
|
-
const rootConfigPath =
|
|
107860
|
+
const rootConfigPath = path29.join(workdir, ".nax", "config.json");
|
|
107535
107861
|
const profileOverride = profileOverrideFromConfig(config2);
|
|
107536
107862
|
const storyEffectiveConfigs = new Map;
|
|
107537
107863
|
const configResults = await Promise.allSettled(stories.filter((story) => storyPackageDir(story)).map(async (story) => {
|
|
@@ -108514,7 +108840,8 @@ async function run(options) {
|
|
|
108514
108840
|
pluginRegistry,
|
|
108515
108841
|
interactionChain,
|
|
108516
108842
|
shutdownController,
|
|
108517
|
-
runtime
|
|
108843
|
+
runtime,
|
|
108844
|
+
sealApprovals
|
|
108518
108845
|
} = setupResult;
|
|
108519
108846
|
prd = setupResult.prd;
|
|
108520
108847
|
const agentManager = runtime.agentManager;
|
|
@@ -108636,7 +108963,8 @@ async function run(options) {
|
|
|
108636
108963
|
projectKey: runtime.projectKey,
|
|
108637
108964
|
curatorRollupPath: runtime.curatorRollupPath,
|
|
108638
108965
|
logFilePath,
|
|
108639
|
-
config: config2
|
|
108966
|
+
config: config2,
|
|
108967
|
+
sealApprovals
|
|
108640
108968
|
});
|
|
108641
108969
|
logger?.debug("execution", "Runner finally \u2014 cleanupRun done, run() returning");
|
|
108642
108970
|
} finally {
|
|
@@ -109101,12 +109429,12 @@ async function checkDependenciesInstalled(workdir) {
|
|
|
109101
109429
|
{ path: "vendor" }
|
|
109102
109430
|
];
|
|
109103
109431
|
const found = [];
|
|
109104
|
-
for (const { path:
|
|
109105
|
-
const fullPath = `${workdir}/${
|
|
109432
|
+
for (const { path: path30 } of depPaths) {
|
|
109433
|
+
const fullPath = `${workdir}/${path30}`;
|
|
109106
109434
|
if (existsSync36(fullPath)) {
|
|
109107
109435
|
const stats = statSync6(fullPath);
|
|
109108
109436
|
if (stats.isDirectory()) {
|
|
109109
|
-
found.push(
|
|
109437
|
+
found.push(path30);
|
|
109110
109438
|
}
|
|
109111
109439
|
}
|
|
109112
109440
|
}
|
|
@@ -110535,8 +110863,8 @@ var init_plan_runtime = __esm(() => {
|
|
|
110535
110863
|
init_git_env();
|
|
110536
110864
|
init_plan_helpers();
|
|
110537
110865
|
_planDeps = {
|
|
110538
|
-
readFile: (
|
|
110539
|
-
writeFile: (
|
|
110866
|
+
readFile: (path30) => Bun.file(path30).text(),
|
|
110867
|
+
writeFile: (path30, content) => Bun.write(path30, content).then(() => {}),
|
|
110540
110868
|
scanSourceRoots: (workdir) => scanSourceRoots(workdir),
|
|
110541
110869
|
createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
|
|
110542
110870
|
claimProjectIdentity,
|
|
@@ -110545,10 +110873,10 @@ var init_plan_runtime = __esm(() => {
|
|
|
110545
110873
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd, ...opts.env ? { env: opts.env } : {} } : {});
|
|
110546
110874
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
110547
110875
|
},
|
|
110548
|
-
mkdirp: (
|
|
110549
|
-
existsSync: (
|
|
110876
|
+
mkdirp: (path30) => Bun.spawn(["mkdir", "-p", path30]).exited.then(() => {}),
|
|
110877
|
+
existsSync: (path30) => existsSync38(path30),
|
|
110550
110878
|
discoverWorkspacePackages: (repoRoot) => discoverWorkspacePackages2(repoRoot),
|
|
110551
|
-
readPackageJsonAt: (
|
|
110879
|
+
readPackageJsonAt: (path30) => Bun.file(path30).json().catch(() => null),
|
|
110552
110880
|
createInteractionBridge: () => createCliInteractionBridge(),
|
|
110553
110881
|
initInteractionChain: (cfg, headless) => initInteractionChain(cfg, headless),
|
|
110554
110882
|
runPrecheck: async (config2, prd, opts) => {
|
|
@@ -110569,7 +110897,7 @@ function assertSpecLintClean(specContent, options) {
|
|
|
110569
110897
|
return [];
|
|
110570
110898
|
const findings = lintSpecContent(specContent, {
|
|
110571
110899
|
maxAcCount: options.maxAcCount,
|
|
110572
|
-
fileExists: (
|
|
110900
|
+
fileExists: (path30) => existsSync39(join118(options.workdir, path30))
|
|
110573
110901
|
});
|
|
110574
110902
|
const blocking = findings.filter((finding) => BLOCKING_SPEC_LINT_CODES.has(finding.code));
|
|
110575
110903
|
if (blocking.length > 0) {
|
|
@@ -110629,7 +110957,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
110629
110957
|
}));
|
|
110630
110958
|
const codebaseContext = buildSourceRootsSection(normalizedRoots);
|
|
110631
110959
|
const relativePackages = [
|
|
110632
|
-
...new Set(sourceRoots.map((root) => root.path).filter((
|
|
110960
|
+
...new Set(sourceRoots.map((root) => root.path).filter((path30) => path30 !== ".").map((path30) => path30.startsWith("/") ? path30.replace(`${workdir}/`, "") : path30))
|
|
110633
110961
|
];
|
|
110634
110962
|
const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
|
|
110635
110963
|
const packageJson = await deps.readPackageJsonAt(join119(workdir, relativePath, "package.json"));
|
|
@@ -110772,7 +111100,7 @@ var init_persist_prd = __esm(() => {
|
|
|
110772
111100
|
init_prd();
|
|
110773
111101
|
init_finalize_routing();
|
|
110774
111102
|
_persistPrdDeps = {
|
|
110775
|
-
existsSync: (
|
|
111103
|
+
existsSync: (path30) => defaultExistsSync(path30),
|
|
110776
111104
|
discoverWorkspacePackages: (repoRoot) => discoverWorkspacePackages2(repoRoot)
|
|
110777
111105
|
};
|
|
110778
111106
|
});
|
|
@@ -111035,10 +111363,10 @@ var init_plan2 = __esm(() => {
|
|
|
111035
111363
|
});
|
|
111036
111364
|
|
|
111037
111365
|
// src/cli/plugins.ts
|
|
111038
|
-
import * as
|
|
111366
|
+
import * as path30 from "path";
|
|
111039
111367
|
async function pluginsListCommand(config2, workdir, overrideGlobalPluginsDir) {
|
|
111040
|
-
const globalPluginsDir = overrideGlobalPluginsDir ??
|
|
111041
|
-
const projectPluginsDir =
|
|
111368
|
+
const globalPluginsDir = overrideGlobalPluginsDir ?? path30.join(globalConfigDir(), "plugins");
|
|
111369
|
+
const projectPluginsDir = path30.join(workdir, ".nax", "plugins");
|
|
111042
111370
|
const configPlugins = config2.plugins || [];
|
|
111043
111371
|
const registry4 = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, workdir, config2.disabledPlugins);
|
|
111044
111372
|
const plugins = registry4.plugins;
|
|
@@ -111088,10 +111416,10 @@ function formatSource(type, sourcePath) {
|
|
|
111088
111416
|
return `built-in (${sourcePath})`;
|
|
111089
111417
|
}
|
|
111090
111418
|
if (type === "global") {
|
|
111091
|
-
return `global (${
|
|
111419
|
+
return `global (${path30.basename(sourcePath)})`;
|
|
111092
111420
|
}
|
|
111093
111421
|
if (type === "project") {
|
|
111094
|
-
return `project (${
|
|
111422
|
+
return `project (${path30.basename(sourcePath)})`;
|
|
111095
111423
|
}
|
|
111096
111424
|
return `config (${sourcePath})`;
|
|
111097
111425
|
}
|
|
@@ -111133,7 +111461,7 @@ async function exportPromptCommand(options) {
|
|
|
111133
111461
|
var VALID_EXPORT_ROLES;
|
|
111134
111462
|
var init_prompts_export = __esm(() => {
|
|
111135
111463
|
init_prompts();
|
|
111136
|
-
VALID_EXPORT_ROLES = ["test-writer", "implementer", "verifier", "
|
|
111464
|
+
VALID_EXPORT_ROLES = ["test-writer", "implementer", "verifier", "tdd-simple"];
|
|
111137
111465
|
});
|
|
111138
111466
|
|
|
111139
111467
|
// src/cli/prompts-init.ts
|
|
@@ -111175,7 +111503,6 @@ async function autoWirePromptsConfig(workdir) {
|
|
|
111175
111503
|
"test-writer": ".nax/templates/test-writer.md",
|
|
111176
111504
|
implementer: ".nax/templates/implementer.md",
|
|
111177
111505
|
verifier: ".nax/templates/verifier.md",
|
|
111178
|
-
"single-session": ".nax/templates/single-session.md",
|
|
111179
111506
|
"tdd-simple": ".nax/templates/tdd-simple.md"
|
|
111180
111507
|
}
|
|
111181
111508
|
}
|
|
@@ -111196,7 +111523,6 @@ ${exampleConfig}`);
|
|
|
111196
111523
|
"test-writer": ".nax/templates/test-writer.md",
|
|
111197
111524
|
implementer: ".nax/templates/implementer.md",
|
|
111198
111525
|
verifier: ".nax/templates/verifier.md",
|
|
111199
|
-
"single-session": ".nax/templates/single-session.md",
|
|
111200
111526
|
"tdd-simple": ".nax/templates/tdd-simple.md"
|
|
111201
111527
|
};
|
|
111202
111528
|
prompts.overrides = overrides;
|
|
@@ -111255,7 +111581,6 @@ var init_prompts_init = __esm(() => {
|
|
|
111255
111581
|
{ file: "test-writer.md", role: "test-writer" },
|
|
111256
111582
|
{ file: "implementer.md", role: "implementer", variant: "standard" },
|
|
111257
111583
|
{ file: "verifier.md", role: "verifier" },
|
|
111258
|
-
{ file: "single-session.md", role: "single-session" },
|
|
111259
111584
|
{ file: "tdd-simple.md", role: "tdd-simple" }
|
|
111260
111585
|
];
|
|
111261
111586
|
});
|
|
@@ -111493,8 +111818,8 @@ async function resolveRunProfileOverride(opts) {
|
|
|
111493
111818
|
return cliChain;
|
|
111494
111819
|
if (opts.envProfile)
|
|
111495
111820
|
return;
|
|
111496
|
-
const readJson2 = opts._readJson ?? (async (
|
|
111497
|
-
const file2 = Bun.file(
|
|
111821
|
+
const readJson2 = opts._readJson ?? (async (path31) => {
|
|
111822
|
+
const file2 = Bun.file(path31);
|
|
111498
111823
|
if (!await file2.exists())
|
|
111499
111824
|
return;
|
|
111500
111825
|
return file2.json();
|
|
@@ -111874,11 +112199,11 @@ var init_rules_cli_deps = __esm(() => {
|
|
|
111874
112199
|
init_logger2();
|
|
111875
112200
|
init_rules_lint();
|
|
111876
112201
|
_rulesCLIDeps = {
|
|
111877
|
-
readFile: async (
|
|
111878
|
-
writeFile: async (
|
|
111879
|
-
await Bun.write(
|
|
112202
|
+
readFile: async (path31) => Bun.file(path31).text(),
|
|
112203
|
+
writeFile: async (path31, content) => {
|
|
112204
|
+
await Bun.write(path31, content);
|
|
111880
112205
|
},
|
|
111881
|
-
fileExists: async (
|
|
112206
|
+
fileExists: async (path31) => Bun.file(path31).exists(),
|
|
111882
112207
|
globInDir: (dir) => {
|
|
111883
112208
|
try {
|
|
111884
112209
|
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join127(dir, f));
|
|
@@ -111886,8 +112211,8 @@ var init_rules_cli_deps = __esm(() => {
|
|
|
111886
112211
|
return [];
|
|
111887
112212
|
}
|
|
111888
112213
|
},
|
|
111889
|
-
mkdir: async (
|
|
111890
|
-
await mkdir33(
|
|
112214
|
+
mkdir: async (path31) => {
|
|
112215
|
+
await mkdir33(path31, { recursive: true });
|
|
111891
112216
|
},
|
|
111892
112217
|
globCanonicalRuleFiles: (workdir) => _rulesLintDeps.globCanonicalRuleFiles(workdir),
|
|
111893
112218
|
globHasMatch: (pattern, cwd) => _rulesLintDeps.globHasMatch(pattern, cwd),
|
|
@@ -112404,10 +112729,10 @@ var init_setup_analyze = __esm(() => {
|
|
|
112404
112729
|
init_detect();
|
|
112405
112730
|
CANONICAL_SCRIPTS = ["build", "test", "lint", "type-check", "lint:fix"];
|
|
112406
112731
|
_analyzeRepoDeps = {
|
|
112407
|
-
fileExists: async (
|
|
112408
|
-
readJson: async (
|
|
112732
|
+
fileExists: async (path31) => Bun.file(path31).exists(),
|
|
112733
|
+
readJson: async (path31) => {
|
|
112409
112734
|
try {
|
|
112410
|
-
const f = Bun.file(
|
|
112735
|
+
const f = Bun.file(path31);
|
|
112411
112736
|
if (!await f.exists())
|
|
112412
112737
|
return null;
|
|
112413
112738
|
return JSON.parse(await f.text());
|
|
@@ -112462,9 +112787,9 @@ async function fillScripts(workdir, analysis) {
|
|
|
112462
112787
|
var TYPE_CHECK_KEY = "type-check", TYPE_CHECK_SCRIPT = "tsc --noEmit -p tsconfig.json", TYPE_CHECK_TURBO_PASSTHROUGH = "turbo run type-check", _fillScriptsDeps;
|
|
112463
112788
|
var init_setup_fill = __esm(() => {
|
|
112464
112789
|
_fillScriptsDeps = {
|
|
112465
|
-
readJson: async (
|
|
112790
|
+
readJson: async (path31) => {
|
|
112466
112791
|
try {
|
|
112467
|
-
const f = Bun.file(
|
|
112792
|
+
const f = Bun.file(path31);
|
|
112468
112793
|
if (!await f.exists())
|
|
112469
112794
|
return null;
|
|
112470
112795
|
return JSON.parse(await f.text());
|
|
@@ -112472,8 +112797,8 @@ var init_setup_fill = __esm(() => {
|
|
|
112472
112797
|
return null;
|
|
112473
112798
|
}
|
|
112474
112799
|
},
|
|
112475
|
-
writeFile: async (
|
|
112476
|
-
await Bun.write(
|
|
112800
|
+
writeFile: async (path31, content) => {
|
|
112801
|
+
await Bun.write(path31, content);
|
|
112477
112802
|
}
|
|
112478
112803
|
};
|
|
112479
112804
|
});
|
|
@@ -112531,9 +112856,9 @@ async function writeSetupConfig(workdir, config2, monoConfigs, _opts, deps = _wr
|
|
|
112531
112856
|
var _writeSetupDeps;
|
|
112532
112857
|
var init_setup_write = __esm(() => {
|
|
112533
112858
|
_writeSetupDeps = {
|
|
112534
|
-
writeFile: (
|
|
112535
|
-
mkdir: async (
|
|
112536
|
-
const proc = Bun.spawn(["mkdir", "-p",
|
|
112859
|
+
writeFile: (path31, content) => Bun.write(path31, content).then(() => {}),
|
|
112860
|
+
mkdir: async (path31) => {
|
|
112861
|
+
const proc = Bun.spawn(["mkdir", "-p", path31]);
|
|
112537
112862
|
await proc.exited;
|
|
112538
112863
|
}
|
|
112539
112864
|
};
|
|
@@ -112617,7 +112942,7 @@ var init_setup = __esm(() => {
|
|
|
112617
112942
|
},
|
|
112618
112943
|
generateSetupPlan: (ctx, analysis) => generateSetupPlan(ctx, analysis),
|
|
112619
112944
|
runGate: (workdir, config2) => runSetupGate(workdir, config2),
|
|
112620
|
-
fileExists: (
|
|
112945
|
+
fileExists: (path31) => Bun.file(path31).exists(),
|
|
112621
112946
|
writeSetupConfig: (workdir, config2, monoConfigs, opts) => writeSetupConfig(workdir, config2, monoConfigs, opts),
|
|
112622
112947
|
stdout: (msg) => {
|
|
112623
112948
|
process.stdout.write(`${msg}
|
|
@@ -112634,7 +112959,7 @@ var init_setup = __esm(() => {
|
|
|
112634
112959
|
import { existsSync as existsSync43 } from "fs";
|
|
112635
112960
|
import { join as join135 } from "path";
|
|
112636
112961
|
async function resolveSpecPaths(options, deps) {
|
|
112637
|
-
const explicit = (options.paths ?? []).filter((
|
|
112962
|
+
const explicit = (options.paths ?? []).filter((path31) => path31.trim().length > 0);
|
|
112638
112963
|
if (explicit.length > 0)
|
|
112639
112964
|
return [...explicit];
|
|
112640
112965
|
if (!options.feature)
|
|
@@ -112662,7 +112987,7 @@ async function lintOne(specPath, options, deps) {
|
|
|
112662
112987
|
}
|
|
112663
112988
|
const findings = lintSpecContent(content, {
|
|
112664
112989
|
maxAcCount: options.maxAcCount,
|
|
112665
|
-
fileExists: (
|
|
112990
|
+
fileExists: (path31) => deps.fileExists(join135(options.dir, path31))
|
|
112666
112991
|
});
|
|
112667
112992
|
return {
|
|
112668
112993
|
specPath,
|
|
@@ -112728,7 +113053,7 @@ var init_spec_lint_command = __esm(() => {
|
|
|
112728
113053
|
init_prd();
|
|
112729
113054
|
init_features_resolve();
|
|
112730
113055
|
_specLintCommandDeps = {
|
|
112731
|
-
readFile: async (
|
|
113056
|
+
readFile: async (path31) => Bun.file(path31).text(),
|
|
112732
113057
|
fileExists: existsSync43,
|
|
112733
113058
|
write: (line) => {
|
|
112734
113059
|
console.log(line);
|
|
@@ -114648,11 +114973,11 @@ var require_react_reconciler_development = __commonJS(function(exports, module)
|
|
|
114648
114973
|
fiber = fiber.next, id--;
|
|
114649
114974
|
return fiber;
|
|
114650
114975
|
}
|
|
114651
|
-
function copyWithSetImpl(obj,
|
|
114652
|
-
if (index >=
|
|
114976
|
+
function copyWithSetImpl(obj, path31, index, value) {
|
|
114977
|
+
if (index >= path31.length)
|
|
114653
114978
|
return value;
|
|
114654
|
-
var key =
|
|
114655
|
-
updated[key] = copyWithSetImpl(obj[key],
|
|
114979
|
+
var key = path31[index], updated = isArrayImpl(obj) ? obj.slice() : assign2({}, obj);
|
|
114980
|
+
updated[key] = copyWithSetImpl(obj[key], path31, index + 1, value);
|
|
114656
114981
|
return updated;
|
|
114657
114982
|
}
|
|
114658
114983
|
function copyWithRename(obj, oldPath, newPath) {
|
|
@@ -114672,11 +114997,11 @@ var require_react_reconciler_development = __commonJS(function(exports, module)
|
|
|
114672
114997
|
index + 1 === oldPath.length ? (updated[newPath[index]] = updated[oldKey], isArrayImpl(updated) ? updated.splice(oldKey, 1) : delete updated[oldKey]) : updated[oldKey] = copyWithRenameImpl(obj[oldKey], oldPath, newPath, index + 1);
|
|
114673
114998
|
return updated;
|
|
114674
114999
|
}
|
|
114675
|
-
function copyWithDeleteImpl(obj,
|
|
114676
|
-
var key =
|
|
114677
|
-
if (index + 1 ===
|
|
115000
|
+
function copyWithDeleteImpl(obj, path31, index) {
|
|
115001
|
+
var key = path31[index], updated = isArrayImpl(obj) ? obj.slice() : assign2({}, obj);
|
|
115002
|
+
if (index + 1 === path31.length)
|
|
114678
115003
|
return isArrayImpl(updated) ? updated.splice(key, 1) : delete updated[key], updated;
|
|
114679
|
-
updated[key] = copyWithDeleteImpl(obj[key],
|
|
115004
|
+
updated[key] = copyWithDeleteImpl(obj[key], path31, index + 1);
|
|
114680
115005
|
return updated;
|
|
114681
115006
|
}
|
|
114682
115007
|
function shouldSuspendImpl() {
|
|
@@ -124700,29 +125025,29 @@ Check the top-level render call using <` + componentName2 + ">.");
|
|
|
124700
125025
|
var didWarnAboutNestedUpdates = false;
|
|
124701
125026
|
var didWarnAboutFindNodeInStrictMode = {};
|
|
124702
125027
|
var overrideHookState = null, overrideHookStateDeletePath = null, overrideHookStateRenamePath = null, overrideProps = null, overridePropsDeletePath = null, overridePropsRenamePath = null, scheduleUpdate = null, scheduleRetry = null, setErrorHandler = null, setSuspenseHandler = null;
|
|
124703
|
-
overrideHookState = function(fiber, id,
|
|
125028
|
+
overrideHookState = function(fiber, id, path31, value) {
|
|
124704
125029
|
id = findHook(fiber, id);
|
|
124705
|
-
id !== null && (
|
|
125030
|
+
id !== null && (path31 = copyWithSetImpl(id.memoizedState, path31, 0, value), id.memoizedState = path31, id.baseState = path31, fiber.memoizedProps = assign2({}, fiber.memoizedProps), path31 = enqueueConcurrentRenderForLane(fiber, 2), path31 !== null && scheduleUpdateOnFiber(path31, fiber, 2));
|
|
124706
125031
|
};
|
|
124707
|
-
overrideHookStateDeletePath = function(fiber, id,
|
|
125032
|
+
overrideHookStateDeletePath = function(fiber, id, path31) {
|
|
124708
125033
|
id = findHook(fiber, id);
|
|
124709
|
-
id !== null && (
|
|
125034
|
+
id !== null && (path31 = copyWithDeleteImpl(id.memoizedState, path31, 0), id.memoizedState = path31, id.baseState = path31, fiber.memoizedProps = assign2({}, fiber.memoizedProps), path31 = enqueueConcurrentRenderForLane(fiber, 2), path31 !== null && scheduleUpdateOnFiber(path31, fiber, 2));
|
|
124710
125035
|
};
|
|
124711
125036
|
overrideHookStateRenamePath = function(fiber, id, oldPath, newPath) {
|
|
124712
125037
|
id = findHook(fiber, id);
|
|
124713
125038
|
id !== null && (oldPath = copyWithRename(id.memoizedState, oldPath, newPath), id.memoizedState = oldPath, id.baseState = oldPath, fiber.memoizedProps = assign2({}, fiber.memoizedProps), oldPath = enqueueConcurrentRenderForLane(fiber, 2), oldPath !== null && scheduleUpdateOnFiber(oldPath, fiber, 2));
|
|
124714
125039
|
};
|
|
124715
|
-
overrideProps = function(fiber,
|
|
124716
|
-
fiber.pendingProps = copyWithSetImpl(fiber.memoizedProps,
|
|
125040
|
+
overrideProps = function(fiber, path31, value) {
|
|
125041
|
+
fiber.pendingProps = copyWithSetImpl(fiber.memoizedProps, path31, 0, value);
|
|
124717
125042
|
fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
|
|
124718
|
-
|
|
124719
|
-
|
|
125043
|
+
path31 = enqueueConcurrentRenderForLane(fiber, 2);
|
|
125044
|
+
path31 !== null && scheduleUpdateOnFiber(path31, fiber, 2);
|
|
124720
125045
|
};
|
|
124721
|
-
overridePropsDeletePath = function(fiber,
|
|
124722
|
-
fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps,
|
|
125046
|
+
overridePropsDeletePath = function(fiber, path31) {
|
|
125047
|
+
fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps, path31, 0);
|
|
124723
125048
|
fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
|
|
124724
|
-
|
|
124725
|
-
|
|
125049
|
+
path31 = enqueueConcurrentRenderForLane(fiber, 2);
|
|
125050
|
+
path31 !== null && scheduleUpdateOnFiber(path31, fiber, 2);
|
|
124726
125051
|
};
|
|
124727
125052
|
overridePropsRenamePath = function(fiber, oldPath, newPath) {
|
|
124728
125053
|
fiber.pendingProps = copyWithRename(fiber.memoizedProps, oldPath, newPath);
|
|
@@ -126361,11 +126686,38 @@ init_config_profile();
|
|
|
126361
126686
|
init_features_resolve();
|
|
126362
126687
|
init_generate();
|
|
126363
126688
|
|
|
126689
|
+
// src/cli/run-max-iterations.ts
|
|
126690
|
+
function parseMaxIterationsFlag(raw) {
|
|
126691
|
+
if (raw === undefined)
|
|
126692
|
+
return { ok: true, value: undefined };
|
|
126693
|
+
const value = Number.parseInt(raw, 10);
|
|
126694
|
+
if (!Number.isFinite(value) || value < 1) {
|
|
126695
|
+
return { ok: false, message: "--max-iterations must be a positive integer" };
|
|
126696
|
+
}
|
|
126697
|
+
return { ok: true, value };
|
|
126698
|
+
}
|
|
126699
|
+
function applyMaxIterationsFlag(config2, flag) {
|
|
126700
|
+
if (flag === undefined)
|
|
126701
|
+
return { ...config2, execution: { ...config2.execution } };
|
|
126702
|
+
return { ...config2, execution: { ...config2.execution, maxIterations: flag } };
|
|
126703
|
+
}
|
|
126704
|
+
|
|
126364
126705
|
// src/cli/run-mode.ts
|
|
126365
126706
|
function resolveUseHeadless(input) {
|
|
126366
126707
|
return !input.isTTY || input.headlessFlag || input.headlessEnv || input.formatterMode === "json";
|
|
126367
126708
|
}
|
|
126368
126709
|
|
|
126710
|
+
// src/cli/run-parallel.ts
|
|
126711
|
+
function parseParallelFlag(raw) {
|
|
126712
|
+
if (raw === undefined)
|
|
126713
|
+
return { ok: true, value: undefined };
|
|
126714
|
+
const value = Number.parseInt(raw, 10);
|
|
126715
|
+
if (!Number.isFinite(value) || value < 1) {
|
|
126716
|
+
return { ok: false, message: "--parallel must be a positive integer (omit it to run sequentially)" };
|
|
126717
|
+
}
|
|
126718
|
+
return { ok: true, value };
|
|
126719
|
+
}
|
|
126720
|
+
|
|
126369
126721
|
// bin/nax.ts
|
|
126370
126722
|
init_status_dispatch();
|
|
126371
126723
|
|
|
@@ -126384,14 +126736,14 @@ function resolveEffective(detected, configPatterns) {
|
|
|
126384
126736
|
return "detected";
|
|
126385
126737
|
return "none";
|
|
126386
126738
|
}
|
|
126387
|
-
async function loadRawConfig(
|
|
126388
|
-
const f = Bun.file(
|
|
126739
|
+
async function loadRawConfig(path31) {
|
|
126740
|
+
const f = Bun.file(path31);
|
|
126389
126741
|
if (!await f.exists())
|
|
126390
126742
|
return {};
|
|
126391
126743
|
return JSON.parse(await f.text());
|
|
126392
126744
|
}
|
|
126393
|
-
async function writeRawConfig(
|
|
126394
|
-
await Bun.write(
|
|
126745
|
+
async function writeRawConfig(path31, data) {
|
|
126746
|
+
await Bun.write(path31, `${JSON.stringify(data, null, 2)}
|
|
126395
126747
|
`);
|
|
126396
126748
|
}
|
|
126397
126749
|
function deepSet(obj, keyPath, value) {
|
|
@@ -131475,8 +131827,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
131475
131827
|
}
|
|
131476
131828
|
|
|
131477
131829
|
// node_modules/ink/build/components/ErrorOverview.js
|
|
131478
|
-
var cleanupPath = (
|
|
131479
|
-
return
|
|
131830
|
+
var cleanupPath = (path31) => {
|
|
131831
|
+
return path31?.replace(`file://${cwd()}/`, "");
|
|
131480
131832
|
};
|
|
131481
131833
|
var stackUtils = new import_stack_utils.default({
|
|
131482
131834
|
cwd: cwd(),
|
|
@@ -134517,7 +134869,7 @@ program2.command("setup").description("Analyze repo and generate .nax/config.jso
|
|
|
134517
134869
|
});
|
|
134518
134870
|
process.exit(exitCode);
|
|
134519
134871
|
});
|
|
134520
|
-
program2.command("run").description("Run the orchestration loop for a feature").requiredOption("-f, --feature <name>", "Feature name").option("-a, --agent <name>", "Force a specific agent").option("-m, --max-iterations <n>", "Max iterations"
|
|
134872
|
+
program2.command("run").description("Run the orchestration loop for a feature").requiredOption("-f, --feature <name>", "Feature name").option("-a, --agent <name>", "Force a specific agent").option("-m, --max-iterations <n>", "Max iterations").option("--max-cost <usd>", "Override cost limit (USD) for this run \u2014 aborts execution when exceeded").option("--dry-run", "Show plan without executing", false).option("--no-context", "Disable context builder (skip file context in prompts)").option("--no-batch", "Disable story batching (execute all stories individually)").option("--parallel <n>", "Max parallel sessions (omit = sequential)").option("--plan", "Run plan phase first before execution", false).option("--from <spec-path>", "Path to spec file (required when --plan is used)").option("--no-spec-lint", "Plan even when the spec declares sections that extract to nothing").option("--one-shot", "Skip interactive planning Q&A, use single LLM call (ACP only)", false).option("--force", "Force overwrite existing prd.json when using --plan", false).option("--headless", "Force headless mode (disable TUI, use pipe mode)", false).option("--verbose", "Enable verbose logging (debug level)", false).option("--quiet", "Quiet mode (warnings and errors only)", false).option("--silent", "Silent mode (errors only)", false).option("--json", "JSON mode (raw JSONL output to stdout)", false).option("-d, --dir <path>", "Working directory", process.cwd()).option("--skip-precheck", "Skip precheck validations (advanced users only)", false).option("--profile <name>", "Profile(s) to overlay (comma-separated or repeated; later overrides earlier)", collectProfile, []).option("--schedule <when>", "Defer run start until <when> (e.g. 30m, 1h30m, 17:00, 2026-07-02T02:00)").option("--compare <agents>", "Bake-off mode: comma-separated list of contestant agents (e.g. claude,codex)").option("--fresh", "Ignore any existing checkpoint.jsonl and re-run every incomplete story from scratch", false).addOption(new Option("--no-resume", "Alias for --fresh: never auto-resume from a prior checkpoint").default("__UNSET__")).action(async (options) => {
|
|
134521
134873
|
try {
|
|
134522
134874
|
validateFeatureName(options.feature);
|
|
134523
134875
|
} catch (err) {
|
|
@@ -134531,6 +134883,17 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
134531
134883
|
console.error(source_default.red(`Invalid directory: ${err.message}`));
|
|
134532
134884
|
process.exit(1);
|
|
134533
134885
|
}
|
|
134886
|
+
const maxIterationsFlag = parseMaxIterationsFlag(options.maxIterations);
|
|
134887
|
+
if (!maxIterationsFlag.ok) {
|
|
134888
|
+
console.error(source_default.red(maxIterationsFlag.message));
|
|
134889
|
+
process.exit(1);
|
|
134890
|
+
}
|
|
134891
|
+
const parallelFlag = parseParallelFlag(options.parallel);
|
|
134892
|
+
if (!parallelFlag.ok) {
|
|
134893
|
+
console.error(source_default.red(parallelFlag.message));
|
|
134894
|
+
process.exit(1);
|
|
134895
|
+
}
|
|
134896
|
+
const parallel = parallelFlag.value;
|
|
134534
134897
|
try {
|
|
134535
134898
|
const { assertCompareAgentExclusive: assertCompareAgentExclusive2 } = await Promise.resolve().then(() => (init_preflight(), exports_preflight));
|
|
134536
134899
|
assertCompareAgentExclusive2({ compare: options.compare, agent: options.agent });
|
|
@@ -134720,12 +135083,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
134720
135083
|
config2.agent ??= {};
|
|
134721
135084
|
config2.agent.default = options.agent;
|
|
134722
135085
|
}
|
|
134723
|
-
|
|
134724
|
-
if (!Number.isFinite(maxIterations) || maxIterations < 1) {
|
|
134725
|
-
console.error(source_default.red("--max-iterations must be a positive integer"));
|
|
134726
|
-
process.exit(1);
|
|
134727
|
-
}
|
|
134728
|
-
config2.execution.maxIterations = maxIterations;
|
|
135086
|
+
config2 = applyMaxIterationsFlag(config2, maxIterationsFlag.value);
|
|
134729
135087
|
if (options.maxCost !== undefined) {
|
|
134730
135088
|
const maxCost = Number(options.maxCost);
|
|
134731
135089
|
if (!Number.isFinite(maxCost) || maxCost <= 0) {
|
|
@@ -134765,15 +135123,6 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
134765
135123
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
134766
135124
|
}
|
|
134767
135125
|
const statusFilePath = join142(outputDir, "status.json");
|
|
134768
|
-
let parallel;
|
|
134769
|
-
if (options.parallel !== undefined) {
|
|
134770
|
-
parallel = Number.parseInt(options.parallel, 10);
|
|
134771
|
-
if (Number.isNaN(parallel) || parallel < 0) {
|
|
134772
|
-
tuiInstance?.unmount();
|
|
134773
|
-
console.error(source_default.red("--parallel must be a non-negative integer"));
|
|
134774
|
-
process.exit(1);
|
|
134775
|
-
}
|
|
134776
|
-
}
|
|
134777
135126
|
if (scheduleGate.target) {
|
|
134778
135127
|
const scheduleController = new AbortController;
|
|
134779
135128
|
const onSigint = () => scheduleController.abort();
|
|
@@ -135205,8 +135554,8 @@ configProfileCmd.command("current").description("Show the currently active profi
|
|
|
135205
135554
|
});
|
|
135206
135555
|
configProfileCmd.command("create <name>").description("Create a new empty profile").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (name, options) => {
|
|
135207
135556
|
try {
|
|
135208
|
-
const
|
|
135209
|
-
console.log(`Created profile at: ${
|
|
135557
|
+
const path31 = await profileCreateCommand(name, options.dir);
|
|
135558
|
+
console.log(`Created profile at: ${path31}`);
|
|
135210
135559
|
} catch (err) {
|
|
135211
135560
|
console.error(source_default.red(`Error: ${err.message}`));
|
|
135212
135561
|
process.exit(1);
|