@nathapp/nax 0.78.0 → 0.79.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nax.js +756 -415
- package/flows/nax-finish/nax-finish.flow.ts +21 -12
- package/flows/nax-finish/steps/commit-round.ts +8 -8
- package/flows/nax-finish/types.ts +23 -9
- package/package.json +7 -2
package/dist/nax.js
CHANGED
|
@@ -16640,7 +16640,10 @@ var init_schemas_context = __esm(() => {
|
|
|
16640
16640
|
enabled: exports_external.boolean().default(true),
|
|
16641
16641
|
maxStoryAge: exports_external.number().int().min(1).default(10),
|
|
16642
16642
|
scoreMultiplier: exports_external.number().min(0).max(1).default(0.4)
|
|
16643
|
-
}).default(() => ({ enabled: true, maxStoryAge: 10, scoreMultiplier: 0.4 }))
|
|
16643
|
+
}).default(() => ({ enabled: true, maxStoryAge: 10, scoreMultiplier: 0.4 })),
|
|
16644
|
+
manifest: exports_external.object({
|
|
16645
|
+
retentionDays: exports_external.number().int().min(1)
|
|
16646
|
+
}).optional()
|
|
16644
16647
|
}).default(() => ({
|
|
16645
16648
|
enabled: false,
|
|
16646
16649
|
minScore: 0.1,
|
|
@@ -17984,9 +17987,9 @@ var init_severity = __esm(() => {
|
|
|
17984
17987
|
info: 0,
|
|
17985
17988
|
unverifiable: 0,
|
|
17986
17989
|
low: 1,
|
|
17987
|
-
warning:
|
|
17988
|
-
error:
|
|
17989
|
-
critical:
|
|
17990
|
+
warning: 2,
|
|
17991
|
+
error: 3,
|
|
17992
|
+
critical: 4
|
|
17990
17993
|
};
|
|
17991
17994
|
});
|
|
17992
17995
|
|
|
@@ -18681,6 +18684,17 @@ var init_logger = __esm(() => {
|
|
|
18681
18684
|
});
|
|
18682
18685
|
|
|
18683
18686
|
// src/logger/index.ts
|
|
18687
|
+
var exports_logger = {};
|
|
18688
|
+
__export(exports_logger, {
|
|
18689
|
+
resetLogger: () => resetLogger,
|
|
18690
|
+
initLogger: () => initLogger,
|
|
18691
|
+
getSafeLogger: () => getSafeLogger,
|
|
18692
|
+
getLogger: () => getLogger,
|
|
18693
|
+
formatJsonl: () => formatJsonl,
|
|
18694
|
+
formatConsole: () => formatConsole,
|
|
18695
|
+
addSink: () => addSink,
|
|
18696
|
+
Logger: () => Logger
|
|
18697
|
+
});
|
|
18684
18698
|
var init_logger2 = __esm(() => {
|
|
18685
18699
|
init_logger();
|
|
18686
18700
|
init_formatters();
|
|
@@ -18836,6 +18850,55 @@ var init_config_guards = __esm(() => {
|
|
|
18836
18850
|
};
|
|
18837
18851
|
});
|
|
18838
18852
|
|
|
18853
|
+
// src/config/dotenv.ts
|
|
18854
|
+
function parseDotenv(content) {
|
|
18855
|
+
if (!content)
|
|
18856
|
+
return {};
|
|
18857
|
+
const result = {};
|
|
18858
|
+
for (const rawLine of content.split(`
|
|
18859
|
+
`)) {
|
|
18860
|
+
const line = rawLine.trim();
|
|
18861
|
+
if (!line || line.startsWith("#"))
|
|
18862
|
+
continue;
|
|
18863
|
+
const stripped = line.startsWith("export ") ? line.slice(7).trim() : line;
|
|
18864
|
+
const eqIndex = stripped.indexOf("=");
|
|
18865
|
+
if (eqIndex === -1)
|
|
18866
|
+
continue;
|
|
18867
|
+
const key = stripped.slice(0, eqIndex).trim();
|
|
18868
|
+
let value = stripped.slice(eqIndex + 1).trim();
|
|
18869
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
18870
|
+
value = value.slice(1, -1);
|
|
18871
|
+
}
|
|
18872
|
+
result[key] = value;
|
|
18873
|
+
}
|
|
18874
|
+
return result;
|
|
18875
|
+
}
|
|
18876
|
+
function resolveEnvVars(config2, env2) {
|
|
18877
|
+
if (typeof config2 === "string") {
|
|
18878
|
+
return resolveString(config2, env2);
|
|
18879
|
+
}
|
|
18880
|
+
if (Array.isArray(config2)) {
|
|
18881
|
+
return config2.map((item) => resolveEnvVars(item, env2));
|
|
18882
|
+
}
|
|
18883
|
+
if (config2 !== null && typeof config2 === "object") {
|
|
18884
|
+
const result = {};
|
|
18885
|
+
for (const [key, value] of Object.entries(config2)) {
|
|
18886
|
+
result[key] = resolveEnvVars(value, env2);
|
|
18887
|
+
}
|
|
18888
|
+
return result;
|
|
18889
|
+
}
|
|
18890
|
+
return config2;
|
|
18891
|
+
}
|
|
18892
|
+
function resolveString(str, env2) {
|
|
18893
|
+
return str.replace(/\$\$([A-Za-z_][A-Za-z0-9_]*)/g, `${DOUBLE_DOLLAR_PLACEHOLDER}$1`).replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, varName) => {
|
|
18894
|
+
if (!(varName in env2)) {
|
|
18895
|
+
throw new Error(`Environment variable $${varName} (${varName}) is not defined`);
|
|
18896
|
+
}
|
|
18897
|
+
return env2[varName];
|
|
18898
|
+
}).replace(new RegExp(`${DOUBLE_DOLLAR_PLACEHOLDER}([A-Za-z_][A-Za-z0-9_]*)`, "g"), "$$$1");
|
|
18899
|
+
}
|
|
18900
|
+
var DOUBLE_DOLLAR_PLACEHOLDER = "__DOLLAR_ESCAPE__";
|
|
18901
|
+
|
|
18839
18902
|
// src/config/merge.ts
|
|
18840
18903
|
function mergePackageConfig(root, packageOverride) {
|
|
18841
18904
|
const hasAnyMergeableField = packageOverride.agent !== undefined || packageOverride.models !== undefined || packageOverride.routing !== undefined || packageOverride.execution !== undefined || packageOverride.review !== undefined || packageOverride.acceptance !== undefined || packageOverride.quality !== undefined || packageOverride.context !== undefined || packageOverride.project !== undefined;
|
|
@@ -18874,6 +18937,10 @@ function mergePackageConfig(root, packageOverride) {
|
|
|
18874
18937
|
mutationCheck: {
|
|
18875
18938
|
...root.execution.mutationCheck,
|
|
18876
18939
|
...packageOverride.execution?.mutationCheck
|
|
18940
|
+
},
|
|
18941
|
+
rectification: {
|
|
18942
|
+
...root.execution.rectification,
|
|
18943
|
+
...packageOverride.execution?.rectification
|
|
18877
18944
|
}
|
|
18878
18945
|
},
|
|
18879
18946
|
review: {
|
|
@@ -18910,11 +18977,13 @@ function mergePackageConfig(root, packageOverride) {
|
|
|
18910
18977
|
},
|
|
18911
18978
|
...packageOverride.review?.commands
|
|
18912
18979
|
},
|
|
18913
|
-
semantic: packageOverride.review?.semantic !== undefined ? { ...root.review.semantic, ...packageOverride.review.semantic } : root.review.semantic
|
|
18980
|
+
semantic: packageOverride.review?.semantic !== undefined ? { ...root.review.semantic, ...packageOverride.review.semantic } : root.review.semantic,
|
|
18981
|
+
adversarial: packageOverride.review?.adversarial !== undefined ? { ...root.review.adversarial, ...packageOverride.review.adversarial } : root.review.adversarial
|
|
18914
18982
|
},
|
|
18915
18983
|
acceptance: {
|
|
18916
18984
|
...root.acceptance,
|
|
18917
|
-
...packageOverride.acceptance
|
|
18985
|
+
...packageOverride.acceptance,
|
|
18986
|
+
fix: packageOverride.acceptance?.fix !== undefined ? { ...root.acceptance.fix, ...packageOverride.acceptance.fix } : root.acceptance.fix
|
|
18918
18987
|
},
|
|
18919
18988
|
quality: {
|
|
18920
18989
|
...root.quality,
|
|
@@ -18922,7 +18991,9 @@ function mergePackageConfig(root, packageOverride) {
|
|
|
18922
18991
|
...root.quality.commands,
|
|
18923
18992
|
...packageOverride.quality?.commands
|
|
18924
18993
|
},
|
|
18925
|
-
testing: packageOverride.quality?.testing !== undefined ? { ...root.quality.testing, ...packageOverride.quality.testing } : root.quality.testing
|
|
18994
|
+
testing: packageOverride.quality?.testing !== undefined ? { ...root.quality.testing, ...packageOverride.quality.testing } : root.quality.testing,
|
|
18995
|
+
autofix: packageOverride.quality?.autofix !== undefined ? { ...root.quality.autofix, ...packageOverride.quality.autofix } : root.quality.autofix,
|
|
18996
|
+
lintOutput: packageOverride.quality?.lintOutput !== undefined ? { ...root.quality.lintOutput, ...packageOverride.quality.lintOutput } : root.quality.lintOutput
|
|
18926
18997
|
},
|
|
18927
18998
|
context: {
|
|
18928
18999
|
...root.context,
|
|
@@ -18935,6 +19006,10 @@ function mergePackageConfig(root, packageOverride) {
|
|
|
18935
19006
|
stages: {
|
|
18936
19007
|
...root.context.v2?.stages,
|
|
18937
19008
|
...packageOverride.context?.v2?.stages
|
|
19009
|
+
},
|
|
19010
|
+
rules: {
|
|
19011
|
+
...root.context.v2?.rules,
|
|
19012
|
+
...packageOverride.context?.v2?.rules
|
|
18938
19013
|
}
|
|
18939
19014
|
}
|
|
18940
19015
|
},
|
|
@@ -19153,55 +19228,6 @@ function projectConfigDir(projectRoot) {
|
|
|
19153
19228
|
var GLOBAL_CONFIG_DIR_ENV = "NAX_GLOBAL_CONFIG_DIR", PROJECT_NAX_DIR = ".nax";
|
|
19154
19229
|
var init_paths = () => {};
|
|
19155
19230
|
|
|
19156
|
-
// src/config/dotenv.ts
|
|
19157
|
-
function parseDotenv(content) {
|
|
19158
|
-
if (!content)
|
|
19159
|
-
return {};
|
|
19160
|
-
const result = {};
|
|
19161
|
-
for (const rawLine of content.split(`
|
|
19162
|
-
`)) {
|
|
19163
|
-
const line = rawLine.trim();
|
|
19164
|
-
if (!line || line.startsWith("#"))
|
|
19165
|
-
continue;
|
|
19166
|
-
const stripped = line.startsWith("export ") ? line.slice(7).trim() : line;
|
|
19167
|
-
const eqIndex = stripped.indexOf("=");
|
|
19168
|
-
if (eqIndex === -1)
|
|
19169
|
-
continue;
|
|
19170
|
-
const key = stripped.slice(0, eqIndex).trim();
|
|
19171
|
-
let value = stripped.slice(eqIndex + 1).trim();
|
|
19172
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
19173
|
-
value = value.slice(1, -1);
|
|
19174
|
-
}
|
|
19175
|
-
result[key] = value;
|
|
19176
|
-
}
|
|
19177
|
-
return result;
|
|
19178
|
-
}
|
|
19179
|
-
function resolveEnvVars(config2, env2) {
|
|
19180
|
-
if (typeof config2 === "string") {
|
|
19181
|
-
return resolveString(config2, env2);
|
|
19182
|
-
}
|
|
19183
|
-
if (Array.isArray(config2)) {
|
|
19184
|
-
return config2.map((item) => resolveEnvVars(item, env2));
|
|
19185
|
-
}
|
|
19186
|
-
if (config2 !== null && typeof config2 === "object") {
|
|
19187
|
-
const result = {};
|
|
19188
|
-
for (const [key, value] of Object.entries(config2)) {
|
|
19189
|
-
result[key] = resolveEnvVars(value, env2);
|
|
19190
|
-
}
|
|
19191
|
-
return result;
|
|
19192
|
-
}
|
|
19193
|
-
return config2;
|
|
19194
|
-
}
|
|
19195
|
-
function resolveString(str, env2) {
|
|
19196
|
-
return str.replace(/\$\$([A-Za-z_][A-Za-z0-9_]*)/g, `${DOUBLE_DOLLAR_PLACEHOLDER}$1`).replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, varName) => {
|
|
19197
|
-
if (!(varName in env2)) {
|
|
19198
|
-
throw new Error(`Environment variable $${varName} (${varName}) is not defined`);
|
|
19199
|
-
}
|
|
19200
|
-
return env2[varName];
|
|
19201
|
-
}).replace(new RegExp(`${DOUBLE_DOLLAR_PLACEHOLDER}([A-Za-z_][A-Za-z0-9_]*)`, "g"), "$$$1");
|
|
19202
|
-
}
|
|
19203
|
-
var DOUBLE_DOLLAR_PLACEHOLDER = "__DOLLAR_ESCAPE__";
|
|
19204
|
-
|
|
19205
19231
|
// src/config/profile.ts
|
|
19206
19232
|
import { readdirSync } from "fs";
|
|
19207
19233
|
import { join as join2 } from "path";
|
|
@@ -19471,8 +19497,9 @@ async function loadConfig(startDir, cliOverrides) {
|
|
|
19471
19497
|
}
|
|
19472
19498
|
for (const name of overlayChain) {
|
|
19473
19499
|
const profileData = await loadProfile(name, projectRoot);
|
|
19474
|
-
|
|
19475
|
-
|
|
19500
|
+
const profileEnv = await loadProfileEnv(name, projectRoot);
|
|
19501
|
+
const resolvedProfileData = Object.keys(profileEnv).length > 0 ? resolveEnvVars(profileData, profileEnv) : profileData;
|
|
19502
|
+
rawConfig = deepMergeConfig(rawConfig, resolvedProfileData);
|
|
19476
19503
|
}
|
|
19477
19504
|
if (cliOverrides) {
|
|
19478
19505
|
rawConfig = deepMergeConfig(rawConfig, cliOverrides);
|
|
@@ -20052,6 +20079,7 @@ __export(exports_config, {
|
|
|
20052
20079
|
DESCRIPTION_QUALITY_RULES: () => DESCRIPTION_QUALITY_RULES,
|
|
20053
20080
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
20054
20081
|
DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG: () => DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG,
|
|
20082
|
+
ContextV2ConfigSchema: () => ContextV2ConfigSchema,
|
|
20055
20083
|
ConfiguredModelSchema: () => ConfiguredModelSchema,
|
|
20056
20084
|
COMPLEXITY_GUIDE: () => COMPLEXITY_GUIDE,
|
|
20057
20085
|
AutoRouteConfigSchema: () => AutoRouteConfigSchema,
|
|
@@ -20097,9 +20125,11 @@ var CompleteError, SessionFailureError, SessionTurnError;
|
|
|
20097
20125
|
var init_types3 = __esm(() => {
|
|
20098
20126
|
CompleteError = class CompleteError extends Error {
|
|
20099
20127
|
exitCode;
|
|
20100
|
-
|
|
20128
|
+
retryable;
|
|
20129
|
+
constructor(message, exitCode, retryable) {
|
|
20101
20130
|
super(message);
|
|
20102
20131
|
this.exitCode = exitCode;
|
|
20132
|
+
this.retryable = retryable;
|
|
20103
20133
|
this.name = "CompleteError";
|
|
20104
20134
|
}
|
|
20105
20135
|
};
|
|
@@ -20276,6 +20306,21 @@ var init_bun_deps = __esm(() => {
|
|
|
20276
20306
|
});
|
|
20277
20307
|
|
|
20278
20308
|
// src/agents/acp/parse-agent-error.ts
|
|
20309
|
+
function classifyCompleteError(error48) {
|
|
20310
|
+
if (error48.retryable === undefined)
|
|
20311
|
+
return null;
|
|
20312
|
+
return {
|
|
20313
|
+
output: error48.message,
|
|
20314
|
+
tokenUsage: { inputTokens: 0, outputTokens: 0 },
|
|
20315
|
+
estimatedCostUsd: 0,
|
|
20316
|
+
adapterFailure: {
|
|
20317
|
+
category: "quality",
|
|
20318
|
+
outcome: "fail-adapter-error",
|
|
20319
|
+
retriable: error48.retryable,
|
|
20320
|
+
message: error48.message.slice(0, 500)
|
|
20321
|
+
}
|
|
20322
|
+
};
|
|
20323
|
+
}
|
|
20279
20324
|
function parseAgentError(stderr) {
|
|
20280
20325
|
if (!stderr) {
|
|
20281
20326
|
return { type: "unknown" };
|
|
@@ -20603,42 +20648,55 @@ function parseSessionIds(stdout) {
|
|
|
20603
20648
|
return { sessionId: undefined, recordId: undefined };
|
|
20604
20649
|
}
|
|
20605
20650
|
|
|
20606
|
-
// src/agents/acp/
|
|
20607
|
-
|
|
20608
|
-
async function readAndParseLines(stream, state, onActivity) {
|
|
20651
|
+
// src/agents/acp/stdout-line-reader.ts
|
|
20652
|
+
function readAndParseLines(stream, state, onActivity) {
|
|
20609
20653
|
const decoder = new TextDecoder;
|
|
20610
20654
|
let remainder = "";
|
|
20611
20655
|
const reader = stream.getReader();
|
|
20612
|
-
|
|
20613
|
-
|
|
20614
|
-
|
|
20615
|
-
|
|
20616
|
-
|
|
20617
|
-
remainder += decoder.decode(value, { stream: true });
|
|
20618
|
-
for (;; ) {
|
|
20619
|
-
const nl = remainder.indexOf(`
|
|
20620
|
-
`);
|
|
20621
|
-
if (nl < 0)
|
|
20656
|
+
const promise2 = (async () => {
|
|
20657
|
+
try {
|
|
20658
|
+
while (true) {
|
|
20659
|
+
const { done, value } = await reader.read();
|
|
20660
|
+
if (done)
|
|
20622
20661
|
break;
|
|
20623
|
-
|
|
20624
|
-
|
|
20625
|
-
|
|
20626
|
-
|
|
20627
|
-
if (
|
|
20628
|
-
|
|
20662
|
+
remainder += decoder.decode(value, { stream: true });
|
|
20663
|
+
for (;; ) {
|
|
20664
|
+
const nl = remainder.indexOf(`
|
|
20665
|
+
`);
|
|
20666
|
+
if (nl < 0)
|
|
20667
|
+
break;
|
|
20668
|
+
const line = remainder.slice(0, nl);
|
|
20669
|
+
remainder = remainder.slice(nl + 1);
|
|
20670
|
+
if (line.trim()) {
|
|
20671
|
+
const activity = parseAcpxJsonLine(line, state);
|
|
20672
|
+
if (activity && onActivity)
|
|
20673
|
+
onActivity(activity);
|
|
20674
|
+
}
|
|
20629
20675
|
}
|
|
20630
20676
|
}
|
|
20677
|
+
remainder += decoder.decode();
|
|
20678
|
+
if (remainder.trim()) {
|
|
20679
|
+
const activity = parseAcpxJsonLine(remainder.trim(), state);
|
|
20680
|
+
if (activity && onActivity)
|
|
20681
|
+
onActivity(activity);
|
|
20682
|
+
}
|
|
20683
|
+
} finally {
|
|
20684
|
+
reader.releaseLock();
|
|
20631
20685
|
}
|
|
20632
|
-
|
|
20633
|
-
|
|
20634
|
-
|
|
20635
|
-
|
|
20636
|
-
|
|
20686
|
+
})();
|
|
20687
|
+
return {
|
|
20688
|
+
promise: promise2,
|
|
20689
|
+
cancel: () => {
|
|
20690
|
+
reader.cancel().catch(() => {});
|
|
20637
20691
|
}
|
|
20638
|
-
}
|
|
20639
|
-
reader.releaseLock();
|
|
20640
|
-
}
|
|
20692
|
+
};
|
|
20641
20693
|
}
|
|
20694
|
+
var init_stdout_line_reader = __esm(() => {
|
|
20695
|
+
init_agents();
|
|
20696
|
+
});
|
|
20697
|
+
|
|
20698
|
+
// src/agents/acp/spawn-client.ts
|
|
20699
|
+
import { randomUUID } from "crypto";
|
|
20642
20700
|
|
|
20643
20701
|
class SpawnAcpSession {
|
|
20644
20702
|
agentName;
|
|
@@ -20793,7 +20851,8 @@ class SpawnAcpSession {
|
|
|
20793
20851
|
});
|
|
20794
20852
|
}
|
|
20795
20853
|
} : undefined;
|
|
20796
|
-
const
|
|
20854
|
+
const parseHandle = readAndParseLines(proc.stdout, parseState, onActivity);
|
|
20855
|
+
const parsePromise = parseHandle.promise.catch(() => {});
|
|
20797
20856
|
const stderrPromise = new Response(proc.stderr).text().catch(() => "");
|
|
20798
20857
|
const exitCode = await proc.exited;
|
|
20799
20858
|
const makeDrain = (ms) => {
|
|
@@ -20805,10 +20864,16 @@ class SpawnAcpSession {
|
|
|
20805
20864
|
};
|
|
20806
20865
|
const drainA = makeDrain(_spawnClientDeps.streamDrainTimeoutMs);
|
|
20807
20866
|
const drainB = makeDrain(_spawnClientDeps.streamDrainTimeoutMs);
|
|
20808
|
-
const
|
|
20809
|
-
|
|
20867
|
+
const stdoutRaceResult = Promise.race([
|
|
20868
|
+
parsePromise.then(() => "parsed"),
|
|
20869
|
+
drainA.promise.then(() => "drain")
|
|
20870
|
+
]).finally(() => drainA.cancel());
|
|
20871
|
+
const [stdoutWinner, stderr] = await Promise.all([
|
|
20872
|
+
stdoutRaceResult,
|
|
20810
20873
|
Promise.race([stderrPromise, drainB.promise]).finally(() => drainB.cancel())
|
|
20811
20874
|
]);
|
|
20875
|
+
if (stdoutWinner === "drain")
|
|
20876
|
+
parseHandle.cancel();
|
|
20812
20877
|
emit?.({ ...baseEvent, kind: "agent.process_update", status: "exited", exitCode, timestamp: now() });
|
|
20813
20878
|
if (exitCode !== 0) {
|
|
20814
20879
|
const parsedOnError = finalizeParseState(parseState);
|
|
@@ -21079,6 +21144,7 @@ var init_spawn_client = __esm(() => {
|
|
|
21079
21144
|
init_env();
|
|
21080
21145
|
init_model_spec();
|
|
21081
21146
|
init_reasoning_effort();
|
|
21147
|
+
init_stdout_line_reader();
|
|
21082
21148
|
_spawnClientDeps = {
|
|
21083
21149
|
spawn: typedSpawn,
|
|
21084
21150
|
streamDrainTimeoutMs: ACPX_STREAM_DRAIN_TIMEOUT_MS
|
|
@@ -21372,11 +21438,54 @@ var init_adapter_output = __esm(() => {
|
|
|
21372
21438
|
CONTEXT_TOOL_CALL_PATTERN = /<nax_tool_call\s+name="([^"]+)">\s*([\s\S]*?)\s*<\/nax_tool_call>/i;
|
|
21373
21439
|
});
|
|
21374
21440
|
|
|
21375
|
-
// src/agents/acp/
|
|
21441
|
+
// src/agents/acp/agent-entries.ts
|
|
21376
21442
|
function resolveRegistryEntry(agentName) {
|
|
21377
21443
|
return AGENT_REGISTRY[agentName] ?? DEFAULT_ENTRY;
|
|
21378
21444
|
}
|
|
21445
|
+
var AGENT_REGISTRY, DEFAULT_ENTRY, ACP_ADAPTER_NAMES;
|
|
21446
|
+
var init_agent_entries = __esm(() => {
|
|
21447
|
+
AGENT_REGISTRY = {
|
|
21448
|
+
claude: {
|
|
21449
|
+
binary: "claude",
|
|
21450
|
+
displayName: "Claude Code (ACP)",
|
|
21451
|
+
supportedTiers: ["fast", "balanced", "powerful"],
|
|
21452
|
+
maxContextTokens: 200000
|
|
21453
|
+
},
|
|
21454
|
+
codex: {
|
|
21455
|
+
binary: "codex",
|
|
21456
|
+
displayName: "OpenAI Codex (ACP)",
|
|
21457
|
+
supportedTiers: ["fast", "balanced"],
|
|
21458
|
+
maxContextTokens: 128000
|
|
21459
|
+
},
|
|
21460
|
+
gemini: {
|
|
21461
|
+
binary: "gemini",
|
|
21462
|
+
displayName: "Gemini CLI (ACP)",
|
|
21463
|
+
supportedTiers: ["fast", "balanced", "powerful"],
|
|
21464
|
+
maxContextTokens: 1e6
|
|
21465
|
+
},
|
|
21466
|
+
opencode: {
|
|
21467
|
+
binary: "opencode",
|
|
21468
|
+
displayName: "opencode (ACP)",
|
|
21469
|
+
supportedTiers: ["fast", "balanced", "powerful"],
|
|
21470
|
+
maxContextTokens: 128000
|
|
21471
|
+
},
|
|
21472
|
+
pi: {
|
|
21473
|
+
binary: "pi",
|
|
21474
|
+
displayName: "Pi Coding Agent (ACP)",
|
|
21475
|
+
supportedTiers: ["fast", "balanced", "powerful"],
|
|
21476
|
+
maxContextTokens: 128000
|
|
21477
|
+
}
|
|
21478
|
+
};
|
|
21479
|
+
DEFAULT_ENTRY = {
|
|
21480
|
+
binary: "claude",
|
|
21481
|
+
displayName: "ACP Agent",
|
|
21482
|
+
supportedTiers: ["balanced"],
|
|
21483
|
+
maxContextTokens: 128000
|
|
21484
|
+
};
|
|
21485
|
+
ACP_ADAPTER_NAMES = new Set(Object.keys(AGENT_REGISTRY));
|
|
21486
|
+
});
|
|
21379
21487
|
|
|
21488
|
+
// src/agents/acp/adapter.ts
|
|
21380
21489
|
class AcpAgentAdapter {
|
|
21381
21490
|
name;
|
|
21382
21491
|
displayName;
|
|
@@ -21428,7 +21537,9 @@ class AcpAgentAdapter {
|
|
|
21428
21537
|
timeoutPromise.catch(() => {});
|
|
21429
21538
|
let response;
|
|
21430
21539
|
try {
|
|
21431
|
-
|
|
21540
|
+
const promptPromise = session.prompt(prompt);
|
|
21541
|
+
promptPromise.catch(() => {});
|
|
21542
|
+
response = await Promise.race([promptPromise, timeoutPromise]);
|
|
21432
21543
|
} finally {
|
|
21433
21544
|
clearTimeout(timeoutId);
|
|
21434
21545
|
}
|
|
@@ -21441,7 +21552,7 @@ class AcpAgentAdapter {
|
|
|
21441
21552
|
cancelled: true
|
|
21442
21553
|
};
|
|
21443
21554
|
}
|
|
21444
|
-
throw new CompleteError("complete() failed: stop reason is error");
|
|
21555
|
+
throw new CompleteError("complete() failed: stop reason is error", undefined, response.retryable);
|
|
21445
21556
|
}
|
|
21446
21557
|
const text = response.messages.filter((m) => m.role === "assistant").map((m) => m.content).join(`
|
|
21447
21558
|
`).trim();
|
|
@@ -21456,7 +21567,7 @@ class AcpAgentAdapter {
|
|
|
21456
21567
|
throw new CompleteError("complete() returned empty output");
|
|
21457
21568
|
}
|
|
21458
21569
|
const tokenUsage = response.cumulative_token_usage ? this._mapper.toInternal(response.cumulative_token_usage) : { inputTokens: 0, outputTokens: 0 };
|
|
21459
|
-
const estimatedCostUsd = tokenUsage.inputTokens > 0 ? estimateCostFromTokenUsage(tokenUsage, _options.modelDef.model) : 0;
|
|
21570
|
+
const estimatedCostUsd = tokenUsage.inputTokens > 0 || tokenUsage.outputTokens > 0 ? estimateCostFromTokenUsage(tokenUsage, _options.modelDef.model) : 0;
|
|
21460
21571
|
const exactCostUsd = response.exactCostUsd;
|
|
21461
21572
|
if (exactCostUsd !== undefined) {
|
|
21462
21573
|
getSafeLogger()?.info("acp-adapter", "complete() cost", {
|
|
@@ -21481,6 +21592,11 @@ class AcpAgentAdapter {
|
|
|
21481
21592
|
return await tryOneAgent(this.name);
|
|
21482
21593
|
} catch (err) {
|
|
21483
21594
|
const error48 = err instanceof Error ? err : new Error(String(err));
|
|
21595
|
+
if (error48 instanceof CompleteError) {
|
|
21596
|
+
const classified = classifyCompleteError(error48);
|
|
21597
|
+
if (classified)
|
|
21598
|
+
return classified;
|
|
21599
|
+
}
|
|
21484
21600
|
const parsed = _fallbackDeps.parseAgentError(error48.message);
|
|
21485
21601
|
if (parsed.type === "auth") {
|
|
21486
21602
|
return {
|
|
@@ -21747,7 +21863,7 @@ class AcpAgentAdapter {
|
|
|
21747
21863
|
}
|
|
21748
21864
|
}
|
|
21749
21865
|
}
|
|
21750
|
-
var INTERACTION_TIMEOUT_MS
|
|
21866
|
+
var INTERACTION_TIMEOUT_MS;
|
|
21751
21867
|
var init_adapter = __esm(() => {
|
|
21752
21868
|
init_errors();
|
|
21753
21869
|
init_logger2();
|
|
@@ -21756,42 +21872,11 @@ var init_adapter = __esm(() => {
|
|
|
21756
21872
|
init_token_mapper();
|
|
21757
21873
|
init_adapter_lifecycle();
|
|
21758
21874
|
init_adapter_output();
|
|
21875
|
+
init_agent_entries();
|
|
21759
21876
|
init_adapter_lifecycle();
|
|
21760
21877
|
init_adapter_output();
|
|
21878
|
+
init_agent_entries();
|
|
21761
21879
|
INTERACTION_TIMEOUT_MS = 5 * 60 * 1000;
|
|
21762
|
-
AGENT_REGISTRY = {
|
|
21763
|
-
claude: {
|
|
21764
|
-
binary: "claude",
|
|
21765
|
-
displayName: "Claude Code (ACP)",
|
|
21766
|
-
supportedTiers: ["fast", "balanced", "powerful"],
|
|
21767
|
-
maxContextTokens: 200000
|
|
21768
|
-
},
|
|
21769
|
-
codex: {
|
|
21770
|
-
binary: "codex",
|
|
21771
|
-
displayName: "OpenAI Codex (ACP)",
|
|
21772
|
-
supportedTiers: ["fast", "balanced"],
|
|
21773
|
-
maxContextTokens: 128000
|
|
21774
|
-
},
|
|
21775
|
-
gemini: {
|
|
21776
|
-
binary: "gemini",
|
|
21777
|
-
displayName: "Gemini CLI (ACP)",
|
|
21778
|
-
supportedTiers: ["fast", "balanced", "powerful"],
|
|
21779
|
-
maxContextTokens: 1e6
|
|
21780
|
-
},
|
|
21781
|
-
opencode: {
|
|
21782
|
-
binary: "opencode",
|
|
21783
|
-
displayName: "opencode (ACP)",
|
|
21784
|
-
supportedTiers: ["fast", "balanced", "powerful"],
|
|
21785
|
-
maxContextTokens: 128000
|
|
21786
|
-
}
|
|
21787
|
-
};
|
|
21788
|
-
DEFAULT_ENTRY = {
|
|
21789
|
-
binary: "claude",
|
|
21790
|
-
displayName: "ACP Agent",
|
|
21791
|
-
supportedTiers: ["balanced"],
|
|
21792
|
-
maxContextTokens: 128000
|
|
21793
|
-
};
|
|
21794
|
-
ACP_ADAPTER_NAMES = new Set(Object.keys(AGENT_REGISTRY));
|
|
21795
21880
|
});
|
|
21796
21881
|
|
|
21797
21882
|
// src/agents/acp/parser.ts
|
|
@@ -21802,12 +21887,17 @@ function createParseState() {
|
|
|
21802
21887
|
exactCostUsd: undefined,
|
|
21803
21888
|
stopReason: undefined,
|
|
21804
21889
|
error: undefined,
|
|
21805
|
-
retryable: false
|
|
21890
|
+
retryable: false,
|
|
21891
|
+
sawJsonLine: false
|
|
21806
21892
|
};
|
|
21807
21893
|
}
|
|
21808
21894
|
function parseAcpxJsonLine(line, state) {
|
|
21809
21895
|
try {
|
|
21810
21896
|
const event = JSON.parse(line);
|
|
21897
|
+
if (!state.sawJsonLine) {
|
|
21898
|
+
state.text = "";
|
|
21899
|
+
state.sawJsonLine = true;
|
|
21900
|
+
}
|
|
21811
21901
|
if (event.jsonrpc === "2.0") {
|
|
21812
21902
|
if (event.method === "session/update" && event.params?.update) {
|
|
21813
21903
|
const update = event.params.update;
|
|
@@ -21860,11 +21950,18 @@ function parseAcpxJsonLine(line, state) {
|
|
|
21860
21950
|
state.stopReason = result.stop_reason;
|
|
21861
21951
|
if (result.usage && typeof result.usage === "object") {
|
|
21862
21952
|
const u = result.usage;
|
|
21953
|
+
const asNumber = (...values) => {
|
|
21954
|
+
for (const v of values) {
|
|
21955
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
21956
|
+
return v;
|
|
21957
|
+
}
|
|
21958
|
+
return 0;
|
|
21959
|
+
};
|
|
21863
21960
|
state.tokenUsage = {
|
|
21864
|
-
input_tokens: u.inputTokens
|
|
21865
|
-
output_tokens: u.outputTokens
|
|
21866
|
-
cache_read_input_tokens: u.cachedReadTokens
|
|
21867
|
-
cache_creation_input_tokens: u.cachedWriteTokens
|
|
21961
|
+
input_tokens: asNumber(u.inputTokens, u.input_tokens),
|
|
21962
|
+
output_tokens: asNumber(u.outputTokens, u.output_tokens),
|
|
21963
|
+
cache_read_input_tokens: asNumber(u.cachedReadTokens, u.cache_read_input_tokens),
|
|
21964
|
+
cache_creation_input_tokens: asNumber(u.cachedWriteTokens, u.cache_creation_input_tokens)
|
|
21868
21965
|
};
|
|
21869
21966
|
}
|
|
21870
21967
|
}
|
|
@@ -21907,7 +22004,7 @@ function parseAcpxJsonLine(line, state) {
|
|
|
21907
22004
|
state.error = typeof event.error === "string" ? event.error : event.error.message ?? JSON.stringify(event.error);
|
|
21908
22005
|
}
|
|
21909
22006
|
} catch {
|
|
21910
|
-
if (!state.text)
|
|
22007
|
+
if (!state.text && !state.sawJsonLine)
|
|
21911
22008
|
state.text = line;
|
|
21912
22009
|
}
|
|
21913
22010
|
return;
|
|
@@ -21996,7 +22093,7 @@ var KNOWN_AGENT_NAMES, _registryTestAdapters;
|
|
|
21996
22093
|
var init_registry = __esm(() => {
|
|
21997
22094
|
init_logger2();
|
|
21998
22095
|
init_adapter();
|
|
21999
|
-
KNOWN_AGENT_NAMES = ["claude", "codex", "opencode", "gemini", "aider"];
|
|
22096
|
+
KNOWN_AGENT_NAMES = ["claude", "codex", "opencode", "gemini", "aider", "pi"];
|
|
22000
22097
|
_registryTestAdapters = new Map;
|
|
22001
22098
|
});
|
|
22002
22099
|
|
|
@@ -22336,7 +22433,7 @@ function trySameAgentRetry(result, state, deps) {
|
|
|
22336
22433
|
outcome: "timeout-retry",
|
|
22337
22434
|
timeoutRetryAttempts: newAttempts,
|
|
22338
22435
|
kind: { kind: "timeout-retry", attempt: newAttempts },
|
|
22339
|
-
currentRunOptions: resolveTimeoutRetryOptions(currentRunOptions, timeoutConfig, config2.execution),
|
|
22436
|
+
currentRunOptions: resolveTimeoutRetryOptions(currentRunOptions, timeoutConfig, config2.execution, requestRunOptions),
|
|
22340
22437
|
fallbackRecord: {
|
|
22341
22438
|
outcome: result.adapterFailure?.outcome ?? "fail-timeout",
|
|
22342
22439
|
category: result.adapterFailure?.category ?? "quality",
|
|
@@ -22375,8 +22472,9 @@ function extractTimeoutRetryConfig(config2) {
|
|
|
22375
22472
|
budgetMultiplier: fromConfig?.budgetMultiplier ?? DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG.budgetMultiplier
|
|
22376
22473
|
};
|
|
22377
22474
|
}
|
|
22378
|
-
function resolveTimeoutRetryOptions(prev, timeoutConfig, executionConfig) {
|
|
22379
|
-
const
|
|
22475
|
+
function resolveTimeoutRetryOptions(prev, timeoutConfig, executionConfig, baseRunOptions) {
|
|
22476
|
+
const baseBudget = baseRunOptions?.timeoutSeconds ?? prev.timeoutSeconds;
|
|
22477
|
+
const budget = baseBudget ?? executionConfig?.sessionTimeoutSeconds ?? DEFAULT_CONFIG.execution.sessionTimeoutSeconds;
|
|
22380
22478
|
return { ...prev, timeoutSeconds: budget * timeoutConfig.budgetMultiplier };
|
|
22381
22479
|
}
|
|
22382
22480
|
function timeoutRetryShouldRetry(attempts, config2) {
|
|
@@ -30156,7 +30254,7 @@ function validateStory(raw, index, allIds) {
|
|
|
30156
30254
|
const rawDeps = s.dependencies;
|
|
30157
30255
|
const dependencies = Array.isArray(rawDeps) ? rawDeps : [];
|
|
30158
30256
|
for (const dep of dependencies) {
|
|
30159
|
-
if (!allIds.has(dep)) {
|
|
30257
|
+
if (!allIds.has(normalizeStoryId(dep))) {
|
|
30160
30258
|
throw new NaxError(`[schema] story[${index}].dependencies references unknown story ID "${dep}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, dep });
|
|
30161
30259
|
}
|
|
30162
30260
|
}
|
|
@@ -30418,6 +30516,12 @@ async function loadPRD(path) {
|
|
|
30418
30516
|
throw new Error(`PRD file is too large (${sizeMB} MB exceeds ${limitMB} MB limit). Split this feature into smaller features or reduce story count.`);
|
|
30419
30517
|
}
|
|
30420
30518
|
const prd = await Bun.file(path).json();
|
|
30519
|
+
if (!Array.isArray(prd.userStories)) {
|
|
30520
|
+
throw new NaxError(`PRD file is missing or has a corrupt "userStories" array: ${path}`, "PRD_INVALID", {
|
|
30521
|
+
stage: "prd",
|
|
30522
|
+
path
|
|
30523
|
+
});
|
|
30524
|
+
}
|
|
30421
30525
|
for (const story of prd.userStories) {
|
|
30422
30526
|
story.attempts = story.attempts ?? 0;
|
|
30423
30527
|
story.priorErrors = story.priorErrors ?? [];
|
|
@@ -30464,7 +30568,7 @@ function getNextStory(prd, currentStoryId, maxRetries) {
|
|
|
30464
30568
|
return currentStory;
|
|
30465
30569
|
}
|
|
30466
30570
|
}
|
|
30467
|
-
const eligible = prd.userStories.filter((s) => !s.passes && s.status !== "passed" && s.status !== "skipped" && s.status !== "blocked" && s.status !== "failed" && s.status !== "paused" && s.status !== "decomposed" && hasSatisfiedDependencies(s, storyIds, completedIds));
|
|
30571
|
+
const eligible = prd.userStories.filter((s) => !s.passes && s.status !== "passed" && s.status !== "skipped" && s.status !== "blocked" && s.status !== "failed" && s.status !== "paused" && s.status !== "decomposed" && s.status !== "regression-failed" && hasSatisfiedDependencies(s, storyIds, completedIds));
|
|
30468
30572
|
if (eligible.length === 0)
|
|
30469
30573
|
return null;
|
|
30470
30574
|
return eligible.reduce((best, s) => (s.priority ?? 0) > (best.priority ?? 0) ? s : best);
|
|
@@ -30495,7 +30599,7 @@ function markStoryPassed(prd, storyId, _statusWriter) {
|
|
|
30495
30599
|
const parent = prd.userStories.find((s) => s.id === parentId);
|
|
30496
30600
|
if (parent && parent.status === "decomposed") {
|
|
30497
30601
|
const siblings = prd.userStories.filter((s) => s.parentStoryId === parentId);
|
|
30498
|
-
const allSiblingsPassed = siblings.length > 0 && siblings.every((s) => s.passes || s.status === "passed");
|
|
30602
|
+
const allSiblingsPassed = siblings.length > 0 && siblings.every((s) => s.passes || s.status === "passed" || s.status === "skipped");
|
|
30499
30603
|
if (allSiblingsPassed) {
|
|
30500
30604
|
parent.passes = true;
|
|
30501
30605
|
parent.status = "passed";
|
|
@@ -31572,19 +31676,17 @@ ${newContextSection}`);
|
|
|
31572
31676
|
result += sections.acceptanceCriteria;
|
|
31573
31677
|
remainingChars -= sections.acceptanceCriteria.length;
|
|
31574
31678
|
}
|
|
31575
|
-
|
|
31576
|
-
|
|
31679
|
+
const trimmable = sections.context ?? sections.other;
|
|
31680
|
+
if (trimmable && remainingChars > 0) {
|
|
31681
|
+
const reserveForMessage = trimmable.length > remainingChars ? trimmedMessage.length : 0;
|
|
31577
31682
|
const maxContextChars = Math.max(0, remainingChars - reserveForMessage);
|
|
31578
|
-
const trimmedContext =
|
|
31683
|
+
const trimmedContext = trimmable.substring(0, maxContextChars);
|
|
31579
31684
|
result += trimmedContext;
|
|
31580
|
-
if (trimmedContext.length <
|
|
31685
|
+
if (trimmedContext.length < trimmable.length) {
|
|
31581
31686
|
result += trimmedMessage;
|
|
31582
31687
|
}
|
|
31583
31688
|
}
|
|
31584
|
-
|
|
31585
|
-
result += sections.other;
|
|
31586
|
-
}
|
|
31587
|
-
return result;
|
|
31689
|
+
return result.trim() ? result : prompt;
|
|
31588
31690
|
}
|
|
31589
31691
|
extractSections(prompt) {
|
|
31590
31692
|
const sections = {};
|
|
@@ -32911,6 +33013,80 @@ var init_tool_runtime = __esm(() => {
|
|
|
32911
33013
|
init_pull_tools();
|
|
32912
33014
|
});
|
|
32913
33015
|
|
|
33016
|
+
// src/context/engine/manifest-purge.ts
|
|
33017
|
+
import { dirname as dirname7, resolve as resolve11 } from "path";
|
|
33018
|
+
async function purgeStaleManifests(projectDir, retentionDays) {
|
|
33019
|
+
const allEntries = await _manifestPurgeDeps.scan(MANIFEST_PATTERN, projectDir, MAX_MANIFEST_SCAN);
|
|
33020
|
+
if (allEntries.length >= MAX_MANIFEST_SCAN) {
|
|
33021
|
+
_manifestPurgeDeps.debugLog("manifest-purge", `Manifest scan reached MAX_MANIFEST_SCAN=${MAX_MANIFEST_SCAN}; stopping further examination`, { cap: MAX_MANIFEST_SCAN, projectDir });
|
|
33022
|
+
}
|
|
33023
|
+
const entries = allEntries.slice(0, MAX_MANIFEST_SCAN);
|
|
33024
|
+
const cutoffMs = _manifestPurgeDeps.now() - retentionDays * DAY_MS;
|
|
33025
|
+
let deleted = 0;
|
|
33026
|
+
const touchedDirs = new Set;
|
|
33027
|
+
for (const relPath of entries) {
|
|
33028
|
+
const absPath = resolve11(projectDir, relPath);
|
|
33029
|
+
let mtimeMs;
|
|
33030
|
+
try {
|
|
33031
|
+
mtimeMs = await _manifestPurgeDeps.statMtime(absPath);
|
|
33032
|
+
} catch {
|
|
33033
|
+
continue;
|
|
33034
|
+
}
|
|
33035
|
+
if (mtimeMs >= cutoffMs)
|
|
33036
|
+
continue;
|
|
33037
|
+
try {
|
|
33038
|
+
await _manifestPurgeDeps.unlink(absPath);
|
|
33039
|
+
deleted++;
|
|
33040
|
+
touchedDirs.add(dirname7(absPath));
|
|
33041
|
+
} catch {}
|
|
33042
|
+
}
|
|
33043
|
+
for (const storyDir of touchedDirs) {
|
|
33044
|
+
await _manifestPurgeDeps.rmdirIfEmpty(storyDir);
|
|
33045
|
+
}
|
|
33046
|
+
return deleted;
|
|
33047
|
+
}
|
|
33048
|
+
var MAX_MANIFEST_SCAN = 5000, DAY_MS = 86400000, MANIFEST_PATTERN = ".nax/features/*/stories/*/{context-manifest-*,rebuild-manifest}.json", _manifestPurgeDeps;
|
|
33049
|
+
var init_manifest_purge = __esm(() => {
|
|
33050
|
+
init_logger2();
|
|
33051
|
+
_manifestPurgeDeps = {
|
|
33052
|
+
now: () => Date.now(),
|
|
33053
|
+
scan: async (pattern, cwd, cap) => {
|
|
33054
|
+
const results = [];
|
|
33055
|
+
const g = new Bun.Glob(pattern);
|
|
33056
|
+
for (const entry of g.scanSync({ cwd, absolute: false, dot: true })) {
|
|
33057
|
+
if (results.length >= cap)
|
|
33058
|
+
break;
|
|
33059
|
+
results.push(entry);
|
|
33060
|
+
}
|
|
33061
|
+
return results;
|
|
33062
|
+
},
|
|
33063
|
+
statMtime: async (path3) => {
|
|
33064
|
+
const file3 = Bun.file(path3);
|
|
33065
|
+
if (!await file3.exists())
|
|
33066
|
+
throw new Error(`stat: file not found: ${path3}`);
|
|
33067
|
+
const stat = await file3.stat();
|
|
33068
|
+
return stat.mtimeMs;
|
|
33069
|
+
},
|
|
33070
|
+
unlink: async (path3) => {
|
|
33071
|
+
const { unlink: nodeUnlink } = await import("fs/promises");
|
|
33072
|
+
await nodeUnlink(path3);
|
|
33073
|
+
},
|
|
33074
|
+
rmdirIfEmpty: async (path3) => {
|
|
33075
|
+
const { rmdir } = await import("fs/promises");
|
|
33076
|
+
try {
|
|
33077
|
+
await rmdir(path3);
|
|
33078
|
+
return true;
|
|
33079
|
+
} catch {
|
|
33080
|
+
return false;
|
|
33081
|
+
}
|
|
33082
|
+
},
|
|
33083
|
+
debugLog: (stage, message, data) => {
|
|
33084
|
+
const logger = getLogger();
|
|
33085
|
+
logger?.debug(stage, message, data);
|
|
33086
|
+
}
|
|
33087
|
+
};
|
|
33088
|
+
});
|
|
33089
|
+
|
|
32914
33090
|
// src/context/engine/index.ts
|
|
32915
33091
|
var init_engine = __esm(() => {
|
|
32916
33092
|
init_orchestrator();
|
|
@@ -32936,6 +33112,7 @@ var init_engine = __esm(() => {
|
|
|
32936
33112
|
init_stage_assembler();
|
|
32937
33113
|
init_tool_runtime();
|
|
32938
33114
|
init_manifest_store();
|
|
33115
|
+
init_manifest_purge();
|
|
32939
33116
|
});
|
|
32940
33117
|
|
|
32941
33118
|
// src/prompts/core/section-accumulator.ts
|
|
@@ -35606,12 +35783,12 @@ function acceptanceDiagnoseRawArrayToFindings(raw) {
|
|
|
35606
35783
|
}
|
|
35607
35784
|
|
|
35608
35785
|
// src/findings/path-utils.ts
|
|
35609
|
-
import { relative as relative8, resolve as
|
|
35786
|
+
import { relative as relative8, resolve as resolve12 } from "path";
|
|
35610
35787
|
function rebaseToWorkdir(rawPath, cwd, workdir) {
|
|
35611
35788
|
if (rawPath.startsWith("/")) {
|
|
35612
35789
|
return relative8(workdir, rawPath);
|
|
35613
35790
|
}
|
|
35614
|
-
return relative8(workdir,
|
|
35791
|
+
return relative8(workdir, resolve12(cwd, rawPath));
|
|
35615
35792
|
}
|
|
35616
35793
|
var init_path_utils = () => {};
|
|
35617
35794
|
|
|
@@ -36549,7 +36726,7 @@ function validateRoutingDecision(parsed, config2, story) {
|
|
|
36549
36726
|
throw new Error(`Invalid complexity: ${parsed.complexity}`);
|
|
36550
36727
|
}
|
|
36551
36728
|
const modelTier = parsed.modelTier;
|
|
36552
|
-
const tierExistsInAnyAgent = Object.values(config2.models).some((agentTiers) =>
|
|
36729
|
+
const tierExistsInAnyAgent = Object.values(config2.models).some((agentTiers) => typeof modelTier === "string" && Object.hasOwn(agentTiers, modelTier));
|
|
36553
36730
|
if (!tierExistsInAnyAgent) {
|
|
36554
36731
|
throw new Error(`Invalid modelTier: ${modelTier} (not found in any agent's tier map)`);
|
|
36555
36732
|
}
|
|
@@ -37806,10 +37983,13 @@ function validateAdversarialShape(parsed) {
|
|
|
37806
37983
|
const acks = extractAcks(obj.acks);
|
|
37807
37984
|
return {
|
|
37808
37985
|
passed: obj.passed,
|
|
37809
|
-
findings: obj.findings,
|
|
37986
|
+
findings: obj.findings.filter(isAdversarialFindingShaped),
|
|
37810
37987
|
...acks.length > 0 && { acks }
|
|
37811
37988
|
};
|
|
37812
37989
|
}
|
|
37990
|
+
function isAdversarialFindingShaped(f) {
|
|
37991
|
+
return typeof f === "object" && f !== null && !Array.isArray(f);
|
|
37992
|
+
}
|
|
37813
37993
|
function formatFindings(findings) {
|
|
37814
37994
|
return findings.map((f) => `[${f.severity}][${f.category}] ${f.file}:${f.line} \u2014 ${f.issue}
|
|
37815
37995
|
Suggestion: ${f.suggestion}`).join(`
|
|
@@ -39653,9 +39833,10 @@ var init_debate_hybrid = __esm(() => {
|
|
|
39653
39833
|
return { ...proposal, output: `Agent "failed" during rebuttal`, estimatedCostUsd: totalCostUsd };
|
|
39654
39834
|
}
|
|
39655
39835
|
if (round < ctx.input.rounds) {
|
|
39656
|
-
const settledRound = await raceAgainstAbort(Promise.
|
|
39657
|
-
|
|
39658
|
-
|
|
39836
|
+
const settledRound = await raceAgainstAbort(Promise.allSettled(ctx.input.rebutBarriers[round - 1].map((b) => b.promise)), ctx.input.signal, ctx.input.storyId);
|
|
39837
|
+
const roundOutputs = settledRound.map((r) => r.status === "fulfilled" ? r.value : "");
|
|
39838
|
+
priorRoundOutputs.push(roundOutputs);
|
|
39839
|
+
roundInputs = roundOutputs;
|
|
39659
39840
|
}
|
|
39660
39841
|
}
|
|
39661
39842
|
return { ...lastTurn, estimatedCostUsd: totalCostUsd };
|
|
@@ -39701,7 +39882,8 @@ var init_debate_plan = __esm(() => {
|
|
|
39701
39882
|
return proposal;
|
|
39702
39883
|
}
|
|
39703
39884
|
}
|
|
39704
|
-
const
|
|
39885
|
+
const peerProposalsSettled = await raceAgainstAbort(Promise.allSettled(ctx.input.proposalBarriers.map((barrier) => barrier.promise)), ctx.input.signal, ctx.input.storyId);
|
|
39886
|
+
const peerProposals = peerProposalsSettled.map((r) => r.status === "fulfilled" ? r.value : "");
|
|
39705
39887
|
const rebutResult = ctx.input.turnSemaphore ? await ctx.input.turnSemaphore.run(() => ctx.send(ctx.input.buildRebutPrompt(peerProposals))) : await ctx.send(ctx.input.buildRebutPrompt(peerProposals));
|
|
39706
39888
|
ctx.input.rebuttalBarrier.resolve(rebutResult.output);
|
|
39707
39889
|
const decision = await raceAgainstAbort(ctx.input.selectionSignal, ctx.input.signal, ctx.input.storyId);
|
|
@@ -39740,8 +39922,18 @@ async function getChangedFiles(workdir, fromRef = "HEAD") {
|
|
|
39740
39922
|
stdout: "pipe",
|
|
39741
39923
|
stderr: "pipe"
|
|
39742
39924
|
});
|
|
39743
|
-
const output = await
|
|
39744
|
-
|
|
39925
|
+
const [output, stderr, exitCode] = await Promise.all([
|
|
39926
|
+
Bun.readableStreamToText(proc.stdout),
|
|
39927
|
+
Bun.readableStreamToText(proc.stderr),
|
|
39928
|
+
proc.exited
|
|
39929
|
+
]);
|
|
39930
|
+
if (exitCode !== 0) {
|
|
39931
|
+
throw new NaxError(`git diff --name-only ${fromRef} failed (exit ${exitCode}): ${stderr.trim()}`, "GIT_DIFF_FAILED", {
|
|
39932
|
+
stage: "tdd-isolation",
|
|
39933
|
+
fromRef,
|
|
39934
|
+
exitCode
|
|
39935
|
+
});
|
|
39936
|
+
}
|
|
39745
39937
|
return output.trim().split(`
|
|
39746
39938
|
`).filter(Boolean);
|
|
39747
39939
|
}
|
|
@@ -39816,6 +40008,7 @@ async function verifyImplementerIsolation(workdir, beforeRef, testFilePatterns =
|
|
|
39816
40008
|
}
|
|
39817
40009
|
var _isolationDeps, SRC_PATTERNS, LITE_STUB_ADDED_LINES_CEILING = 20;
|
|
39818
40010
|
var init_isolation = __esm(() => {
|
|
40011
|
+
init_errors();
|
|
39819
40012
|
init_test_runners();
|
|
39820
40013
|
init_bun_deps();
|
|
39821
40014
|
_isolationDeps = { spawn };
|
|
@@ -41323,17 +41516,18 @@ async function executeWithTimeout(command, timeoutSeconds, env2, options) {
|
|
|
41323
41516
|
stdout: "pipe",
|
|
41324
41517
|
stderr: "pipe",
|
|
41325
41518
|
env: env2 || normalizeEnvironment(process.env),
|
|
41326
|
-
cwd: options?.cwd
|
|
41519
|
+
cwd: options?.cwd,
|
|
41520
|
+
detached: true
|
|
41327
41521
|
});
|
|
41328
41522
|
const stdoutPromise = new Response(proc.stdout).text().catch(() => "");
|
|
41329
41523
|
const stderrPromise = new Response(proc.stderr).text().catch(() => "");
|
|
41330
41524
|
const timeoutMs = timeoutSeconds * 1000;
|
|
41331
41525
|
let timedOut = false;
|
|
41332
41526
|
const timer = { id: undefined };
|
|
41333
|
-
const timeoutPromise = new Promise((
|
|
41527
|
+
const timeoutPromise = new Promise((resolve13) => {
|
|
41334
41528
|
timer.id = setTimeout(() => {
|
|
41335
41529
|
timedOut = true;
|
|
41336
|
-
|
|
41530
|
+
resolve13();
|
|
41337
41531
|
}, timeoutMs);
|
|
41338
41532
|
});
|
|
41339
41533
|
const processPromise = proc.exited;
|
|
@@ -42197,8 +42391,8 @@ function makeDeclarationSink() {
|
|
|
42197
42391
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
42198
42392
|
function createDrainDeadline(deadlineMs) {
|
|
42199
42393
|
let timeoutId;
|
|
42200
|
-
const promise2 = new Promise((
|
|
42201
|
-
timeoutId = setTimeout(() =>
|
|
42394
|
+
const promise2 = new Promise((resolve13) => {
|
|
42395
|
+
timeoutId = setTimeout(() => resolve13(""), deadlineMs);
|
|
42202
42396
|
});
|
|
42203
42397
|
return {
|
|
42204
42398
|
promise: promise2,
|
|
@@ -45080,14 +45274,14 @@ var init_prepare_inputs = __esm(() => {
|
|
|
45080
45274
|
});
|
|
45081
45275
|
|
|
45082
45276
|
// src/utils/nax-project-root.ts
|
|
45083
|
-
import { dirname as
|
|
45277
|
+
import { dirname as dirname8, join as join27, resolve as resolve13 } from "path";
|
|
45084
45278
|
async function findNaxProjectRoot(startDir) {
|
|
45085
|
-
let dir =
|
|
45279
|
+
let dir = resolve13(startDir);
|
|
45086
45280
|
for (let depth = 0;depth < MAX_NAX_WALK_DEPTH; depth++) {
|
|
45087
45281
|
if (await _naxProjectRootDeps.exists(join27(dir, ".nax", "config.json"))) {
|
|
45088
45282
|
return dir;
|
|
45089
45283
|
}
|
|
45090
|
-
const parent =
|
|
45284
|
+
const parent = dirname8(dir);
|
|
45091
45285
|
if (parent === dir)
|
|
45092
45286
|
break;
|
|
45093
45287
|
dir = parent;
|
|
@@ -45108,7 +45302,7 @@ var package_default;
|
|
|
45108
45302
|
var init_package = __esm(() => {
|
|
45109
45303
|
package_default = {
|
|
45110
45304
|
name: "@nathapp/nax",
|
|
45111
|
-
version: "0.
|
|
45305
|
+
version: "0.79.0",
|
|
45112
45306
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
45113
45307
|
type: "module",
|
|
45114
45308
|
bin: {
|
|
@@ -45155,9 +45349,14 @@ var init_package = __esm(() => {
|
|
|
45155
45349
|
"check:dispatch-context": "bash scripts/check-dispatch-context.sh",
|
|
45156
45350
|
"check:naxconfig-cast": "bash scripts/check-no-silent-naxconfig-cast.sh",
|
|
45157
45351
|
"check:runtime-cleanup": "bash scripts/check-runtime-cleanup.sh",
|
|
45352
|
+
"check:scripts": "bash scripts/check-scripts.sh",
|
|
45158
45353
|
"check:adapter-no-config-import": "bash scripts/check-adapter-no-config-import.sh",
|
|
45354
|
+
"check:test-typecheck": "bun run scripts/check-test-typecheck.ts",
|
|
45355
|
+
"check:test-typecheck:update": "bun run scripts/check-test-typecheck.ts --update-baseline",
|
|
45356
|
+
"check:test-as-unknown-as": "bun run scripts/check-test-as-unknown-as.ts",
|
|
45357
|
+
"check:test-as-unknown-as:update": "bun run scripts/check-test-as-unknown-as.ts --update-baseline",
|
|
45159
45358
|
"check:gate-reachability": "bun run scripts/check-gate-reachability.ts",
|
|
45160
|
-
"check:all": "bun run lint && bun run check:test-mocks && bun run check:process-cwd && bun run check:no-adapter-wrap && bun run check:dispatch-context && bun run check:naxconfig-cast && bun run check:runtime-cleanup && bun run check:adapter-no-config-import && bun run check:gate-reachability",
|
|
45359
|
+
"check:all": "bun run lint && bun run check:test-mocks && bun run check:process-cwd && bun run check:no-adapter-wrap && bun run check:scripts && bun run check:dispatch-context && bun run check:naxconfig-cast && bun run check:runtime-cleanup && bun run check:adapter-no-config-import && bun run check:test-typecheck && bun run check:test-as-unknown-as && bun run check:gate-reachability",
|
|
45161
45360
|
prepublishOnly: "bun run build",
|
|
45162
45361
|
"test:full": "FULL=1 NAX_PRECHECK=1 bun test test/ --timeout=60000"
|
|
45163
45362
|
},
|
|
@@ -45216,8 +45415,8 @@ var init_version = __esm(() => {
|
|
|
45216
45415
|
NAX_VERSION = package_default.version;
|
|
45217
45416
|
NAX_COMMIT = (() => {
|
|
45218
45417
|
try {
|
|
45219
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
45220
|
-
return "
|
|
45418
|
+
if (/^[0-9a-f]{6,10}$/.test("fdfb7e0e"))
|
|
45419
|
+
return "fdfb7e0e";
|
|
45221
45420
|
} catch {}
|
|
45222
45421
|
try {
|
|
45223
45422
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -46054,7 +46253,7 @@ function appendFilesToCommand(command, files) {
|
|
|
46054
46253
|
}
|
|
46055
46254
|
async function listChangedFiles(workdir, baseRef) {
|
|
46056
46255
|
const proc = Bun.spawn({
|
|
46057
|
-
cmd: ["git", "diff", "--name-only", `${baseRef}..HEAD`],
|
|
46256
|
+
cmd: ["git", "diff", "--relative", "--name-only", `${baseRef}..HEAD`],
|
|
46058
46257
|
cwd: workdir,
|
|
46059
46258
|
stdout: "pipe",
|
|
46060
46259
|
stderr: "pipe"
|
|
@@ -50239,7 +50438,7 @@ function createPackageRegistry(loader, repoRoot) {
|
|
|
50239
50438
|
return "";
|
|
50240
50439
|
return packageDir;
|
|
50241
50440
|
}
|
|
50242
|
-
function
|
|
50441
|
+
function resolve14(packageDir) {
|
|
50243
50442
|
const key = toRelativeKey(packageDir);
|
|
50244
50443
|
const cached2 = cache.get(key);
|
|
50245
50444
|
if (cached2 !== undefined) {
|
|
@@ -50276,9 +50475,9 @@ function createPackageRegistry(loader, repoRoot) {
|
|
|
50276
50475
|
all() {
|
|
50277
50476
|
return [...cache.values()];
|
|
50278
50477
|
},
|
|
50279
|
-
resolve:
|
|
50478
|
+
resolve: resolve14,
|
|
50280
50479
|
repo() {
|
|
50281
|
-
return
|
|
50480
|
+
return resolve14(undefined);
|
|
50282
50481
|
},
|
|
50283
50482
|
hydrate
|
|
50284
50483
|
};
|
|
@@ -51956,6 +52155,11 @@ var init_session = __esm(() => {
|
|
|
51956
52155
|
});
|
|
51957
52156
|
|
|
51958
52157
|
// src/verification/flake-probe.ts
|
|
52158
|
+
function probeRanNoTests(output) {
|
|
52159
|
+
if (!output)
|
|
52160
|
+
return false;
|
|
52161
|
+
return NO_TESTS_EXECUTED_MARKERS.some((re) => re.test(output));
|
|
52162
|
+
}
|
|
51959
52163
|
function escapeRegex2(input) {
|
|
51960
52164
|
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
51961
52165
|
}
|
|
@@ -51998,7 +52202,7 @@ async function runFlakeProbe(input) {
|
|
|
51998
52202
|
} catch {
|
|
51999
52203
|
continue;
|
|
52000
52204
|
}
|
|
52001
|
-
if (result.success && result.countsTowardEscalation) {
|
|
52205
|
+
if (result.success && result.countsTowardEscalation && !probeRanNoTests(result.output)) {
|
|
52002
52206
|
probePasses += 1;
|
|
52003
52207
|
}
|
|
52004
52208
|
}
|
|
@@ -52007,10 +52211,11 @@ async function runFlakeProbe(input) {
|
|
|
52007
52211
|
}
|
|
52008
52212
|
return { verdict: "consistent-failure", probeRuns };
|
|
52009
52213
|
}
|
|
52010
|
-
var _flakeProbeDeps;
|
|
52214
|
+
var NO_TESTS_EXECUTED_MARKERS, _flakeProbeDeps;
|
|
52011
52215
|
var init_flake_probe = __esm(() => {
|
|
52012
52216
|
init_errors();
|
|
52013
52217
|
init_executor();
|
|
52218
|
+
NO_TESTS_EXECUTED_MARKERS = [/no tests? to run/i, /^ran 0 tests?/im];
|
|
52014
52219
|
_flakeProbeDeps = {
|
|
52015
52220
|
execute: executeWithTimeout
|
|
52016
52221
|
};
|
|
@@ -52586,9 +52791,9 @@ async function buildProjectMetadata(workdir, config2) {
|
|
|
52586
52791
|
name: detected?.name,
|
|
52587
52792
|
language: detected?.lang,
|
|
52588
52793
|
dependencies: detected?.dependencies ?? [],
|
|
52589
|
-
testCommand: config2.
|
|
52590
|
-
lintCommand: config2.
|
|
52591
|
-
typecheckCommand: config2.
|
|
52794
|
+
testCommand: config2.quality?.commands?.test ?? undefined,
|
|
52795
|
+
lintCommand: config2.quality?.commands?.lint ?? undefined,
|
|
52796
|
+
typecheckCommand: config2.quality?.commands?.typecheck ?? undefined
|
|
52592
52797
|
};
|
|
52593
52798
|
}
|
|
52594
52799
|
function formatMetadataSection(metadata) {
|
|
@@ -53892,7 +54097,9 @@ async function finalizePlanRun(ctx, opts, successful, rebuttalList, outputPaths,
|
|
|
53892
54097
|
} : await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.callContext, ctx.storyId, resolverTimeoutMs, opts.workdir, opts.feature, buildPlanSynthesisSuffix(opts.specContent), finalizedProposals.map((p) => p.debater), agentManager);
|
|
53893
54098
|
let finalOutcome = outcome.outcome;
|
|
53894
54099
|
let winningOutput = outcome.output ?? finalizedProposals[0]?.output;
|
|
53895
|
-
|
|
54100
|
+
if (selectionSummary.winnerOutputPath) {
|
|
54101
|
+
winningOutput = await readWinnerOutput(selectionSummary.winnerOutputPath, winningOutput);
|
|
54102
|
+
}
|
|
53896
54103
|
let runCostUsd = totalCostUsd;
|
|
53897
54104
|
if (config2.postDebateVerifier && winningOutput) {
|
|
53898
54105
|
const verifierCtx = {
|
|
@@ -54039,7 +54246,8 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
|
54039
54246
|
const settled = await Promise.allSettled(callOpPromises);
|
|
54040
54247
|
for (let i = 0;i < settled.length; i++) {
|
|
54041
54248
|
const res = settled[i];
|
|
54042
|
-
|
|
54249
|
+
const succeeded = res.status === "fulfilled" && res.value.success;
|
|
54250
|
+
if (succeeded && res.status === "fulfilled") {
|
|
54043
54251
|
successful.push({
|
|
54044
54252
|
debater: resolved[i].debater,
|
|
54045
54253
|
agentName: resolved[i].agentName,
|
|
@@ -54064,7 +54272,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
|
54064
54272
|
stage: ctx.stage,
|
|
54065
54273
|
debaterIndex: i,
|
|
54066
54274
|
agent: resolved[i].debater.agent,
|
|
54067
|
-
error: res.reason instanceof Error ? res.reason.message : String(res.reason)
|
|
54275
|
+
error: res.status === "rejected" ? res.reason instanceof Error ? res.reason.message : String(res.reason) : `debate op returned success:false \u2014 ${res.value.rebut}`
|
|
54068
54276
|
});
|
|
54069
54277
|
}
|
|
54070
54278
|
}
|
|
@@ -54075,6 +54283,8 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
|
54075
54283
|
resolver.resolve({});
|
|
54076
54284
|
const proposalBarriers = resolved.map(() => Promise.withResolvers());
|
|
54077
54285
|
const rebuttalBarriers = resolved.map(() => Promise.withResolvers());
|
|
54286
|
+
for (const barrier of proposalBarriers)
|
|
54287
|
+
barrier.promise.catch(() => {});
|
|
54078
54288
|
const rebutBuilder = new DebatePromptBuilder({ taskContext, outputFormat: "", stage: "plan" }, { debaters: resolved.map((e) => e.debater), sessionMode: "stateful" });
|
|
54079
54289
|
const callOpPromisesB = resolved.map(({ debater, agentName }, index) => {
|
|
54080
54290
|
const debaterCtx = {
|
|
@@ -54100,6 +54310,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
|
54100
54310
|
callOpPromisesB[i].then((result) => {
|
|
54101
54311
|
rebuttalBarriers[i].resolve(result.rebut ?? "");
|
|
54102
54312
|
}, (err) => {
|
|
54313
|
+
proposalBarriers[i].reject(err);
|
|
54103
54314
|
rebuttalBarriers[i].reject(err);
|
|
54104
54315
|
});
|
|
54105
54316
|
}
|
|
@@ -54221,7 +54432,6 @@ async function runStateful(ctx, prompt) {
|
|
|
54221
54432
|
const rebuttalBuilder = buildRebuttalPromptBuilder(ctx.stage, prompt, resolved.map((entry) => entry.debater));
|
|
54222
54433
|
const signal = resolveStatefulSignal(ctx);
|
|
54223
54434
|
const noopBuildRebutPrompt = () => "";
|
|
54224
|
-
const localProposalBarrier = () => [Promise.withResolvers()];
|
|
54225
54435
|
const debaterRole = (index) => `debate-${ctx.stage}-${index}`;
|
|
54226
54436
|
const debaterCallContext = (agentName, index) => ({
|
|
54227
54437
|
...createDebaterCallContext(ctx, agentName),
|
|
@@ -54233,12 +54443,13 @@ async function runStateful(ctx, prompt) {
|
|
|
54233
54443
|
throw new NaxError("[debate] Stateful debate aborted", "CALL_OP_ABORTED", { storyId: ctx.storyId });
|
|
54234
54444
|
}
|
|
54235
54445
|
};
|
|
54446
|
+
const proposalBarriers = resolved.map(() => Promise.withResolvers());
|
|
54236
54447
|
const proposalSettled = await allSettledBounded(resolved.map(({ debater, agentName }, index) => () => callOp(debaterCallContext(agentName, index), statefulDebaterOp, {
|
|
54237
54448
|
debater,
|
|
54238
54449
|
index,
|
|
54239
54450
|
proposePrompt: proposalBuilder.buildProposalPrompt(index),
|
|
54240
54451
|
buildRebutPrompt: noopBuildRebutPrompt,
|
|
54241
|
-
proposalBarriers
|
|
54452
|
+
proposalBarriers,
|
|
54242
54453
|
signal,
|
|
54243
54454
|
storyId: ctx.storyId,
|
|
54244
54455
|
skipRebuttal: true
|
|
@@ -54304,6 +54515,7 @@ async function runStateful(ctx, prompt) {
|
|
|
54304
54515
|
});
|
|
54305
54516
|
return buildFailedResult(ctx.storyId, ctx.stage, ctx.stageConfig, 0);
|
|
54306
54517
|
}
|
|
54518
|
+
const rebuttalRoundBarriers = successfulProposals.map(() => Promise.withResolvers());
|
|
54307
54519
|
const rebuttals = shouldRunRebuttal ? (await allSettledBounded(successfulProposals.map((proposal, index) => () => callOp(debaterCallContext(proposal.agentName, proposal.resolvedIndex), statefulDebaterOp, {
|
|
54308
54520
|
debater: proposal.debater,
|
|
54309
54521
|
index,
|
|
@@ -54312,7 +54524,7 @@ async function runStateful(ctx, prompt) {
|
|
|
54312
54524
|
output: entry.output
|
|
54313
54525
|
}))),
|
|
54314
54526
|
buildRebutPrompt: noopBuildRebutPrompt,
|
|
54315
|
-
proposalBarriers:
|
|
54527
|
+
proposalBarriers: rebuttalRoundBarriers,
|
|
54316
54528
|
signal,
|
|
54317
54529
|
storyId: ctx.storyId,
|
|
54318
54530
|
skipRebuttal: true
|
|
@@ -54962,9 +55174,9 @@ ${request.summary}
|
|
|
54962
55174
|
throw new Error("CLI plugin not initialized");
|
|
54963
55175
|
}
|
|
54964
55176
|
let timeoutId;
|
|
54965
|
-
const timeoutPromise = new Promise((
|
|
55177
|
+
const timeoutPromise = new Promise((resolve15) => {
|
|
54966
55178
|
timeoutId = setTimeout(() => {
|
|
54967
|
-
|
|
55179
|
+
resolve15({
|
|
54968
55180
|
requestId: request.id,
|
|
54969
55181
|
action: "skip",
|
|
54970
55182
|
respondedBy: "timeout",
|
|
@@ -55120,9 +55332,9 @@ ${request.summary}
|
|
|
55120
55332
|
if (!this.rl) {
|
|
55121
55333
|
throw new Error("CLI plugin not initialized");
|
|
55122
55334
|
}
|
|
55123
|
-
return new Promise((
|
|
55335
|
+
return new Promise((resolve15) => {
|
|
55124
55336
|
this.rl?.question(prompt, (answer) => {
|
|
55125
|
-
|
|
55337
|
+
resolve15(answer);
|
|
55126
55338
|
});
|
|
55127
55339
|
});
|
|
55128
55340
|
}
|
|
@@ -55480,9 +55692,9 @@ ${partLabel}${chunks[i]}`;
|
|
|
55480
55692
|
parseUpdate(requestId, update) {
|
|
55481
55693
|
if (update.callback_query) {
|
|
55482
55694
|
const data = update.callback_query.data;
|
|
55483
|
-
if (!data.startsWith(requestId))
|
|
55484
|
-
return null;
|
|
55485
55695
|
const parts = data.split(":");
|
|
55696
|
+
if (parts[0] !== requestId)
|
|
55697
|
+
return null;
|
|
55486
55698
|
if (parts.length < 2)
|
|
55487
55699
|
return null;
|
|
55488
55700
|
const action = parts[1];
|
|
@@ -55609,11 +55821,11 @@ function installServePortZeroCompat() {
|
|
|
55609
55821
|
const originalFetch = globalThis.fetch.bind(globalThis);
|
|
55610
55822
|
const patchedServe = (options) => {
|
|
55611
55823
|
const requestedPort = typeof options.port === "number" ? options.port : 0;
|
|
55612
|
-
if (
|
|
55824
|
+
if (!inMemoryServers.has(requestedPort)) {
|
|
55613
55825
|
try {
|
|
55614
55826
|
return originalServe(options);
|
|
55615
55827
|
} catch {
|
|
55616
|
-
return createInMemoryServer(options, requestedPort);
|
|
55828
|
+
return createInMemoryServer(options, requestedPort === 0 ? nextCompatPort() : requestedPort);
|
|
55617
55829
|
}
|
|
55618
55830
|
}
|
|
55619
55831
|
return createInMemoryServer(options, nextCompatPort());
|
|
@@ -55736,7 +55948,7 @@ class WebhookInteractionPlugin {
|
|
|
55736
55948
|
this.registeredRequestIds.delete(requestId);
|
|
55737
55949
|
return early;
|
|
55738
55950
|
}
|
|
55739
|
-
return new Promise((
|
|
55951
|
+
return new Promise((resolve15) => {
|
|
55740
55952
|
const existingCallback = this.receiveCallbacks.get(requestId);
|
|
55741
55953
|
if (existingCallback) {
|
|
55742
55954
|
this.clearReceiveTimer(requestId);
|
|
@@ -55751,7 +55963,7 @@ class WebhookInteractionPlugin {
|
|
|
55751
55963
|
this.clearReceiveTimer(requestId);
|
|
55752
55964
|
this.receiveCallbacks.delete(requestId);
|
|
55753
55965
|
this.registeredRequestIds.delete(requestId);
|
|
55754
|
-
|
|
55966
|
+
resolve15({
|
|
55755
55967
|
requestId,
|
|
55756
55968
|
action: "skip",
|
|
55757
55969
|
respondedBy: "timeout",
|
|
@@ -55763,7 +55975,7 @@ class WebhookInteractionPlugin {
|
|
|
55763
55975
|
this.clearReceiveTimer(requestId);
|
|
55764
55976
|
this.receiveCallbacks.delete(requestId);
|
|
55765
55977
|
this.registeredRequestIds.delete(requestId);
|
|
55766
|
-
|
|
55978
|
+
resolve15(response);
|
|
55767
55979
|
});
|
|
55768
55980
|
});
|
|
55769
55981
|
}
|
|
@@ -56039,8 +56251,8 @@ Options:`);
|
|
|
56039
56251
|
stage: "run"
|
|
56040
56252
|
});
|
|
56041
56253
|
}
|
|
56042
|
-
if (parsed.confidence < 0 || parsed.confidence > 1) {
|
|
56043
|
-
throw new NaxError(`Invalid confidence: ${parsed.confidence} (must be 0-1)`, "AUTO_APPROVE_PARSE_FAILED", {
|
|
56254
|
+
if (typeof parsed.confidence !== "number" || !Number.isFinite(parsed.confidence) || parsed.confidence < 0 || parsed.confidence > 1) {
|
|
56255
|
+
throw new NaxError(`Invalid confidence: ${parsed.confidence} (must be a number 0-1)`, "AUTO_APPROVE_PARSE_FAILED", {
|
|
56044
56256
|
stage: "run"
|
|
56045
56257
|
});
|
|
56046
56258
|
}
|
|
@@ -56136,7 +56348,7 @@ function createTriggerRequest(trigger, context, config2) {
|
|
|
56136
56348
|
const metadata = TRIGGER_METADATA[trigger];
|
|
56137
56349
|
const { fallback, timeout } = getTriggerConfig(trigger, config2);
|
|
56138
56350
|
const summary = substituteTemplate(metadata.defaultSummary, context);
|
|
56139
|
-
const id = `trigger-${trigger}-${Date.now()}`;
|
|
56351
|
+
const id = `trigger-${trigger}-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
|
|
56140
56352
|
return {
|
|
56141
56353
|
id,
|
|
56142
56354
|
type: "confirm",
|
|
@@ -56315,10 +56527,10 @@ function validateFeatureName(feature) {
|
|
|
56315
56527
|
|
|
56316
56528
|
// src/plan/critic.ts
|
|
56317
56529
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
56318
|
-
import { dirname as
|
|
56530
|
+
import { dirname as dirname9, join as join43 } from "path";
|
|
56319
56531
|
async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
|
|
56320
56532
|
const path8 = join43(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
|
|
56321
|
-
await mkdir6(
|
|
56533
|
+
await mkdir6(dirname9(path8), { recursive: true });
|
|
56322
56534
|
await Bun.write(path8, formatSpecDeltas(findings, manifest));
|
|
56323
56535
|
return path8;
|
|
56324
56536
|
}
|
|
@@ -56393,13 +56605,13 @@ function createCliInteractionBridge() {
|
|
|
56393
56605
|
process.stdout.write(`
|
|
56394
56606
|
\uD83E\uDD16 Agent: ${text}
|
|
56395
56607
|
You: `);
|
|
56396
|
-
return new Promise((
|
|
56608
|
+
return new Promise((resolve15) => {
|
|
56397
56609
|
const rl = createInterface2({ input: process.stdin, terminal: false });
|
|
56398
56610
|
rl.once("line", (line) => {
|
|
56399
56611
|
rl.close();
|
|
56400
|
-
|
|
56612
|
+
resolve15(line.trim());
|
|
56401
56613
|
});
|
|
56402
|
-
rl.once("close", () =>
|
|
56614
|
+
rl.once("close", () => resolve15(""));
|
|
56403
56615
|
});
|
|
56404
56616
|
}
|
|
56405
56617
|
};
|
|
@@ -58463,14 +58675,14 @@ var init_status_cost = __esm(() => {
|
|
|
58463
58675
|
|
|
58464
58676
|
// src/commands/common.ts
|
|
58465
58677
|
import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync4 } from "fs";
|
|
58466
|
-
import { join as join46, resolve as
|
|
58678
|
+
import { join as join46, resolve as resolve15 } from "path";
|
|
58467
58679
|
function resolveProject2(options = {}) {
|
|
58468
58680
|
const { dir, feature } = options;
|
|
58469
58681
|
let projectRoot;
|
|
58470
58682
|
let naxDir;
|
|
58471
58683
|
let configPath;
|
|
58472
58684
|
if (dir) {
|
|
58473
|
-
projectRoot = realpathSync4(
|
|
58685
|
+
projectRoot = realpathSync4(resolve15(dir));
|
|
58474
58686
|
naxDir = join46(projectRoot, ".nax");
|
|
58475
58687
|
if (!existsSync16(naxDir)) {
|
|
58476
58688
|
throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
|
|
@@ -58527,12 +58739,28 @@ No features found in this project.`;
|
|
|
58527
58739
|
featureDir
|
|
58528
58740
|
};
|
|
58529
58741
|
}
|
|
58742
|
+
function resolveSingleFeature(naxDir, remediationHint = "pass -f <name>") {
|
|
58743
|
+
const featuresDir = join46(naxDir, "features");
|
|
58744
|
+
const available = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort() : [];
|
|
58745
|
+
if (available.length === 1)
|
|
58746
|
+
return available[0];
|
|
58747
|
+
if (available.length === 0) {
|
|
58748
|
+
throw new NaxError(`No feature specified and no features found \u2014 ${remediationHint}.`, "FEATURE_NOT_SPECIFIED", {
|
|
58749
|
+
featuresDir
|
|
58750
|
+
});
|
|
58751
|
+
}
|
|
58752
|
+
throw new NaxError(`No feature specified and multiple features exist \u2014 ${remediationHint}.
|
|
58753
|
+
|
|
58754
|
+
Available features:
|
|
58755
|
+
${available.map((f) => ` - ${f}`).join(`
|
|
58756
|
+
`)}`, "FEATURE_AMBIGUOUS", { featuresDir, available });
|
|
58757
|
+
}
|
|
58530
58758
|
async function resolveProjectAsync(options = {}) {
|
|
58531
58759
|
const { dir } = options;
|
|
58532
58760
|
if (!dir) {
|
|
58533
58761
|
return resolveProject2(options);
|
|
58534
58762
|
}
|
|
58535
|
-
if (existsSync16(
|
|
58763
|
+
if (existsSync16(resolve15(dir))) {
|
|
58536
58764
|
return resolveProject2(options);
|
|
58537
58765
|
}
|
|
58538
58766
|
const isPlainName = !dir.includes("/") && !dir.includes("\\");
|
|
@@ -58548,17 +58776,17 @@ async function resolveProjectAsync(options = {}) {
|
|
|
58548
58776
|
} catch {}
|
|
58549
58777
|
}
|
|
58550
58778
|
throw new NaxError(`No project found for name or path: "${dir}"
|
|
58551
|
-
Checked filesystem path: ${
|
|
58779
|
+
Checked filesystem path: ${resolve15(dir)}
|
|
58552
58780
|
Checked identity registry: ${registryIdentityPath}
|
|
58553
|
-
Tip: use an absolute or relative path, or run "nax init" in your project directory first.`, "PROJECT_NOT_FOUND", { dir, resolvedPath:
|
|
58781
|
+
Tip: use an absolute or relative path, or run "nax init" in your project directory first.`, "PROJECT_NOT_FOUND", { dir, resolvedPath: resolve15(dir), registryIdentityPath });
|
|
58554
58782
|
}
|
|
58555
58783
|
try {
|
|
58556
58784
|
return resolveProject2(options);
|
|
58557
58785
|
} catch (err) {
|
|
58558
58786
|
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
58559
|
-
throw new NaxError(`Path does not exist: ${
|
|
58787
|
+
throw new NaxError(`Path does not exist: ${resolve15(dir)}`, "PROJECT_NOT_FOUND", {
|
|
58560
58788
|
dir,
|
|
58561
|
-
resolvedPath:
|
|
58789
|
+
resolvedPath: resolve15(dir),
|
|
58562
58790
|
cause: err
|
|
58563
58791
|
});
|
|
58564
58792
|
}
|
|
@@ -58566,7 +58794,7 @@ Tip: use an absolute or relative path, or run "nax init" in your project directo
|
|
|
58566
58794
|
}
|
|
58567
58795
|
}
|
|
58568
58796
|
function findProjectRoot(startDir) {
|
|
58569
|
-
let current =
|
|
58797
|
+
let current = resolve15(startDir);
|
|
58570
58798
|
let depth = 0;
|
|
58571
58799
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
58572
58800
|
const naxDir = join46(current, ".nax");
|
|
@@ -58596,7 +58824,7 @@ __export(exports_status_features, {
|
|
|
58596
58824
|
_statusFeaturesDeps: () => _statusFeaturesDeps
|
|
58597
58825
|
});
|
|
58598
58826
|
import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
|
|
58599
|
-
import { basename as basename10, join as join47, resolve as
|
|
58827
|
+
import { basename as basename10, join as join47, resolve as resolve16 } from "path";
|
|
58600
58828
|
function isPidAlive(pid) {
|
|
58601
58829
|
try {
|
|
58602
58830
|
process.kill(pid, 0);
|
|
@@ -58911,7 +59139,7 @@ async function displayFeatureStatus(options = {}) {
|
|
|
58911
59139
|
if (options.feature) {
|
|
58912
59140
|
let featureDir;
|
|
58913
59141
|
if (options.dir) {
|
|
58914
|
-
const projectDir =
|
|
59142
|
+
const projectDir = resolve16(options.dir);
|
|
58915
59143
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
58916
59144
|
const projectKey = config2?.name?.trim() || basename10(projectDir);
|
|
58917
59145
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
@@ -59150,9 +59378,11 @@ var init_events = () => {};
|
|
|
59150
59378
|
// src/acceptance/fix-generator.ts
|
|
59151
59379
|
function findRelatedStories(failedAC, prd) {
|
|
59152
59380
|
const relatedStoryIds = [];
|
|
59381
|
+
const escapedAC = failedAC.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
59382
|
+
const acPattern = new RegExp(`(?<![\\w-])${escapedAC}(?!\\d)`);
|
|
59153
59383
|
for (const story of prd.userStories) {
|
|
59154
59384
|
for (const ac of story.acceptanceCriteria) {
|
|
59155
|
-
if (
|
|
59385
|
+
if (acPattern.test(ac)) {
|
|
59156
59386
|
relatedStoryIds.push(story.id);
|
|
59157
59387
|
break;
|
|
59158
59388
|
}
|
|
@@ -59525,6 +59755,58 @@ function logTestOutput(logger, stage, output, opts = {}) {
|
|
|
59525
59755
|
output: lines
|
|
59526
59756
|
});
|
|
59527
59757
|
}
|
|
59758
|
+
// src/verification/flake-baseline-diff.ts
|
|
59759
|
+
async function resolveFlakeBaselineDiff(config2, workdir, storyWorkdir) {
|
|
59760
|
+
try {
|
|
59761
|
+
const resolved = await resolveTestFilePatterns(config2, workdir, storyWorkdir);
|
|
59762
|
+
const baseRef = await getMergeBase(workdir);
|
|
59763
|
+
if (baseRef === undefined) {
|
|
59764
|
+
throw new NaxError("getMergeBase() found no usable ref (empty/detached repo)", "FLAKE_BASELINE_NO_MERGE_BASE", {
|
|
59765
|
+
stage: "flake-triage",
|
|
59766
|
+
workdir
|
|
59767
|
+
});
|
|
59768
|
+
}
|
|
59769
|
+
const preflight = await gitWithTimeout(["diff", "--name-only", baseRef], workdir);
|
|
59770
|
+
if (preflight.exitCode !== 0) {
|
|
59771
|
+
throw new NaxError(`git diff --name-only ${baseRef} failed (exit ${preflight.exitCode})`, "FLAKE_BASELINE_GIT_DIFF_FAILED", {
|
|
59772
|
+
stage: "flake-triage",
|
|
59773
|
+
baseRef,
|
|
59774
|
+
exitCode: preflight.exitCode
|
|
59775
|
+
});
|
|
59776
|
+
}
|
|
59777
|
+
const changedTestFiles = await getChangedTestFiles(workdir, workdir, baseRef, storyWorkdir, [...resolved.regex]);
|
|
59778
|
+
const changedNonTestFiles = await getChangedNonTestFiles(workdir, baseRef, storyWorkdir, [...resolved.regex], undefined, workdir);
|
|
59779
|
+
const mappedTestFiles = await mapSourceToTests(changedNonTestFiles, workdir, storyWorkdir, [...resolved.globs]);
|
|
59780
|
+
return { changedTestFiles, mappedTestFiles };
|
|
59781
|
+
} catch (err) {
|
|
59782
|
+
getSafeLogger()?.warn("flake-triage", "Baseline diff resolution failed \u2014 skipping triage this gate (fail closed)", {
|
|
59783
|
+
error: errorMessage(err)
|
|
59784
|
+
});
|
|
59785
|
+
return null;
|
|
59786
|
+
}
|
|
59787
|
+
}
|
|
59788
|
+
var init_flake_baseline_diff = __esm(() => {
|
|
59789
|
+
init_errors();
|
|
59790
|
+
init_logger2();
|
|
59791
|
+
init_test_runners();
|
|
59792
|
+
init_git();
|
|
59793
|
+
init_smart_runner();
|
|
59794
|
+
});
|
|
59795
|
+
|
|
59796
|
+
// src/verification/shell-quote.ts
|
|
59797
|
+
function shellQuoteArg(arg) {
|
|
59798
|
+
return `'${arg.replaceAll("'", `'\\''`)}'`;
|
|
59799
|
+
}
|
|
59800
|
+
|
|
59801
|
+
// src/verification/index.ts
|
|
59802
|
+
var init_verification = __esm(() => {
|
|
59803
|
+
init_executor();
|
|
59804
|
+
init_runners();
|
|
59805
|
+
init_flake_probe();
|
|
59806
|
+
init_flake_triage();
|
|
59807
|
+
init_flake_baseline_diff();
|
|
59808
|
+
init_mutation();
|
|
59809
|
+
});
|
|
59528
59810
|
|
|
59529
59811
|
// src/pipeline/stages/acceptance.ts
|
|
59530
59812
|
var exports_acceptance2 = {};
|
|
@@ -59546,6 +59828,7 @@ var init_acceptance3 = __esm(() => {
|
|
|
59546
59828
|
init_logger2();
|
|
59547
59829
|
init_prd();
|
|
59548
59830
|
init_test_runners();
|
|
59831
|
+
init_verification();
|
|
59549
59832
|
_acceptanceStageDeps = {
|
|
59550
59833
|
runHardeningPass: async (ctx) => {
|
|
59551
59834
|
const { runHardeningPass: runHardeningPass2 } = await Promise.resolve().then(() => (init_acceptance2(), exports_acceptance));
|
|
@@ -59624,18 +59907,9 @@ var init_acceptance3 = __esm(() => {
|
|
|
59624
59907
|
cmd: testCmdParts.join(" "),
|
|
59625
59908
|
packageDir
|
|
59626
59909
|
});
|
|
59627
|
-
const
|
|
59628
|
-
|
|
59629
|
-
|
|
59630
|
-
stderr: "pipe"
|
|
59631
|
-
});
|
|
59632
|
-
const [exitCode, stdout, stderr] = await Promise.all([
|
|
59633
|
-
proc.exited,
|
|
59634
|
-
new Response(proc.stdout).text(),
|
|
59635
|
-
new Response(proc.stderr).text()
|
|
59636
|
-
]);
|
|
59637
|
-
const output = `${stdout}
|
|
59638
|
-
${stderr}`;
|
|
59910
|
+
const execution = await executeWithTimeout(testCmdParts.map(shellQuoteArg).join(" "), Math.ceil(ctx.config.acceptance.timeoutMs / 1000), undefined, { cwd: packageDir });
|
|
59911
|
+
const exitCode = execution.exitCode ?? (execution.success ? 0 : 1);
|
|
59912
|
+
const output = execution.output ?? "";
|
|
59639
59913
|
allOutputParts.push(output);
|
|
59640
59914
|
const failedACs = parseTestFailures2(output);
|
|
59641
59915
|
const overrides = ctx.prd.acceptanceOverrides ?? {};
|
|
@@ -60049,7 +60323,7 @@ async function runAcceptanceSetup(ctx, featureDir, phaseStartTime) {
|
|
|
60049
60323
|
cmd: runCmd.join(" "),
|
|
60050
60324
|
packageDir
|
|
60051
60325
|
});
|
|
60052
|
-
const { exitCode } = await _acceptanceSetupDeps.runTest(testPath, packageDir, runCmd);
|
|
60326
|
+
const { exitCode } = await _acceptanceSetupDeps.runTest(testPath, packageDir, runCmd, ctx.config.acceptance.timeoutMs);
|
|
60053
60327
|
if (exitCode !== 0) {
|
|
60054
60328
|
redFailCount++;
|
|
60055
60329
|
}
|
|
@@ -60086,6 +60360,7 @@ var init_acceptance_setup = __esm(() => {
|
|
|
60086
60360
|
init_logger2();
|
|
60087
60361
|
init_operations();
|
|
60088
60362
|
init_git();
|
|
60363
|
+
init_verification();
|
|
60089
60364
|
init_event_bus();
|
|
60090
60365
|
_acceptanceSetupDeps = {
|
|
60091
60366
|
getAgent: (_name) => {
|
|
@@ -60146,20 +60421,14 @@ var init_acceptance_setup = __esm(() => {
|
|
|
60146
60421
|
loadGroupConfig: async (projectDir, relativeWorkdir) => {
|
|
60147
60422
|
return loadConfigForWorkdir(path13.join(projectDir, ".nax", "config.json"), relativeWorkdir || undefined);
|
|
60148
60423
|
},
|
|
60149
|
-
runTest: async (_testPath, _workdir, _cmd) => {
|
|
60150
|
-
const
|
|
60151
|
-
|
|
60152
|
-
cwd: _workdir,
|
|
60153
|
-
stdout: "pipe",
|
|
60154
|
-
stderr: "pipe"
|
|
60424
|
+
runTest: async (_testPath, _workdir, _cmd, timeoutMs = 1800000) => {
|
|
60425
|
+
const execution = await executeWithTimeout(_cmd.map(shellQuoteArg).join(" "), Math.ceil(timeoutMs / 1000), undefined, {
|
|
60426
|
+
cwd: _workdir
|
|
60155
60427
|
});
|
|
60156
|
-
|
|
60157
|
-
|
|
60158
|
-
|
|
60159
|
-
|
|
60160
|
-
]);
|
|
60161
|
-
return { exitCode, output: `${stdout}
|
|
60162
|
-
${stderr}` };
|
|
60428
|
+
return {
|
|
60429
|
+
exitCode: execution.exitCode ?? (execution.success ? 0 : 1),
|
|
60430
|
+
output: execution.output ?? ""
|
|
60431
|
+
};
|
|
60163
60432
|
},
|
|
60164
60433
|
callOp: async (pipelineCtx, packageDir, op, input, storyId) => {
|
|
60165
60434
|
if (!pipelineCtx.runtime) {
|
|
@@ -60591,7 +60860,7 @@ var init_constitution = __esm(() => {
|
|
|
60591
60860
|
});
|
|
60592
60861
|
|
|
60593
60862
|
// src/pipeline/stages/constitution.ts
|
|
60594
|
-
import { dirname as
|
|
60863
|
+
import { dirname as dirname10 } from "path";
|
|
60595
60864
|
var constitutionStage;
|
|
60596
60865
|
var init_constitution2 = __esm(() => {
|
|
60597
60866
|
init_constitution();
|
|
@@ -60601,7 +60870,7 @@ var init_constitution2 = __esm(() => {
|
|
|
60601
60870
|
enabled: (ctx) => ctx.config.constitution.enabled,
|
|
60602
60871
|
async execute(ctx) {
|
|
60603
60872
|
const logger = getLogger();
|
|
60604
|
-
const ngentDir = ctx.featureDir ?
|
|
60873
|
+
const ngentDir = ctx.featureDir ? dirname10(dirname10(ctx.featureDir)) : `${ctx.workdir}/nax`;
|
|
60605
60874
|
const result = await loadConstitution(ngentDir, ctx.config.constitution);
|
|
60606
60875
|
if (result) {
|
|
60607
60876
|
ctx.constitution = result;
|
|
@@ -61133,11 +61402,11 @@ async function spawnWithTimeout(proc, timeoutMs) {
|
|
|
61133
61402
|
]);
|
|
61134
61403
|
return { stdout, exitCode };
|
|
61135
61404
|
})(),
|
|
61136
|
-
new Promise((
|
|
61405
|
+
new Promise((resolve17) => setTimeout(() => {
|
|
61137
61406
|
try {
|
|
61138
61407
|
proc.kill("SIGKILL");
|
|
61139
61408
|
} catch {}
|
|
61140
|
-
|
|
61409
|
+
resolve17({ stdout: "", exitCode: 1 });
|
|
61141
61410
|
}, timeoutMs))
|
|
61142
61411
|
]);
|
|
61143
61412
|
return result;
|
|
@@ -61908,38 +62177,6 @@ var init_story_orchestrator_logging = __esm(() => {
|
|
|
61908
62177
|
init_logger2();
|
|
61909
62178
|
init_phase_eval();
|
|
61910
62179
|
});
|
|
61911
|
-
// src/verification/flake-baseline-diff.ts
|
|
61912
|
-
async function resolveFlakeBaselineDiff(config2, workdir, storyWorkdir) {
|
|
61913
|
-
try {
|
|
61914
|
-
const resolved = await resolveTestFilePatterns(config2, workdir, storyWorkdir);
|
|
61915
|
-
const baseRef = await getMergeBase(workdir);
|
|
61916
|
-
const changedTestFiles = await getChangedTestFiles(workdir, workdir, baseRef, storyWorkdir, [...resolved.regex]);
|
|
61917
|
-
const changedNonTestFiles = await getChangedNonTestFiles(workdir, baseRef, storyWorkdir, [...resolved.regex], undefined, workdir);
|
|
61918
|
-
const mappedTestFiles = await mapSourceToTests(changedNonTestFiles, workdir, storyWorkdir, [...resolved.globs]);
|
|
61919
|
-
return { changedTestFiles, mappedTestFiles };
|
|
61920
|
-
} catch (err) {
|
|
61921
|
-
getSafeLogger()?.warn("flake-triage", "Baseline diff resolution failed \u2014 skipping triage this gate (fail closed)", {
|
|
61922
|
-
error: errorMessage(err)
|
|
61923
|
-
});
|
|
61924
|
-
return null;
|
|
61925
|
-
}
|
|
61926
|
-
}
|
|
61927
|
-
var init_flake_baseline_diff = __esm(() => {
|
|
61928
|
-
init_logger2();
|
|
61929
|
-
init_test_runners();
|
|
61930
|
-
init_git();
|
|
61931
|
-
init_smart_runner();
|
|
61932
|
-
});
|
|
61933
|
-
|
|
61934
|
-
// src/verification/index.ts
|
|
61935
|
-
var init_verification = __esm(() => {
|
|
61936
|
-
init_executor();
|
|
61937
|
-
init_runners();
|
|
61938
|
-
init_flake_probe();
|
|
61939
|
-
init_flake_triage();
|
|
61940
|
-
init_flake_baseline_diff();
|
|
61941
|
-
init_mutation();
|
|
61942
|
-
});
|
|
61943
62180
|
|
|
61944
62181
|
// src/execution/story-orchestrator/flake-triage-seam.ts
|
|
61945
62182
|
var productionTriageSeam = async (gateFindings, { ctx, rawOutput, quarantineMemo }) => {
|
|
@@ -63036,7 +63273,7 @@ var init_story_orchestrator = __esm(() => {
|
|
|
63036
63273
|
});
|
|
63037
63274
|
|
|
63038
63275
|
// src/execution/build-plan-for-strategy.ts
|
|
63039
|
-
import { join as join52, normalize as normalize5, resolve as
|
|
63276
|
+
import { join as join52, normalize as normalize5, resolve as resolve17, sep as sep6 } from "path";
|
|
63040
63277
|
function requiresInitialRefCapture(strategy) {
|
|
63041
63278
|
return isThreeSessionStrategy(strategy);
|
|
63042
63279
|
}
|
|
@@ -63051,7 +63288,7 @@ function resolveStoryPathAnchors(ctxPackageDir, storyWorkdir) {
|
|
|
63051
63288
|
if (segments.length === 0) {
|
|
63052
63289
|
return { repoRoot: base, packageDir: base };
|
|
63053
63290
|
}
|
|
63054
|
-
const candidateRoot =
|
|
63291
|
+
const candidateRoot = resolve17(base, ...Array(segments.length).fill(".."));
|
|
63055
63292
|
if (join52(candidateRoot, normalizedRel) === base) {
|
|
63056
63293
|
return { repoRoot: candidateRoot, packageDir: base };
|
|
63057
63294
|
}
|
|
@@ -67451,19 +67688,20 @@ function h2PullToolEmptyResult(observations, threshold) {
|
|
|
67451
67688
|
const byKeyword = new Map;
|
|
67452
67689
|
for (const obs of pulls) {
|
|
67453
67690
|
const keyword = obs.payload.keyword;
|
|
67691
|
+
const site = `${obs.featureId}/${obs.storyId}`;
|
|
67454
67692
|
const existing = byKeyword.get(keyword);
|
|
67455
67693
|
if (existing) {
|
|
67456
|
-
existing.
|
|
67694
|
+
existing.sites.push(site);
|
|
67457
67695
|
} else {
|
|
67458
|
-
byKeyword.set(keyword, {
|
|
67696
|
+
byKeyword.set(keyword, { sites: [site], featureId: obs.featureId });
|
|
67459
67697
|
}
|
|
67460
67698
|
}
|
|
67461
67699
|
const proposals = [];
|
|
67462
67700
|
for (const [keyword, data] of byKeyword.entries()) {
|
|
67463
|
-
if (data.
|
|
67701
|
+
if (data.sites.length < threshold)
|
|
67464
67702
|
continue;
|
|
67465
|
-
const count = data.
|
|
67466
|
-
const unique = uniqueStoryIds(data.
|
|
67703
|
+
const count = data.sites.length;
|
|
67704
|
+
const unique = uniqueStoryIds(data.sites);
|
|
67467
67705
|
proposals.push({
|
|
67468
67706
|
id: "H2",
|
|
67469
67707
|
severity: "MED",
|
|
@@ -67480,23 +67718,24 @@ function h3RepeatedRectification(observations, threshold) {
|
|
|
67480
67718
|
const cycles = observations.filter((o) => o.kind === "rectify-cycle");
|
|
67481
67719
|
const byStory = new Map;
|
|
67482
67720
|
for (const obs of cycles) {
|
|
67483
|
-
const
|
|
67721
|
+
const key = `${obs.featureId}/${obs.storyId}`;
|
|
67722
|
+
const existing = byStory.get(key);
|
|
67484
67723
|
if (existing) {
|
|
67485
67724
|
existing.count++;
|
|
67486
67725
|
} else {
|
|
67487
|
-
byStory.set(
|
|
67726
|
+
byStory.set(key, { count: 1, featureId: obs.featureId, storyId: obs.storyId });
|
|
67488
67727
|
}
|
|
67489
67728
|
}
|
|
67490
67729
|
const proposals = [];
|
|
67491
|
-
for (const
|
|
67492
|
-
if (
|
|
67730
|
+
for (const { count, featureId, storyId } of byStory.values()) {
|
|
67731
|
+
if (count < threshold)
|
|
67493
67732
|
continue;
|
|
67494
67733
|
proposals.push({
|
|
67495
67734
|
id: "H3",
|
|
67496
67735
|
severity: "HIGH",
|
|
67497
|
-
target: { canonicalFile: `.nax/features/${
|
|
67498
|
-
description: `Repeated rectification cycle: story ${storyId} required ${
|
|
67499
|
-
evidence: `Story ${storyId} triggered ${
|
|
67736
|
+
target: { canonicalFile: `.nax/features/${featureId}/context.md`, action: "add" },
|
|
67737
|
+
description: `Repeated rectification cycle: story ${storyId} required ${count} rectify attempts`,
|
|
67738
|
+
evidence: `Story ${storyId} triggered ${count} rectify cycles`,
|
|
67500
67739
|
sourceKinds: ["rectify-cycle"],
|
|
67501
67740
|
storyIds: [storyId]
|
|
67502
67741
|
});
|
|
@@ -67508,19 +67747,20 @@ function h4EscalationChain(observations, threshold) {
|
|
|
67508
67747
|
const byPath = new Map;
|
|
67509
67748
|
for (const obs of escalations) {
|
|
67510
67749
|
const key = `${obs.payload.from}->${obs.payload.to}`;
|
|
67750
|
+
const site = `${obs.featureId}/${obs.storyId}`;
|
|
67511
67751
|
const existing = byPath.get(key);
|
|
67512
67752
|
if (existing) {
|
|
67513
|
-
existing.
|
|
67753
|
+
existing.sites.push(site);
|
|
67514
67754
|
} else {
|
|
67515
|
-
byPath.set(key, {
|
|
67755
|
+
byPath.set(key, { sites: [site], featureId: obs.featureId });
|
|
67516
67756
|
}
|
|
67517
67757
|
}
|
|
67518
67758
|
const proposals = [];
|
|
67519
67759
|
for (const [escalationPath, data] of byPath.entries()) {
|
|
67520
|
-
if (data.
|
|
67760
|
+
if (data.sites.length < threshold)
|
|
67521
67761
|
continue;
|
|
67522
|
-
const count = data.
|
|
67523
|
-
const unique = uniqueStoryIds(data.
|
|
67762
|
+
const count = data.sites.length;
|
|
67763
|
+
const unique = uniqueStoryIds(data.sites);
|
|
67524
67764
|
proposals.push({
|
|
67525
67765
|
id: "H4",
|
|
67526
67766
|
severity: "MED",
|
|
@@ -67567,15 +67807,17 @@ function h6FixCycleUnchanged(observations, threshold) {
|
|
|
67567
67807
|
const iterations = observations.filter((o) => o.kind === "fix-cycle-iteration");
|
|
67568
67808
|
const byStory = new Map;
|
|
67569
67809
|
for (const obs of iterations) {
|
|
67570
|
-
const
|
|
67810
|
+
const key = `${obs.featureId}/${obs.storyId}`;
|
|
67811
|
+
const existing = byStory.get(key);
|
|
67571
67812
|
if (existing) {
|
|
67572
67813
|
existing.push(obs);
|
|
67573
67814
|
} else {
|
|
67574
|
-
byStory.set(
|
|
67815
|
+
byStory.set(key, [obs]);
|
|
67575
67816
|
}
|
|
67576
67817
|
}
|
|
67577
67818
|
const proposals = [];
|
|
67578
|
-
for (const
|
|
67819
|
+
for (const storyIterations of byStory.values()) {
|
|
67820
|
+
const storyId = storyIterations[0].storyId;
|
|
67579
67821
|
const ordered = [...storyIterations].sort((a, b) => (a.payload.iterationNum ?? a.payload.iteration) - (b.payload.iterationNum ?? b.payload.iteration));
|
|
67580
67822
|
let currentStreak = 0;
|
|
67581
67823
|
let maxStreak = 0;
|
|
@@ -69697,6 +69939,15 @@ async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, di
|
|
|
69697
69939
|
const effectiveProjectRoot = projectRoot || projectDir;
|
|
69698
69940
|
const pluginNames = new Set;
|
|
69699
69941
|
const disabledSet = new Set(disabledPlugins ?? []);
|
|
69942
|
+
const registerLoadedPlugin = (entry) => {
|
|
69943
|
+
const existingIndex = loadedPlugins.findIndex((p) => p.plugin.name === entry.plugin.name);
|
|
69944
|
+
if (existingIndex >= 0) {
|
|
69945
|
+
loadedPlugins[existingIndex] = entry;
|
|
69946
|
+
} else {
|
|
69947
|
+
loadedPlugins.push(entry);
|
|
69948
|
+
}
|
|
69949
|
+
pluginNames.add(entry.plugin.name);
|
|
69950
|
+
};
|
|
69700
69951
|
const logger = getSafeLogger6();
|
|
69701
69952
|
if (!disabledSet.has(curatorPlugin.name)) {
|
|
69702
69953
|
if (curatorPlugin.setup) {
|
|
@@ -69784,11 +70035,10 @@ async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, di
|
|
|
69784
70035
|
if (pluginNames.has(validated.name)) {
|
|
69785
70036
|
logger?.warn("plugins", `Plugin name collision: '${validated.name}' (global directory)`);
|
|
69786
70037
|
}
|
|
69787
|
-
|
|
70038
|
+
registerLoadedPlugin({
|
|
69788
70039
|
plugin: validated,
|
|
69789
70040
|
source: { type: "global", path: plugin.path }
|
|
69790
70041
|
});
|
|
69791
|
-
pluginNames.add(validated.name);
|
|
69792
70042
|
}
|
|
69793
70043
|
}
|
|
69794
70044
|
const projectPlugins = await discoverPlugins(projectDir, isTestFileFn);
|
|
@@ -69803,11 +70053,10 @@ async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, di
|
|
|
69803
70053
|
if (pluginNames.has(validated.name)) {
|
|
69804
70054
|
logger?.warn("plugins", `Plugin name collision: '${validated.name}' (project directory overrides global)`);
|
|
69805
70055
|
}
|
|
69806
|
-
|
|
70056
|
+
registerLoadedPlugin({
|
|
69807
70057
|
plugin: validated,
|
|
69808
70058
|
source: { type: "project", path: plugin.path }
|
|
69809
70059
|
});
|
|
69810
|
-
pluginNames.add(validated.name);
|
|
69811
70060
|
}
|
|
69812
70061
|
}
|
|
69813
70062
|
for (const entry of configPlugins) {
|
|
@@ -69821,11 +70070,10 @@ async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, di
|
|
|
69821
70070
|
if (pluginNames.has(validated.name)) {
|
|
69822
70071
|
logger?.warn("plugins", `Plugin name collision: '${validated.name}' (config entry overrides previous)`);
|
|
69823
70072
|
}
|
|
69824
|
-
|
|
70073
|
+
registerLoadedPlugin({
|
|
69825
70074
|
plugin: validated,
|
|
69826
70075
|
source: { type: "config", path: entry.module }
|
|
69827
70076
|
});
|
|
69828
|
-
pluginNames.add(validated.name);
|
|
69829
70077
|
}
|
|
69830
70078
|
}
|
|
69831
70079
|
return new PluginRegistry(loadedPlugins, builtinPostRunActions);
|
|
@@ -70158,8 +70406,8 @@ var init_types10 = __esm(() => {
|
|
|
70158
70406
|
import { join as join88 } from "path";
|
|
70159
70407
|
function createDrainDeadline2(deadlineMs) {
|
|
70160
70408
|
let timeoutId;
|
|
70161
|
-
const promise2 = new Promise((
|
|
70162
|
-
timeoutId = setTimeout(() =>
|
|
70409
|
+
const promise2 = new Promise((resolve22) => {
|
|
70410
|
+
timeoutId = setTimeout(() => resolve22(""), deadlineMs);
|
|
70163
70411
|
});
|
|
70164
70412
|
return {
|
|
70165
70413
|
promise: promise2,
|
|
@@ -70279,14 +70527,17 @@ async function executeHook(hookDef, ctx, workdir) {
|
|
|
70279
70527
|
env: buildAllowedEnv({ env: env2 })
|
|
70280
70528
|
});
|
|
70281
70529
|
let timedOut = false;
|
|
70530
|
+
let killTimeoutId;
|
|
70282
70531
|
const timeoutId = setTimeout(() => {
|
|
70283
70532
|
timedOut = true;
|
|
70284
70533
|
killProcessGroup(proc.pid, "SIGTERM");
|
|
70534
|
+
killTimeoutId = setTimeout(() => killProcessGroup(proc.pid, "SIGKILL"), HOOK_KILL_GRACE_MS);
|
|
70285
70535
|
}, timeout);
|
|
70286
70536
|
const stdoutPromise = new Response(proc.stdout).text().catch(() => "");
|
|
70287
70537
|
const stderrPromise = new Response(proc.stderr).text().catch(() => "");
|
|
70288
70538
|
const exitCode = await proc.exited;
|
|
70289
70539
|
clearTimeout(timeoutId);
|
|
70540
|
+
clearTimeout(killTimeoutId);
|
|
70290
70541
|
const [stdout, stderr] = timedOut ? await (async () => {
|
|
70291
70542
|
const stdoutDrain = createDrainDeadline2(STREAM_DRAIN_TIMEOUT_MS2);
|
|
70292
70543
|
const stderrDrain = createDrainDeadline2(STREAM_DRAIN_TIMEOUT_MS2);
|
|
@@ -70339,7 +70590,7 @@ async function fireHook(config2, event, ctx, workdir) {
|
|
|
70339
70590
|
}
|
|
70340
70591
|
}
|
|
70341
70592
|
}
|
|
70342
|
-
var DEFAULT_TIMEOUT = 5000, STREAM_DRAIN_TIMEOUT_MS2 = 2000;
|
|
70593
|
+
var DEFAULT_TIMEOUT = 5000, STREAM_DRAIN_TIMEOUT_MS2 = 2000, HOOK_KILL_GRACE_MS = 5000;
|
|
70343
70594
|
var init_runner5 = __esm(() => {
|
|
70344
70595
|
init_env();
|
|
70345
70596
|
init_logger2();
|
|
@@ -70697,7 +70948,7 @@ var init_crash_recovery = __esm(() => {
|
|
|
70697
70948
|
});
|
|
70698
70949
|
|
|
70699
70950
|
// src/acceptance/import-resolution.ts
|
|
70700
|
-
import { resolve as
|
|
70951
|
+
import { resolve as resolve22, sep as sep9 } from "path";
|
|
70701
70952
|
function languageFromExtension(testFilePath) {
|
|
70702
70953
|
if (!testFilePath)
|
|
70703
70954
|
return;
|
|
@@ -70719,8 +70970,8 @@ async function resolveLanguage(opts) {
|
|
|
70719
70970
|
return "typescript";
|
|
70720
70971
|
}
|
|
70721
70972
|
async function readCapped(relPath, packageDir) {
|
|
70722
|
-
const resolvedPackageDir =
|
|
70723
|
-
const fullPath =
|
|
70973
|
+
const resolvedPackageDir = resolve22(packageDir);
|
|
70974
|
+
const fullPath = resolve22(resolvedPackageDir, relPath);
|
|
70724
70975
|
if (fullPath !== resolvedPackageDir && !fullPath.startsWith(resolvedPackageDir + sep9)) {
|
|
70725
70976
|
return null;
|
|
70726
70977
|
}
|
|
@@ -71306,7 +71557,7 @@ async function runAcceptanceLoop(ctx) {
|
|
|
71306
71557
|
const prdDirty = false;
|
|
71307
71558
|
logger?.info("acceptance", "All stories complete, running acceptance validation");
|
|
71308
71559
|
const { acceptanceStage: acceptanceStage2 } = await _runAcceptanceTestsOnceDeps.importAcceptanceStage();
|
|
71309
|
-
|
|
71560
|
+
do {
|
|
71310
71561
|
const attemptCtx = { ...ctx, acceptanceRetries };
|
|
71311
71562
|
const firstStory = prd.userStories[0];
|
|
71312
71563
|
const acceptanceContext = buildAcceptanceContext(attemptCtx, prd);
|
|
@@ -71412,7 +71663,7 @@ async function runAcceptanceLoop(ctx) {
|
|
|
71412
71663
|
const success2 = finalCheck.passed && remainingFindings.length === 0;
|
|
71413
71664
|
const failureMessages = !success2 ? finalCheck.failedACs.length > 0 ? finalCheck.failedACs : remainingFindings.length > 0 ? remainingFindings.map((f) => f.message) : ["acceptance validation failed (unknown cause)"] : undefined;
|
|
71414
71665
|
return buildResult(success2, prd, totalCost2, iterations, storiesCompleted, prdDirty, failureMessages, acceptanceRetries + totalInternalIterations, finalCheck.missingTargets);
|
|
71415
|
-
}
|
|
71666
|
+
} while (acceptanceRetries < maxRetries);
|
|
71416
71667
|
return buildResult(false, prd, totalCost2, iterations, storiesCompleted, prdDirty);
|
|
71417
71668
|
}
|
|
71418
71669
|
var _acceptanceLoopDeps, _acceptanceFixCycleDeps, _runAcceptanceTestsOnceDeps, MAX_STUB_REGENS = 2;
|
|
@@ -71441,7 +71692,7 @@ var init_acceptance_loop = __esm(() => {
|
|
|
71441
71692
|
|
|
71442
71693
|
// src/session/scratch-purge.ts
|
|
71443
71694
|
import { mkdir as mkdir12, rename as rename2, rm } from "fs/promises";
|
|
71444
|
-
import { dirname as
|
|
71695
|
+
import { dirname as dirname16, join as join89 } from "path";
|
|
71445
71696
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
71446
71697
|
const sessionsDir = join89(projectDir, ".nax", "features", featureName, "sessions");
|
|
71447
71698
|
const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
|
|
@@ -71492,7 +71743,7 @@ var init_scratch_purge = __esm(() => {
|
|
|
71492
71743
|
readFile: (path26) => Bun.file(path26).text(),
|
|
71493
71744
|
remove: (path26) => rm(path26, { recursive: true, force: true }),
|
|
71494
71745
|
move: async (src, dest) => {
|
|
71495
|
-
await mkdir12(
|
|
71746
|
+
await mkdir12(dirname16(dest), { recursive: true });
|
|
71496
71747
|
await rename2(src, dest);
|
|
71497
71748
|
},
|
|
71498
71749
|
now: () => Date.now()
|
|
@@ -71566,10 +71817,11 @@ async function runRegressionFlakeTriage(params) {
|
|
|
71566
71817
|
testCommand,
|
|
71567
71818
|
quarantineMemo,
|
|
71568
71819
|
triageFn,
|
|
71820
|
+
resolveBaselineDiffFn,
|
|
71569
71821
|
flakeDetection
|
|
71570
71822
|
} = params;
|
|
71571
71823
|
const logger = getSafeLogger();
|
|
71572
|
-
const baselineDiff = await
|
|
71824
|
+
const baselineDiff = await resolveBaselineDiffFn(config2, workdir);
|
|
71573
71825
|
if (baselineDiff === null) {
|
|
71574
71826
|
const untriaged = regressionFindings.filter((f) => f.category === "failed-test");
|
|
71575
71827
|
const testFilesInFailures2 = new Set;
|
|
@@ -71625,7 +71877,6 @@ async function runRegressionFlakeTriage(params) {
|
|
|
71625
71877
|
var init_run_regression_triage = __esm(() => {
|
|
71626
71878
|
init_logger2();
|
|
71627
71879
|
init_test_runners();
|
|
71628
|
-
init_verification();
|
|
71629
71880
|
});
|
|
71630
71881
|
|
|
71631
71882
|
// src/execution/lifecycle/run-regression.ts
|
|
@@ -71779,6 +72030,7 @@ async function runDeferredRegression(options) {
|
|
|
71779
72030
|
testCommand,
|
|
71780
72031
|
quarantineMemo,
|
|
71781
72032
|
triageFn: _regressionDeps.triageFlakyFindings,
|
|
72033
|
+
resolveBaselineDiffFn: _regressionDeps.resolveFlakeBaselineDiff,
|
|
71782
72034
|
flakeDetection: config2.execution.flakeDetection
|
|
71783
72035
|
});
|
|
71784
72036
|
if (triageOutcome.shortCircuit) {
|
|
@@ -71957,7 +72209,8 @@ var init_run_regression = __esm(() => {
|
|
|
71957
72209
|
runVerification: fullSuite,
|
|
71958
72210
|
runFixCycle: (cycle, ctx, name) => runFixCycle(cycle, ctx, name),
|
|
71959
72211
|
parseTestOutput,
|
|
71960
|
-
triageFlakyFindings
|
|
72212
|
+
triageFlakyFindings,
|
|
72213
|
+
resolveFlakeBaselineDiff
|
|
71961
72214
|
};
|
|
71962
72215
|
});
|
|
71963
72216
|
|
|
@@ -72221,6 +72474,17 @@ async function handleRunCompletion(options) {
|
|
|
72221
72474
|
logger?.warn("run.complete", "Failed to purge stale session scratch", { error: String(err) });
|
|
72222
72475
|
}
|
|
72223
72476
|
}
|
|
72477
|
+
const manifestCfg = config2.context?.v2?.manifest;
|
|
72478
|
+
if (manifestCfg?.retentionDays) {
|
|
72479
|
+
try {
|
|
72480
|
+
const purged = await _runCompletionDeps.purgeStaleManifests(effectiveProjectDir, manifestCfg.retentionDays);
|
|
72481
|
+
if (purged > 0) {
|
|
72482
|
+
logger?.info("run.complete", "Purged stale context manifests", { purged });
|
|
72483
|
+
}
|
|
72484
|
+
} catch (err) {
|
|
72485
|
+
logger?.warn("run.complete", "Failed to purge stale context manifests", { error: String(err) });
|
|
72486
|
+
}
|
|
72487
|
+
}
|
|
72224
72488
|
const storyMetricsSummary = allStoryMetrics.map((sm) => ({
|
|
72225
72489
|
storyId: sm.storyId,
|
|
72226
72490
|
complexity: sm.complexity,
|
|
@@ -72274,6 +72538,7 @@ async function handleRunCompletion(options) {
|
|
|
72274
72538
|
}
|
|
72275
72539
|
var _runCompletionDeps;
|
|
72276
72540
|
var init_run_completion = __esm(() => {
|
|
72541
|
+
init_engine();
|
|
72277
72542
|
init_pipeline();
|
|
72278
72543
|
init_agents();
|
|
72279
72544
|
init_runner5();
|
|
@@ -72289,7 +72554,8 @@ var init_run_completion = __esm(() => {
|
|
|
72289
72554
|
_runCompletionDeps = {
|
|
72290
72555
|
runDeferredRegression,
|
|
72291
72556
|
fireHook,
|
|
72292
|
-
closeAllRunSessions
|
|
72557
|
+
closeAllRunSessions,
|
|
72558
|
+
purgeStaleManifests
|
|
72293
72559
|
};
|
|
72294
72560
|
});
|
|
72295
72561
|
|
|
@@ -74636,7 +74902,18 @@ function selectNextStories(prd, config2, batchPlan, currentBatchIndex, lastStory
|
|
|
74636
74902
|
const batch = batchPlan[currentBatchIndex];
|
|
74637
74903
|
const storiesToExecute = batch.stories.filter((s) => !s.passes && s.status !== "passed" && s.status !== "skipped" && s.status !== "blocked" && s.status !== "failed" && s.status !== "paused" && s.status !== "decomposed");
|
|
74638
74904
|
if (storiesToExecute.length === 0) {
|
|
74639
|
-
|
|
74905
|
+
const fallbackStory = getNextStory(prd, lastStoryId, config2.execution.rectification?.maxAttemptsTotal ?? 12);
|
|
74906
|
+
if (!fallbackStory)
|
|
74907
|
+
return null;
|
|
74908
|
+
return {
|
|
74909
|
+
selection: {
|
|
74910
|
+
story: fallbackStory,
|
|
74911
|
+
storiesToExecute: [fallbackStory],
|
|
74912
|
+
routing: buildPreviewRouting(fallbackStory, config2),
|
|
74913
|
+
isBatchExecution: false
|
|
74914
|
+
},
|
|
74915
|
+
nextBatchIndex: currentBatchIndex + 1
|
|
74916
|
+
};
|
|
74640
74917
|
}
|
|
74641
74918
|
const story2 = storiesToExecute[0];
|
|
74642
74919
|
return {
|
|
@@ -74833,6 +75110,8 @@ async function runParallelBatch(options) {
|
|
|
74833
75110
|
const worktreeManager = await _parallelBatchDeps.createWorktreeManager();
|
|
74834
75111
|
const worktreePaths = new Map;
|
|
74835
75112
|
const storyStartTimes = new Map;
|
|
75113
|
+
const preExecutionFailures = [];
|
|
75114
|
+
const preExecutionFailureEndTimes = new Map;
|
|
74836
75115
|
for (const story of stories) {
|
|
74837
75116
|
storyStartTimes.set(story.id, Date.now());
|
|
74838
75117
|
try {
|
|
@@ -74842,7 +75121,18 @@ async function runParallelBatch(options) {
|
|
|
74842
75121
|
storyId: story.id,
|
|
74843
75122
|
error: error48 instanceof Error ? error48.message : String(error48)
|
|
74844
75123
|
});
|
|
74845
|
-
|
|
75124
|
+
preExecutionFailures.push({
|
|
75125
|
+
story,
|
|
75126
|
+
pipelineResult: {
|
|
75127
|
+
success: false,
|
|
75128
|
+
finalAction: "fail",
|
|
75129
|
+
reason: error48 instanceof Error ? error48.message : String(error48),
|
|
75130
|
+
stoppedAtStage: "worktree-create",
|
|
75131
|
+
context: { ...pipelineContext, story, stories: [story], workdir }
|
|
75132
|
+
}
|
|
75133
|
+
});
|
|
75134
|
+
preExecutionFailureEndTimes.set(story.id, Date.now());
|
|
75135
|
+
continue;
|
|
74846
75136
|
}
|
|
74847
75137
|
worktreePaths.set(story.id, path29.join(workdir, ".nax-wt", story.id));
|
|
74848
75138
|
}
|
|
@@ -74872,7 +75162,6 @@ async function runParallelBatch(options) {
|
|
|
74872
75162
|
}
|
|
74873
75163
|
const dependencyContexts = new Map;
|
|
74874
75164
|
const readyStories = [];
|
|
74875
|
-
const preExecutionFailures = [];
|
|
74876
75165
|
for (const story of stories) {
|
|
74877
75166
|
const worktreeRoot = worktreePaths.get(story.id);
|
|
74878
75167
|
if (!worktreeRoot)
|
|
@@ -74967,7 +75256,7 @@ async function runParallelBatch(options) {
|
|
|
74967
75256
|
storyEndTimes.set(story.id, batchEndMs);
|
|
74968
75257
|
}
|
|
74969
75258
|
for (const { story } of failed) {
|
|
74970
|
-
storyEndTimes.set(story.id, batchEndMs);
|
|
75259
|
+
storyEndTimes.set(story.id, preExecutionFailureEndTimes.get(story.id) ?? batchEndMs);
|
|
74971
75260
|
}
|
|
74972
75261
|
const mergeConflicts = [];
|
|
74973
75262
|
for (const conflict of workerResult.mergeConflicts) {
|
|
@@ -75238,7 +75527,7 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
75238
75527
|
});
|
|
75239
75528
|
for (const { story, pipelineResult } of batchResult.failed) {
|
|
75240
75529
|
const storyRouting = prd.userStories.find((s) => s.id === story.id)?.routing;
|
|
75241
|
-
await handlePipelineFailure({
|
|
75530
|
+
const failureResult = await handlePipelineFailure({
|
|
75242
75531
|
config: ctx.config,
|
|
75243
75532
|
prd,
|
|
75244
75533
|
prdPath: ctx.prdPath,
|
|
@@ -75267,12 +75556,13 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
75267
75556
|
runtime: ctx.runtime,
|
|
75268
75557
|
abortSignal: ctx.abortSignal
|
|
75269
75558
|
}, pipelineResult);
|
|
75559
|
+
prd = failureResult.prd;
|
|
75270
75560
|
}
|
|
75271
75561
|
reconcileBatchOutcome(prd, batchResult);
|
|
75272
75562
|
await savePRD(prd, ctx.prdPath);
|
|
75273
75563
|
await pipelineEventBus.drain();
|
|
75274
75564
|
totalCost2 += batchResult.totalCost;
|
|
75275
|
-
storiesCompleted += batchResult.completed.length;
|
|
75565
|
+
storiesCompleted += batchResult.completed.length + batchResult.mergeConflicts.filter((c) => c.rectified).length;
|
|
75276
75566
|
prdDirty = true;
|
|
75277
75567
|
if (ctx.sessionManager) {
|
|
75278
75568
|
for (const story of batchResult.completed) {
|
|
@@ -75695,7 +75985,7 @@ var init_runner_execution = __esm(() => {
|
|
|
75695
75985
|
|
|
75696
75986
|
// src/execution/status-file.ts
|
|
75697
75987
|
import { rename as rename3, unlink as unlink4 } from "fs/promises";
|
|
75698
|
-
import { resolve as
|
|
75988
|
+
import { resolve as resolve23 } from "path";
|
|
75699
75989
|
function countProgress(prd) {
|
|
75700
75990
|
const stories = prd.userStories;
|
|
75701
75991
|
const passed = stories.filter((s) => s.status === "passed").length;
|
|
@@ -75740,7 +76030,7 @@ function buildStatusSnapshot(state) {
|
|
|
75740
76030
|
return snapshot;
|
|
75741
76031
|
}
|
|
75742
76032
|
async function writeStatusFile(filePath, status) {
|
|
75743
|
-
const resolvedPath =
|
|
76033
|
+
const resolvedPath = resolve23(filePath);
|
|
75744
76034
|
if (filePath.includes("../") || filePath.includes("..\\")) {
|
|
75745
76035
|
throw new Error("Invalid status file path: path traversal detected");
|
|
75746
76036
|
}
|
|
@@ -77052,8 +77342,10 @@ async function run(options) {
|
|
|
77052
77342
|
const runId = `run-${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
77053
77343
|
const origLoadCheckpoints = _storyOrchestratorDeps.loadCheckpoints;
|
|
77054
77344
|
const origRecordGreen = _storyOrchestratorDeps.recordGreen;
|
|
77055
|
-
|
|
77056
|
-
|
|
77345
|
+
if (featureDir) {
|
|
77346
|
+
applyResumeModeDeps(featureDir, resumeMode);
|
|
77347
|
+
applyRecordGreenDeps(featureDir, runId);
|
|
77348
|
+
}
|
|
77057
77349
|
let iterations = 0;
|
|
77058
77350
|
let storiesCompleted = 0;
|
|
77059
77351
|
let totalCost2 = 0;
|
|
@@ -77740,14 +78032,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
|
|
|
77740
78032
|
prevActScopeDepth !== actScopeDepth - 1 && console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. ");
|
|
77741
78033
|
actScopeDepth = prevActScopeDepth;
|
|
77742
78034
|
}
|
|
77743
|
-
function recursivelyFlushAsyncActWork(returnValue,
|
|
78035
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve24, reject) {
|
|
77744
78036
|
var queue = ReactSharedInternals.actQueue;
|
|
77745
78037
|
if (queue !== null)
|
|
77746
78038
|
if (queue.length !== 0)
|
|
77747
78039
|
try {
|
|
77748
78040
|
flushActQueue(queue);
|
|
77749
78041
|
enqueueTask(function() {
|
|
77750
|
-
return recursivelyFlushAsyncActWork(returnValue,
|
|
78042
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve24, reject);
|
|
77751
78043
|
});
|
|
77752
78044
|
return;
|
|
77753
78045
|
} catch (error48) {
|
|
@@ -77755,7 +78047,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
|
|
|
77755
78047
|
}
|
|
77756
78048
|
else
|
|
77757
78049
|
ReactSharedInternals.actQueue = null;
|
|
77758
|
-
0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) :
|
|
78050
|
+
0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve24(returnValue);
|
|
77759
78051
|
}
|
|
77760
78052
|
function flushActQueue(queue) {
|
|
77761
78053
|
if (!isFlushing) {
|
|
@@ -77931,14 +78223,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
|
|
|
77931
78223
|
didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = true, console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"));
|
|
77932
78224
|
});
|
|
77933
78225
|
return {
|
|
77934
|
-
then: function(
|
|
78226
|
+
then: function(resolve24, reject) {
|
|
77935
78227
|
didAwaitActCall = true;
|
|
77936
78228
|
thenable.then(function(returnValue) {
|
|
77937
78229
|
popActScope(prevActQueue, prevActScopeDepth);
|
|
77938
78230
|
if (prevActScopeDepth === 0) {
|
|
77939
78231
|
try {
|
|
77940
78232
|
flushActQueue(queue), enqueueTask(function() {
|
|
77941
|
-
return recursivelyFlushAsyncActWork(returnValue,
|
|
78233
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve24, reject);
|
|
77942
78234
|
});
|
|
77943
78235
|
} catch (error$0) {
|
|
77944
78236
|
ReactSharedInternals.thrownErrors.push(error$0);
|
|
@@ -77949,7 +78241,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
|
|
|
77949
78241
|
reject(_thrownError);
|
|
77950
78242
|
}
|
|
77951
78243
|
} else
|
|
77952
|
-
|
|
78244
|
+
resolve24(returnValue);
|
|
77953
78245
|
}, function(error48) {
|
|
77954
78246
|
popActScope(prevActQueue, prevActScopeDepth);
|
|
77955
78247
|
0 < ReactSharedInternals.thrownErrors.length ? (error48 = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(error48)) : reject(error48);
|
|
@@ -77965,11 +78257,11 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
|
|
|
77965
78257
|
if (0 < ReactSharedInternals.thrownErrors.length)
|
|
77966
78258
|
throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
|
|
77967
78259
|
return {
|
|
77968
|
-
then: function(
|
|
78260
|
+
then: function(resolve24, reject) {
|
|
77969
78261
|
didAwaitActCall = true;
|
|
77970
78262
|
prevActScopeDepth === 0 ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
|
|
77971
|
-
return recursivelyFlushAsyncActWork(returnValue$jscomp$0,
|
|
77972
|
-
})) :
|
|
78263
|
+
return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve24, reject);
|
|
78264
|
+
})) : resolve24(returnValue$jscomp$0);
|
|
77973
78265
|
}
|
|
77974
78266
|
};
|
|
77975
78267
|
};
|
|
@@ -80811,8 +81103,8 @@ It can also happen if the client has a browser extension installed which messes
|
|
|
80811
81103
|
currentEntangledActionThenable = {
|
|
80812
81104
|
status: "pending",
|
|
80813
81105
|
value: undefined,
|
|
80814
|
-
then: function(
|
|
80815
|
-
entangledListeners.push(
|
|
81106
|
+
then: function(resolve24) {
|
|
81107
|
+
entangledListeners.push(resolve24);
|
|
80816
81108
|
}
|
|
80817
81109
|
};
|
|
80818
81110
|
}
|
|
@@ -80836,8 +81128,8 @@ It can also happen if the client has a browser extension installed which messes
|
|
|
80836
81128
|
status: "pending",
|
|
80837
81129
|
value: null,
|
|
80838
81130
|
reason: null,
|
|
80839
|
-
then: function(
|
|
80840
|
-
listeners.push(
|
|
81131
|
+
then: function(resolve24) {
|
|
81132
|
+
listeners.push(resolve24);
|
|
80841
81133
|
}
|
|
80842
81134
|
};
|
|
80843
81135
|
thenable.then(function() {
|
|
@@ -107009,7 +107301,7 @@ __export(exports_curator, {
|
|
|
107009
107301
|
});
|
|
107010
107302
|
import { readdirSync as readdirSync9 } from "fs";
|
|
107011
107303
|
import { unlink as unlink6 } from "fs/promises";
|
|
107012
|
-
import { join as join104, resolve as
|
|
107304
|
+
import { join as join104, resolve as resolve24, sep as sep10 } from "path";
|
|
107013
107305
|
function listRunIds(runsDir) {
|
|
107014
107306
|
try {
|
|
107015
107307
|
return readdirSync9(runsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
@@ -107127,15 +107419,15 @@ async function curatorStatus(options) {
|
|
|
107127
107419
|
}
|
|
107128
107420
|
}
|
|
107129
107421
|
function resolveCanonicalTargetPath(projectDir, canonicalFile) {
|
|
107130
|
-
const target =
|
|
107131
|
-
const root =
|
|
107422
|
+
const target = resolve24(projectDir, canonicalFile);
|
|
107423
|
+
const root = resolve24(projectDir);
|
|
107132
107424
|
if (target !== root && !target.startsWith(root + sep10))
|
|
107133
107425
|
return null;
|
|
107134
107426
|
const relative18 = target.slice(root.length + 1).replaceAll(sep10, "/");
|
|
107135
107427
|
return CURATOR_TARGET_SHAPES.some((shape) => shape.test(relative18)) ? target : null;
|
|
107136
107428
|
}
|
|
107137
107429
|
function isWithinCanonicalRulesDir(projectDir, targetPath) {
|
|
107138
|
-
const rulesRoot =
|
|
107430
|
+
const rulesRoot = resolve24(projectDir, CANONICAL_RULES_DIR);
|
|
107139
107431
|
return targetPath === rulesRoot || targetPath.startsWith(rulesRoot + sep10);
|
|
107140
107432
|
}
|
|
107141
107433
|
async function curatorCommit(options) {
|
|
@@ -108076,6 +108368,12 @@ async function parseRunLog(logPath) {
|
|
|
108076
108368
|
return [];
|
|
108077
108369
|
}
|
|
108078
108370
|
}
|
|
108371
|
+
function findRunStart(entries) {
|
|
108372
|
+
return entries.find((e) => e.stage === "run.start");
|
|
108373
|
+
}
|
|
108374
|
+
function findRunSummary(entries) {
|
|
108375
|
+
return entries.find((e) => e.stage === "run.complete" && e.data?.totalStories !== undefined);
|
|
108376
|
+
}
|
|
108079
108377
|
async function runsListCommand(options) {
|
|
108080
108378
|
const logger = getLogger();
|
|
108081
108379
|
const { feature, workdir } = options;
|
|
@@ -108094,8 +108392,8 @@ async function runsListCommand(options) {
|
|
|
108094
108392
|
for (const file3 of files.sort().reverse()) {
|
|
108095
108393
|
const logPath = join48(runsDir, file3);
|
|
108096
108394
|
const entries = await parseRunLog(logPath);
|
|
108097
|
-
const startEvent = entries
|
|
108098
|
-
const completeEvent = entries
|
|
108395
|
+
const startEvent = findRunStart(entries);
|
|
108396
|
+
const completeEvent = findRunSummary(entries);
|
|
108099
108397
|
if (!startEvent) {
|
|
108100
108398
|
logger.warn("cli", "Run log missing run.start event", { file: file3 });
|
|
108101
108399
|
continue;
|
|
@@ -108125,8 +108423,8 @@ async function runsShowCommand(options) {
|
|
|
108125
108423
|
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
108126
108424
|
}
|
|
108127
108425
|
const entries = await parseRunLog(logPath);
|
|
108128
|
-
const startEvent = entries
|
|
108129
|
-
const completeEvent = entries
|
|
108426
|
+
const startEvent = findRunStart(entries);
|
|
108427
|
+
const completeEvent = findRunSummary(entries);
|
|
108130
108428
|
const storyEvents = entries.filter((e) => e.stage === "execution" && e.data?.storyId);
|
|
108131
108429
|
if (!startEvent) {
|
|
108132
108430
|
logger.error("cli", "Run log missing run.start event", { runId });
|
|
@@ -108966,6 +109264,7 @@ async function profileUseCommand(profileName, startDir) {
|
|
|
108966
109264
|
await Bun.write(configPath, JSON.stringify(rest, null, 2));
|
|
108967
109265
|
return "Profile reset to default.";
|
|
108968
109266
|
}
|
|
109267
|
+
await loadProfile(profileName, startDir);
|
|
108969
109268
|
const updated = { ...existing, profile: profileName };
|
|
108970
109269
|
await Bun.write(configPath, JSON.stringify(updated, null, 2));
|
|
108971
109270
|
return `Now using profile: ${profileName}`;
|
|
@@ -109098,7 +109397,7 @@ init_canonical_loader();
|
|
|
109098
109397
|
init_errors();
|
|
109099
109398
|
init_logger2();
|
|
109100
109399
|
import { mkdir as mkdir11 } from "fs/promises";
|
|
109101
|
-
import { join as join76, resolve as
|
|
109400
|
+
import { join as join76, resolve as resolve21, sep as sep8 } from "path";
|
|
109102
109401
|
|
|
109103
109402
|
// src/cli/rules-lint.ts
|
|
109104
109403
|
init_engine();
|
|
@@ -109250,13 +109549,13 @@ import { basename as basename15, join as join75 } from "path";
|
|
|
109250
109549
|
|
|
109251
109550
|
// src/cli/rules-migrate-plan.ts
|
|
109252
109551
|
init_errors();
|
|
109253
|
-
import { resolve as
|
|
109552
|
+
import { resolve as resolve20, sep as sep7 } from "path";
|
|
109254
109553
|
async function planMigration(sources, options) {
|
|
109255
109554
|
const writes = [];
|
|
109256
109555
|
const skips = [];
|
|
109257
|
-
const resolvedTargetDir =
|
|
109556
|
+
const resolvedTargetDir = resolve20(options.targetDir);
|
|
109258
109557
|
for (const source of sources) {
|
|
109259
|
-
const resolvedTargetPath =
|
|
109558
|
+
const resolvedTargetPath = resolve20(source.targetPath);
|
|
109260
109559
|
if (!resolvedTargetPath.startsWith(`${resolvedTargetDir}${sep7}`) && resolvedTargetPath !== resolvedTargetDir) {
|
|
109261
109560
|
throw new NaxError(`Migration target escapes ${options.targetDir}: ${source.targetFileName} -> ${source.targetPath}`, "RULES_MIGRATE_TARGET_ESCAPE", { stage: "rules-migrate-plan", targetDir: options.targetDir, entry: source.targetPath });
|
|
109262
109561
|
}
|
|
@@ -109469,8 +109768,8 @@ async function exportRuleDirectory(input) {
|
|
|
109469
109768
|
const drifted = [];
|
|
109470
109769
|
for (const rule of rules) {
|
|
109471
109770
|
const rel = rule.path ?? rule.fileName;
|
|
109472
|
-
const target =
|
|
109473
|
-
if (!target.startsWith(`${
|
|
109771
|
+
const target = resolve21(workdir, ruleDir, rel);
|
|
109772
|
+
if (!target.startsWith(`${resolve21(workdir, ruleDir)}${sep8}`)) {
|
|
109474
109773
|
throw new NaxError(`Rule path escapes ${ruleDir}: ${rel}`, "RULES_EXPORT_PATH_ESCAPE", {
|
|
109475
109774
|
stage: "rules-export",
|
|
109476
109775
|
rule: rel
|
|
@@ -109490,9 +109789,9 @@ async function exportRuleDirectory(input) {
|
|
|
109490
109789
|
}
|
|
109491
109790
|
await _rulesCLIDeps.writeFile(target, content);
|
|
109492
109791
|
}
|
|
109493
|
-
const expected = new Set(rules.map((r) =>
|
|
109792
|
+
const expected = new Set(rules.map((r) => resolve21(workdir, ruleDir, r.path ?? r.fileName)));
|
|
109494
109793
|
for (const existing of _rulesCLIDeps.globInDir(join76(workdir, ruleDir))) {
|
|
109495
|
-
if (!expected.has(
|
|
109794
|
+
if (!expected.has(resolve21(existing))) {
|
|
109496
109795
|
_rulesCLIDeps.getLogger().warn("rules-export", "Generated rules dir contains a file with no canonical source", {
|
|
109497
109796
|
file: existing,
|
|
109498
109797
|
hint: `Delete it, or add the rule to ${CANONICAL_RULES_DIR}/`
|
|
@@ -110166,17 +110465,29 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
110166
110465
|
} catch {
|
|
110167
110466
|
throw new Error(`Run not found in registry: ${runId}`);
|
|
110168
110467
|
}
|
|
110169
|
-
let
|
|
110468
|
+
let exactMatch = null;
|
|
110469
|
+
const prefixMatches = [];
|
|
110170
110470
|
for (const entry of entries) {
|
|
110171
110471
|
const metaPath = join81(runsDir, entry, "meta.json");
|
|
110172
110472
|
try {
|
|
110173
110473
|
const meta3 = await Bun.file(metaPath).json();
|
|
110174
|
-
if (meta3.runId === runId
|
|
110175
|
-
|
|
110474
|
+
if (meta3.runId === runId) {
|
|
110475
|
+
exactMatch = meta3;
|
|
110176
110476
|
break;
|
|
110177
110477
|
}
|
|
110478
|
+
if (meta3.runId.startsWith(runId)) {
|
|
110479
|
+
prefixMatches.push(meta3);
|
|
110480
|
+
}
|
|
110178
110481
|
} catch {}
|
|
110179
110482
|
}
|
|
110483
|
+
let matched = exactMatch;
|
|
110484
|
+
if (!matched) {
|
|
110485
|
+
if (prefixMatches.length > 1) {
|
|
110486
|
+
const candidates = prefixMatches.map((m) => m.runId).join(", ");
|
|
110487
|
+
throw new Error(`Ambiguous run ID "${runId}" matches multiple runs: ${candidates}`);
|
|
110488
|
+
}
|
|
110489
|
+
matched = prefixMatches[0] ?? null;
|
|
110490
|
+
}
|
|
110180
110491
|
if (!matched) {
|
|
110181
110492
|
throw new Error(`Run not found in registry: ${runId}`);
|
|
110182
110493
|
}
|
|
@@ -110398,13 +110709,7 @@ async function logsCommand(options) {
|
|
|
110398
110709
|
}
|
|
110399
110710
|
const resolved = resolveProject2({ dir: options.dir });
|
|
110400
110711
|
const naxDir = join83(resolved.projectDir, ".nax");
|
|
110401
|
-
const
|
|
110402
|
-
const configFile = Bun.file(configPath);
|
|
110403
|
-
const config2 = await configFile.json();
|
|
110404
|
-
const featureName = config2.feature;
|
|
110405
|
-
if (!featureName) {
|
|
110406
|
-
throw new Error("No feature specified in config.json");
|
|
110407
|
-
}
|
|
110712
|
+
const featureName = resolveSingleFeature(naxDir, "pass -r <runId> (see `nax runs list`)");
|
|
110408
110713
|
const featureDir = join83(naxDir, "features", featureName);
|
|
110409
110714
|
const runsDir = join83(featureDir, "runs");
|
|
110410
110715
|
if (!existsSync30(runsDir)) {
|
|
@@ -110444,17 +110749,16 @@ async function precheckCommand(options) {
|
|
|
110444
110749
|
const result2 = await runEnvironmentPrecheck(config3, resolved.projectDir, { format });
|
|
110445
110750
|
process.exit(result2.passed ? EXIT_CODES.SUCCESS : EXIT_CODES.BLOCKER);
|
|
110446
110751
|
}
|
|
110752
|
+
const naxDir = join84(resolved.projectDir, ".nax");
|
|
110447
110753
|
let featureName = options.feature;
|
|
110448
110754
|
if (!featureName) {
|
|
110449
|
-
|
|
110450
|
-
|
|
110451
|
-
|
|
110452
|
-
|
|
110453
|
-
console.error(source_default.red("No feature specified. Use -f flag or set feature in config.json"));
|
|
110755
|
+
try {
|
|
110756
|
+
featureName = resolveSingleFeature(naxDir);
|
|
110757
|
+
} catch (err) {
|
|
110758
|
+
console.error(source_default.red(err instanceof Error ? err.message : String(err)));
|
|
110454
110759
|
process.exit(1);
|
|
110455
110760
|
}
|
|
110456
110761
|
}
|
|
110457
|
-
const naxDir = join84(resolved.projectDir, ".nax");
|
|
110458
110762
|
const featureDir = join84(naxDir, "features", featureName);
|
|
110459
110763
|
const prdPath = join84(featureDir, "prd.json");
|
|
110460
110764
|
if (!existsSync31(featureDir)) {
|
|
@@ -110478,7 +110782,7 @@ async function precheckCommand(options) {
|
|
|
110478
110782
|
// src/commands/replay.ts
|
|
110479
110783
|
init_errors();
|
|
110480
110784
|
import { existsSync as existsSync32 } from "fs";
|
|
110481
|
-
import { dirname as
|
|
110785
|
+
import { dirname as dirname15 } from "path";
|
|
110482
110786
|
|
|
110483
110787
|
// src/replay/discovery.ts
|
|
110484
110788
|
init_errors();
|
|
@@ -110791,7 +111095,7 @@ async function readJsonOrUndefined(path25) {
|
|
|
110791
111095
|
}
|
|
110792
111096
|
async function readMetricsFromProject(meta3) {
|
|
110793
111097
|
const { loadRunMetrics: loadRunMetrics2 } = await Promise.resolve().then(() => (init_tracker(), exports_tracker));
|
|
110794
|
-
const outputDir =
|
|
111098
|
+
const outputDir = dirname15(dirname15(dirname15(meta3.eventsDir)));
|
|
110795
111099
|
const all = await loadRunMetrics2(outputDir);
|
|
110796
111100
|
return all.find((m) => m.runId === meta3.runId);
|
|
110797
111101
|
}
|
|
@@ -110924,10 +111228,11 @@ function registerResumeCommand(program2) {
|
|
|
110924
111228
|
const { findProjectDir: findProjectDir2 } = await Promise.resolve().then(() => (init_config(), exports_config));
|
|
110925
111229
|
const { run: run2 } = await Promise.resolve().then(() => (init_execution2(), exports_execution));
|
|
110926
111230
|
const { applyResumeModeDeps: applyResumeModeDeps2 } = await Promise.resolve().then(() => (init_checkpoint(), exports_checkpoint));
|
|
110927
|
-
const { existsSync: existsSync38 } = await import("fs");
|
|
111231
|
+
const { existsSync: existsSync38, mkdirSync: mkdirSync8 } = await import("fs");
|
|
110928
111232
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
|
|
110929
111233
|
const { loadPRD: loadPRD2 } = await Promise.resolve().then(() => (init_prd(), exports_prd));
|
|
110930
111234
|
const { loadHooksConfig: loadHooksConfig2 } = await Promise.resolve().then(() => (init_hooks(), exports_hooks));
|
|
111235
|
+
const { initLogger: initLogger2 } = await Promise.resolve().then(() => (init_logger2(), exports_logger));
|
|
110931
111236
|
const naxDir = findProjectDir2(cmdOpts.dir);
|
|
110932
111237
|
if (!naxDir) {
|
|
110933
111238
|
process.stderr.write(`nax not initialized. Run: nax init
|
|
@@ -110952,6 +111257,11 @@ function registerResumeCommand(program2) {
|
|
|
110952
111257
|
const projectKey = config2.name?.trim() || basename19(cmdOpts.dir);
|
|
110953
111258
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
110954
111259
|
const statusFilePath = join99(outputDir, "status.json");
|
|
111260
|
+
const runsDir = join99(outputDir, "features", feature, "runs");
|
|
111261
|
+
mkdirSync8(runsDir, { recursive: true });
|
|
111262
|
+
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
111263
|
+
const logFilePath = join99(runsDir, `${runId}.jsonl`);
|
|
111264
|
+
initLogger2({ level: "info", filePath: logFilePath, useChalk: true, headless: true, suppressConsole: false });
|
|
110955
111265
|
const result = await run2({
|
|
110956
111266
|
prdPath,
|
|
110957
111267
|
workdir: cmdOpts.dir,
|
|
@@ -110962,7 +111272,7 @@ function registerResumeCommand(program2) {
|
|
|
110962
111272
|
dryRun: false,
|
|
110963
111273
|
useBatch: true,
|
|
110964
111274
|
statusFile: statusFilePath,
|
|
110965
|
-
logFilePath
|
|
111275
|
+
logFilePath,
|
|
110966
111276
|
formatterMode: "normal",
|
|
110967
111277
|
headless: true,
|
|
110968
111278
|
skipPrecheck: false,
|
|
@@ -116785,8 +117095,8 @@ class Ink {
|
|
|
116785
117095
|
}
|
|
116786
117096
|
}
|
|
116787
117097
|
async waitUntilExit() {
|
|
116788
|
-
this.exitPromise ||= new Promise((
|
|
116789
|
-
this.resolveExitPromise =
|
|
117098
|
+
this.exitPromise ||= new Promise((resolve24, reject2) => {
|
|
117099
|
+
this.resolveExitPromise = resolve24;
|
|
116790
117100
|
this.rejectExitPromise = reject2;
|
|
116791
117101
|
});
|
|
116792
117102
|
if (!this.beforeExitHandler) {
|
|
@@ -118571,15 +118881,17 @@ function usePipelineBusEvents(initialStories) {
|
|
|
118571
118881
|
});
|
|
118572
118882
|
const unsubCompleted = pipelineEventBus.on("story:completed", (event) => {
|
|
118573
118883
|
setState((prev) => {
|
|
118884
|
+
const prevStoryCost = prev.stories.find((s) => s.story.id === event.storyId)?.cost ?? 0;
|
|
118885
|
+
const costDelta = event.cost ?? 0;
|
|
118886
|
+
const storyCost = prevStoryCost + costDelta;
|
|
118887
|
+
const totalCost2 = prev.totalCost + costDelta;
|
|
118574
118888
|
const newStories = prev.stories.map((s) => {
|
|
118575
118889
|
if (s.story.id === event.storyId) {
|
|
118576
118890
|
const status = event.passed ? "passed" : "failed";
|
|
118577
|
-
const storyCost = event.cost ?? s.cost;
|
|
118578
118891
|
return { ...s, status, cost: storyCost };
|
|
118579
118892
|
}
|
|
118580
118893
|
return s;
|
|
118581
118894
|
});
|
|
118582
|
-
const totalCost2 = newStories.reduce((sum2, s) => sum2 + (s.cost ?? 0), 0);
|
|
118583
118895
|
const { [event.storyId]: _removed, ...remainingSteps } = prev.storySteps;
|
|
118584
118896
|
const lastFailedStoryId = event.passed ? prev.lastFailedStoryId : event.storyId;
|
|
118585
118897
|
return { ...prev, stories: newStories, totalCost: totalCost2, storySteps: remainingSteps, lastFailedStoryId };
|
|
@@ -118814,7 +119126,7 @@ function App2({ feature, version: version2, stories: initialStories, events, que
|
|
|
118814
119126
|
break;
|
|
118815
119127
|
}
|
|
118816
119128
|
};
|
|
118817
|
-
use_input_default((input) => {
|
|
119129
|
+
use_input_default((input, key) => {
|
|
118818
119130
|
if (showQuitConfirm || showAbortConfirm) {
|
|
118819
119131
|
const inputKey = input.toLowerCase();
|
|
118820
119132
|
if (inputKey === "y") {
|
|
@@ -118824,7 +119136,7 @@ function App2({ feature, version: version2, stories: initialStories, events, que
|
|
|
118824
119136
|
writeQueueCommand(queueFilePath, { type: "ABORT" });
|
|
118825
119137
|
setShowAbortConfirm(false);
|
|
118826
119138
|
}
|
|
118827
|
-
} else if (inputKey === "n" ||
|
|
119139
|
+
} else if (inputKey === "n" || key.escape) {
|
|
118828
119140
|
setShowQuitConfirm(false);
|
|
118829
119141
|
setShowAbortConfirm(false);
|
|
118830
119142
|
}
|
|
@@ -119051,7 +119363,7 @@ async function promptForConfirmation(question) {
|
|
|
119051
119363
|
if (!process.stdin.isTTY) {
|
|
119052
119364
|
return true;
|
|
119053
119365
|
}
|
|
119054
|
-
return new Promise((
|
|
119366
|
+
return new Promise((resolve25) => {
|
|
119055
119367
|
process.stdout.write(source_default.bold(`${question} [Y/n] `));
|
|
119056
119368
|
process.stdin.setRawMode(true);
|
|
119057
119369
|
process.stdin.resume();
|
|
@@ -119060,13 +119372,17 @@ async function promptForConfirmation(question) {
|
|
|
119060
119372
|
process.stdin.setRawMode(false);
|
|
119061
119373
|
process.stdin.pause();
|
|
119062
119374
|
process.stdin.removeListener("data", handler);
|
|
119063
|
-
const answer = char.toLowerCase();
|
|
119064
119375
|
process.stdout.write(`
|
|
119065
119376
|
`);
|
|
119377
|
+
if (char === "\x03") {
|
|
119378
|
+
resolve25(false);
|
|
119379
|
+
process.exit(130);
|
|
119380
|
+
}
|
|
119381
|
+
const answer = char.toLowerCase();
|
|
119066
119382
|
if (answer === "n") {
|
|
119067
|
-
|
|
119383
|
+
resolve25(false);
|
|
119068
119384
|
} else {
|
|
119069
|
-
|
|
119385
|
+
resolve25(true);
|
|
119070
119386
|
}
|
|
119071
119387
|
};
|
|
119072
119388
|
process.stdin.on("data", handler);
|
|
@@ -119245,6 +119561,12 @@ program2.command("setup").description("Analyze repo and generate .nax/config.jso
|
|
|
119245
119561
|
process.exit(exitCode);
|
|
119246
119562
|
});
|
|
119247
119563
|
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", "20").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 (0=auto, 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("--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) => {
|
|
119564
|
+
try {
|
|
119565
|
+
validateFeatureName(options.feature);
|
|
119566
|
+
} catch (err) {
|
|
119567
|
+
console.error(source_default.red(`Invalid feature name: ${err.message}`));
|
|
119568
|
+
process.exit(1);
|
|
119569
|
+
}
|
|
119248
119570
|
let workdir;
|
|
119249
119571
|
try {
|
|
119250
119572
|
workdir = validateDirectory(options.dir);
|
|
@@ -119338,6 +119660,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
119338
119660
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
119339
119661
|
process.exit(1);
|
|
119340
119662
|
}
|
|
119663
|
+
const projectRoot = join105(naxDir, "..");
|
|
119341
119664
|
const featureDir = join105(naxDir, "features", options.feature);
|
|
119342
119665
|
const prdPath = join105(featureDir, "prd.json");
|
|
119343
119666
|
if (options.plan && options.from) {
|
|
@@ -119368,7 +119691,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
119368
119691
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
119369
119692
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
119370
119693
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
119371
|
-
const planResult = await planCommand(
|
|
119694
|
+
const planResult = await planCommand(projectRoot, config2, {
|
|
119372
119695
|
from: options.from,
|
|
119373
119696
|
feature: options.feature,
|
|
119374
119697
|
auto: options.oneShot ?? false,
|
|
@@ -119432,7 +119755,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
119432
119755
|
config2.agent ??= {};
|
|
119433
119756
|
config2.agent.default = options.agent;
|
|
119434
119757
|
}
|
|
119435
|
-
|
|
119758
|
+
const maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
119759
|
+
if (!Number.isFinite(maxIterations) || maxIterations < 1) {
|
|
119760
|
+
console.error(source_default.red("--max-iterations must be a positive integer"));
|
|
119761
|
+
process.exit(1);
|
|
119762
|
+
}
|
|
119763
|
+
config2.execution.maxIterations = maxIterations;
|
|
119436
119764
|
if (options.maxCost !== undefined) {
|
|
119437
119765
|
const maxCost = Number(options.maxCost);
|
|
119438
119766
|
if (!Number.isFinite(maxCost) || maxCost <= 0) {
|
|
@@ -119509,25 +119837,32 @@ Scheduled run cancelled.`));
|
|
|
119509
119837
|
const exitOutcome = typeof bakeoffResult?.outcome === "number" ? bakeoffResult.outcome : 0;
|
|
119510
119838
|
process.exit(exitOutcome);
|
|
119511
119839
|
}
|
|
119512
|
-
|
|
119513
|
-
|
|
119514
|
-
|
|
119515
|
-
|
|
119516
|
-
|
|
119517
|
-
|
|
119518
|
-
|
|
119519
|
-
|
|
119520
|
-
|
|
119521
|
-
|
|
119522
|
-
|
|
119523
|
-
|
|
119524
|
-
|
|
119525
|
-
|
|
119526
|
-
|
|
119527
|
-
|
|
119528
|
-
|
|
119529
|
-
|
|
119530
|
-
|
|
119840
|
+
let result2;
|
|
119841
|
+
try {
|
|
119842
|
+
result2 = await run({
|
|
119843
|
+
prdPath,
|
|
119844
|
+
workdir,
|
|
119845
|
+
config: config2,
|
|
119846
|
+
hooks,
|
|
119847
|
+
feature: options.feature,
|
|
119848
|
+
featureDir,
|
|
119849
|
+
dryRun: options.dryRun,
|
|
119850
|
+
useBatch: options.batch ?? true,
|
|
119851
|
+
parallel,
|
|
119852
|
+
eventEmitter,
|
|
119853
|
+
statusFile: statusFilePath,
|
|
119854
|
+
logFilePath,
|
|
119855
|
+
formatterMode: useHeadless ? formatterMode : undefined,
|
|
119856
|
+
headless: useHeadless,
|
|
119857
|
+
skipPrecheck: options.skipPrecheck ?? false,
|
|
119858
|
+
agentStreamEvents,
|
|
119859
|
+
resumeMode: options.fresh === true || options.resume === false ? "fresh" : "auto"
|
|
119860
|
+
});
|
|
119861
|
+
} finally {
|
|
119862
|
+
if (tuiInstance) {
|
|
119863
|
+
tuiInstance.unmount();
|
|
119864
|
+
}
|
|
119865
|
+
}
|
|
119531
119866
|
const latestSymlink = join105(runsDir, "latest.jsonl");
|
|
119532
119867
|
try {
|
|
119533
119868
|
if (existsSync39(latestSymlink)) {
|
|
@@ -119539,9 +119874,6 @@ Scheduled run cancelled.`));
|
|
|
119539
119874
|
} catch (error48) {
|
|
119540
119875
|
console.error(source_default.yellow(`Warning: Failed to create latest.jsonl symlink: ${error48}`));
|
|
119541
119876
|
}
|
|
119542
|
-
if (tuiInstance) {
|
|
119543
|
-
tuiInstance.unmount();
|
|
119544
|
-
}
|
|
119545
119877
|
if (useHeadless) {
|
|
119546
119878
|
console.log(source_default.dim(`
|
|
119547
119879
|
\u2500\u2500 Summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
|
|
@@ -119613,6 +119945,12 @@ function printHumanReadable(result2) {
|
|
|
119613
119945
|
}
|
|
119614
119946
|
var features = program2.command("features").description("Manage features");
|
|
119615
119947
|
features.command("create <name>").description("Create a new feature").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (name, options) => {
|
|
119948
|
+
try {
|
|
119949
|
+
validateFeatureName(name);
|
|
119950
|
+
} catch (err) {
|
|
119951
|
+
console.error(source_default.red(`Invalid feature name: ${err.message}`));
|
|
119952
|
+
process.exit(1);
|
|
119953
|
+
}
|
|
119616
119954
|
let workdir;
|
|
119617
119955
|
try {
|
|
119618
119956
|
workdir = validateDirectory(options.dir);
|
|
@@ -119768,12 +120106,13 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
119768
120106
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
119769
120107
|
process.exit(1);
|
|
119770
120108
|
}
|
|
120109
|
+
const projectRoot = join105(naxDir, "..");
|
|
119771
120110
|
const cliOverrides = {};
|
|
119772
120111
|
const cliProfiles = options.profile ?? [];
|
|
119773
120112
|
if (cliProfiles.length > 0) {
|
|
119774
120113
|
cliOverrides.profile = cliProfiles;
|
|
119775
120114
|
}
|
|
119776
|
-
const config2 = await loadConfig(
|
|
120115
|
+
const config2 = await loadConfig(projectRoot, cliOverrides);
|
|
119777
120116
|
const featureLogDir = join105(naxDir, "features", options.feature, "plan");
|
|
119778
120117
|
mkdirSync8(featureLogDir, { recursive: true });
|
|
119779
120118
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
@@ -119782,7 +120121,7 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
119782
120121
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
119783
120122
|
try {
|
|
119784
120123
|
if (options.decompose) {
|
|
119785
|
-
await planDecomposeCommand(
|
|
120124
|
+
await planDecomposeCommand(projectRoot, config2, {
|
|
119786
120125
|
feature: options.feature,
|
|
119787
120126
|
storyId: options.decompose
|
|
119788
120127
|
});
|
|
@@ -119794,7 +120133,7 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
119794
120133
|
console.error(source_default.red("Error: --from <spec-path> is required unless --decompose is used"));
|
|
119795
120134
|
process.exit(1);
|
|
119796
120135
|
}
|
|
119797
|
-
const planResult = await planCommand(
|
|
120136
|
+
const planResult = await planCommand(projectRoot, config2, {
|
|
119798
120137
|
from: options.from,
|
|
119799
120138
|
feature: options.feature,
|
|
119800
120139
|
auto: options.auto || options.oneShot,
|
|
@@ -119990,6 +120329,7 @@ runs.command("list").description("List all runs for a feature").requiredOption("
|
|
|
119990
120329
|
console.error(source_default.red(`Invalid directory: ${err.message}`));
|
|
119991
120330
|
process.exit(1);
|
|
119992
120331
|
}
|
|
120332
|
+
initLogger({ level: "info", useChalk: true });
|
|
119993
120333
|
await runsListCommand({ feature: options.feature, workdir });
|
|
119994
120334
|
});
|
|
119995
120335
|
runs.command("show <run-id>").description("Show detailed information for a specific run").requiredOption("-f, --feature <name>", "Feature name").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (runId, options) => {
|
|
@@ -120000,6 +120340,7 @@ runs.command("show <run-id>").description("Show detailed information for a speci
|
|
|
120000
120340
|
console.error(source_default.red(`Invalid directory: ${err.message}`));
|
|
120001
120341
|
process.exit(1);
|
|
120002
120342
|
}
|
|
120343
|
+
initLogger({ level: "info", useChalk: true });
|
|
120003
120344
|
await runsShowCommand({ runId, feature: options.feature, workdir });
|
|
120004
120345
|
});
|
|
120005
120346
|
registerReplayCommand(program2);
|