@nathapp/nax 0.80.0-canary.4 → 0.80.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 +805 -460
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -18428,19 +18428,23 @@ function redactValue(input, seen = new WeakSet, depth = 0) {
|
|
|
18428
18428
|
}
|
|
18429
18429
|
var SECRET_KEY_PATTERN, SECRET_VALUE_PATTERNS, REDACTED = "[REDACTED]", MAX_REDACT_DEPTH = 100, CIRCULAR_REF_MARKER = "[Circular]";
|
|
18430
18430
|
var init_redact = __esm(() => {
|
|
18431
|
-
SECRET_KEY_PATTERN = /(SECRET|TOKEN(?!s\b)|API_?KEY|PASSWORD|PRIVATE_?KEY|ACCESS_?KEY|WEBHOOK)/i;
|
|
18431
|
+
SECRET_KEY_PATTERN = /(SECRET|TOKEN(?!s\b)|API_?KEY|PASSWORD|PRIVATE_?KEY|ACCESS_?KEY|WEBHOOK|(?:\w+)?_URL|\w+_URI|\w+_DSN|CONNECTION\s*STRING)/i;
|
|
18432
18432
|
SECRET_VALUE_PATTERNS = [
|
|
18433
18433
|
/sk-[A-Za-z0-9_-]{16,}/g,
|
|
18434
18434
|
/ghp_[A-Za-z0-9]{16,}/g,
|
|
18435
18435
|
/gh[opsu]_[A-Za-z0-9]{16,}/g,
|
|
18436
|
+
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
18436
18437
|
/npm_[A-Za-z0-9]{8,}/g,
|
|
18437
18438
|
/AKIA[0-9A-Z]{16}/g,
|
|
18438
18439
|
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
18440
|
+
/\b\d{6,}:[A-Za-z0-9_-]{30,}\b/g,
|
|
18439
18441
|
/(?:SECRET|TOKEN|API_?KEY|PASSWORD|PRIVATE_?KEY|ACCESS_?KEY|WEBHOOK)=[^\s"',]+/gi,
|
|
18440
18442
|
/-----BEGIN [A-Z ]*(?:PRIVATE KEY|CERTIFICATE)(?: BLOCK)?-----[\s\S]{0,65536}?-----END [A-Z ]*(?:PRIVATE KEY|CERTIFICATE)(?: BLOCK)?-----/g,
|
|
18441
18443
|
/eyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}/g,
|
|
18442
|
-
/\
|
|
18443
|
-
|
|
18444
|
+
/\bBearer\s+(?=[A-Za-z0-9\-._~+/]*[0-9+/_-])[A-Za-z0-9\-._~+/]{8,}={0,2}/gi,
|
|
18445
|
+
/\b[Bb][Aa][Ss][Ii][Cc]\s+(?=[A-Za-z0-9+/]*[A-Z])(?=[A-Za-z0-9+/]*[a-z])[A-Za-z0-9+/]{8,}={0,2}/g,
|
|
18446
|
+
/(?:x-api-key|api[_-]?key)\s*[:=]\s*[^\s"',]+/gi,
|
|
18447
|
+
/\b[a-z][a-z0-9+.-]*:\/\/(?:[^/\s@]*:[^/\s@]+)@/gi
|
|
18444
18448
|
];
|
|
18445
18449
|
});
|
|
18446
18450
|
|
|
@@ -19122,23 +19126,66 @@ var init_config_guards = __esm(() => {
|
|
|
19122
19126
|
});
|
|
19123
19127
|
|
|
19124
19128
|
// src/config/dotenv.ts
|
|
19129
|
+
function parseQuotedValue(raw, quote) {
|
|
19130
|
+
let value = "";
|
|
19131
|
+
for (let i = 1;i < raw.length; i++) {
|
|
19132
|
+
const c = raw[i];
|
|
19133
|
+
if (quote === '"' && c === "\\" && i + 1 < raw.length) {
|
|
19134
|
+
const next = raw[i + 1];
|
|
19135
|
+
if (next === "n") {
|
|
19136
|
+
value += `
|
|
19137
|
+
`;
|
|
19138
|
+
i++;
|
|
19139
|
+
continue;
|
|
19140
|
+
}
|
|
19141
|
+
if (next === '"' || next === "\\") {
|
|
19142
|
+
value += next;
|
|
19143
|
+
i++;
|
|
19144
|
+
continue;
|
|
19145
|
+
}
|
|
19146
|
+
value += c;
|
|
19147
|
+
continue;
|
|
19148
|
+
}
|
|
19149
|
+
if (c === quote) {
|
|
19150
|
+
return value;
|
|
19151
|
+
}
|
|
19152
|
+
value += c;
|
|
19153
|
+
}
|
|
19154
|
+
return value;
|
|
19155
|
+
}
|
|
19156
|
+
function stripInlineComment(raw) {
|
|
19157
|
+
for (let i = 0;i < raw.length; i++) {
|
|
19158
|
+
if (raw[i] === "#" && (i === 0 || /\s/.test(raw[i - 1] ?? ""))) {
|
|
19159
|
+
return raw.slice(0, i);
|
|
19160
|
+
}
|
|
19161
|
+
}
|
|
19162
|
+
return raw;
|
|
19163
|
+
}
|
|
19125
19164
|
function parseDotenv(content) {
|
|
19126
19165
|
if (!content)
|
|
19127
19166
|
return {};
|
|
19128
19167
|
const result = {};
|
|
19129
19168
|
for (const rawLine of content.split(`
|
|
19130
19169
|
`)) {
|
|
19131
|
-
|
|
19170
|
+
let line = rawLine.trim();
|
|
19132
19171
|
if (!line || line.startsWith("#"))
|
|
19133
19172
|
continue;
|
|
19134
|
-
|
|
19135
|
-
|
|
19173
|
+
if (line.startsWith("export ")) {
|
|
19174
|
+
line = line.slice(7).trim();
|
|
19175
|
+
if (line.length >= 2 && (line[0] === '"' || line[0] === "'") && line.endsWith(line[0])) {
|
|
19176
|
+
line = line.slice(1, -1);
|
|
19177
|
+
}
|
|
19178
|
+
}
|
|
19179
|
+
const eqIndex = line.indexOf("=");
|
|
19136
19180
|
if (eqIndex === -1)
|
|
19137
19181
|
continue;
|
|
19138
|
-
const key =
|
|
19139
|
-
|
|
19140
|
-
|
|
19141
|
-
|
|
19182
|
+
const key = line.slice(0, eqIndex).trim();
|
|
19183
|
+
const rawValue = line.slice(eqIndex + 1).trim();
|
|
19184
|
+
let value;
|
|
19185
|
+
if (rawValue.startsWith('"') || rawValue.startsWith("'")) {
|
|
19186
|
+
value = parseQuotedValue(rawValue, rawValue[0]);
|
|
19187
|
+
} else {
|
|
19188
|
+
value = stripInlineComment(rawValue).trim();
|
|
19142
19189
|
}
|
|
19143
19190
|
result[key] = value;
|
|
19144
19191
|
}
|
|
@@ -19161,12 +19208,13 @@ function resolveEnvVars(config2, env2, path = []) {
|
|
|
19161
19208
|
return config2;
|
|
19162
19209
|
}
|
|
19163
19210
|
function resolveString(str, env2, path) {
|
|
19164
|
-
|
|
19211
|
+
const resolveOne = (varName) => {
|
|
19165
19212
|
if (!(varName in env2)) {
|
|
19166
19213
|
throw new UnresolvedEnvVarError(varName, path);
|
|
19167
19214
|
}
|
|
19168
19215
|
return env2[varName];
|
|
19169
|
-
}
|
|
19216
|
+
};
|
|
19217
|
+
return str.replace(/\$\$(\{[A-Za-z_][A-Za-z0-9_]*\}|[A-Za-z_][A-Za-z0-9_]*)/g, `${DOUBLE_DOLLAR_PLACEHOLDER}$1`).replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, varName) => resolveOne(varName)).replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, varName) => resolveOne(varName)).replace(new RegExp(`${DOUBLE_DOLLAR_PLACEHOLDER}(\\{[A-Za-z_][A-Za-z0-9_]*\\}|[A-Za-z_][A-Za-z0-9_]*)`, "g"), "$$$1");
|
|
19170
19218
|
}
|
|
19171
19219
|
var UnresolvedEnvVarError, DOUBLE_DOLLAR_PLACEHOLDER = "__DOLLAR_ESCAPE__";
|
|
19172
19220
|
var init_dotenv = __esm(() => {
|
|
@@ -19526,6 +19574,14 @@ async function loadProfile(profileName, projectRoot) {
|
|
|
19526
19574
|
}
|
|
19527
19575
|
return base;
|
|
19528
19576
|
}
|
|
19577
|
+
function sensitiveFilteredProcessEnv() {
|
|
19578
|
+
const filtered = {};
|
|
19579
|
+
for (const [key, value] of Object.entries(_profileDeps.env)) {
|
|
19580
|
+
if (value !== undefined && !SENSITIVE_ENV_KEY_PATTERN.test(key))
|
|
19581
|
+
filtered[key] = value;
|
|
19582
|
+
}
|
|
19583
|
+
return filtered;
|
|
19584
|
+
}
|
|
19529
19585
|
async function loadProfileEnv(profileName, projectRoot) {
|
|
19530
19586
|
validateProfileName(profileName);
|
|
19531
19587
|
const globalPath = join2(globalConfigDir(), "profiles", `${profileName}.env`);
|
|
@@ -19533,11 +19589,7 @@ async function loadProfileEnv(profileName, projectRoot) {
|
|
|
19533
19589
|
const globalFile = Bun.file(globalPath);
|
|
19534
19590
|
const projectFile = Bun.file(projectPath);
|
|
19535
19591
|
const [globalExists, projectExists] = await Promise.all([globalFile.exists(), projectFile.exists()]);
|
|
19536
|
-
let merged =
|
|
19537
|
-
for (const [key, value] of Object.entries(_profileDeps.env)) {
|
|
19538
|
-
if (value !== undefined && !SENSITIVE_ENV_KEY_PATTERN.test(key))
|
|
19539
|
-
merged[key] = value;
|
|
19540
|
-
}
|
|
19592
|
+
let merged = sensitiveFilteredProcessEnv();
|
|
19541
19593
|
if (!globalExists && !projectExists) {
|
|
19542
19594
|
return merged;
|
|
19543
19595
|
}
|
|
@@ -19645,6 +19697,20 @@ var init_profile = __esm(() => {
|
|
|
19645
19697
|
// src/config/loader.ts
|
|
19646
19698
|
import { existsSync as existsSync3 } from "fs";
|
|
19647
19699
|
import { basename as basename2, dirname, join as join3, resolve as resolve3 } from "path";
|
|
19700
|
+
function resolveEnvVarsWarnOnFailure(config2, logger, layerName) {
|
|
19701
|
+
try {
|
|
19702
|
+
return resolveEnvVars(config2, sensitiveFilteredProcessEnv());
|
|
19703
|
+
} catch (err) {
|
|
19704
|
+
if (err instanceof UnresolvedEnvVarError) {
|
|
19705
|
+
logger?.warn("config", `${layerName} references undefined environment variable \u2014 left unresolved`, {
|
|
19706
|
+
varName: err.varName,
|
|
19707
|
+
path: err.path.join(".") || "(root)"
|
|
19708
|
+
});
|
|
19709
|
+
return config2;
|
|
19710
|
+
}
|
|
19711
|
+
throw err;
|
|
19712
|
+
}
|
|
19713
|
+
}
|
|
19648
19714
|
function globalConfigPath() {
|
|
19649
19715
|
return join3(globalConfigDir(), "config.json");
|
|
19650
19716
|
}
|
|
@@ -19696,6 +19762,7 @@ async function loadConfig(startDir, cliOverrides) {
|
|
|
19696
19762
|
});
|
|
19697
19763
|
}
|
|
19698
19764
|
}
|
|
19765
|
+
rawConfig = resolveEnvVarsWarnOnFailure(rawConfig, logger, "global/project config");
|
|
19699
19766
|
for (const name of overlayChain) {
|
|
19700
19767
|
const profileData = await loadProfile(name, projectRoot);
|
|
19701
19768
|
const profileEnv = await loadProfileEnv(name, projectRoot);
|
|
@@ -19794,13 +19861,23 @@ async function loadConfigForWorkdir(rootConfigPath, packageDir, cliOverrides) {
|
|
|
19794
19861
|
const { profile: packageProfile, ...packageFields } = packageOverride;
|
|
19795
19862
|
let merged = mergePackageConfig(rootConfig, packageFields);
|
|
19796
19863
|
merged = stripRemovedNoOpKeys(merged, defaultConfigWarn);
|
|
19864
|
+
const envResolvedMerged = resolveEnvVarsWarnOnFailure(merged, logger, `per-package config (${packageDir})`);
|
|
19797
19865
|
const packageChain = parseProfileList(packageProfile).filter((name) => name && name !== "default");
|
|
19798
|
-
let rawMerged =
|
|
19866
|
+
let rawMerged = envResolvedMerged;
|
|
19799
19867
|
if (packageChain.length > 0) {
|
|
19800
19868
|
const packageRoot = join3(repoRoot, packageDir);
|
|
19801
19869
|
for (const name of packageChain) {
|
|
19802
19870
|
const profileData = await loadProfile(name, packageRoot);
|
|
19803
|
-
|
|
19871
|
+
const profileEnv = await loadProfileEnv(name, packageRoot);
|
|
19872
|
+
let resolvedProfileData;
|
|
19873
|
+
try {
|
|
19874
|
+
resolvedProfileData = Object.keys(profileEnv).length > 0 ? resolveEnvVars(profileData, profileEnv) : profileData;
|
|
19875
|
+
} catch (err) {
|
|
19876
|
+
const varName = err instanceof UnresolvedEnvVarError ? err.varName : undefined;
|
|
19877
|
+
const path = err instanceof UnresolvedEnvVarError ? err.path.join(".") : undefined;
|
|
19878
|
+
throw new NaxError(`Per-package profile "${name}" (${packageDir}) references an undefined environment variable${varName ? ` $${varName}` : ""}${path ? ` at "${path}"` : ""}.`, "PROFILE_ENV_VAR_UNRESOLVED", { stage: "config", profileName: name, packageDir, varName, path, cause: err });
|
|
19879
|
+
}
|
|
19880
|
+
rawMerged = deepMergeConfig(rawMerged, resolvedProfileData);
|
|
19804
19881
|
}
|
|
19805
19882
|
rawMerged.profile = packageChain.join("+");
|
|
19806
19883
|
rawMerged.profileChain = packageChain;
|
|
@@ -19809,6 +19886,7 @@ async function loadConfigForWorkdir(rootConfigPath, packageDir, cliOverrides) {
|
|
|
19809
19886
|
rejectLegacyRectificationKeys(rawMerged);
|
|
19810
19887
|
rejectDeadQualityFlags(rawMerged);
|
|
19811
19888
|
rejectUnimplementedScopedProfile(rawMerged);
|
|
19889
|
+
rejectUnimplementedPermissionsBlock(rawMerged);
|
|
19812
19890
|
rawMerged = stripRemovedNoOpKeys(rawMerged, defaultConfigWarn);
|
|
19813
19891
|
const result = NaxConfigSchema.safeParse(rawMerged);
|
|
19814
19892
|
if (!result.success) {
|
|
@@ -20250,6 +20328,7 @@ __export(exports_config, {
|
|
|
20250
20328
|
trackedSpawnDeadlines: () => trackedSpawnDeadlines,
|
|
20251
20329
|
testPatternConfigSelector: () => testPatternConfigSelector,
|
|
20252
20330
|
tddConfigSelector: () => tddConfigSelector,
|
|
20331
|
+
sensitiveFilteredProcessEnv: () => sensitiveFilteredProcessEnv,
|
|
20253
20332
|
routingConfigSelector: () => routingConfigSelector,
|
|
20254
20333
|
reviewConfigSelector: () => reviewConfigSelector,
|
|
20255
20334
|
resolveTestStrategy: () => resolveTestStrategy,
|
|
@@ -23503,10 +23582,10 @@ class AgentManager {
|
|
|
23503
23582
|
const adapter = this._resolveRegistry().getAgent(currentAgent);
|
|
23504
23583
|
if (!adapter) {
|
|
23505
23584
|
_finalStatus = "error";
|
|
23506
|
-
|
|
23507
|
-
|
|
23508
|
-
|
|
23509
|
-
};
|
|
23585
|
+
throw new NaxError(`Agent "${currentAgent}" not found in registry`, "AGENT_NOT_FOUND", {
|
|
23586
|
+
stage: "complete",
|
|
23587
|
+
agentName: currentAgent
|
|
23588
|
+
});
|
|
23510
23589
|
}
|
|
23511
23590
|
let result;
|
|
23512
23591
|
try {
|
|
@@ -23655,7 +23734,7 @@ class AgentManager {
|
|
|
23655
23734
|
throw new NaxError("AgentManager.runAsSession: _sendPrompt is not wired \u2014 pass sendPrompt at construction via NaxRuntime", "SEND_PROMPT_UNAVAILABLE", { stage: opts.pipelineStage ?? "run", agentName });
|
|
23656
23735
|
}
|
|
23657
23736
|
const stage = opts.pipelineStage ?? "run";
|
|
23658
|
-
const resolvedPermissions = resolvePermissions(this._config, stage);
|
|
23737
|
+
const resolvedPermissions = resolvePermissions(opts.config ?? this._config, stage);
|
|
23659
23738
|
const sessionRole = handle.role ?? opts.sessionRole ?? "main";
|
|
23660
23739
|
const start = Date.now();
|
|
23661
23740
|
try {
|
|
@@ -23697,7 +23776,7 @@ class AgentManager {
|
|
|
23697
23776
|
}
|
|
23698
23777
|
async completeAs(agentName, prompt, options) {
|
|
23699
23778
|
const stage = options.pipelineStage ?? "complete";
|
|
23700
|
-
const resolvedPermissions = resolvePermissions(this._config, stage);
|
|
23779
|
+
const resolvedPermissions = resolvePermissions(options.config ?? this._config, stage);
|
|
23701
23780
|
const augmented = {
|
|
23702
23781
|
...options,
|
|
23703
23782
|
resolvedPermissions,
|
|
@@ -25127,7 +25206,9 @@ function buildDigest(chunks) {
|
|
|
25127
25206
|
const scopeRank = Object.fromEntries(SCOPE_ORDER2.map((s, i) => [s, i]));
|
|
25128
25207
|
const sorted = [...chunks].sort((a, b) => {
|
|
25129
25208
|
const scopeDiff = (scopeRank[a.scope] ?? 99) - (scopeRank[b.scope] ?? 99);
|
|
25130
|
-
|
|
25209
|
+
if (scopeDiff !== 0)
|
|
25210
|
+
return scopeDiff;
|
|
25211
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
25131
25212
|
});
|
|
25132
25213
|
const lines = [];
|
|
25133
25214
|
for (const chunk of sorted) {
|
|
@@ -25508,16 +25589,13 @@ var init_manager_run = __esm(() => {
|
|
|
25508
25589
|
// src/session/manager-sweep.ts
|
|
25509
25590
|
function sweepOrphansImpl(sessions, ttlMs) {
|
|
25510
25591
|
const cutoff = _sessionManagerDeps.nowMs() - ttlMs;
|
|
25511
|
-
const terminal = ["COMPLETED", "FAILED"];
|
|
25512
25592
|
let removed = 0;
|
|
25513
25593
|
for (const [id, session] of sessions.entries()) {
|
|
25514
|
-
if (!terminal.includes(session.state))
|
|
25515
|
-
continue;
|
|
25516
25594
|
const lastActivityMs = session.lastActivityAt ? new Date(session.lastActivityAt).getTime() : Number.NaN;
|
|
25517
|
-
if (
|
|
25518
|
-
|
|
25519
|
-
|
|
25520
|
-
|
|
25595
|
+
if (Number.isFinite(lastActivityMs) && lastActivityMs >= cutoff)
|
|
25596
|
+
continue;
|
|
25597
|
+
sessions.delete(id);
|
|
25598
|
+
removed++;
|
|
25521
25599
|
}
|
|
25522
25600
|
if (removed > 0) {
|
|
25523
25601
|
getLogger().debug("session", "Swept orphan sessions", { removed });
|
|
@@ -25821,8 +25899,10 @@ class SessionManager {
|
|
|
25821
25899
|
if (terminal.includes(session.state))
|
|
25822
25900
|
continue;
|
|
25823
25901
|
const updated = { ...session, state: "COMPLETED", lastActivityAt: now };
|
|
25824
|
-
this._sessions.set(id, updated);
|
|
25825
25902
|
this._persistDescriptor(updated);
|
|
25903
|
+
this._sessions.delete(id);
|
|
25904
|
+
if (updated.handle)
|
|
25905
|
+
this._liveHandles.delete(updated.handle);
|
|
25826
25906
|
closed.push({ ...updated });
|
|
25827
25907
|
getLogger().debug("session", "Session closed by closeStory", {
|
|
25828
25908
|
storyId,
|
|
@@ -25869,7 +25949,7 @@ class SessionManager {
|
|
|
25869
25949
|
if (!adapter) {
|
|
25870
25950
|
throw new NaxError(`SessionManager.openSession: no adapter found for agent "${opts.agentName}"`, "ADAPTER_NOT_FOUND", { stage: "session", agentName: opts.agentName });
|
|
25871
25951
|
}
|
|
25872
|
-
const resolvedPermissions = resolvePermissions(this._config, opts.pipelineStage);
|
|
25952
|
+
const resolvedPermissions = resolvePermissions(opts.config ?? this._config, opts.pipelineStage);
|
|
25873
25953
|
const existingDescriptor = this._findByName(name);
|
|
25874
25954
|
const resume = existingDescriptor !== undefined;
|
|
25875
25955
|
const handle = await adapter.openSession(name, {
|
|
@@ -28886,19 +28966,55 @@ function buildTestCandidates(sourceFile, workdir, packagePrefix, shapes, testDir
|
|
|
28886
28966
|
const sourceAbs = `${workdir}/${sourceFile}`;
|
|
28887
28967
|
return [...new Set(candidates)].filter((c) => c !== sourceAbs);
|
|
28888
28968
|
}
|
|
28969
|
+
function tokenizeCommand(command) {
|
|
28970
|
+
const tokens = [];
|
|
28971
|
+
let current = "";
|
|
28972
|
+
let quote = null;
|
|
28973
|
+
for (const char of command.trim()) {
|
|
28974
|
+
if (quote) {
|
|
28975
|
+
current += char;
|
|
28976
|
+
if (char === quote)
|
|
28977
|
+
quote = null;
|
|
28978
|
+
continue;
|
|
28979
|
+
}
|
|
28980
|
+
if (char === '"' || char === "'") {
|
|
28981
|
+
quote = char;
|
|
28982
|
+
current += char;
|
|
28983
|
+
continue;
|
|
28984
|
+
}
|
|
28985
|
+
if (/\s/.test(char)) {
|
|
28986
|
+
if (current) {
|
|
28987
|
+
tokens.push(current);
|
|
28988
|
+
current = "";
|
|
28989
|
+
}
|
|
28990
|
+
continue;
|
|
28991
|
+
}
|
|
28992
|
+
current += char;
|
|
28993
|
+
}
|
|
28994
|
+
if (current)
|
|
28995
|
+
tokens.push(current);
|
|
28996
|
+
return tokens;
|
|
28997
|
+
}
|
|
28998
|
+
function unquote(token) {
|
|
28999
|
+
if (token.length >= 2 && (token[0] === '"' || token[0] === "'") && token[token.length - 1] === token[0]) {
|
|
29000
|
+
return token.slice(1, -1);
|
|
29001
|
+
}
|
|
29002
|
+
return token;
|
|
29003
|
+
}
|
|
28889
29004
|
function buildSmartTestCommand(testFiles, baseCommand) {
|
|
28890
29005
|
if (testFiles.length === 0) {
|
|
28891
29006
|
return baseCommand;
|
|
28892
29007
|
}
|
|
28893
29008
|
const shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
28894
29009
|
const quotedTestFiles = testFiles.map(shellQuote);
|
|
28895
|
-
const parts = baseCommand
|
|
29010
|
+
const parts = tokenizeCommand(baseCommand);
|
|
28896
29011
|
let lastPathIndex = -1;
|
|
28897
29012
|
for (let i = parts.length - 1;i >= 0; i--) {
|
|
28898
|
-
|
|
29013
|
+
const bare = unquote(parts[i]);
|
|
29014
|
+
if (!bare.includes("/"))
|
|
28899
29015
|
continue;
|
|
28900
|
-
const precededByPathFlag = i > 0 && PATH_TAKING_FLAGS.includes(parts[i - 1]);
|
|
28901
|
-
const isCombinedFlagValue = PATH_TAKING_FLAGS.some((flag) =>
|
|
29016
|
+
const precededByPathFlag = i > 0 && PATH_TAKING_FLAGS.includes(unquote(parts[i - 1]));
|
|
29017
|
+
const isCombinedFlagValue = PATH_TAKING_FLAGS.some((flag) => bare.startsWith(`${flag}=`));
|
|
28902
29018
|
if (precededByPathFlag || isCombinedFlagValue)
|
|
28903
29019
|
continue;
|
|
28904
29020
|
lastPathIndex = i;
|
|
@@ -30742,8 +30858,22 @@ function validateInjectedStory(raw, existingIds) {
|
|
|
30742
30858
|
} else {
|
|
30743
30859
|
id = deriveNextStoryId(existingIds);
|
|
30744
30860
|
}
|
|
30745
|
-
|
|
30746
|
-
|
|
30861
|
+
if (Array.isArray(s.tags)) {
|
|
30862
|
+
for (let i = 0;i < s.tags.length; i++) {
|
|
30863
|
+
if (typeof s.tags[i] !== "string") {
|
|
30864
|
+
throw new NaxError(`[queue] INJECT story.tags[${i}] must be a string (got ${typeof s.tags[i]})`, "SCHEMA_VALIDATION_FAILED", { stage: "queue", tagIndex: i, tagType: typeof s.tags[i] });
|
|
30865
|
+
}
|
|
30866
|
+
}
|
|
30867
|
+
}
|
|
30868
|
+
if (Array.isArray(s.dependencies)) {
|
|
30869
|
+
for (let i = 0;i < s.dependencies.length; i++) {
|
|
30870
|
+
if (typeof s.dependencies[i] !== "string") {
|
|
30871
|
+
throw new NaxError(`[queue] INJECT story.dependencies[${i}] must be a string (got ${typeof s.dependencies[i]})`, "SCHEMA_VALIDATION_FAILED", { stage: "queue", depIndex: i, depType: typeof s.dependencies[i] });
|
|
30872
|
+
}
|
|
30873
|
+
}
|
|
30874
|
+
}
|
|
30875
|
+
const tags = Array.isArray(s.tags) ? s.tags : [];
|
|
30876
|
+
const dependencies = Array.isArray(s.dependencies) ? s.dependencies : [];
|
|
30747
30877
|
for (const dep of dependencies) {
|
|
30748
30878
|
if (!existingIds.has(dep)) {
|
|
30749
30879
|
throw new NaxError(`[queue] INJECT story.dependencies references unknown story ID "${dep}"`, "SCHEMA_VALIDATION_FAILED", { stage: "queue", dep });
|
|
@@ -30929,14 +31059,28 @@ function validateStory(raw, index, allIds, seenIds) {
|
|
|
30929
31059
|
}
|
|
30930
31060
|
const noTestJustification = typeof rawJustification === "string" && rawJustification.trim() !== "" ? rawJustification.trim() : undefined;
|
|
30931
31061
|
const rawDeps = s.dependencies;
|
|
30932
|
-
const dependencies = Array.isArray(rawDeps) ?
|
|
31062
|
+
const dependencies = Array.isArray(rawDeps) ? (() => {
|
|
31063
|
+
for (const [i, dep] of rawDeps.entries()) {
|
|
31064
|
+
if (typeof dep !== "string") {
|
|
31065
|
+
throw new NaxError(`[schema] story[${index}].dependencies[${i}] must be a string (got ${typeof dep})`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, depIndex: i, depType: typeof dep });
|
|
31066
|
+
}
|
|
31067
|
+
}
|
|
31068
|
+
return Array.from(new Set(rawDeps.map((dep) => normalizeStoryId(dep))));
|
|
31069
|
+
})() : [];
|
|
30933
31070
|
for (const dep of dependencies) {
|
|
30934
31071
|
if (!allIds.has(normalizeStoryId(dep))) {
|
|
30935
31072
|
throw new NaxError(`[schema] story[${index}].dependencies references unknown story ID "${dep}"`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, dep });
|
|
30936
31073
|
}
|
|
30937
31074
|
}
|
|
30938
31075
|
const rawTags = s.tags;
|
|
30939
|
-
const tags = Array.isArray(rawTags) ?
|
|
31076
|
+
const tags = Array.isArray(rawTags) ? (() => {
|
|
31077
|
+
for (const [i, tag] of rawTags.entries()) {
|
|
31078
|
+
if (typeof tag !== "string") {
|
|
31079
|
+
throw new NaxError(`[schema] story[${index}].tags[${i}] must be a string (got ${typeof tag})`, "SCHEMA_VALIDATION_FAILED", { stage: "schema", index, tagIndex: i, tagType: typeof tag });
|
|
31080
|
+
}
|
|
31081
|
+
}
|
|
31082
|
+
return rawTags;
|
|
31083
|
+
})() : [];
|
|
30940
31084
|
const rawWorkdir = s.workdir;
|
|
30941
31085
|
let workdir;
|
|
30942
31086
|
if (rawWorkdir !== undefined && rawWorkdir !== null) {
|
|
@@ -33054,6 +33198,84 @@ function computePollutionMetrics(manifests) {
|
|
|
33054
33198
|
};
|
|
33055
33199
|
}
|
|
33056
33200
|
|
|
33201
|
+
// src/utils/path-file-lock.ts
|
|
33202
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
33203
|
+
import { open, readdir, stat as stat2, unlink as unlink2 } from "fs/promises";
|
|
33204
|
+
import { basename as basename7, dirname as dirname8 } from "path";
|
|
33205
|
+
function buildCandidatePath(targetPath) {
|
|
33206
|
+
const time3 = _pathFileLockDeps.now().toString().padStart(LOCK_TIME_WIDTH, "0");
|
|
33207
|
+
return `${targetPath}.lock.${time3}.${process.pid}.${_pathFileLockDeps.randomUUID()}`;
|
|
33208
|
+
}
|
|
33209
|
+
function candidatePid(fileName) {
|
|
33210
|
+
const segments = fileName.split(".");
|
|
33211
|
+
const pid = Number(segments.at(-2));
|
|
33212
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
33213
|
+
}
|
|
33214
|
+
function candidateTime(fileName) {
|
|
33215
|
+
const segments = fileName.split(".");
|
|
33216
|
+
const timestamp = Number(segments.at(-3));
|
|
33217
|
+
return Number.isFinite(timestamp) ? timestamp : null;
|
|
33218
|
+
}
|
|
33219
|
+
async function listLiveCandidates(targetPath) {
|
|
33220
|
+
const directory = dirname8(targetPath);
|
|
33221
|
+
const prefix = `${basename7(targetPath)}.lock.`;
|
|
33222
|
+
const candidates = (await _pathFileLockDeps.readdir(directory).catch(() => [])).filter((name) => name.startsWith(prefix));
|
|
33223
|
+
const live = [];
|
|
33224
|
+
for (const candidate of candidates) {
|
|
33225
|
+
const pid = candidatePid(candidate);
|
|
33226
|
+
const createdAt = candidateTime(candidate);
|
|
33227
|
+
const candidatePath = `${directory}/${candidate}`;
|
|
33228
|
+
if (pid !== null && createdAt !== null && _pathFileLockDeps.isPidAlive(pid)) {
|
|
33229
|
+
const stats = await _pathFileLockDeps.stat(candidatePath).catch(() => null);
|
|
33230
|
+
if (stats)
|
|
33231
|
+
live.push({ name: candidate, createdAt: stats.birthtimeMs });
|
|
33232
|
+
} else {
|
|
33233
|
+
await _pathFileLockDeps.unlink(candidatePath).catch(() => {});
|
|
33234
|
+
}
|
|
33235
|
+
}
|
|
33236
|
+
return live.sort((a, b) => a.createdAt - b.createdAt || a.name.localeCompare(b.name)).map(({ name }) => name);
|
|
33237
|
+
}
|
|
33238
|
+
async function acquire(targetPath, retryMs, timeoutMs) {
|
|
33239
|
+
const candidatePath = buildCandidatePath(targetPath);
|
|
33240
|
+
const handle = await _pathFileLockDeps.open(candidatePath, "wx");
|
|
33241
|
+
await handle.close();
|
|
33242
|
+
const deadline = _pathFileLockDeps.now() + timeoutMs;
|
|
33243
|
+
while (_pathFileLockDeps.now() < deadline) {
|
|
33244
|
+
const candidates = await listLiveCandidates(targetPath);
|
|
33245
|
+
if (candidates[0] === basename7(candidatePath)) {
|
|
33246
|
+
return async () => {
|
|
33247
|
+
await _pathFileLockDeps.unlink(candidatePath).catch(() => {});
|
|
33248
|
+
};
|
|
33249
|
+
}
|
|
33250
|
+
await _pathFileLockDeps.sleep(retryMs);
|
|
33251
|
+
}
|
|
33252
|
+
await _pathFileLockDeps.unlink(candidatePath).catch(() => {});
|
|
33253
|
+
throw new Error(`[path-lock] Timed out acquiring path lock: ${targetPath}`);
|
|
33254
|
+
}
|
|
33255
|
+
async function withPathFileLock(targetPath, operation, options = {}) {
|
|
33256
|
+
const retryMs = options.retryMs ?? DEFAULT_RETRY_MS;
|
|
33257
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
33258
|
+
const release = await acquire(targetPath, retryMs, timeoutMs);
|
|
33259
|
+
try {
|
|
33260
|
+
return await operation();
|
|
33261
|
+
} finally {
|
|
33262
|
+
await release();
|
|
33263
|
+
}
|
|
33264
|
+
}
|
|
33265
|
+
var DEFAULT_RETRY_MS = 10, DEFAULT_TIMEOUT_MS = 5000, LOCK_TIME_WIDTH = 13, _pathFileLockDeps;
|
|
33266
|
+
var init_path_file_lock = __esm(() => {
|
|
33267
|
+
_pathFileLockDeps = {
|
|
33268
|
+
open,
|
|
33269
|
+
readdir,
|
|
33270
|
+
stat: stat2,
|
|
33271
|
+
unlink: unlink2,
|
|
33272
|
+
randomUUID: randomUUID5,
|
|
33273
|
+
now: () => Date.now(),
|
|
33274
|
+
sleep: (ms) => new Promise((resolve9) => setTimeout(resolve9, ms)),
|
|
33275
|
+
isPidAlive: isProcessAlive
|
|
33276
|
+
};
|
|
33277
|
+
});
|
|
33278
|
+
|
|
33057
33279
|
// src/metrics/types.ts
|
|
33058
33280
|
class TokenUsage {
|
|
33059
33281
|
inputTokens;
|
|
@@ -33303,9 +33525,12 @@ async function saveRunMetrics(outputDir, runMetrics) {
|
|
|
33303
33525
|
cacheCreationInputTokens: totalCacheCreationInputTokens
|
|
33304
33526
|
})
|
|
33305
33527
|
} : runMetrics;
|
|
33306
|
-
const
|
|
33307
|
-
|
|
33308
|
-
|
|
33528
|
+
const allMetrics = await withPathFileLock(metricsPath, async () => {
|
|
33529
|
+
const existing = await loadJsonFile(metricsPath, "metrics");
|
|
33530
|
+
const base = Array.isArray(existing) ? existing : [];
|
|
33531
|
+
base.push(finalMetrics);
|
|
33532
|
+
return base;
|
|
33533
|
+
});
|
|
33309
33534
|
const isTruncating = allMetrics.length > MAX_RETAINED_RUNS;
|
|
33310
33535
|
const cappedMetrics = isTruncating ? allMetrics.slice(allMetrics.length - MAX_RETAINED_RUNS) : allMetrics;
|
|
33311
33536
|
if (isTruncating && !hasWarnedAboutRunTruncation) {
|
|
@@ -33331,6 +33556,7 @@ var init_tracker = __esm(() => {
|
|
|
33331
33556
|
init_manifest_store();
|
|
33332
33557
|
init_logger2();
|
|
33333
33558
|
init_json_file();
|
|
33559
|
+
init_path_file_lock();
|
|
33334
33560
|
});
|
|
33335
33561
|
|
|
33336
33562
|
// src/metrics/aggregator.ts
|
|
@@ -33521,12 +33747,12 @@ function buildLastRun(runs) {
|
|
|
33521
33747
|
function buildModelEfficiency(metrics) {
|
|
33522
33748
|
if (!metrics)
|
|
33523
33749
|
return [];
|
|
33524
|
-
return Object.entries(metrics.modelEfficiency).map(([model,
|
|
33750
|
+
return Object.entries(metrics.modelEfficiency).map(([model, stat3]) => ({
|
|
33525
33751
|
model,
|
|
33526
|
-
attempts:
|
|
33527
|
-
passRate:
|
|
33528
|
-
avgCost:
|
|
33529
|
-
totalCost:
|
|
33752
|
+
attempts: stat3.attempts,
|
|
33753
|
+
passRate: stat3.passRate,
|
|
33754
|
+
avgCost: stat3.avgCost,
|
|
33755
|
+
totalCost: stat3.totalCost
|
|
33530
33756
|
})).sort((x, y) => y.totalCost - x.totalCost);
|
|
33531
33757
|
}
|
|
33532
33758
|
function stripInternalFields(report) {
|
|
@@ -33658,6 +33884,19 @@ var init_prior_run_failure = __esm(() => {
|
|
|
33658
33884
|
};
|
|
33659
33885
|
});
|
|
33660
33886
|
|
|
33887
|
+
// src/utils/jsonl-tail.ts
|
|
33888
|
+
async function readJsonlTail(path2, maxBytes = DEFAULT_JSONL_TAIL_BYTES) {
|
|
33889
|
+
const file3 = Bun.file(path2);
|
|
33890
|
+
const size = file3.size;
|
|
33891
|
+
if (size <= maxBytes)
|
|
33892
|
+
return file3.text();
|
|
33893
|
+
const tail = await file3.slice(size - maxBytes).text();
|
|
33894
|
+
const newlineIndex = tail.indexOf(`
|
|
33895
|
+
`);
|
|
33896
|
+
return newlineIndex === -1 ? "" : tail.slice(newlineIndex + 1);
|
|
33897
|
+
}
|
|
33898
|
+
var DEFAULT_JSONL_TAIL_BYTES = 65536;
|
|
33899
|
+
|
|
33661
33900
|
// src/context/engine/providers/session-scratch.ts
|
|
33662
33901
|
import { createHash as createHash9 } from "crypto";
|
|
33663
33902
|
function contentHash86(content) {
|
|
@@ -33762,12 +34001,12 @@ class SessionScratchProvider {
|
|
|
33762
34001
|
}
|
|
33763
34002
|
var MAX_ENTRIES_PER_DIR = 20, MAX_CHUNK_TOKENS3 = 500, _sessionScratchDeps;
|
|
33764
34003
|
var init_session_scratch = __esm(() => {
|
|
33765
|
-
|
|
34004
|
+
init_session();
|
|
33766
34005
|
init_path_filters();
|
|
33767
34006
|
init_scratch_neutralizer();
|
|
33768
34007
|
_sessionScratchDeps = {
|
|
33769
34008
|
fileExists: (path2) => Bun.file(path2).exists(),
|
|
33770
|
-
readFile: (path2) =>
|
|
34009
|
+
readFile: (path2) => readJsonlTail(path2)
|
|
33771
34010
|
};
|
|
33772
34011
|
});
|
|
33773
34012
|
|
|
@@ -33976,9 +34215,7 @@ async function autoDetectContextFiles(options) {
|
|
|
33976
34215
|
"-I",
|
|
33977
34216
|
"-E",
|
|
33978
34217
|
"-e",
|
|
33979
|
-
grepPattern
|
|
33980
|
-
"--",
|
|
33981
|
-
"src/"
|
|
34218
|
+
grepPattern
|
|
33982
34219
|
];
|
|
33983
34220
|
try {
|
|
33984
34221
|
const proc = Bun.spawn(grepCommand, {
|
|
@@ -34001,6 +34238,9 @@ async function autoDetectContextFiles(options) {
|
|
|
34001
34238
|
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
34002
34239
|
const filtered = allFiles.filter((filePath) => {
|
|
34003
34240
|
const lower = filePath.toLowerCase();
|
|
34241
|
+
if (lower.includes("node_modules/") || lower.startsWith(".git/") || lower.includes("/.git/") || lower.startsWith(".nax/") || lower.includes("/.nax/")) {
|
|
34242
|
+
return false;
|
|
34243
|
+
}
|
|
34004
34244
|
if (isTestFileByPatterns(filePath, testFilePatterns)) {
|
|
34005
34245
|
return false;
|
|
34006
34246
|
}
|
|
@@ -34201,6 +34441,7 @@ var init_parent_context = __esm(() => {
|
|
|
34201
34441
|
});
|
|
34202
34442
|
|
|
34203
34443
|
// src/context/test-scanner.ts
|
|
34444
|
+
import { stat as stat3 } from "fs/promises";
|
|
34204
34445
|
import path2 from "path";
|
|
34205
34446
|
var {Glob: Glob2 } = globalThis.Bun;
|
|
34206
34447
|
function extractTestStructure(source) {
|
|
@@ -34248,8 +34489,8 @@ function deriveTestPatterns(contextFiles, resolvedGlobs) {
|
|
|
34248
34489
|
const suffixes = resolvedGlobs ? extractGlobSuffixes(resolvedGlobs) : DEFAULT_TS_DERIVE_SUFFIXES;
|
|
34249
34490
|
const effectiveSuffixes = suffixes.length > 0 ? suffixes : DEFAULT_TS_DERIVE_SUFFIXES;
|
|
34250
34491
|
for (const filePath of contextFiles) {
|
|
34251
|
-
const
|
|
34252
|
-
const basenameNoExt =
|
|
34492
|
+
const basename8 = path2.basename(filePath);
|
|
34493
|
+
const basenameNoExt = basename8.replace(/\.[^.]+$/, "");
|
|
34253
34494
|
for (const suffix of effectiveSuffixes) {
|
|
34254
34495
|
patterns.add(`${basenameNoExt}${suffix}`);
|
|
34255
34496
|
}
|
|
@@ -34311,8 +34552,8 @@ async function scanTestFiles(options) {
|
|
|
34311
34552
|
const files = [];
|
|
34312
34553
|
for await (const filePath of glob.scan({ cwd: scanDir, absolute: false })) {
|
|
34313
34554
|
if (allowedBasenames !== null) {
|
|
34314
|
-
const
|
|
34315
|
-
if (!allowedBasenames.has(
|
|
34555
|
+
const basename8 = path2.basename(filePath);
|
|
34556
|
+
if (!allowedBasenames.has(basename8)) {
|
|
34316
34557
|
continue;
|
|
34317
34558
|
}
|
|
34318
34559
|
}
|
|
@@ -34325,6 +34566,24 @@ async function scanTestFiles(options) {
|
|
|
34325
34566
|
}
|
|
34326
34567
|
const fullPath = path2.join(scanDir, filePath);
|
|
34327
34568
|
try {
|
|
34569
|
+
try {
|
|
34570
|
+
const fileStat = await stat3(fullPath);
|
|
34571
|
+
if (fileStat.size > MAX_TEST_FILE_SIZE_BYTES) {
|
|
34572
|
+
getLogger().debug("test-scanner", "File exceeds size cap \u2014 skipped without read", {
|
|
34573
|
+
path: fullPath,
|
|
34574
|
+
sizeBytes: fileStat.size,
|
|
34575
|
+
capBytes: MAX_TEST_FILE_SIZE_BYTES
|
|
34576
|
+
});
|
|
34577
|
+
continue;
|
|
34578
|
+
}
|
|
34579
|
+
} catch (statErr) {
|
|
34580
|
+
if (statErr.code !== "ENOENT") {
|
|
34581
|
+
getLogger().debug("test-scanner", "stat failed \u2014 falling through to read", {
|
|
34582
|
+
path: fullPath,
|
|
34583
|
+
error: errorMessage(statErr)
|
|
34584
|
+
});
|
|
34585
|
+
}
|
|
34586
|
+
}
|
|
34328
34587
|
const source = await Bun.file(fullPath).text();
|
|
34329
34588
|
const { describes, testCount } = extractTestStructure(source);
|
|
34330
34589
|
if (testCount > 0 || describes.length > 0) {
|
|
@@ -34424,10 +34683,11 @@ async function generateTestCoverageSummary(options) {
|
|
|
34424
34683
|
const tokens = estimateTokens2(summary);
|
|
34425
34684
|
return { files, totalTests, summary, tokens };
|
|
34426
34685
|
}
|
|
34427
|
-
var DEFAULT_MAX_SCAN_FILES = 200;
|
|
34686
|
+
var DEFAULT_MAX_SCAN_FILES = 200, MAX_TEST_FILE_SIZE_BYTES;
|
|
34428
34687
|
var init_test_scanner = __esm(() => {
|
|
34429
34688
|
init_logger2();
|
|
34430
34689
|
init_conventions();
|
|
34690
|
+
MAX_TEST_FILE_SIZE_BYTES = 1 * 1024 * 1024;
|
|
34431
34691
|
});
|
|
34432
34692
|
|
|
34433
34693
|
// src/context/formatter.ts
|
|
@@ -34990,6 +35250,25 @@ var init_context = __esm(() => {
|
|
|
34990
35250
|
init_fragments();
|
|
34991
35251
|
});
|
|
34992
35252
|
|
|
35253
|
+
// src/context/engine/providers/canonical-rules-cache.ts
|
|
35254
|
+
function memoizedLoadCanonicalRules(workdir, options) {
|
|
35255
|
+
const cached2 = canonicalRulesCache.get(workdir);
|
|
35256
|
+
if (cached2)
|
|
35257
|
+
return cached2;
|
|
35258
|
+
const loaded = loadCanonicalRules(workdir, options);
|
|
35259
|
+
canonicalRulesCache.set(workdir, loaded);
|
|
35260
|
+
loaded.catch(() => canonicalRulesCache.delete(workdir));
|
|
35261
|
+
return loaded;
|
|
35262
|
+
}
|
|
35263
|
+
function _resetCanonicalRulesCache() {
|
|
35264
|
+
canonicalRulesCache.clear();
|
|
35265
|
+
}
|
|
35266
|
+
var canonicalRulesCache;
|
|
35267
|
+
var init_canonical_rules_cache = __esm(() => {
|
|
35268
|
+
init_canonical_loader();
|
|
35269
|
+
canonicalRulesCache = new Map;
|
|
35270
|
+
});
|
|
35271
|
+
|
|
34993
35272
|
// src/context/engine/providers/static-rules.ts
|
|
34994
35273
|
import { createHash as createHash10 } from "crypto";
|
|
34995
35274
|
import { join as join20, relative as relative7 } from "path";
|
|
@@ -35018,7 +35297,7 @@ function globToRegex3(pattern) {
|
|
|
35018
35297
|
const beforeSlash = i > 0 && pattern[i - 1] === "/";
|
|
35019
35298
|
const afterSlash = pattern[i + 2] === "/";
|
|
35020
35299
|
if (beforeSlash && afterSlash) {
|
|
35021
|
-
regex = `${regex
|
|
35300
|
+
regex = `${regex}(?:.*\\/)?`;
|
|
35022
35301
|
i += 3;
|
|
35023
35302
|
} else if (afterSlash) {
|
|
35024
35303
|
regex += "(?:.*\\/)?";
|
|
@@ -35125,7 +35404,14 @@ class StaticRulesProvider {
|
|
|
35125
35404
|
mergedRules = [...merged.values()];
|
|
35126
35405
|
}
|
|
35127
35406
|
}
|
|
35128
|
-
mergedRules.sort((a, b) =>
|
|
35407
|
+
mergedRules.sort((a, b) => {
|
|
35408
|
+
const priorityDiff = canonicalRulePriority(a) - canonicalRulePriority(b);
|
|
35409
|
+
if (priorityDiff !== 0)
|
|
35410
|
+
return priorityDiff;
|
|
35411
|
+
const idA = canonicalRuleId(a);
|
|
35412
|
+
const idB = canonicalRuleId(b);
|
|
35413
|
+
return idA < idB ? -1 : idA > idB ? 1 : 0;
|
|
35414
|
+
});
|
|
35129
35415
|
if (mergedRules.length === 0 && (repoRulesAll.length > 0 || packageRulesCount > 0)) {
|
|
35130
35416
|
logger.warn("static-rules", "Canonical rules found but none apply to this package context", {
|
|
35131
35417
|
storyId: request.storyId,
|
|
@@ -35377,6 +35663,8 @@ var init_static_rules = __esm(() => {
|
|
|
35377
35663
|
init_logger2();
|
|
35378
35664
|
init_optimizer();
|
|
35379
35665
|
init_canonical_loader();
|
|
35666
|
+
init_canonical_rules_cache();
|
|
35667
|
+
init_canonical_rules_cache();
|
|
35380
35668
|
_staticRulesDeps = {
|
|
35381
35669
|
readFile: async (path4) => Bun.file(path4).text(),
|
|
35382
35670
|
fileExists: async (path4) => Bun.file(path4).exists(),
|
|
@@ -35387,7 +35675,7 @@ var init_static_rules = __esm(() => {
|
|
|
35387
35675
|
return [];
|
|
35388
35676
|
}
|
|
35389
35677
|
},
|
|
35390
|
-
loadCanonicalRules,
|
|
35678
|
+
loadCanonicalRules: memoizedLoadCanonicalRules,
|
|
35391
35679
|
splitRuleIntoSections,
|
|
35392
35680
|
applySectionBudget
|
|
35393
35681
|
};
|
|
@@ -35527,12 +35815,15 @@ async function readDiagnosticsDir(scratchDir) {
|
|
|
35527
35815
|
} catch {
|
|
35528
35816
|
return null;
|
|
35529
35817
|
}
|
|
35530
|
-
const
|
|
35531
|
-
if (
|
|
35818
|
+
const parsedEntries = parseToolDiagnosticsJsonl(raw);
|
|
35819
|
+
if (parsedEntries.length === 0)
|
|
35532
35820
|
return null;
|
|
35533
|
-
const
|
|
35821
|
+
const entries = parsedEntries.slice(-MAX_ENTRIES_PER_DIR2);
|
|
35822
|
+
let content = renderEntries(entries);
|
|
35534
35823
|
if (!content)
|
|
35535
35824
|
return null;
|
|
35825
|
+
if (content.length > MAX_CHUNK_CHARS)
|
|
35826
|
+
content = content.slice(0, MAX_CHUNK_CHARS);
|
|
35536
35827
|
const hash2 = contentHash89(content);
|
|
35537
35828
|
const tokens = Math.ceil(content.length / 4);
|
|
35538
35829
|
return {
|
|
@@ -35563,13 +35854,14 @@ class ToolDiagnosticsProvider {
|
|
|
35563
35854
|
return { chunks, pullTools: [] };
|
|
35564
35855
|
}
|
|
35565
35856
|
}
|
|
35566
|
-
var _toolDiagnosticsDeps;
|
|
35857
|
+
var _toolDiagnosticsDeps, MAX_ENTRIES_PER_DIR2 = 20, MAX_CHUNK_TOKENS4 = 500, MAX_CHUNK_CHARS;
|
|
35567
35858
|
var init_tool_diagnostics = __esm(() => {
|
|
35568
35859
|
init_session();
|
|
35569
35860
|
_toolDiagnosticsDeps = {
|
|
35570
35861
|
fileExists: (path4) => Bun.file(path4).exists(),
|
|
35571
|
-
readFile: (path4) =>
|
|
35862
|
+
readFile: (path4) => readJsonlTail(path4)
|
|
35572
35863
|
};
|
|
35864
|
+
MAX_CHUNK_CHARS = MAX_CHUNK_TOKENS4 * 4;
|
|
35573
35865
|
});
|
|
35574
35866
|
|
|
35575
35867
|
// src/context/engine/orchestrator-factory.ts
|
|
@@ -35760,17 +36052,19 @@ class PluginProviderCache {
|
|
|
35760
36052
|
const hit = this.cache.get(key);
|
|
35761
36053
|
if (hit)
|
|
35762
36054
|
return hit;
|
|
35763
|
-
const
|
|
35764
|
-
this.cache.set(key,
|
|
35765
|
-
|
|
36055
|
+
const loading = _pluginCacheDeps.loadProviders(enabled, workdir);
|
|
36056
|
+
this.cache.set(key, loading);
|
|
36057
|
+
loading.catch(() => this.cache.delete(key));
|
|
36058
|
+
return loading;
|
|
35766
36059
|
}
|
|
35767
36060
|
async disposeAll() {
|
|
35768
36061
|
if (this.disposed)
|
|
35769
36062
|
return;
|
|
35770
36063
|
this.disposed = true;
|
|
35771
36064
|
const logger = getLogger();
|
|
36065
|
+
const allProviders = await Promise.all([...this.cache.values()].map((loading) => loading.catch(() => [])));
|
|
35772
36066
|
const disposals = [];
|
|
35773
|
-
for (const providers of
|
|
36067
|
+
for (const providers of allProviders) {
|
|
35774
36068
|
for (const provider of providers) {
|
|
35775
36069
|
const initialisable = provider;
|
|
35776
36070
|
if (typeof initialisable.dispose !== "function")
|
|
@@ -35862,17 +36156,22 @@ var init_provider_weights = __esm(() => {
|
|
|
35862
36156
|
// src/context/engine/provider-weights-cache.ts
|
|
35863
36157
|
class ProviderWeightsCache {
|
|
35864
36158
|
cache = new Map;
|
|
36159
|
+
generations = new Map;
|
|
35865
36160
|
async loadOrGet(featureId, projectDir) {
|
|
35866
36161
|
const cached2 = this.cache.get(featureId);
|
|
35867
36162
|
if (cached2)
|
|
35868
36163
|
return cached2;
|
|
36164
|
+
const startGeneration = this.generations.get(featureId) ?? 0;
|
|
35869
36165
|
const stored = await _providerWeightsCacheDeps.loadFeatureManifests({ featureId, projectDir });
|
|
35870
36166
|
const weights = _providerWeightsCacheDeps.deriveProviderWeights(stored.map((s) => s.manifest));
|
|
35871
|
-
this.
|
|
36167
|
+
if ((this.generations.get(featureId) ?? 0) === startGeneration) {
|
|
36168
|
+
this.cache.set(featureId, weights);
|
|
36169
|
+
}
|
|
35872
36170
|
return weights;
|
|
35873
36171
|
}
|
|
35874
36172
|
invalidate(featureId) {
|
|
35875
36173
|
this.cache.delete(featureId);
|
|
36174
|
+
this.generations.set(featureId, (this.generations.get(featureId) ?? 0) + 1);
|
|
35876
36175
|
}
|
|
35877
36176
|
}
|
|
35878
36177
|
var _providerWeightsCacheDeps;
|
|
@@ -36003,7 +36302,7 @@ var init_query_scratch = __esm(() => {
|
|
|
36003
36302
|
});
|
|
36004
36303
|
|
|
36005
36304
|
// src/context/engine/stage-assembler.ts
|
|
36006
|
-
import { readdir } from "fs/promises";
|
|
36305
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
36007
36306
|
import { isAbsolute as isAbsolute8, join as join22, resolve as resolve10 } from "path";
|
|
36008
36307
|
function dedupeScratchDirs(dirs) {
|
|
36009
36308
|
return [...new Set(dirs.filter((dir) => Boolean(dir)))];
|
|
@@ -36137,7 +36436,7 @@ var init_stage_assembler = __esm(() => {
|
|
|
36137
36436
|
init_stage_config();
|
|
36138
36437
|
DISK_DISCOVERY_TTL_MS = 4 * 60 * 60 * 1000;
|
|
36139
36438
|
_stageAssemblerDeps = {
|
|
36140
|
-
readdir: (path4) =>
|
|
36439
|
+
readdir: (path4) => readdir2(path4),
|
|
36141
36440
|
readDescriptor: async (path4) => {
|
|
36142
36441
|
const f = Bun.file(path4);
|
|
36143
36442
|
if (!await f.exists())
|
|
@@ -36534,7 +36833,7 @@ var init_effectiveness = __esm(() => {
|
|
|
36534
36833
|
});
|
|
36535
36834
|
|
|
36536
36835
|
// src/context/engine/manifest-purge.ts
|
|
36537
|
-
import { dirname as
|
|
36836
|
+
import { dirname as dirname9, resolve as resolve11 } from "path";
|
|
36538
36837
|
async function purgeStaleManifests(projectDir, retentionDays) {
|
|
36539
36838
|
const allEntries = await _manifestPurgeDeps.scan(MANIFEST_PATTERN, projectDir, MAX_MANIFEST_SCAN);
|
|
36540
36839
|
if (allEntries.length >= MAX_MANIFEST_SCAN) {
|
|
@@ -36557,7 +36856,7 @@ async function purgeStaleManifests(projectDir, retentionDays) {
|
|
|
36557
36856
|
try {
|
|
36558
36857
|
await _manifestPurgeDeps.unlink(absPath);
|
|
36559
36858
|
deleted++;
|
|
36560
|
-
touchedDirs.add(
|
|
36859
|
+
touchedDirs.add(dirname9(absPath));
|
|
36561
36860
|
} catch {}
|
|
36562
36861
|
}
|
|
36563
36862
|
for (const storyDir of touchedDirs) {
|
|
@@ -36586,8 +36885,8 @@ var init_manifest_purge = __esm(() => {
|
|
|
36586
36885
|
const file3 = Bun.file(path4);
|
|
36587
36886
|
if (!await file3.exists())
|
|
36588
36887
|
throw new Error(`stat: file not found: ${path4}`);
|
|
36589
|
-
const
|
|
36590
|
-
return
|
|
36888
|
+
const stat4 = await file3.stat();
|
|
36889
|
+
return stat4.mtimeMs;
|
|
36591
36890
|
},
|
|
36592
36891
|
unlink: async (path4) => {
|
|
36593
36892
|
const { unlink: nodeUnlink } = await import("fs/promises");
|
|
@@ -38067,7 +38366,7 @@ if none of the proposals are acceptable and none can be reasonably synthesized i
|
|
|
38067
38366
|
}
|
|
38068
38367
|
function buildDebateDiffSection(ctx) {
|
|
38069
38368
|
if (ctx.mode === "ref") {
|
|
38070
|
-
const
|
|
38369
|
+
const stat4 = ctx.stat ?? "(no stat available)";
|
|
38071
38370
|
const ref = ctx.storyGitRef;
|
|
38072
38371
|
const excludes = [
|
|
38073
38372
|
...new Set([...ctx.productionExcludePatterns ?? [], ":!.nax/", ":!**/.nax/", ":!.nax-pids", ":!**/.nax-pids"])
|
|
@@ -38076,7 +38375,7 @@ function buildDebateDiffSection(ctx) {
|
|
|
38076
38375
|
return [
|
|
38077
38376
|
"## Changed Files",
|
|
38078
38377
|
"```",
|
|
38079
|
-
|
|
38378
|
+
stat4,
|
|
38080
38379
|
"```",
|
|
38081
38380
|
"",
|
|
38082
38381
|
`## Git Baseline: \`${ref}\``,
|
|
@@ -38209,7 +38508,7 @@ ${diff}\`\`\`
|
|
|
38209
38508
|
|
|
38210
38509
|
`;
|
|
38211
38510
|
}
|
|
38212
|
-
function buildRefDiffSection(storyGitRef,
|
|
38511
|
+
function buildRefDiffSection(storyGitRef, stat4, excludePatterns) {
|
|
38213
38512
|
const merged = [...new Set([...excludePatterns, ":!.nax/", ":!.nax-pids"])];
|
|
38214
38513
|
const excludeArgs = merged.map((p) => `'${p}'`).join(" ");
|
|
38215
38514
|
const productionDiffCmd = `git diff --unified=3 ${storyGitRef}..HEAD -- . ${excludeArgs}`;
|
|
@@ -38217,7 +38516,7 @@ function buildRefDiffSection(storyGitRef, stat2, excludePatterns) {
|
|
|
38217
38516
|
const logCmd = `git log --oneline ${storyGitRef}..HEAD`;
|
|
38218
38517
|
return `## Changed Files
|
|
38219
38518
|
\`\`\`
|
|
38220
|
-
${
|
|
38519
|
+
${stat4}
|
|
38221
38520
|
\`\`\`
|
|
38222
38521
|
|
|
38223
38522
|
## Git Baseline: \`${storyGitRef}\`
|
|
@@ -38439,17 +38738,17 @@ The configured blocking threshold is \`"${threshold}"\`. Findings with severity
|
|
|
38439
38738
|
|
|
38440
38739
|
`;
|
|
38441
38740
|
}
|
|
38442
|
-
function buildAdversarialRefDiffSection(storyGitRef,
|
|
38741
|
+
function buildAdversarialRefDiffSection(storyGitRef, stat4, excludePatterns = [], testGlobs = [], refExcludePatterns = []) {
|
|
38443
38742
|
const merged = [...new Set([...excludePatterns, ":!.nax/", ":!**/.nax/", ":!.nax-pids", ":!**/.nax-pids"])];
|
|
38444
38743
|
const excludeArgs = merged.map((p) => `'${p}'`).join(" ");
|
|
38445
38744
|
const productionExcludes = [
|
|
38446
38745
|
...new Set([...refExcludePatterns, ":!.nax/", ":!**/.nax/", ":!.nax-pids", ":!**/.nax-pids"])
|
|
38447
38746
|
];
|
|
38448
38747
|
const productionExcludeArgs = productionExcludes.map((p) => `'${p}'`).join(" ");
|
|
38449
|
-
const statBlock =
|
|
38748
|
+
const statBlock = stat4 ? `## Changed Files Summary
|
|
38450
38749
|
|
|
38451
38750
|
\`\`\`
|
|
38452
|
-
${
|
|
38751
|
+
${stat4}
|
|
38453
38752
|
\`\`\`
|
|
38454
38753
|
|
|
38455
38754
|
` : "";
|
|
@@ -38662,7 +38961,7 @@ var init_adversarial_review_builder = __esm(() => {
|
|
|
38662
38961
|
mode,
|
|
38663
38962
|
diff,
|
|
38664
38963
|
storyGitRef,
|
|
38665
|
-
stat:
|
|
38964
|
+
stat: stat4,
|
|
38666
38965
|
testInventory,
|
|
38667
38966
|
excludePatterns,
|
|
38668
38967
|
testGlobs,
|
|
@@ -38690,7 +38989,7 @@ ${config2.rules.map((r) => `- ${r}`).join(`
|
|
|
38690
38989
|
` : "";
|
|
38691
38990
|
let diffBlock;
|
|
38692
38991
|
if (mode === "ref" && storyGitRef) {
|
|
38693
|
-
diffBlock = buildAdversarialRefDiffSection(storyGitRef,
|
|
38992
|
+
diffBlock = buildAdversarialRefDiffSection(storyGitRef, stat4, excludePatterns ?? [], testGlobs ?? [], refExcludePatterns ?? []);
|
|
38694
38993
|
} else if (mode === "embedded" && diff) {
|
|
38695
38994
|
diffBlock = buildAdversarialEmbeddedDiffSection(diff, testInventory);
|
|
38696
38995
|
} else {
|
|
@@ -39150,8 +39449,8 @@ function stripMarkdownInline(s) {
|
|
|
39150
39449
|
function extractLocusKeywords(finding) {
|
|
39151
39450
|
const keywords = [];
|
|
39152
39451
|
if (finding.file) {
|
|
39153
|
-
const
|
|
39154
|
-
const stem =
|
|
39452
|
+
const basename8 = finding.file.split("/").pop() ?? "";
|
|
39453
|
+
const stem = basename8.replace(/\.[^.]+$/, "");
|
|
39155
39454
|
for (const part of stem.split(/[-_]/)) {
|
|
39156
39455
|
if (part.length >= 3)
|
|
39157
39456
|
keywords.push(part.toLowerCase());
|
|
@@ -40669,58 +40968,58 @@ function proposeAdjustments(bandStats, mapping, thresholds = {}) {
|
|
|
40669
40968
|
const adjustments = [];
|
|
40670
40969
|
const skipped = [];
|
|
40671
40970
|
const hints = [];
|
|
40672
|
-
for (const
|
|
40673
|
-
const currentTier = mapping[
|
|
40971
|
+
for (const stat4 of bandStats) {
|
|
40972
|
+
const currentTier = mapping[stat4.complexity];
|
|
40674
40973
|
if (currentTier === undefined) {
|
|
40675
40974
|
skipped.push({
|
|
40676
|
-
complexity:
|
|
40975
|
+
complexity: stat4.complexity,
|
|
40677
40976
|
reason: "missing-mapping"
|
|
40678
40977
|
});
|
|
40679
40978
|
continue;
|
|
40680
40979
|
}
|
|
40681
|
-
if (
|
|
40980
|
+
if (stat4.sampleCount < t.minSamples) {
|
|
40682
40981
|
skipped.push({
|
|
40683
|
-
complexity:
|
|
40982
|
+
complexity: stat4.complexity,
|
|
40684
40983
|
reason: "insufficient-samples",
|
|
40685
|
-
sampleCount:
|
|
40984
|
+
sampleCount: stat4.sampleCount,
|
|
40686
40985
|
minSamples: t.minSamples
|
|
40687
40986
|
});
|
|
40688
40987
|
continue;
|
|
40689
40988
|
}
|
|
40690
|
-
const upgraded =
|
|
40691
|
-
const downgraded =
|
|
40989
|
+
const upgraded = stat4.escalationRate >= t.upgradeEscalationRate && stat4.mismatchRate >= t.upgradeMismatchRate;
|
|
40990
|
+
const downgraded = stat4.firstPassRate >= t.downgradeFirstPassRate && stat4.escalationRate <= t.downgradeEscalationRate && stat4.mismatchRate > 0;
|
|
40692
40991
|
if (upgraded) {
|
|
40693
40992
|
const to = nextTier(currentTier, "upgrade");
|
|
40694
40993
|
if (to !== null) {
|
|
40695
40994
|
adjustments.push({
|
|
40696
|
-
band:
|
|
40697
|
-
complexity:
|
|
40995
|
+
band: stat4.complexity,
|
|
40996
|
+
complexity: stat4.complexity,
|
|
40698
40997
|
from: currentTier,
|
|
40699
40998
|
to,
|
|
40700
40999
|
fromTier: currentTier,
|
|
40701
41000
|
toTier: to,
|
|
40702
41001
|
direction: "upgrade",
|
|
40703
|
-
rationale: `escalationRate=${
|
|
41002
|
+
rationale: `escalationRate=${stat4.escalationRate} \u2265 ${t.upgradeEscalationRate}, mismatchRate=${stat4.mismatchRate} \u2265 ${t.upgradeMismatchRate}`
|
|
40704
41003
|
});
|
|
40705
41004
|
}
|
|
40706
41005
|
} else if (downgraded) {
|
|
40707
41006
|
const to = nextTier(currentTier, "downgrade");
|
|
40708
41007
|
if (to !== null) {
|
|
40709
41008
|
adjustments.push({
|
|
40710
|
-
band:
|
|
40711
|
-
complexity:
|
|
41009
|
+
band: stat4.complexity,
|
|
41010
|
+
complexity: stat4.complexity,
|
|
40712
41011
|
from: currentTier,
|
|
40713
41012
|
to,
|
|
40714
41013
|
fromTier: currentTier,
|
|
40715
41014
|
toTier: to,
|
|
40716
41015
|
direction: "downgrade",
|
|
40717
|
-
rationale: `firstPassRate=${
|
|
41016
|
+
rationale: `firstPassRate=${stat4.firstPassRate} \u2265 ${t.downgradeFirstPassRate} and escalationRate=${stat4.escalationRate} \u2264 ${t.downgradeEscalationRate}`
|
|
40718
41017
|
});
|
|
40719
41018
|
}
|
|
40720
41019
|
}
|
|
40721
|
-
if (
|
|
41020
|
+
if (stat4.mismatchRate >= t.upgradeMismatchRate) {
|
|
40722
41021
|
hints.push({
|
|
40723
|
-
message: `classify.ts: high mismatch for band "${
|
|
41022
|
+
message: `classify.ts: high mismatch for band "${stat4.complexity}" (mismatchRate=${stat4.mismatchRate}) \u2014 review keyword classification.`
|
|
40724
41023
|
});
|
|
40725
41024
|
}
|
|
40726
41025
|
}
|
|
@@ -43799,7 +44098,7 @@ var init_implement = __esm(() => {
|
|
|
43799
44098
|
});
|
|
43800
44099
|
|
|
43801
44100
|
// src/tdd/verdict-reader.ts
|
|
43802
|
-
import { unlink as
|
|
44101
|
+
import { unlink as unlink3 } from "fs/promises";
|
|
43803
44102
|
import path5 from "path";
|
|
43804
44103
|
function isValidTestFailureDiagnosis(value) {
|
|
43805
44104
|
if (!value || typeof value !== "object")
|
|
@@ -44019,7 +44318,7 @@ async function readVerdict(workdir) {
|
|
|
44019
44318
|
async function cleanupVerdict(workdir) {
|
|
44020
44319
|
const verdictPath = path5.join(workdir, VERDICT_FILE);
|
|
44021
44320
|
try {
|
|
44022
|
-
await
|
|
44321
|
+
await unlink3(verdictPath);
|
|
44023
44322
|
} catch {}
|
|
44024
44323
|
}
|
|
44025
44324
|
var VERDICT_FILE = ".nax-verifier-verdict.json";
|
|
@@ -45075,11 +45374,11 @@ async function executeWithTimeout(command, timeoutSeconds, env2, options) {
|
|
|
45075
45374
|
if (!exitedDuringGrace) {
|
|
45076
45375
|
killProcessGroup(pid, "SIGKILL");
|
|
45077
45376
|
}
|
|
45078
|
-
const [
|
|
45377
|
+
const [out2, err2] = await Promise.all([
|
|
45079
45378
|
raceWithDeadline(stdoutPromise, drainTimeoutMs),
|
|
45080
45379
|
raceWithDeadline(stderrPromise, drainTimeoutMs)
|
|
45081
45380
|
]);
|
|
45082
|
-
const parts = [
|
|
45381
|
+
const parts = [out2 !== DRAIN_TIMEOUT ? out2 : "", err2 !== DRAIN_TIMEOUT ? err2 : ""].filter(Boolean);
|
|
45083
45382
|
const partialOutput = parts.join(`
|
|
45084
45383
|
`) || undefined;
|
|
45085
45384
|
return {
|
|
@@ -45093,7 +45392,12 @@ async function executeWithTimeout(command, timeoutSeconds, env2, options) {
|
|
|
45093
45392
|
};
|
|
45094
45393
|
}
|
|
45095
45394
|
const exitCode = typeof raceResult === "number" ? raceResult : 0;
|
|
45096
|
-
const [
|
|
45395
|
+
const [out, err] = await Promise.all([
|
|
45396
|
+
raceWithDeadline(stdoutPromise, drainTimeoutMs),
|
|
45397
|
+
raceWithDeadline(stderrPromise, drainTimeoutMs)
|
|
45398
|
+
]);
|
|
45399
|
+
const stdout = out !== DRAIN_TIMEOUT ? out : "";
|
|
45400
|
+
const stderr = err !== DRAIN_TIMEOUT ? err : "";
|
|
45097
45401
|
const output = `${stdout}
|
|
45098
45402
|
${stderr}`;
|
|
45099
45403
|
return {
|
|
@@ -45121,9 +45425,31 @@ function appendForceExitFlag(command) {
|
|
|
45121
45425
|
return appendFlag(command, "--forceExit");
|
|
45122
45426
|
}
|
|
45123
45427
|
function appendFlag(command, flag) {
|
|
45124
|
-
|
|
45125
|
-
|
|
45126
|
-
|
|
45428
|
+
let quote = null;
|
|
45429
|
+
for (let i = 0;i < command.length; i++) {
|
|
45430
|
+
const char = command[i];
|
|
45431
|
+
if (quote) {
|
|
45432
|
+
if (char === quote)
|
|
45433
|
+
quote = null;
|
|
45434
|
+
continue;
|
|
45435
|
+
}
|
|
45436
|
+
if (char === '"' || char === "'") {
|
|
45437
|
+
quote = char;
|
|
45438
|
+
continue;
|
|
45439
|
+
}
|
|
45440
|
+
if (char === "|" || char === ">") {
|
|
45441
|
+
let splitIndex = i;
|
|
45442
|
+
if (char === ">") {
|
|
45443
|
+
let digitStart = splitIndex;
|
|
45444
|
+
while (digitStart > 0 && /[0-9]/.test(command[digitStart - 1] ?? ""))
|
|
45445
|
+
digitStart--;
|
|
45446
|
+
const charBeforeDigits = command[digitStart - 1];
|
|
45447
|
+
const isStandaloneFdToken = digitStart < splitIndex && (digitStart === 0 || /[\s|;&]/.test(charBeforeDigits ?? ""));
|
|
45448
|
+
if (isStandaloneFdToken)
|
|
45449
|
+
splitIndex = digitStart;
|
|
45450
|
+
}
|
|
45451
|
+
return `${command.slice(0, splitIndex).trimEnd()} ${flag} ${command.slice(splitIndex)}`;
|
|
45452
|
+
}
|
|
45127
45453
|
}
|
|
45128
45454
|
return `${command} ${flag}`;
|
|
45129
45455
|
}
|
|
@@ -45933,7 +46259,7 @@ function createDrainDeadline(deadlineMs) {
|
|
|
45933
46259
|
};
|
|
45934
46260
|
}
|
|
45935
46261
|
async function runQualityCommand(opts) {
|
|
45936
|
-
const { commandName, command, workdir, storyId, timeoutMs =
|
|
46262
|
+
const { commandName, command, workdir, storyId, timeoutMs = DEFAULT_TIMEOUT_MS2, env: env2, stripEnvVars } = opts;
|
|
45937
46263
|
if (!command || command.trim() === "") {
|
|
45938
46264
|
return {
|
|
45939
46265
|
commandName,
|
|
@@ -46046,7 +46372,7 @@ async function runQualityCommand(opts) {
|
|
|
46046
46372
|
};
|
|
46047
46373
|
}
|
|
46048
46374
|
}
|
|
46049
|
-
var
|
|
46375
|
+
var DEFAULT_TIMEOUT_MS2 = 120000, SIGKILL_GRACE_PERIOD_MS = 5000, STREAM_DRAIN_TIMEOUT_MS = 2000, _qualityRunnerDeps;
|
|
46050
46376
|
var init_runner = __esm(() => {
|
|
46051
46377
|
init_logger2();
|
|
46052
46378
|
_qualityRunnerDeps = {
|
|
@@ -47512,12 +47838,12 @@ function journalPathFor(repoRoot, storyId) {
|
|
|
47512
47838
|
return join31(journalDir(repoRoot), journalFileName(storyId));
|
|
47513
47839
|
}
|
|
47514
47840
|
async function mayHaveJournal(candidateRoots) {
|
|
47515
|
-
const { stat:
|
|
47841
|
+
const { stat: stat4 } = await import("fs/promises");
|
|
47516
47842
|
for (const root of candidateRoots) {
|
|
47517
47843
|
if (!root)
|
|
47518
47844
|
continue;
|
|
47519
47845
|
try {
|
|
47520
|
-
if ((await
|
|
47846
|
+
if ((await stat4(journalDir(root))).isDirectory())
|
|
47521
47847
|
return true;
|
|
47522
47848
|
} catch {}
|
|
47523
47849
|
}
|
|
@@ -47529,8 +47855,8 @@ async function recordInFlight(repoRoot, entry) {
|
|
|
47529
47855
|
await Bun.write(journalPathFor(repoRoot, entry.storyId), JSON.stringify(entry));
|
|
47530
47856
|
}
|
|
47531
47857
|
async function clearInFlight(repoRoot, storyId) {
|
|
47532
|
-
const { unlink:
|
|
47533
|
-
await
|
|
47858
|
+
const { unlink: unlink4 } = await import("fs/promises");
|
|
47859
|
+
await unlink4(journalPathFor(repoRoot, storyId)).catch(() => {});
|
|
47534
47860
|
}
|
|
47535
47861
|
async function readEntry(path7) {
|
|
47536
47862
|
try {
|
|
@@ -47544,11 +47870,11 @@ async function readEntry(path7) {
|
|
|
47544
47870
|
}
|
|
47545
47871
|
}
|
|
47546
47872
|
async function restoreInFlight(repoRoot) {
|
|
47547
|
-
const { readdir:
|
|
47873
|
+
const { readdir: readdir3, unlink: unlink4 } = await import("fs/promises");
|
|
47548
47874
|
const dir = journalDir(repoRoot);
|
|
47549
47875
|
let names;
|
|
47550
47876
|
try {
|
|
47551
|
-
names = await
|
|
47877
|
+
names = await readdir3(dir);
|
|
47552
47878
|
} catch {
|
|
47553
47879
|
return [];
|
|
47554
47880
|
}
|
|
@@ -47559,13 +47885,13 @@ async function restoreInFlight(repoRoot) {
|
|
|
47559
47885
|
const path7 = join31(dir, name);
|
|
47560
47886
|
const entry = await readEntry(path7);
|
|
47561
47887
|
if (!entry) {
|
|
47562
|
-
await
|
|
47888
|
+
await unlink4(path7).catch(() => {});
|
|
47563
47889
|
continue;
|
|
47564
47890
|
}
|
|
47565
47891
|
if (!isInside(repoRoot, entry.file))
|
|
47566
47892
|
continue;
|
|
47567
47893
|
results.push(await restoreEntry(entry));
|
|
47568
|
-
await
|
|
47894
|
+
await unlink4(path7).catch(() => {});
|
|
47569
47895
|
}
|
|
47570
47896
|
return results;
|
|
47571
47897
|
}
|
|
@@ -48486,8 +48812,12 @@ function recordReviewIteration(store, storyId, roundFindings) {
|
|
|
48486
48812
|
startedAt: now,
|
|
48487
48813
|
finishedAt: now
|
|
48488
48814
|
};
|
|
48489
|
-
|
|
48815
|
+
const merged = [...prior, iteration];
|
|
48816
|
+
const trimmed = merged.length > MAX_ITERATIONS_PER_STORY ? merged.slice(-MAX_ITERATIONS_PER_STORY) : merged;
|
|
48817
|
+
const renumbered = trimmed.map((it, i) => ({ ...it, iterationNum: i + 1 }));
|
|
48818
|
+
store.set(storyId, renumbered);
|
|
48490
48819
|
}
|
|
48820
|
+
var MAX_ITERATIONS_PER_STORY = 10;
|
|
48491
48821
|
var init_review_iteration_store = __esm(() => {
|
|
48492
48822
|
init_findings();
|
|
48493
48823
|
});
|
|
@@ -48602,15 +48932,15 @@ async function collectDiffStat(workdir, storyGitRef, options) {
|
|
|
48602
48932
|
]);
|
|
48603
48933
|
return exitCode === 0 ? stdout.trim() : "";
|
|
48604
48934
|
}
|
|
48605
|
-
function truncateDiff(diff,
|
|
48935
|
+
function truncateDiff(diff, stat4) {
|
|
48606
48936
|
if (diff.length <= DIFF_CAP_BYTES) {
|
|
48607
48937
|
return diff;
|
|
48608
48938
|
}
|
|
48609
48939
|
const truncated = diff.slice(0, DIFF_CAP_BYTES);
|
|
48610
48940
|
const visibleFiles = (truncated.match(/^diff --git/gm) ?? []).length;
|
|
48611
48941
|
const totalFiles = (diff.match(/^diff --git/gm) ?? []).length;
|
|
48612
|
-
const statPreamble =
|
|
48613
|
-
${
|
|
48942
|
+
const statPreamble = stat4 ? `## File Summary (all changed files)
|
|
48943
|
+
${stat4}
|
|
48614
48944
|
|
|
48615
48945
|
## Diff (truncated \u2014 ${visibleFiles}/${totalFiles} files shown)
|
|
48616
48946
|
` : "";
|
|
@@ -48831,25 +49161,25 @@ async function prepareSemanticReviewInput(args) {
|
|
|
48831
49161
|
};
|
|
48832
49162
|
}
|
|
48833
49163
|
const { packageDir, packageDirRelative } = derivePackageDirs(workdir, projectDir);
|
|
48834
|
-
const
|
|
49164
|
+
const stat4 = await collectDiffStat(workdir, effectiveRef, { naxIgnoreIndex, packageDir });
|
|
48835
49165
|
const resolved = args.resolvedTestPatterns ?? await resolveTestFilePatterns(config2 ?? reviewConfigSelector.select(DEFAULT_CONFIG), projectDir ?? workdir, packageDirRelative);
|
|
48836
49166
|
const excludePatterns = [...resolveReviewExcludePatterns(semanticConfig.excludePatterns, resolved)];
|
|
48837
49167
|
const diffMode = semanticConfig.diffMode ?? "ref";
|
|
48838
49168
|
if (diffMode === "ref") {
|
|
48839
|
-
if (!
|
|
49169
|
+
if (!stat4) {
|
|
48840
49170
|
return { effectiveRef, stat: "", diff: undefined, excludePatterns, skipReason: "no changes detected" };
|
|
48841
49171
|
}
|
|
48842
|
-
return { effectiveRef, stat:
|
|
49172
|
+
return { effectiveRef, stat: stat4, diff: undefined, excludePatterns };
|
|
48843
49173
|
}
|
|
48844
49174
|
const rawDiff = await collectDiff(workdir, effectiveRef, excludePatterns, { naxIgnoreIndex, packageDir });
|
|
48845
49175
|
if (rawDiff === null) {
|
|
48846
|
-
return { effectiveRef, stat:
|
|
49176
|
+
return { effectiveRef, stat: stat4, diff: undefined, excludePatterns, skipReason: "git diff failed" };
|
|
48847
49177
|
}
|
|
48848
|
-
const diff = truncateDiff(rawDiff, rawDiff.length > DIFF_CAP_BYTES ?
|
|
49178
|
+
const diff = truncateDiff(rawDiff, rawDiff.length > DIFF_CAP_BYTES ? stat4 : undefined);
|
|
48849
49179
|
if (!diff) {
|
|
48850
|
-
return { effectiveRef, stat:
|
|
49180
|
+
return { effectiveRef, stat: stat4, diff: undefined, excludePatterns, skipReason: "no production code changes" };
|
|
48851
49181
|
}
|
|
48852
|
-
return { effectiveRef, stat:
|
|
49182
|
+
return { effectiveRef, stat: stat4, diff, excludePatterns };
|
|
48853
49183
|
}
|
|
48854
49184
|
async function prepareAdversarialReviewInput(args) {
|
|
48855
49185
|
const { workdir, projectDir, storyId, storyGitRef, config: config2, naxIgnoreIndex, adversarialConfig } = args;
|
|
@@ -48867,9 +49197,9 @@ async function prepareAdversarialReviewInput(args) {
|
|
|
48867
49197
|
};
|
|
48868
49198
|
}
|
|
48869
49199
|
const { packageDir, packageDirRelative } = derivePackageDirs(workdir, projectDir);
|
|
48870
|
-
const
|
|
49200
|
+
const stat4 = await collectDiffStat(workdir, effectiveRef, { naxIgnoreIndex, packageDir });
|
|
48871
49201
|
const diffMode = adversarialConfig.diffMode ?? "ref";
|
|
48872
|
-
if (diffMode === "ref" && !
|
|
49202
|
+
if (diffMode === "ref" && !stat4) {
|
|
48873
49203
|
return {
|
|
48874
49204
|
effectiveRef,
|
|
48875
49205
|
stat: "",
|
|
@@ -48890,7 +49220,7 @@ async function prepareAdversarialReviewInput(args) {
|
|
|
48890
49220
|
if (diffMode === "ref") {
|
|
48891
49221
|
return {
|
|
48892
49222
|
effectiveRef,
|
|
48893
|
-
stat:
|
|
49223
|
+
stat: stat4,
|
|
48894
49224
|
diff: undefined,
|
|
48895
49225
|
testInventory: undefined,
|
|
48896
49226
|
excludePatterns,
|
|
@@ -48902,7 +49232,7 @@ async function prepareAdversarialReviewInput(args) {
|
|
|
48902
49232
|
if (!diff) {
|
|
48903
49233
|
return {
|
|
48904
49234
|
effectiveRef,
|
|
48905
|
-
stat:
|
|
49235
|
+
stat: stat4,
|
|
48906
49236
|
diff: undefined,
|
|
48907
49237
|
testInventory: undefined,
|
|
48908
49238
|
excludePatterns,
|
|
@@ -48915,7 +49245,7 @@ async function prepareAdversarialReviewInput(args) {
|
|
|
48915
49245
|
naxIgnoreIndex,
|
|
48916
49246
|
packageDir
|
|
48917
49247
|
});
|
|
48918
|
-
return { effectiveRef, stat:
|
|
49248
|
+
return { effectiveRef, stat: stat4, diff, testInventory, excludePatterns, testGlobs, refExcludePatterns };
|
|
48919
49249
|
}
|
|
48920
49250
|
var init_prepare_inputs = __esm(() => {
|
|
48921
49251
|
init_config();
|
|
@@ -48924,14 +49254,14 @@ var init_prepare_inputs = __esm(() => {
|
|
|
48924
49254
|
});
|
|
48925
49255
|
|
|
48926
49256
|
// src/utils/nax-project-root.ts
|
|
48927
|
-
import { dirname as
|
|
49257
|
+
import { dirname as dirname10, join as join33, resolve as resolve13 } from "path";
|
|
48928
49258
|
async function findNaxProjectRoot(startDir) {
|
|
48929
49259
|
let dir = resolve13(startDir);
|
|
48930
49260
|
for (let depth = 0;depth < MAX_NAX_WALK_DEPTH; depth++) {
|
|
48931
49261
|
if (await _naxProjectRootDeps.exists(join33(dir, ".nax", "config.json"))) {
|
|
48932
49262
|
return dir;
|
|
48933
49263
|
}
|
|
48934
|
-
const parent =
|
|
49264
|
+
const parent = dirname10(dir);
|
|
48935
49265
|
if (parent === dir)
|
|
48936
49266
|
break;
|
|
48937
49267
|
dir = parent;
|
|
@@ -48952,7 +49282,7 @@ var package_default;
|
|
|
48952
49282
|
var init_package = __esm(() => {
|
|
48953
49283
|
package_default = {
|
|
48954
49284
|
name: "@nathapp/nax",
|
|
48955
|
-
version: "0.80.0
|
|
49285
|
+
version: "0.80.0",
|
|
48956
49286
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
48957
49287
|
type: "module",
|
|
48958
49288
|
bin: {
|
|
@@ -49067,8 +49397,8 @@ var init_version = __esm(() => {
|
|
|
49067
49397
|
NAX_VERSION = package_default.version;
|
|
49068
49398
|
NAX_COMMIT = (() => {
|
|
49069
49399
|
try {
|
|
49070
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
49071
|
-
return "
|
|
49400
|
+
if (/^[0-9a-f]{6,10}$/.test("b4df507e"))
|
|
49401
|
+
return "b4df507e";
|
|
49072
49402
|
} catch {}
|
|
49073
49403
|
try {
|
|
49074
49404
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -49309,7 +49639,7 @@ async function runAdversarialReview(opts) {
|
|
|
49309
49639
|
};
|
|
49310
49640
|
}
|
|
49311
49641
|
const effectiveRef = prepared.effectiveRef;
|
|
49312
|
-
const
|
|
49642
|
+
const stat4 = prepared.stat;
|
|
49313
49643
|
const diff = prepared.diff;
|
|
49314
49644
|
const testInventory = prepared.testInventory;
|
|
49315
49645
|
const effectiveRefExcludePatterns = prepared.refExcludePatterns;
|
|
@@ -49370,7 +49700,7 @@ async function runAdversarialReview(opts) {
|
|
|
49370
49700
|
mode: diffMode,
|
|
49371
49701
|
diff,
|
|
49372
49702
|
storyGitRef: effectiveRef,
|
|
49373
|
-
stat:
|
|
49703
|
+
stat: stat4,
|
|
49374
49704
|
testInventory,
|
|
49375
49705
|
excludePatterns: adversarialConfig.excludePatterns,
|
|
49376
49706
|
testGlobs,
|
|
@@ -50162,7 +50492,7 @@ async function runSemanticDebate(opts) {
|
|
|
50162
50492
|
story,
|
|
50163
50493
|
diffMode,
|
|
50164
50494
|
diff,
|
|
50165
|
-
stat:
|
|
50495
|
+
stat: stat4,
|
|
50166
50496
|
semanticConfig,
|
|
50167
50497
|
effectiveRef,
|
|
50168
50498
|
startTime,
|
|
@@ -50428,7 +50758,7 @@ async function runSemanticReview(opts) {
|
|
|
50428
50758
|
};
|
|
50429
50759
|
}
|
|
50430
50760
|
const effectiveRef = prepared.effectiveRef;
|
|
50431
|
-
const
|
|
50761
|
+
const stat4 = prepared.stat;
|
|
50432
50762
|
const diff = prepared.diff;
|
|
50433
50763
|
const excludePatterns = prepared.excludePatterns;
|
|
50434
50764
|
const effectiveAgentManager = runtime?.agentManager ?? agentManager;
|
|
@@ -50468,7 +50798,7 @@ async function runSemanticReview(opts) {
|
|
|
50468
50798
|
mode: diffMode,
|
|
50469
50799
|
diff,
|
|
50470
50800
|
storyGitRef: effectiveRef,
|
|
50471
|
-
stat:
|
|
50801
|
+
stat: stat4,
|
|
50472
50802
|
priorSemanticIterations,
|
|
50473
50803
|
excludePatterns: semanticConfig.excludePatterns
|
|
50474
50804
|
});
|
|
@@ -50501,7 +50831,7 @@ async function runSemanticReview(opts) {
|
|
|
50501
50831
|
story,
|
|
50502
50832
|
diffMode,
|
|
50503
50833
|
diff,
|
|
50504
|
-
stat:
|
|
50834
|
+
stat: stat4,
|
|
50505
50835
|
semanticConfig,
|
|
50506
50836
|
effectiveRef,
|
|
50507
50837
|
startTime,
|
|
@@ -50535,7 +50865,7 @@ async function runSemanticReview(opts) {
|
|
|
50535
50865
|
mode: diffMode,
|
|
50536
50866
|
diff,
|
|
50537
50867
|
storyGitRef: effectiveRef,
|
|
50538
|
-
stat:
|
|
50868
|
+
stat: stat4,
|
|
50539
50869
|
priorSemanticIterations,
|
|
50540
50870
|
excludePatterns,
|
|
50541
50871
|
featureCtxBlock,
|
|
@@ -50841,18 +51171,11 @@ function normalizeMechanicalFindings(checkName, result, workdir) {
|
|
|
50841
51171
|
}
|
|
50842
51172
|
async function getUncommittedFilesImpl(workdir) {
|
|
50843
51173
|
try {
|
|
50844
|
-
const
|
|
50845
|
-
cmd: ["git", "diff", "--name-only", "HEAD"],
|
|
50846
|
-
cwd: workdir,
|
|
50847
|
-
stdout: "pipe",
|
|
50848
|
-
stderr: "pipe"
|
|
50849
|
-
});
|
|
50850
|
-
const exitCode = await proc.exited;
|
|
51174
|
+
const { stdout, exitCode } = await gitWithTimeout(["diff", "--name-only", "HEAD"], workdir);
|
|
50851
51175
|
if (exitCode !== 0) {
|
|
50852
51176
|
return [];
|
|
50853
51177
|
}
|
|
50854
|
-
|
|
50855
|
-
return output.trim().split(`
|
|
51178
|
+
return stdout.trim().split(`
|
|
50856
51179
|
`).filter(Boolean);
|
|
50857
51180
|
} catch {
|
|
50858
51181
|
return [];
|
|
@@ -53127,6 +53450,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
53127
53450
|
role: resolvedRunOptions.sessionRole ?? "implementer",
|
|
53128
53451
|
workdir,
|
|
53129
53452
|
pipelineStage: stage,
|
|
53453
|
+
config: config2,
|
|
53130
53454
|
modelDef,
|
|
53131
53455
|
...resolvedRunOptions.modelDef !== undefined ? {} : { modelTier: effectiveTier },
|
|
53132
53456
|
timeoutSeconds: resolvedRunOptions.timeoutSeconds ?? config2.execution?.sessionTimeoutSeconds ?? DEFAULT_CONFIG.execution.sessionTimeoutSeconds,
|
|
@@ -53143,6 +53467,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
53143
53467
|
role: resolvedRunOptions.sessionRole ?? "implementer",
|
|
53144
53468
|
workdir,
|
|
53145
53469
|
pipelineStage: stage,
|
|
53470
|
+
config: config2,
|
|
53146
53471
|
modelDef,
|
|
53147
53472
|
...pinned ? {} : { modelTier: effectiveTier },
|
|
53148
53473
|
timeoutSeconds: resolvedRunOptions.timeoutSeconds ?? config2.execution?.sessionTimeoutSeconds ?? DEFAULT_CONFIG.execution.sessionTimeoutSeconds,
|
|
@@ -53159,6 +53484,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
53159
53484
|
workdir,
|
|
53160
53485
|
projectDir,
|
|
53161
53486
|
pipelineStage: stage,
|
|
53487
|
+
config: config2,
|
|
53162
53488
|
sessionRole: resolvedRunOptions.sessionRole,
|
|
53163
53489
|
signal: resolvedRunOptions.abortSignal,
|
|
53164
53490
|
contextPullTools,
|
|
@@ -55285,8 +55611,8 @@ async function triageFlakyFindings(input) {
|
|
|
55285
55611
|
result.push({ ...f });
|
|
55286
55612
|
return { findings: result, quarantineReport: { keys, reasons } };
|
|
55287
55613
|
}
|
|
55288
|
-
const changedTestSet = new Set(diff.changedTestFiles.map(
|
|
55289
|
-
const mappedTestSet = new Set(diff.mappedTestFiles.map(
|
|
55614
|
+
const changedTestSet = new Set(diff.changedTestFiles.map(basename8));
|
|
55615
|
+
const mappedTestSet = new Set(diff.mappedTestFiles.map(basename8));
|
|
55290
55616
|
const candidates = findings.filter((f) => isProbeCandidate(f, changedTestSet, mappedTestSet));
|
|
55291
55617
|
if (candidates.length > flakeDetection.maxProbesPerGate) {
|
|
55292
55618
|
logger?.info("flake-triage", `Skipping flake triage \u2014 ${candidates.length} candidates exceed maxProbesPerGate=${flakeDetection.maxProbesPerGate}`);
|
|
@@ -55356,7 +55682,7 @@ async function triageFlakyFindings(input) {
|
|
|
55356
55682
|
}
|
|
55357
55683
|
return { findings: result, quarantineReport: { keys, reasons } };
|
|
55358
55684
|
}
|
|
55359
|
-
function
|
|
55685
|
+
function basename8(path8) {
|
|
55360
55686
|
const i = path8.lastIndexOf("/");
|
|
55361
55687
|
return i === -1 ? path8 : path8.slice(i + 1);
|
|
55362
55688
|
}
|
|
@@ -55367,7 +55693,7 @@ function isProbeCandidate(finding, changedTestSet, mappedTestSet) {
|
|
|
55367
55693
|
return false;
|
|
55368
55694
|
if (!finding.rule)
|
|
55369
55695
|
return false;
|
|
55370
|
-
const base =
|
|
55696
|
+
const base = basename8(finding.file);
|
|
55371
55697
|
if (changedTestSet.has(base) || changedTestSet.has(finding.file))
|
|
55372
55698
|
return false;
|
|
55373
55699
|
if (mappedTestSet.has(base) || mappedTestSet.has(finding.file))
|
|
@@ -55404,6 +55730,7 @@ function createSessionRunHop(sessionManager, getAgentManager) {
|
|
|
55404
55730
|
role: options.sessionRole,
|
|
55405
55731
|
workdir: options.workdir,
|
|
55406
55732
|
pipelineStage: options.pipelineStage ?? "run",
|
|
55733
|
+
config: options.config,
|
|
55407
55734
|
modelDef: options.modelDef,
|
|
55408
55735
|
timeoutSeconds: options.timeoutSeconds,
|
|
55409
55736
|
featureName: options.featureName,
|
|
@@ -55422,6 +55749,7 @@ function createSessionRunHop(sessionManager, getAgentManager) {
|
|
|
55422
55749
|
workdir: options.workdir,
|
|
55423
55750
|
projectDir: options.projectDir,
|
|
55424
55751
|
pipelineStage: options.pipelineStage ?? "run",
|
|
55752
|
+
config: options.config,
|
|
55425
55753
|
sessionRole: options.sessionRole,
|
|
55426
55754
|
signal: options.abortSignal,
|
|
55427
55755
|
interactionHandler,
|
|
@@ -55513,7 +55841,7 @@ __export(exports_runtime, {
|
|
|
55513
55841
|
CostAggregator: () => CostAggregator,
|
|
55514
55842
|
AgentStreamEventBus: () => AgentStreamEventBus
|
|
55515
55843
|
});
|
|
55516
|
-
import { basename as
|
|
55844
|
+
import { basename as basename9, join as join38 } from "path";
|
|
55517
55845
|
function createRuntime(config2, workdir, opts) {
|
|
55518
55846
|
const runId = crypto.randomUUID();
|
|
55519
55847
|
const controller = new AbortController;
|
|
@@ -55525,7 +55853,7 @@ function createRuntime(config2, workdir, opts) {
|
|
|
55525
55853
|
const configLoader = createConfigLoader(config2);
|
|
55526
55854
|
const dispatchEvents = new DispatchEventBus;
|
|
55527
55855
|
const agentStreamEvents = opts?.agentStreamEvents ?? new AgentStreamEventBus;
|
|
55528
|
-
const projectKey = config2.name?.trim() ||
|
|
55856
|
+
const projectKey = config2.name?.trim() || basename9(workdir);
|
|
55529
55857
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
55530
55858
|
const globalDir = globalOutputDir();
|
|
55531
55859
|
const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
|
|
@@ -58000,11 +58328,28 @@ class InteractionChain {
|
|
|
58000
58328
|
return this.plugins[0]?.plugin ?? null;
|
|
58001
58329
|
}
|
|
58002
58330
|
async send(request) {
|
|
58003
|
-
|
|
58004
|
-
|
|
58005
|
-
|
|
58331
|
+
if (this.plugins.length === 0) {
|
|
58332
|
+
throw new NaxError("No interaction plugin registered", "INTERACTION_ERROR", {
|
|
58333
|
+
stage: "run",
|
|
58334
|
+
requestId: request.id
|
|
58335
|
+
});
|
|
58336
|
+
}
|
|
58337
|
+
const errors3 = [];
|
|
58338
|
+
for (const entry of this.plugins) {
|
|
58339
|
+
try {
|
|
58340
|
+
await entry.plugin.send(request);
|
|
58341
|
+
return;
|
|
58342
|
+
} catch (err) {
|
|
58343
|
+
const error48 = err instanceof Error ? err : new Error(String(err));
|
|
58344
|
+
errors3.push(error48);
|
|
58345
|
+
}
|
|
58006
58346
|
}
|
|
58007
|
-
|
|
58347
|
+
const errorMessages = errors3.map((e) => e.message).join("; ");
|
|
58348
|
+
throw new NaxError(`All interaction plugins failed: ${errorMessages}`, "INTERACTION_ERROR", {
|
|
58349
|
+
stage: "run",
|
|
58350
|
+
requestId: request.id,
|
|
58351
|
+
pluginCount: this.plugins.length
|
|
58352
|
+
});
|
|
58008
58353
|
}
|
|
58009
58354
|
async receive(requestId, timeout) {
|
|
58010
58355
|
if (this.plugins.length === 0) {
|
|
@@ -58393,10 +58738,10 @@ function buildHeader(request) {
|
|
|
58393
58738
|
const emoji3 = getStageEmoji(request.stage);
|
|
58394
58739
|
let text = `${emoji3} *${request.stage.toUpperCase()}*
|
|
58395
58740
|
`;
|
|
58396
|
-
text += `*Feature:* ${request.featureName}
|
|
58741
|
+
text += `*Feature:* ${sanitizeMarkdown(request.featureName)}
|
|
58397
58742
|
`;
|
|
58398
58743
|
if (request.storyId) {
|
|
58399
|
-
text += `*Story:* ${request.storyId}
|
|
58744
|
+
text += `*Story:* ${sanitizeMarkdown(request.storyId)}
|
|
58400
58745
|
`;
|
|
58401
58746
|
}
|
|
58402
58747
|
text += `
|
|
@@ -58603,16 +58948,24 @@ var init_telegram = __esm(() => {
|
|
|
58603
58948
|
const partLabel = chunks.length > 1 ? `[${i + 1}/${chunks.length}] ` : "";
|
|
58604
58949
|
const text = `${header}
|
|
58605
58950
|
${partLabel}${chunks[i]}`;
|
|
58606
|
-
const
|
|
58607
|
-
|
|
58608
|
-
|
|
58609
|
-
|
|
58610
|
-
|
|
58611
|
-
|
|
58612
|
-
|
|
58613
|
-
|
|
58614
|
-
|
|
58615
|
-
|
|
58951
|
+
const controller = new AbortController;
|
|
58952
|
+
const timer = setTimeout(() => controller.abort(), 5000);
|
|
58953
|
+
let response;
|
|
58954
|
+
try {
|
|
58955
|
+
response = await _telegramPluginDeps.fetch(`https://api.telegram.org/bot${this.botToken}/sendMessage`, {
|
|
58956
|
+
method: "POST",
|
|
58957
|
+
headers: { "Content-Type": "application/json" },
|
|
58958
|
+
body: JSON.stringify({
|
|
58959
|
+
chat_id: this.chatId,
|
|
58960
|
+
text,
|
|
58961
|
+
reply_markup: isLast && keyboard ? { inline_keyboard: keyboard } : undefined,
|
|
58962
|
+
parse_mode: "Markdown"
|
|
58963
|
+
}),
|
|
58964
|
+
signal: controller.signal
|
|
58965
|
+
});
|
|
58966
|
+
} finally {
|
|
58967
|
+
clearTimeout(timer);
|
|
58968
|
+
}
|
|
58616
58969
|
if (!response.ok) {
|
|
58617
58970
|
const errorBody = await response.text().catch(() => "");
|
|
58618
58971
|
throw new Error(`Telegram API error (${response.status}): ${errorBody || response.statusText}`);
|
|
@@ -58648,8 +59001,9 @@ ${partLabel}${chunks[i]}`;
|
|
|
58648
59001
|
});
|
|
58649
59002
|
}
|
|
58650
59003
|
async cancel(requestId) {
|
|
58651
|
-
|
|
59004
|
+
const pending = this.pendingMessages.get(requestId);
|
|
58652
59005
|
this.resolveReceiver(requestId, "skip", "timeout");
|
|
59006
|
+
this.sendTimeoutMessage(requestId, pending);
|
|
58653
59007
|
}
|
|
58654
59008
|
ensurePoller() {
|
|
58655
59009
|
if (this.poller)
|
|
@@ -58709,8 +59063,9 @@ ${partLabel}${chunks[i]}`;
|
|
|
58709
59063
|
});
|
|
58710
59064
|
}
|
|
58711
59065
|
async expireReceiver(requestId) {
|
|
58712
|
-
|
|
59066
|
+
const pending = this.pendingMessages.get(requestId);
|
|
58713
59067
|
this.resolveReceiver(requestId, "skip", "timeout");
|
|
59068
|
+
this.sendTimeoutMessage(requestId, pending);
|
|
58714
59069
|
}
|
|
58715
59070
|
resolveReceiver(requestId, action, respondedBy) {
|
|
58716
59071
|
this.resolveReceiverWithResponse(requestId, { requestId, action, respondedBy, respondedAt: Date.now() });
|
|
@@ -58862,24 +59217,31 @@ ${partLabel}${chunks[i]}`;
|
|
|
58862
59217
|
}
|
|
58863
59218
|
} catch {}
|
|
58864
59219
|
}
|
|
58865
|
-
async sendTimeoutMessage(requestId) {
|
|
58866
|
-
const pending = this.pendingMessages.get(requestId);
|
|
59220
|
+
async sendTimeoutMessage(requestId, pendingArg) {
|
|
59221
|
+
const pending = pendingArg ?? this.pendingMessages.get(requestId);
|
|
58867
59222
|
if (!pending || !this.botToken || !this.chatId) {
|
|
58868
59223
|
this.pendingMessages.delete(requestId);
|
|
58869
59224
|
return;
|
|
58870
59225
|
}
|
|
58871
59226
|
const lastId = pending.ids[pending.ids.length - 1];
|
|
58872
59227
|
try {
|
|
58873
|
-
|
|
58874
|
-
|
|
58875
|
-
|
|
58876
|
-
|
|
58877
|
-
|
|
58878
|
-
|
|
58879
|
-
|
|
58880
|
-
|
|
58881
|
-
|
|
58882
|
-
|
|
59228
|
+
const controller = new AbortController;
|
|
59229
|
+
const timer = setTimeout(() => controller.abort(), CALLBACK_API_TIMEOUT_MS);
|
|
59230
|
+
try {
|
|
59231
|
+
await _telegramPluginDeps.fetch(`https://api.telegram.org/bot${this.botToken}/editMessageText`, {
|
|
59232
|
+
method: "POST",
|
|
59233
|
+
headers: { "Content-Type": "application/json" },
|
|
59234
|
+
body: JSON.stringify({
|
|
59235
|
+
chat_id: this.chatId,
|
|
59236
|
+
message_id: lastId,
|
|
59237
|
+
text: "\u23F1 EXPIRED \u2014 Interaction timed out",
|
|
59238
|
+
reply_markup: { inline_keyboard: [] }
|
|
59239
|
+
}),
|
|
59240
|
+
signal: controller.signal
|
|
59241
|
+
});
|
|
59242
|
+
} finally {
|
|
59243
|
+
clearTimeout(timer);
|
|
59244
|
+
}
|
|
58883
59245
|
} catch {} finally {
|
|
58884
59246
|
this.pendingMessages.delete(requestId);
|
|
58885
59247
|
}
|
|
@@ -58974,7 +59336,7 @@ function installServePortZeroCompat() {
|
|
|
58974
59336
|
const request = input instanceof Request ? input : new Request(input instanceof URL ? input.toString() : input, init);
|
|
58975
59337
|
const url2 = new URL(request.url);
|
|
58976
59338
|
const port = Number.parseInt(url2.port, 10);
|
|
58977
|
-
if ((url2.hostname === "localhost" || url2.hostname === "127.0.0.1") && inMemoryServers.has(port)) {
|
|
59339
|
+
if ((url2.hostname === "localhost" || url2.hostname === "127.0.0.1") && url2.pathname.startsWith(CALLBACK_PATH_PREFIX) && inMemoryServers.has(port)) {
|
|
58978
59340
|
const server = inMemoryServers.get(port);
|
|
58979
59341
|
if (!server) {
|
|
58980
59342
|
return new Response("Not Found", { status: 404 });
|
|
@@ -58985,7 +59347,7 @@ function installServePortZeroCompat() {
|
|
|
58985
59347
|
};
|
|
58986
59348
|
servePortZeroCompatInstalled = true;
|
|
58987
59349
|
}
|
|
58988
|
-
var PORT_ZERO_COMPAT_BASE = 40000, PORT_ZERO_COMPAT_SPAN = 20000, servePortZeroCompatInstalled = false, servePortZeroCounter = 0, inMemoryServers;
|
|
59350
|
+
var PORT_ZERO_COMPAT_BASE = 40000, PORT_ZERO_COMPAT_SPAN = 20000, CALLBACK_PATH_PREFIX = "/nax/interact/", servePortZeroCompatInstalled = false, servePortZeroCounter = 0, inMemoryServers;
|
|
58989
59351
|
var init_webhook_serve_compat = __esm(() => {
|
|
58990
59352
|
init_errors();
|
|
58991
59353
|
inMemoryServers = new Map;
|
|
@@ -59575,10 +59937,10 @@ function validateFeatureName(feature) {
|
|
|
59575
59937
|
|
|
59576
59938
|
// src/plan/critic.ts
|
|
59577
59939
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
59578
|
-
import { dirname as
|
|
59940
|
+
import { dirname as dirname11, join as join47 } from "path";
|
|
59579
59941
|
async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
|
|
59580
59942
|
const path8 = join47(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
|
|
59581
|
-
await mkdir7(
|
|
59943
|
+
await mkdir7(dirname11(path8), { recursive: true });
|
|
59582
59944
|
await Bun.write(path8, formatSpecDeltas(findings, manifest));
|
|
59583
59945
|
return path8;
|
|
59584
59946
|
}
|
|
@@ -59806,8 +60168,8 @@ async function checkStaleLock(workdir) {
|
|
|
59806
60168
|
} else if (lockData.startedAt) {
|
|
59807
60169
|
lockTimeMs = new Date(lockData.startedAt).getTime();
|
|
59808
60170
|
} else {
|
|
59809
|
-
const
|
|
59810
|
-
lockTimeMs =
|
|
60171
|
+
const stat4 = statSync3(lockPath);
|
|
60172
|
+
lockTimeMs = stat4.mtimeMs;
|
|
59811
60173
|
}
|
|
59812
60174
|
const holderAlive = typeof lockData.pid === "number" && isProcessAlive(lockData.pid);
|
|
59813
60175
|
const ageMs = Date.now() - lockTimeMs;
|
|
@@ -61075,10 +61437,10 @@ __export(exports_status_cost, {
|
|
|
61075
61437
|
displayCostMetrics: () => displayCostMetrics,
|
|
61076
61438
|
_costReportEmitDeps: () => _costReportEmitDeps
|
|
61077
61439
|
});
|
|
61078
|
-
import { basename as
|
|
61440
|
+
import { basename as basename10 } from "path";
|
|
61079
61441
|
async function resolveProject(workdir) {
|
|
61080
61442
|
const config2 = await loadConfig(workdir).catch(() => null);
|
|
61081
|
-
const project = config2?.name?.trim() ||
|
|
61443
|
+
const project = config2?.name?.trim() || basename10(workdir);
|
|
61082
61444
|
const outputDir = projectOutputDir(project, config2?.outputDir);
|
|
61083
61445
|
return { project, outputDir };
|
|
61084
61446
|
}
|
|
@@ -61359,7 +61721,7 @@ __export(exports_status_features, {
|
|
|
61359
61721
|
_statusFeaturesDeps: () => _statusFeaturesDeps
|
|
61360
61722
|
});
|
|
61361
61723
|
import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
|
|
61362
|
-
import { basename as
|
|
61724
|
+
import { basename as basename11, join as join51, resolve as resolve15 } from "path";
|
|
61363
61725
|
async function loadStatusFile(featureDir2) {
|
|
61364
61726
|
const statusPath = join51(featureDir2, "status.json");
|
|
61365
61727
|
if (!existsSync17(statusPath)) {
|
|
@@ -61374,7 +61736,7 @@ async function loadStatusFile(featureDir2) {
|
|
|
61374
61736
|
}
|
|
61375
61737
|
async function loadProjectStatusFile(projectDir) {
|
|
61376
61738
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
61377
|
-
const projectKey = config2?.name?.trim() ||
|
|
61739
|
+
const projectKey = config2?.name?.trim() || basename11(projectDir);
|
|
61378
61740
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
61379
61741
|
const statusPath = join51(outputDir, "status.json");
|
|
61380
61742
|
if (!existsSync17(statusPath)) {
|
|
@@ -61448,7 +61810,7 @@ async function getFeatureSummary(featureName, featureDir2) {
|
|
|
61448
61810
|
}
|
|
61449
61811
|
async function displayAllFeatures(projectDir) {
|
|
61450
61812
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
61451
|
-
const projectKey = config2?.name?.trim() ||
|
|
61813
|
+
const projectKey = config2?.name?.trim() || basename11(projectDir);
|
|
61452
61814
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
61453
61815
|
const featuresDir2 = join51(outputDir, "features");
|
|
61454
61816
|
if (!existsSync17(featuresDir2)) {
|
|
@@ -61643,7 +62005,7 @@ async function displayFeatureStatus(options = {}) {
|
|
|
61643
62005
|
if (options.dir) {
|
|
61644
62006
|
const projectDir = resolve15(options.dir);
|
|
61645
62007
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
61646
|
-
const projectKey = config2?.name?.trim() ||
|
|
62008
|
+
const projectKey = config2?.name?.trim() || basename11(projectDir);
|
|
61647
62009
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
61648
62010
|
featureDir2 = join51(outputDir, "features", options.feature);
|
|
61649
62011
|
} else {
|
|
@@ -62025,8 +62387,8 @@ var init_semantic_verdict = __esm(() => {
|
|
|
62025
62387
|
await Bun.write(filePath, content);
|
|
62026
62388
|
},
|
|
62027
62389
|
readdir: async (dir) => {
|
|
62028
|
-
const { readdir:
|
|
62029
|
-
return
|
|
62390
|
+
const { readdir: readdir3 } = await import("fs/promises");
|
|
62391
|
+
return readdir3(dir);
|
|
62030
62392
|
},
|
|
62031
62393
|
readFile: async (filePath) => {
|
|
62032
62394
|
return Bun.file(filePath).text();
|
|
@@ -62944,9 +63306,9 @@ var init_acceptance_setup = __esm(() => {
|
|
|
62944
63306
|
await Bun.write(dest, content);
|
|
62945
63307
|
},
|
|
62946
63308
|
deleteFile: async (filePath) => {
|
|
62947
|
-
const { unlink:
|
|
63309
|
+
const { unlink: unlink4 } = await import("fs/promises");
|
|
62948
63310
|
try {
|
|
62949
|
-
await
|
|
63311
|
+
await unlink4(filePath);
|
|
62950
63312
|
} catch (err) {
|
|
62951
63313
|
if (err.code !== "ENOENT")
|
|
62952
63314
|
throw err;
|
|
@@ -62954,17 +63316,17 @@ var init_acceptance_setup = __esm(() => {
|
|
|
62954
63316
|
},
|
|
62955
63317
|
deleteSemanticVerdicts: async (featureDir2) => {
|
|
62956
63318
|
const dir = `${featureDir2}/semantic-verdicts`;
|
|
62957
|
-
const { readdir:
|
|
63319
|
+
const { readdir: readdir3, unlink: unlink4 } = await import("fs/promises");
|
|
62958
63320
|
let files;
|
|
62959
63321
|
try {
|
|
62960
|
-
files = await
|
|
63322
|
+
files = await readdir3(dir);
|
|
62961
63323
|
} catch (err) {
|
|
62962
63324
|
if (err.code === "ENOENT")
|
|
62963
63325
|
return;
|
|
62964
63326
|
throw err;
|
|
62965
63327
|
}
|
|
62966
63328
|
for (const file3 of files) {
|
|
62967
|
-
await
|
|
63329
|
+
await unlink4(`${dir}/${file3}`);
|
|
62968
63330
|
}
|
|
62969
63331
|
},
|
|
62970
63332
|
readMeta: async (metaPath) => {
|
|
@@ -63405,7 +63767,7 @@ var init_constitution = __esm(() => {
|
|
|
63405
63767
|
});
|
|
63406
63768
|
|
|
63407
63769
|
// src/pipeline/stages/constitution.ts
|
|
63408
|
-
import { dirname as
|
|
63770
|
+
import { dirname as dirname12 } from "path";
|
|
63409
63771
|
var constitutionStage;
|
|
63410
63772
|
var init_constitution2 = __esm(() => {
|
|
63411
63773
|
init_constitution();
|
|
@@ -63415,7 +63777,7 @@ var init_constitution2 = __esm(() => {
|
|
|
63415
63777
|
enabled: (ctx) => ctx.config.constitution.enabled,
|
|
63416
63778
|
async execute(ctx) {
|
|
63417
63779
|
const logger = getLogger();
|
|
63418
|
-
const ngentDir = ctx.featureDir ?
|
|
63780
|
+
const ngentDir = ctx.featureDir ? dirname12(dirname12(ctx.featureDir)) : `${ctx.workdir}/nax`;
|
|
63419
63781
|
const result = await loadConstitution(ngentDir, ctx.config.constitution);
|
|
63420
63782
|
if (result) {
|
|
63421
63783
|
ctx.constitution = result;
|
|
@@ -63594,7 +63956,7 @@ var init_story_context = __esm(() => {
|
|
|
63594
63956
|
});
|
|
63595
63957
|
|
|
63596
63958
|
// src/execution/lock.ts
|
|
63597
|
-
import { rename as rename2, unlink as
|
|
63959
|
+
import { rename as rename2, unlink as unlink4 } from "fs/promises";
|
|
63598
63960
|
import path13 from "path";
|
|
63599
63961
|
function getSafeLogger4() {
|
|
63600
63962
|
try {
|
|
@@ -63658,7 +64020,7 @@ async function acquireLock(workdir) {
|
|
|
63658
64020
|
}
|
|
63659
64021
|
if (claimedPid !== lockPid) {
|
|
63660
64022
|
const restored = claimedContent !== null && await tryExclusiveCreate(lockPath, claimedContent);
|
|
63661
|
-
await
|
|
64023
|
+
await unlink4(tombstonePath).catch(() => {});
|
|
63662
64024
|
if (!restored) {
|
|
63663
64025
|
const logger2 = getSafeLogger4();
|
|
63664
64026
|
logger2?.warn("execution", "Stolen lock could not be restored \u2014 a newer lock already exists", {
|
|
@@ -63671,7 +64033,7 @@ async function acquireLock(workdir) {
|
|
|
63671
64033
|
logger?.warn("execution", "Removing stale lock", {
|
|
63672
64034
|
pid: lockPid
|
|
63673
64035
|
});
|
|
63674
|
-
await
|
|
64036
|
+
await unlink4(tombstonePath).catch(() => {});
|
|
63675
64037
|
}
|
|
63676
64038
|
}
|
|
63677
64039
|
const lockData = {
|
|
@@ -63697,7 +64059,7 @@ async function acquireLock(workdir) {
|
|
|
63697
64059
|
async function releaseLock(workdir) {
|
|
63698
64060
|
const lockPath = path13.join(workdir, "nax.lock");
|
|
63699
64061
|
try {
|
|
63700
|
-
await
|
|
64062
|
+
await unlink4(lockPath);
|
|
63701
64063
|
} catch (error48) {
|
|
63702
64064
|
if (error48.code !== "ENOENT") {
|
|
63703
64065
|
const logger = getSafeLogger4();
|
|
@@ -63718,7 +64080,7 @@ var init_helpers = __esm(() => {
|
|
|
63718
64080
|
});
|
|
63719
64081
|
|
|
63720
64082
|
// src/pipeline/stages/context.ts
|
|
63721
|
-
import { randomUUID as
|
|
64083
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
63722
64084
|
import { join as join57 } from "path";
|
|
63723
64085
|
async function runV2Path(ctx) {
|
|
63724
64086
|
const logger = getLogger();
|
|
@@ -63957,7 +64319,7 @@ var init_context2 = __esm(() => {
|
|
|
63957
64319
|
createOrchestrator: createDefaultOrchestrator,
|
|
63958
64320
|
loadPlugins: loadPluginProviders,
|
|
63959
64321
|
v1FeatureProvider: () => new FeatureContextProvider,
|
|
63960
|
-
uuid: () =>
|
|
64322
|
+
uuid: () => randomUUID6(),
|
|
63961
64323
|
readDigest: readDigestFile,
|
|
63962
64324
|
writeDigest: writeDigestFile,
|
|
63963
64325
|
loadFeatureManifests,
|
|
@@ -63986,23 +64348,30 @@ function spawnGit(deps, args, workdir) {
|
|
|
63986
64348
|
});
|
|
63987
64349
|
}
|
|
63988
64350
|
async function spawnWithTimeout(proc, timeoutMs) {
|
|
63989
|
-
|
|
63990
|
-
|
|
63991
|
-
|
|
63992
|
-
|
|
63993
|
-
|
|
63994
|
-
|
|
63995
|
-
|
|
63996
|
-
|
|
63997
|
-
|
|
63998
|
-
|
|
63999
|
-
|
|
64000
|
-
|
|
64001
|
-
|
|
64002
|
-
|
|
64003
|
-
|
|
64004
|
-
|
|
64005
|
-
|
|
64351
|
+
let timer;
|
|
64352
|
+
try {
|
|
64353
|
+
const result = await Promise.race([
|
|
64354
|
+
(async () => {
|
|
64355
|
+
const [exitCode, stdout] = await Promise.all([
|
|
64356
|
+
proc.exited,
|
|
64357
|
+
new Response(proc.stdout).text(),
|
|
64358
|
+
new Response(proc.stderr).text()
|
|
64359
|
+
]);
|
|
64360
|
+
return { stdout, exitCode };
|
|
64361
|
+
})(),
|
|
64362
|
+
new Promise((resolve16) => {
|
|
64363
|
+
timer = setTimeout(() => {
|
|
64364
|
+
try {
|
|
64365
|
+
proc.kill("SIGKILL");
|
|
64366
|
+
} catch {}
|
|
64367
|
+
resolve16({ stdout: "", exitCode: 1 });
|
|
64368
|
+
}, timeoutMs);
|
|
64369
|
+
})
|
|
64370
|
+
]);
|
|
64371
|
+
return result;
|
|
64372
|
+
} finally {
|
|
64373
|
+
clearTimeout(timer);
|
|
64374
|
+
}
|
|
64006
64375
|
}
|
|
64007
64376
|
function captureFailureSentinel() {
|
|
64008
64377
|
return `__capture_failed__:${Date.now()}:${Math.random().toString(36).slice(2)}`;
|
|
@@ -64076,7 +64445,7 @@ function buildCheckpointLogData(meta3) {
|
|
|
64076
64445
|
const { storyId, ...rest } = meta3;
|
|
64077
64446
|
return { storyId, ...rest };
|
|
64078
64447
|
}
|
|
64079
|
-
var TREE_CAPTURE_TIMEOUT_MS =
|
|
64448
|
+
var TREE_CAPTURE_TIMEOUT_MS = 1000, MAX_UNTRACKED_HASHED = 500, UNTRACKED_HASH_TIMEOUT_MS = 250;
|
|
64080
64449
|
|
|
64081
64450
|
// src/tdd/rollback.ts
|
|
64082
64451
|
import { rm as rm2 } from "fs/promises";
|
|
@@ -65605,7 +65974,19 @@ class ExecutionPlan {
|
|
|
65605
65974
|
});
|
|
65606
65975
|
} else {
|
|
65607
65976
|
const currentTree = await _storyOrchestratorDeps.captureTreeState(this.ctx.packageDir);
|
|
65608
|
-
|
|
65977
|
+
try {
|
|
65978
|
+
await _storyOrchestratorDeps.recordGreen(this.ctx.storyId, name, currentTree);
|
|
65979
|
+
} catch (error48) {
|
|
65980
|
+
if (error48 instanceof NaxError && error48.code === "CHECKPOINT_WRITE_FAILED") {
|
|
65981
|
+
logger?.warn("story-orchestrator", "recordGreen failed \u2014 resume checkpoint lost, story verdict unaffected", {
|
|
65982
|
+
storyId: this.ctx.storyId,
|
|
65983
|
+
phase: name,
|
|
65984
|
+
error: errorMessage(error48)
|
|
65985
|
+
});
|
|
65986
|
+
} else {
|
|
65987
|
+
throw error48;
|
|
65988
|
+
}
|
|
65989
|
+
}
|
|
65609
65990
|
}
|
|
65610
65991
|
}
|
|
65611
65992
|
const gateName = this.state.fullSuiteGate?.slot.op.name;
|
|
@@ -65789,6 +66170,7 @@ class ExecutionPlan {
|
|
|
65789
66170
|
}
|
|
65790
66171
|
}
|
|
65791
66172
|
var init_execution_plan = __esm(() => {
|
|
66173
|
+
init_errors();
|
|
65792
66174
|
init_logger2();
|
|
65793
66175
|
init_non_blocking_fix();
|
|
65794
66176
|
init_phase_eval();
|
|
@@ -68221,12 +68603,12 @@ async function regenerateAcceptanceTest(testPath, acceptanceContext) {
|
|
|
68221
68603
|
const content = await Bun.file(testPath).text();
|
|
68222
68604
|
await Bun.write(bakPath, content);
|
|
68223
68605
|
logger?.info("acceptance", `Backed up acceptance test -> ${bakPath}`);
|
|
68224
|
-
const { unlink:
|
|
68225
|
-
await
|
|
68606
|
+
const { unlink: unlink5 } = await import("fs/promises");
|
|
68607
|
+
await unlink5(testPath);
|
|
68226
68608
|
if (acceptanceContext.featureDir) {
|
|
68227
68609
|
const metaPath = path14.join(acceptanceContext.featureDir, "acceptance-meta.json");
|
|
68228
68610
|
try {
|
|
68229
|
-
await
|
|
68611
|
+
await unlink5(metaPath);
|
|
68230
68612
|
} catch {}
|
|
68231
68613
|
}
|
|
68232
68614
|
let implementationContext;
|
|
@@ -68694,7 +69076,7 @@ var init_acceptance_loop = __esm(() => {
|
|
|
68694
69076
|
|
|
68695
69077
|
// src/session/scratch-purge.ts
|
|
68696
69078
|
import { mkdir as mkdir9, rename as rename3, rm as rm3 } from "fs/promises";
|
|
68697
|
-
import { dirname as
|
|
69079
|
+
import { dirname as dirname13, join as join63 } from "path";
|
|
68698
69080
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
68699
69081
|
const sessionsDir = join63(featureDir(projectDir, featureName), "sessions");
|
|
68700
69082
|
const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
|
|
@@ -68746,7 +69128,7 @@ var init_scratch_purge = __esm(() => {
|
|
|
68746
69128
|
readFile: (path15) => Bun.file(path15).text(),
|
|
68747
69129
|
remove: (path15) => rm3(path15, { recursive: true, force: true }),
|
|
68748
69130
|
move: async (src, dest) => {
|
|
68749
|
-
await mkdir9(
|
|
69131
|
+
await mkdir9(dirname13(dest), { recursive: true });
|
|
68750
69132
|
await rename3(src, dest);
|
|
68751
69133
|
},
|
|
68752
69134
|
now: () => Date.now()
|
|
@@ -69433,6 +69815,7 @@ async function handleRunCompletion(options) {
|
|
|
69433
69815
|
clearLanguageCache();
|
|
69434
69816
|
clearWorkspaceCache();
|
|
69435
69817
|
clearGitRootCache();
|
|
69818
|
+
_resetCanonicalRulesCache();
|
|
69436
69819
|
const finalCounts = countStories(prd);
|
|
69437
69820
|
const fallbackAggregate = deriveRunFallbackAggregates(allStoryMetrics);
|
|
69438
69821
|
pipelineEventBus.emit({
|
|
@@ -69525,7 +69908,7 @@ async function handleRunCompletion(options) {
|
|
|
69525
69908
|
});
|
|
69526
69909
|
statusWriter.setPrd(prd);
|
|
69527
69910
|
statusWriter.setCurrentStory(null);
|
|
69528
|
-
statusWriter.setRunStatus(regressionGateFailed ? "failed" : exitReason === "cost-limit" ? "cost-limit" : isComplete(prd) ? "completed" : isStalled(prd, config2.execution.rectification?.maxAttemptsTotal) ? "stalled" : "
|
|
69911
|
+
statusWriter.setRunStatus(regressionGateFailed ? "failed" : exitReason === "cost-limit" ? "cost-limit" : isComplete(prd) ? "completed" : isStalled(prd, config2.execution.rectification?.maxAttemptsTotal) ? "stalled" : "aborted");
|
|
69529
69912
|
await statusWriter.update(reportedTotal, iterations);
|
|
69530
69913
|
return {
|
|
69531
69914
|
durationMs,
|
|
@@ -69978,9 +70361,9 @@ var init_ensure_package_dirs = __esm(() => {
|
|
|
69978
70361
|
init_logger2();
|
|
69979
70362
|
_ensurePackageDirsDeps = {
|
|
69980
70363
|
exists: async (p) => {
|
|
69981
|
-
const { stat:
|
|
70364
|
+
const { stat: stat4 } = await import("fs/promises");
|
|
69982
70365
|
try {
|
|
69983
|
-
return (await
|
|
70366
|
+
return (await stat4(p)).isDirectory();
|
|
69984
70367
|
} catch {
|
|
69985
70368
|
return false;
|
|
69986
70369
|
}
|
|
@@ -69994,10 +70377,10 @@ var init_ensure_package_dirs = __esm(() => {
|
|
|
69994
70377
|
|
|
69995
70378
|
// src/pipeline/subscribers/events-writer.ts
|
|
69996
70379
|
import { appendFile as appendFile4, mkdir as mkdir10 } from "fs/promises";
|
|
69997
|
-
import { basename as
|
|
70380
|
+
import { basename as basename13, join as join64 } from "path";
|
|
69998
70381
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
69999
70382
|
const logger = getSafeLogger();
|
|
70000
|
-
const project =
|
|
70383
|
+
const project = basename13(workdir);
|
|
70001
70384
|
const eventsDir = join64(getEventsRootDir(), project);
|
|
70002
70385
|
const eventsFile = join64(eventsDir, "events.jsonl");
|
|
70003
70386
|
let dirReady = false;
|
|
@@ -70180,10 +70563,10 @@ var init_interaction2 = __esm(() => {
|
|
|
70180
70563
|
|
|
70181
70564
|
// src/pipeline/subscribers/registry.ts
|
|
70182
70565
|
import { mkdir as mkdir11, writeFile } from "fs/promises";
|
|
70183
|
-
import { basename as
|
|
70566
|
+
import { basename as basename14, join as join65 } from "path";
|
|
70184
70567
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
70185
70568
|
const logger = getSafeLogger();
|
|
70186
|
-
const project =
|
|
70569
|
+
const project = basename14(workdir);
|
|
70187
70570
|
const runDir = join65(getRunsDir(), `${project}-${feature}-${runId}`);
|
|
70188
70571
|
const metaFile = join65(runDir, "meta.json");
|
|
70189
70572
|
const unsub = bus.on("run:started", (_ev) => {
|
|
@@ -71188,12 +71571,7 @@ ${missing.join(`
|
|
|
71188
71571
|
}
|
|
71189
71572
|
async hasWorktreeRecord(projectRoot, branchName) {
|
|
71190
71573
|
try {
|
|
71191
|
-
const
|
|
71192
|
-
cwd: projectRoot,
|
|
71193
|
-
stdout: "pipe",
|
|
71194
|
-
stderr: "pipe"
|
|
71195
|
-
});
|
|
71196
|
-
const [exitCode, stdout] = await Promise.all([proc.exited, new Response(proc.stdout).text()]);
|
|
71574
|
+
const { stdout, exitCode } = await gitWithTimeout(["worktree", "list", "--porcelain"], projectRoot);
|
|
71197
71575
|
if (exitCode !== 0)
|
|
71198
71576
|
return false;
|
|
71199
71577
|
const targetBranch = `refs/heads/${branchName}`;
|
|
@@ -71209,12 +71587,7 @@ ${missing.join(`
|
|
|
71209
71587
|
const branchName = `nax/${storyId}`;
|
|
71210
71588
|
const hadWorktreeRecord = await this.hasWorktreeRecord(projectRoot, branchName);
|
|
71211
71589
|
try {
|
|
71212
|
-
|
|
71213
|
-
cwd: projectRoot,
|
|
71214
|
-
stdout: "pipe",
|
|
71215
|
-
stderr: "pipe"
|
|
71216
|
-
});
|
|
71217
|
-
await pruneProc.exited;
|
|
71590
|
+
await gitWithTimeout(["worktree", "prune"], projectRoot);
|
|
71218
71591
|
} catch {}
|
|
71219
71592
|
let removedLiveWorktree = false;
|
|
71220
71593
|
try {
|
|
@@ -71223,21 +71596,11 @@ ${missing.join(`
|
|
|
71223
71596
|
} catch {}
|
|
71224
71597
|
if (!removedLiveWorktree && hadWorktreeRecord) {
|
|
71225
71598
|
try {
|
|
71226
|
-
|
|
71227
|
-
cwd: projectRoot,
|
|
71228
|
-
stdout: "pipe",
|
|
71229
|
-
stderr: "pipe"
|
|
71230
|
-
});
|
|
71231
|
-
await branchProc.exited;
|
|
71599
|
+
await gitWithTimeout(["branch", "-D", branchName], projectRoot);
|
|
71232
71600
|
} catch {}
|
|
71233
71601
|
}
|
|
71234
71602
|
try {
|
|
71235
|
-
const
|
|
71236
|
-
cwd: projectRoot,
|
|
71237
|
-
stdout: "pipe",
|
|
71238
|
-
stderr: "pipe"
|
|
71239
|
-
});
|
|
71240
|
-
const [exitCode, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).text()]);
|
|
71603
|
+
const { exitCode, stderr } = await gitWithTimeout(["worktree", "add", worktreePath, "-b", branchName], projectRoot);
|
|
71241
71604
|
if (exitCode !== 0) {
|
|
71242
71605
|
throw new NaxError(`Failed to create worktree: ${stderr || "unknown error"}`, "WORKTREE_ERROR", {
|
|
71243
71606
|
stage: "worktree",
|
|
@@ -71292,12 +71655,7 @@ ${missing.join(`
|
|
|
71292
71655
|
const worktreePath = join67(projectRoot, ".nax-wt", storyId);
|
|
71293
71656
|
const branchName = `nax/${storyId}`;
|
|
71294
71657
|
try {
|
|
71295
|
-
const
|
|
71296
|
-
cwd: projectRoot,
|
|
71297
|
-
stdout: "pipe",
|
|
71298
|
-
stderr: "pipe"
|
|
71299
|
-
});
|
|
71300
|
-
const [exitCode, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).text()]);
|
|
71658
|
+
const { exitCode, stderr } = await gitWithTimeout(["worktree", "remove", worktreePath, "--force"], projectRoot);
|
|
71301
71659
|
if (exitCode !== 0) {
|
|
71302
71660
|
if (stderr.includes("not found") || stderr.includes("does not exist") || stderr.includes("no such worktree") || stderr.includes("is not a working tree")) {
|
|
71303
71661
|
throw new NaxError(`Worktree not found: ${worktreePath}`, "WORKTREE_ERROR", {
|
|
@@ -71325,12 +71683,7 @@ ${missing.join(`
|
|
|
71325
71683
|
});
|
|
71326
71684
|
}
|
|
71327
71685
|
try {
|
|
71328
|
-
const
|
|
71329
|
-
cwd: projectRoot,
|
|
71330
|
-
stdout: "pipe",
|
|
71331
|
-
stderr: "pipe"
|
|
71332
|
-
});
|
|
71333
|
-
const [exitCode, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).text()]);
|
|
71686
|
+
const { exitCode, stderr } = await gitWithTimeout(["branch", "-D", branchName], projectRoot);
|
|
71334
71687
|
if (exitCode !== 0) {
|
|
71335
71688
|
if (!stderr.includes("not found")) {
|
|
71336
71689
|
const logger = getSafeLogger();
|
|
@@ -71346,16 +71699,7 @@ ${missing.join(`
|
|
|
71346
71699
|
}
|
|
71347
71700
|
async list(projectRoot) {
|
|
71348
71701
|
try {
|
|
71349
|
-
const
|
|
71350
|
-
cwd: projectRoot,
|
|
71351
|
-
stdout: "pipe",
|
|
71352
|
-
stderr: "pipe"
|
|
71353
|
-
});
|
|
71354
|
-
const [exitCode, stderr, stdout] = await Promise.all([
|
|
71355
|
-
proc.exited,
|
|
71356
|
-
new Response(proc.stderr).text(),
|
|
71357
|
-
new Response(proc.stdout).text()
|
|
71358
|
-
]);
|
|
71702
|
+
const { stdout, stderr, exitCode } = await gitWithTimeout(["worktree", "list", "--porcelain"], projectRoot);
|
|
71359
71703
|
if (exitCode !== 0) {
|
|
71360
71704
|
throw new NaxError(`Failed to list worktrees: ${stderr || "unknown error"}`, "WORKTREE_ERROR", {
|
|
71361
71705
|
stage: "worktree",
|
|
@@ -71398,16 +71742,12 @@ ${missing.join(`
|
|
|
71398
71742
|
return worktrees;
|
|
71399
71743
|
}
|
|
71400
71744
|
}
|
|
71401
|
-
var _managerDeps;
|
|
71402
71745
|
var init_manager3 = __esm(() => {
|
|
71403
71746
|
init_errors();
|
|
71404
71747
|
init_logger2();
|
|
71405
71748
|
init_validate();
|
|
71406
|
-
|
|
71749
|
+
init_git();
|
|
71407
71750
|
init_gitignore();
|
|
71408
|
-
_managerDeps = {
|
|
71409
|
-
spawn
|
|
71410
|
-
};
|
|
71411
71751
|
});
|
|
71412
71752
|
|
|
71413
71753
|
// src/execution/dry-run.ts
|
|
@@ -71467,12 +71807,8 @@ class MergeEngine {
|
|
|
71467
71807
|
this.worktreeManager = worktreeManager;
|
|
71468
71808
|
}
|
|
71469
71809
|
async isMidMerge(projectRoot) {
|
|
71470
|
-
const
|
|
71471
|
-
|
|
71472
|
-
stdout: "pipe",
|
|
71473
|
-
stderr: "pipe"
|
|
71474
|
-
});
|
|
71475
|
-
return await proc.exited === 0;
|
|
71810
|
+
const { exitCode } = await gitWithTimeout(["rev-parse", "-q", "--verify", "MERGE_HEAD"], projectRoot);
|
|
71811
|
+
return exitCode === 0;
|
|
71476
71812
|
}
|
|
71477
71813
|
async merge(projectRoot, storyId) {
|
|
71478
71814
|
const branchName = `nax/${storyId}`;
|
|
@@ -71485,16 +71821,7 @@ class MergeEngine {
|
|
|
71485
71821
|
});
|
|
71486
71822
|
return { success: false, failureKind: "error", error: error48 };
|
|
71487
71823
|
}
|
|
71488
|
-
const
|
|
71489
|
-
cwd: projectRoot,
|
|
71490
|
-
stdout: "pipe",
|
|
71491
|
-
stderr: "pipe"
|
|
71492
|
-
});
|
|
71493
|
-
const [exitCode, stderr, stdout] = await Promise.all([
|
|
71494
|
-
mergeProc.exited,
|
|
71495
|
-
new Response(mergeProc.stderr).text(),
|
|
71496
|
-
new Response(mergeProc.stdout).text()
|
|
71497
|
-
]);
|
|
71824
|
+
const { exitCode, stderr, stdout } = await gitWithTimeout(["merge", "--no-ff", branchName, "-m", `Merge branch '${branchName}'`], projectRoot);
|
|
71498
71825
|
if (exitCode === 0) {
|
|
71499
71826
|
try {
|
|
71500
71827
|
await this.worktreeManager.remove(projectRoot, storyId);
|
|
@@ -71650,36 +71977,15 @@ ${stderr}`);
|
|
|
71650
71977
|
async rebaseWorktree(projectRoot, storyId) {
|
|
71651
71978
|
const worktreePath = `${projectRoot}/.nax-wt/${storyId}`;
|
|
71652
71979
|
try {
|
|
71653
|
-
const
|
|
71654
|
-
cwd: projectRoot,
|
|
71655
|
-
stdout: "pipe",
|
|
71656
|
-
stderr: "pipe"
|
|
71657
|
-
});
|
|
71658
|
-
const [exitCode, currentBranchRaw] = await Promise.all([
|
|
71659
|
-
currentBranchProc.exited,
|
|
71660
|
-
new Response(currentBranchProc.stdout).text()
|
|
71661
|
-
]);
|
|
71980
|
+
const { exitCode, stdout: currentBranchRaw } = await gitWithTimeout(["rev-parse", "--abbrev-ref", "HEAD"], projectRoot);
|
|
71662
71981
|
if (exitCode !== 0) {
|
|
71663
71982
|
throw new Error("Failed to get current branch");
|
|
71664
71983
|
}
|
|
71665
71984
|
const currentBranch = currentBranchRaw.trim();
|
|
71666
|
-
const
|
|
71667
|
-
cwd: worktreePath,
|
|
71668
|
-
stdout: "pipe",
|
|
71669
|
-
stderr: "pipe"
|
|
71670
|
-
});
|
|
71671
|
-
const [rebaseExitCode, rebaseStderr] = await Promise.all([
|
|
71672
|
-
rebaseProc.exited,
|
|
71673
|
-
new Response(rebaseProc.stderr).text()
|
|
71674
|
-
]);
|
|
71985
|
+
const { exitCode: rebaseExitCode, stderr: rebaseStderr } = await gitWithTimeout(["rebase", currentBranch], worktreePath);
|
|
71675
71986
|
if (rebaseExitCode !== 0) {
|
|
71676
71987
|
const stderr = rebaseStderr;
|
|
71677
|
-
|
|
71678
|
-
cwd: worktreePath,
|
|
71679
|
-
stdout: "pipe",
|
|
71680
|
-
stderr: "pipe"
|
|
71681
|
-
});
|
|
71682
|
-
await abortProc.exited;
|
|
71988
|
+
await gitWithTimeout(["rebase", "--abort"], worktreePath);
|
|
71683
71989
|
throw new Error(`Rebase failed: ${stderr || "unknown error"}`);
|
|
71684
71990
|
}
|
|
71685
71991
|
} catch (error48) {
|
|
@@ -71691,12 +71997,7 @@ ${stderr}`);
|
|
|
71691
71997
|
}
|
|
71692
71998
|
async getConflictFiles(projectRoot) {
|
|
71693
71999
|
try {
|
|
71694
|
-
const
|
|
71695
|
-
cwd: projectRoot,
|
|
71696
|
-
stdout: "pipe",
|
|
71697
|
-
stderr: "pipe"
|
|
71698
|
-
});
|
|
71699
|
-
const [exitCode, stdout] = await Promise.all([proc.exited, new Response(proc.stdout).text()]);
|
|
72000
|
+
const { stdout, exitCode } = await gitWithTimeout(["diff", "--name-only", "--diff-filter=U"], projectRoot);
|
|
71700
72001
|
if (exitCode !== 0) {
|
|
71701
72002
|
return [];
|
|
71702
72003
|
}
|
|
@@ -71708,12 +72009,7 @@ ${stderr}`);
|
|
|
71708
72009
|
}
|
|
71709
72010
|
async abortMerge(projectRoot) {
|
|
71710
72011
|
try {
|
|
71711
|
-
const
|
|
71712
|
-
cwd: projectRoot,
|
|
71713
|
-
stdout: "pipe",
|
|
71714
|
-
stderr: "pipe"
|
|
71715
|
-
});
|
|
71716
|
-
const [exitCode, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).text()]);
|
|
72012
|
+
const { exitCode, stderr } = await gitWithTimeout(["merge", "--abort"], projectRoot);
|
|
71717
72013
|
if (exitCode !== 0) {
|
|
71718
72014
|
getSafeLogger()?.error("worktree", "Failed to abort merge", {
|
|
71719
72015
|
exitCode,
|
|
@@ -71730,20 +72026,15 @@ ${stderr}`);
|
|
|
71730
72026
|
}
|
|
71731
72027
|
}
|
|
71732
72028
|
}
|
|
71733
|
-
var _mergeDeps;
|
|
71734
72029
|
var init_merge = __esm(() => {
|
|
71735
72030
|
init_logger2();
|
|
71736
|
-
|
|
71737
|
-
_mergeDeps = {
|
|
71738
|
-
spawn
|
|
71739
|
-
};
|
|
72031
|
+
init_git();
|
|
71740
72032
|
});
|
|
71741
72033
|
|
|
71742
72034
|
// src/worktree/index.ts
|
|
71743
72035
|
var exports_worktree = {};
|
|
71744
72036
|
__export(exports_worktree, {
|
|
71745
72037
|
prepareWorktreeDependencies: () => prepareWorktreeDependencies,
|
|
71746
|
-
_mergeDeps: () => _mergeDeps,
|
|
71747
72038
|
WorktreeManager: () => WorktreeManager,
|
|
71748
72039
|
WorktreeDependencyPreparationError: () => WorktreeDependencyPreparationError,
|
|
71749
72040
|
MergeEngine: () => MergeEngine
|
|
@@ -72018,7 +72309,7 @@ async function removeWorktreeDirectory(projectRoot, storyId) {
|
|
|
72018
72309
|
stdout: "pipe",
|
|
72019
72310
|
stderr: "pipe"
|
|
72020
72311
|
});
|
|
72021
|
-
const [exitCode, stderr] = await Promise.all([
|
|
72312
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
72022
72313
|
proc.exited,
|
|
72023
72314
|
new Response(proc.stdout).text().catch(() => ""),
|
|
72024
72315
|
new Response(proc.stderr).text().catch(() => "")
|
|
@@ -73541,8 +73832,9 @@ function detectForge(remoteUrl) {
|
|
|
73541
73832
|
async function hasOpenPr(forge, branch, deps, cwd) {
|
|
73542
73833
|
const cmd = forge === "github" ? ["gh", "pr", "list", "--head", branch, "--state", "open", "--json", "number"] : ["glab", "mr", "list", "--source-branch", branch, "--state", "opened", "--output", "json"];
|
|
73543
73834
|
const result = await deps.run(cmd, { cwd });
|
|
73544
|
-
if (result.exitCode !== 0)
|
|
73545
|
-
|
|
73835
|
+
if (result.exitCode !== 0) {
|
|
73836
|
+
throw new Error(`hasOpenPr: forge CLI exited with code ${result.exitCode}: ${result.stderr.trim()}`);
|
|
73837
|
+
}
|
|
73546
73838
|
try {
|
|
73547
73839
|
const parsed = JSON.parse(result.stdout);
|
|
73548
73840
|
return Array.isArray(parsed) && parsed.length > 0;
|
|
@@ -73790,12 +74082,27 @@ var init_template = __esm(() => {
|
|
|
73790
74082
|
import * as path20 from "path";
|
|
73791
74083
|
async function defaultRun(cmd, opts) {
|
|
73792
74084
|
const proc = Bun.spawn(cmd, { cwd: opts.cwd, stdout: "pipe", stderr: "pipe" });
|
|
73793
|
-
const
|
|
73794
|
-
|
|
73795
|
-
|
|
73796
|
-
|
|
73797
|
-
|
|
73798
|
-
|
|
74085
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SUBPROCESS_TIMEOUT_MS;
|
|
74086
|
+
let timedOut = false;
|
|
74087
|
+
const timer = setTimeout(() => {
|
|
74088
|
+
timedOut = true;
|
|
74089
|
+
proc.kill();
|
|
74090
|
+
}, timeoutMs);
|
|
74091
|
+
try {
|
|
74092
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
74093
|
+
proc.exited,
|
|
74094
|
+
new Response(proc.stdout).text(),
|
|
74095
|
+
new Response(proc.stderr).text()
|
|
74096
|
+
]);
|
|
74097
|
+
return timedOut ? {
|
|
74098
|
+
exitCode: exitCode === 0 ? 124 : exitCode,
|
|
74099
|
+
stdout,
|
|
74100
|
+
stderr: `${stderr}
|
|
74101
|
+
[auto-pr] command killed after ${timeoutMs}ms timeout`
|
|
74102
|
+
} : { exitCode, stdout, stderr };
|
|
74103
|
+
} finally {
|
|
74104
|
+
clearTimeout(timer);
|
|
74105
|
+
}
|
|
73799
74106
|
}
|
|
73800
74107
|
async function defaultReadText(path21) {
|
|
73801
74108
|
const file3 = Bun.file(path21);
|
|
@@ -73845,7 +74152,7 @@ function toPrBodyContext(context) {
|
|
|
73845
74152
|
stories: context.stories
|
|
73846
74153
|
};
|
|
73847
74154
|
}
|
|
73848
|
-
var PLUGIN_NAME = "nax-auto-pr", PLUGIN_VERSION = "0.1.0", GIT_REMOTE_CMD, _autoPrDeps, autoPrAction, autoPrPlugin;
|
|
74155
|
+
var PLUGIN_NAME = "nax-auto-pr", PLUGIN_VERSION = "0.1.0", GIT_REMOTE_CMD, DEFAULT_SUBPROCESS_TIMEOUT_MS = 30000, _autoPrDeps, autoPrAction, autoPrPlugin;
|
|
73849
74156
|
var init_auto_pr = __esm(() => {
|
|
73850
74157
|
init_forge();
|
|
73851
74158
|
init_pr_body();
|
|
@@ -73911,6 +74218,23 @@ var init_auto_pr = __esm(() => {
|
|
|
73911
74218
|
const message = pushResult.stderr.trim() || `git push exited with code ${pushResult.exitCode}`;
|
|
73912
74219
|
return { success: false, message: `Failed to push branch "${context.branch}" to origin: ${message}` };
|
|
73913
74220
|
}
|
|
74221
|
+
let existsAfterPush = false;
|
|
74222
|
+
try {
|
|
74223
|
+
existsAfterPush = await _autoPrDeps.hasOpenPr(forge, context.branch, { run: _autoPrDeps.run, readText: _autoPrDeps.readText }, context.workdir);
|
|
74224
|
+
} catch (checkErr) {
|
|
74225
|
+
context.logger.warn("Auto-PR re-check inconclusive \u2014 skipping to avoid duplicate PR", {
|
|
74226
|
+
branch: context.branch,
|
|
74227
|
+
error: String(checkErr)
|
|
74228
|
+
});
|
|
74229
|
+
return { success: false, message: `Auto-PR skipped: re-check inconclusive (${String(checkErr)})` };
|
|
74230
|
+
}
|
|
74231
|
+
if (existsAfterPush) {
|
|
74232
|
+
context.logger.warn("Auto-PR skipped \u2014 open PR/MR appeared during push (concurrent run?)", {
|
|
74233
|
+
branch: context.branch,
|
|
74234
|
+
forge
|
|
74235
|
+
});
|
|
74236
|
+
return { success: false, message: `Open PR/MR already exists for branch "${context.branch}"` };
|
|
74237
|
+
}
|
|
73914
74238
|
const template = await _autoPrDeps.findPrTemplate(context.workdir, forge, {
|
|
73915
74239
|
run: _autoPrDeps.run,
|
|
73916
74240
|
readText: _autoPrDeps.readText
|
|
@@ -74157,8 +74481,8 @@ async function writtenThisRun(filePath, runStartedAt) {
|
|
|
74157
74481
|
if (runStartedAt === undefined)
|
|
74158
74482
|
return true;
|
|
74159
74483
|
try {
|
|
74160
|
-
const
|
|
74161
|
-
return
|
|
74484
|
+
const stat4 = await Bun.file(filePath).stat();
|
|
74485
|
+
return stat4.mtimeMs >= runStartedAt;
|
|
74162
74486
|
} catch {
|
|
74163
74487
|
return true;
|
|
74164
74488
|
}
|
|
@@ -75168,14 +75492,23 @@ function buildEscalationMessage(feature, reason, findings) {
|
|
|
75168
75492
|
\u2026and ${omitted} more` : ""}`;
|
|
75169
75493
|
}
|
|
75170
75494
|
async function sendTelegramNotify(cfg, text) {
|
|
75171
|
-
const
|
|
75172
|
-
|
|
75173
|
-
|
|
75174
|
-
|
|
75175
|
-
|
|
75176
|
-
|
|
75495
|
+
const controller = new AbortController;
|
|
75496
|
+
const timer = setTimeout(() => controller.abort(), NOTIFY_FETCH_TIMEOUT_MS);
|
|
75497
|
+
try {
|
|
75498
|
+
const res = await _telegramDeps.fetch(`https://api.telegram.org/bot${cfg.token}/sendMessage`, {
|
|
75499
|
+
method: "POST",
|
|
75500
|
+
headers: { "content-type": "application/json" },
|
|
75501
|
+
body: JSON.stringify({ chat_id: cfg.chatId, text }),
|
|
75502
|
+
signal: controller.signal
|
|
75503
|
+
});
|
|
75504
|
+
return res.ok;
|
|
75505
|
+
} catch {
|
|
75506
|
+
return false;
|
|
75507
|
+
} finally {
|
|
75508
|
+
clearTimeout(timer);
|
|
75509
|
+
}
|
|
75177
75510
|
}
|
|
75178
|
-
var TELEGRAM_MAX_MESSAGE_CHARS = 4096, MAX_FINDING_TITLE_CHARS = 120, _telegramDeps;
|
|
75511
|
+
var TELEGRAM_MAX_MESSAGE_CHARS = 4096, MAX_FINDING_TITLE_CHARS = 120, _telegramDeps, NOTIFY_FETCH_TIMEOUT_MS = 5000;
|
|
75179
75512
|
var init_telegram2 = __esm(() => {
|
|
75180
75513
|
_telegramDeps = { fetch: (...a) => fetch(...a) };
|
|
75181
75514
|
});
|
|
@@ -75410,6 +75743,8 @@ var PLUGIN_NAME4 = "nax-finish", PLUGIN_VERSION4 = "0.1.0", PACKAGE_ROOT_SEARCH_
|
|
|
75410
75743
|
var init_nax_finish = __esm(() => {
|
|
75411
75744
|
init_config2();
|
|
75412
75745
|
init_telegram2();
|
|
75746
|
+
init_telegram2();
|
|
75747
|
+
init_telegram2();
|
|
75413
75748
|
_naxFinishDeps = {
|
|
75414
75749
|
run: defaultRun2,
|
|
75415
75750
|
readResult: defaultReadResult,
|
|
@@ -75511,6 +75846,7 @@ function createBatchQueue(opts) {
|
|
|
75511
75846
|
let overflowing = false;
|
|
75512
75847
|
let tornDown = false;
|
|
75513
75848
|
let timer;
|
|
75849
|
+
const inFlightSends = new Set;
|
|
75514
75850
|
const armTimer = () => {
|
|
75515
75851
|
timer = setTimeout(() => {
|
|
75516
75852
|
doFlush();
|
|
@@ -75534,7 +75870,8 @@ function createBatchQueue(opts) {
|
|
|
75534
75870
|
return Promise.resolve();
|
|
75535
75871
|
const batch = queue;
|
|
75536
75872
|
queue = [];
|
|
75537
|
-
sendWithRetry(batch);
|
|
75873
|
+
const sendPromise = sendWithRetry(batch).finally(() => inFlightSends.delete(sendPromise));
|
|
75874
|
+
inFlightSends.add(sendPromise);
|
|
75538
75875
|
return Promise.resolve();
|
|
75539
75876
|
};
|
|
75540
75877
|
const enqueue = (item) => {
|
|
@@ -75556,7 +75893,10 @@ function createBatchQueue(opts) {
|
|
|
75556
75893
|
armTimer();
|
|
75557
75894
|
return {
|
|
75558
75895
|
enqueue,
|
|
75559
|
-
flushNow: () =>
|
|
75896
|
+
flushNow: async () => {
|
|
75897
|
+
await doFlush();
|
|
75898
|
+
await Promise.all(inFlightSends);
|
|
75899
|
+
},
|
|
75560
75900
|
teardown: () => {
|
|
75561
75901
|
tornDown = true;
|
|
75562
75902
|
if (timer !== undefined)
|
|
@@ -76812,11 +77152,11 @@ function getSafeLogger6() {
|
|
|
76812
77152
|
return getSafeLogger();
|
|
76813
77153
|
}
|
|
76814
77154
|
function extractPluginName(pluginPath) {
|
|
76815
|
-
const
|
|
76816
|
-
if (
|
|
77155
|
+
const basename16 = path25.basename(pluginPath);
|
|
77156
|
+
if (basename16 === "index.ts" || basename16 === "index.js" || basename16 === "index.mjs") {
|
|
76817
77157
|
return path25.basename(path25.dirname(pluginPath));
|
|
76818
77158
|
}
|
|
76819
|
-
return
|
|
77159
|
+
return basename16.replace(/\.(ts|js|mjs)$/, "");
|
|
76820
77160
|
}
|
|
76821
77161
|
async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, disabledPlugins, isTestFileFn, reporters) {
|
|
76822
77162
|
const loadedPlugins = [];
|
|
@@ -77087,7 +77427,7 @@ var init_loader4 = __esm(() => {
|
|
|
77087
77427
|
});
|
|
77088
77428
|
|
|
77089
77429
|
// src/execution/status-file.ts
|
|
77090
|
-
import { rename as rename4, unlink as
|
|
77430
|
+
import { rename as rename4, unlink as unlink5 } from "fs/promises";
|
|
77091
77431
|
import { resolve as resolve20 } from "path";
|
|
77092
77432
|
function countProgress(prd) {
|
|
77093
77433
|
const stories = prd.userStories;
|
|
@@ -77139,7 +77479,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
77139
77479
|
}
|
|
77140
77480
|
const tmpPath = `${resolvedPath}.tmp`;
|
|
77141
77481
|
try {
|
|
77142
|
-
await
|
|
77482
|
+
await unlink5(tmpPath);
|
|
77143
77483
|
} catch {}
|
|
77144
77484
|
await Bun.write(tmpPath, JSON.stringify(status, null, 2));
|
|
77145
77485
|
await rename4(tmpPath, resolvedPath);
|
|
@@ -77300,7 +77640,7 @@ __export(exports_init_context, {
|
|
|
77300
77640
|
generatePackageContextTemplate: () => generatePackageContextTemplate,
|
|
77301
77641
|
generateContextTemplate: () => generateContextTemplate
|
|
77302
77642
|
});
|
|
77303
|
-
import { basename as
|
|
77643
|
+
import { basename as basename16, join as join78 } from "path";
|
|
77304
77644
|
async function bunFileExists(path26) {
|
|
77305
77645
|
return Bun.file(path26).exists();
|
|
77306
77646
|
}
|
|
@@ -77395,7 +77735,7 @@ async function scanProject(projectRoot) {
|
|
|
77395
77735
|
const readmeSnippet = await readReadmeSnippet(projectRoot);
|
|
77396
77736
|
const entryPoints = await detectEntryPoints(projectRoot);
|
|
77397
77737
|
const configFiles = await detectConfigFiles(projectRoot);
|
|
77398
|
-
const projectName = packageManifest?.name ||
|
|
77738
|
+
const projectName = packageManifest?.name || basename16(projectRoot);
|
|
77399
77739
|
return {
|
|
77400
77740
|
projectName,
|
|
77401
77741
|
fileTree,
|
|
@@ -78059,7 +78399,7 @@ __export(exports_migrate, {
|
|
|
78059
78399
|
detectGeneratedContent: () => detectGeneratedContent
|
|
78060
78400
|
});
|
|
78061
78401
|
import { existsSync as existsSync25 } from "fs";
|
|
78062
|
-
import { mkdir as mkdir16, readdir as
|
|
78402
|
+
import { mkdir as mkdir16, readdir as readdir4, rename as rename5 } from "fs/promises";
|
|
78063
78403
|
import path26 from "path";
|
|
78064
78404
|
async function detectGeneratedContent(naxDir) {
|
|
78065
78405
|
if (!existsSync25(naxDir))
|
|
@@ -78067,7 +78407,7 @@ async function detectGeneratedContent(naxDir) {
|
|
|
78067
78407
|
const candidates = [];
|
|
78068
78408
|
let entries = [];
|
|
78069
78409
|
try {
|
|
78070
|
-
entries = await
|
|
78410
|
+
entries = await readdir4(naxDir);
|
|
78071
78411
|
} catch {
|
|
78072
78412
|
return [];
|
|
78073
78413
|
}
|
|
@@ -78080,13 +78420,13 @@ async function detectGeneratedContent(naxDir) {
|
|
|
78080
78420
|
if (existsSync25(featuresDir2)) {
|
|
78081
78421
|
let featureDirs = [];
|
|
78082
78422
|
try {
|
|
78083
|
-
featureDirs = await
|
|
78423
|
+
featureDirs = await readdir4(featuresDir2);
|
|
78084
78424
|
} catch {}
|
|
78085
78425
|
for (const fid of featureDirs) {
|
|
78086
78426
|
const featureDir2 = path26.join(featuresDir2, fid);
|
|
78087
78427
|
let subEntries = [];
|
|
78088
78428
|
try {
|
|
78089
|
-
subEntries = await
|
|
78429
|
+
subEntries = await readdir4(featureDir2);
|
|
78090
78430
|
} catch {
|
|
78091
78431
|
continue;
|
|
78092
78432
|
}
|
|
@@ -78101,7 +78441,7 @@ async function detectGeneratedContent(naxDir) {
|
|
|
78101
78441
|
const storiesDir = path26.join(featureDir2, "stories");
|
|
78102
78442
|
let storyDirs = [];
|
|
78103
78443
|
try {
|
|
78104
|
-
storyDirs = await
|
|
78444
|
+
storyDirs = await readdir4(storiesDir);
|
|
78105
78445
|
} catch {
|
|
78106
78446
|
continue;
|
|
78107
78447
|
}
|
|
@@ -78109,7 +78449,7 @@ async function detectGeneratedContent(naxDir) {
|
|
|
78109
78449
|
const storyDir = path26.join(storiesDir, sid);
|
|
78110
78450
|
let storyEntries = [];
|
|
78111
78451
|
try {
|
|
78112
|
-
storyEntries = await
|
|
78452
|
+
storyEntries = await readdir4(storyDir);
|
|
78113
78453
|
} catch {
|
|
78114
78454
|
continue;
|
|
78115
78455
|
}
|
|
@@ -78877,6 +79217,7 @@ async function setupRun(options) {
|
|
|
78877
79217
|
if (!lockAcquired) {
|
|
78878
79218
|
logger?.error("execution", "Another nax process is already running in this directory");
|
|
78879
79219
|
logger?.error("execution", "If you believe this is an error, remove nax.lock manually");
|
|
79220
|
+
cleanupCrashHandlers();
|
|
78880
79221
|
throw new LockAcquisitionError(workdir);
|
|
78881
79222
|
}
|
|
78882
79223
|
try {
|
|
@@ -79477,19 +79818,19 @@ function parseQueueFile(content) {
|
|
|
79477
79818
|
var init_queue = () => {};
|
|
79478
79819
|
|
|
79479
79820
|
// src/utils/queue-file-lock.ts
|
|
79480
|
-
import { randomUUID as
|
|
79481
|
-
import { open, readdir as
|
|
79482
|
-
import { basename as
|
|
79483
|
-
function
|
|
79484
|
-
const time3 = _queueLockDeps.now().toString().padStart(
|
|
79821
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
79822
|
+
import { open as open2, readdir as readdir5, stat as stat4, unlink as unlink6 } from "fs/promises";
|
|
79823
|
+
import { basename as basename17, dirname as dirname18 } from "path";
|
|
79824
|
+
function buildCandidatePath2(queuePath) {
|
|
79825
|
+
const time3 = _queueLockDeps.now().toString().padStart(LOCK_TIME_WIDTH2, "0");
|
|
79485
79826
|
return `${queuePath}.lock.${time3}.${process.pid}.${_queueLockDeps.randomUUID()}`;
|
|
79486
79827
|
}
|
|
79487
|
-
function
|
|
79828
|
+
function candidatePid2(fileName) {
|
|
79488
79829
|
const segments = fileName.split(".");
|
|
79489
79830
|
const pid = Number(segments.at(-2));
|
|
79490
79831
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
79491
79832
|
}
|
|
79492
|
-
function
|
|
79833
|
+
function candidateTime2(fileName) {
|
|
79493
79834
|
const segments = fileName.split(".");
|
|
79494
79835
|
try {
|
|
79495
79836
|
const timestamp = Number(segments.at(-3));
|
|
@@ -79505,14 +79846,14 @@ function isLiveCandidate(pid, createdAt) {
|
|
|
79505
79846
|
return false;
|
|
79506
79847
|
return _queueLockDeps.isPidAlive(pid);
|
|
79507
79848
|
}
|
|
79508
|
-
async function
|
|
79509
|
-
const directory =
|
|
79510
|
-
const prefix = `${
|
|
79849
|
+
async function listLiveCandidates2(queuePath) {
|
|
79850
|
+
const directory = dirname18(queuePath);
|
|
79851
|
+
const prefix = `${basename17(queuePath)}.lock.`;
|
|
79511
79852
|
const candidates = (await _queueLockDeps.readdir(directory)).filter((name) => name.startsWith(prefix));
|
|
79512
79853
|
const live = [];
|
|
79513
79854
|
for (const candidate of candidates) {
|
|
79514
|
-
const pid =
|
|
79515
|
-
const createdAt =
|
|
79855
|
+
const pid = candidatePid2(candidate);
|
|
79856
|
+
const createdAt = candidateTime2(candidate);
|
|
79516
79857
|
const candidatePath = `${directory}/${candidate}`;
|
|
79517
79858
|
if (isLiveCandidate(pid, createdAt)) {
|
|
79518
79859
|
const stats = await _queueLockDeps.stat(candidatePath).catch(() => null);
|
|
@@ -79523,14 +79864,14 @@ async function listLiveCandidates(queuePath) {
|
|
|
79523
79864
|
}
|
|
79524
79865
|
return live.sort((a, b) => a.createdAt - b.createdAt || a.name.localeCompare(b.name)).map(({ name }) => name);
|
|
79525
79866
|
}
|
|
79526
|
-
async function
|
|
79527
|
-
const candidatePath =
|
|
79867
|
+
async function acquire2(queuePath) {
|
|
79868
|
+
const candidatePath = buildCandidatePath2(queuePath);
|
|
79528
79869
|
const handle = await _queueLockDeps.open(candidatePath, "wx");
|
|
79529
79870
|
await handle.close();
|
|
79530
79871
|
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
79531
79872
|
while (Date.now() < deadline) {
|
|
79532
|
-
const candidates = await
|
|
79533
|
-
if (candidates[0] ===
|
|
79873
|
+
const candidates = await listLiveCandidates2(queuePath);
|
|
79874
|
+
if (candidates[0] === basename17(candidatePath)) {
|
|
79534
79875
|
return () => _queueLockDeps.unlink(candidatePath).catch(() => {});
|
|
79535
79876
|
}
|
|
79536
79877
|
await _queueLockDeps.sleep(LOCK_RETRY_MS);
|
|
@@ -79539,21 +79880,21 @@ async function acquire(queuePath) {
|
|
|
79539
79880
|
throw new Error(`[queue] Timed out acquiring queue lock: ${queuePath}`);
|
|
79540
79881
|
}
|
|
79541
79882
|
async function withQueueFileLock(queuePath, operation) {
|
|
79542
|
-
const release = await
|
|
79883
|
+
const release = await acquire2(queuePath);
|
|
79543
79884
|
try {
|
|
79544
79885
|
return await operation();
|
|
79545
79886
|
} finally {
|
|
79546
79887
|
await release();
|
|
79547
79888
|
}
|
|
79548
79889
|
}
|
|
79549
|
-
var LOCK_RETRY_MS = 10, LOCK_TIMEOUT_MS = 5000,
|
|
79890
|
+
var LOCK_RETRY_MS = 10, LOCK_TIMEOUT_MS = 5000, LOCK_TIME_WIDTH2 = 13, _queueLockDeps;
|
|
79550
79891
|
var init_queue_file_lock = __esm(() => {
|
|
79551
79892
|
_queueLockDeps = {
|
|
79552
|
-
open,
|
|
79553
|
-
readdir:
|
|
79554
|
-
stat:
|
|
79555
|
-
unlink:
|
|
79556
|
-
randomUUID:
|
|
79893
|
+
open: open2,
|
|
79894
|
+
readdir: readdir5,
|
|
79895
|
+
stat: stat4,
|
|
79896
|
+
unlink: unlink6,
|
|
79897
|
+
randomUUID: randomUUID7,
|
|
79557
79898
|
now: () => Date.now(),
|
|
79558
79899
|
sleep: (ms) => new Promise((resolve21) => setTimeout(resolve21, ms)),
|
|
79559
79900
|
isPidAlive: isProcessAlive
|
|
@@ -79561,7 +79902,7 @@ var init_queue_file_lock = __esm(() => {
|
|
|
79561
79902
|
});
|
|
79562
79903
|
|
|
79563
79904
|
// src/execution/queue-handler.ts
|
|
79564
|
-
import { rename as rename6, unlink as
|
|
79905
|
+
import { rename as rename6, unlink as unlink7 } from "fs/promises";
|
|
79565
79906
|
import path29 from "path";
|
|
79566
79907
|
function getSafeLogger7() {
|
|
79567
79908
|
try {
|
|
@@ -79619,7 +79960,12 @@ async function processQueueFile(workdir, processor) {
|
|
|
79619
79960
|
if (commands === null)
|
|
79620
79961
|
return;
|
|
79621
79962
|
const result = await processor(commands);
|
|
79622
|
-
await
|
|
79963
|
+
await unlink7(processingPath).catch((error48) => {
|
|
79964
|
+
logger?.warn("queue", "Failed to clear processed queue file \u2014 next run may re-apply this batch", {
|
|
79965
|
+
error: error48.message,
|
|
79966
|
+
processingPath
|
|
79967
|
+
});
|
|
79968
|
+
});
|
|
79623
79969
|
return result;
|
|
79624
79970
|
});
|
|
79625
79971
|
} catch (error48) {
|
|
@@ -79638,7 +79984,7 @@ async function clearQueueFile(workdir) {
|
|
|
79638
79984
|
await withQueueFileLock(queuePath, async () => {
|
|
79639
79985
|
const file3 = Bun.file(processingPath);
|
|
79640
79986
|
if (await file3.exists())
|
|
79641
|
-
await
|
|
79987
|
+
await unlink7(processingPath);
|
|
79642
79988
|
});
|
|
79643
79989
|
} catch (error48) {
|
|
79644
79990
|
logger?.warn("queue", "Failed to clear queue file", {
|
|
@@ -80652,18 +80998,17 @@ async function runSetupGate(workdir, config2) {
|
|
|
80652
80998
|
return 0;
|
|
80653
80999
|
}
|
|
80654
81000
|
logger.info("setup-verify", "Running verification gate", { storyId: "setup", cmd: testCmd });
|
|
80655
|
-
const
|
|
80656
|
-
|
|
80657
|
-
|
|
80658
|
-
|
|
80659
|
-
|
|
81001
|
+
const result = await runQualityCommand({
|
|
81002
|
+
commandName: "setup-verify",
|
|
81003
|
+
command: testCmd,
|
|
81004
|
+
workdir,
|
|
81005
|
+
storyId: "setup"
|
|
81006
|
+
});
|
|
81007
|
+
return result.exitCode;
|
|
80660
81008
|
}
|
|
80661
|
-
var _setupVerifyDeps;
|
|
80662
81009
|
var init_setup_verify = __esm(() => {
|
|
80663
81010
|
init_logger2();
|
|
80664
|
-
|
|
80665
|
-
spawn: Bun.spawn.bind(Bun)
|
|
80666
|
-
};
|
|
81011
|
+
init_runner();
|
|
80667
81012
|
});
|
|
80668
81013
|
|
|
80669
81014
|
// src/cli/setup-write.ts
|
|
@@ -110155,7 +110500,7 @@ var MAX_WORKTREE_ID_LENGTH = 64, HASH_SUFFIX_LENGTH = 8;
|
|
|
110155
110500
|
var init_worktree_id = () => {};
|
|
110156
110501
|
|
|
110157
110502
|
// src/bakeoff/contestant.ts
|
|
110158
|
-
import { basename as
|
|
110503
|
+
import { basename as basename22, join as join111 } from "path";
|
|
110159
110504
|
function aggregateTotals(metrics) {
|
|
110160
110505
|
let costUsd = 0;
|
|
110161
110506
|
let wallTimeMs = 0;
|
|
@@ -110185,7 +110530,7 @@ async function runContestant(agent, options, deps) {
|
|
|
110185
110530
|
}
|
|
110186
110531
|
};
|
|
110187
110532
|
const worktree = join111(options.projectRoot, ".nax-wt", storyId);
|
|
110188
|
-
const projectKey = options.config.name?.trim() ||
|
|
110533
|
+
const projectKey = options.config.name?.trim() || basename22(options.projectRoot);
|
|
110189
110534
|
const outputDir = join111(projectOutputDir(projectKey, options.outputDir), "bakeoff", feature, agent);
|
|
110190
110535
|
const context = {
|
|
110191
110536
|
profile: agent,
|
|
@@ -110512,7 +110857,7 @@ var init_bakeoff = __esm(() => {
|
|
|
110512
110857
|
});
|
|
110513
110858
|
|
|
110514
110859
|
// src/plugins/builtin/curator/rollup-prune.ts
|
|
110515
|
-
import { rename as rename7, unlink as
|
|
110860
|
+
import { rename as rename7, unlink as unlink9, writeFile as writeFile3 } from "fs/promises";
|
|
110516
110861
|
import { appendFile as appendFile7 } from "fs/promises";
|
|
110517
110862
|
async function scanProjectRunIds(rollupPath, projectKey) {
|
|
110518
110863
|
const maxTsByRunId = new Map;
|
|
@@ -110583,7 +110928,7 @@ async function pruneRollup(input) {
|
|
|
110583
110928
|
await flush();
|
|
110584
110929
|
await rename7(tmpPath, rollupPath);
|
|
110585
110930
|
} catch (err) {
|
|
110586
|
-
await
|
|
110931
|
+
await unlink9(tmpPath).catch(() => {});
|
|
110587
110932
|
throw err;
|
|
110588
110933
|
}
|
|
110589
110934
|
return result2;
|
|
@@ -110604,7 +110949,7 @@ __export(exports_curator, {
|
|
|
110604
110949
|
_curatorCmdDeps: () => _curatorCmdDeps
|
|
110605
110950
|
});
|
|
110606
110951
|
import { readdirSync as readdirSync9 } from "fs";
|
|
110607
|
-
import { unlink as
|
|
110952
|
+
import { unlink as unlink10 } from "fs/promises";
|
|
110608
110953
|
import { join as join114, resolve as resolve23, sep as sep9 } from "path";
|
|
110609
110954
|
function listRunIds(runsDir) {
|
|
110610
110955
|
try {
|
|
@@ -110969,7 +111314,7 @@ var init_curator2 = __esm(() => {
|
|
|
110969
111314
|
},
|
|
110970
111315
|
removeFile: async (p) => {
|
|
110971
111316
|
try {
|
|
110972
|
-
await
|
|
111317
|
+
await unlink10(p);
|
|
110973
111318
|
} catch {}
|
|
110974
111319
|
},
|
|
110975
111320
|
openInEditor: async (filePath) => {
|
|
@@ -110988,7 +111333,7 @@ var init_curator2 = __esm(() => {
|
|
|
110988
111333
|
init_source();
|
|
110989
111334
|
import { existsSync as existsSync41, mkdirSync as mkdirSync8 } from "fs";
|
|
110990
111335
|
import { homedir as homedir3 } from "os";
|
|
110991
|
-
import { basename as
|
|
111336
|
+
import { basename as basename24, join as join115 } from "path";
|
|
110992
111337
|
|
|
110993
111338
|
// node_modules/commander/esm.mjs
|
|
110994
111339
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -111666,12 +112011,12 @@ init_errors();
|
|
|
111666
112011
|
init_logger2();
|
|
111667
112012
|
init_runtime();
|
|
111668
112013
|
import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
|
|
111669
|
-
import { basename as
|
|
112014
|
+
import { basename as basename12, join as join52 } from "path";
|
|
111670
112015
|
async function resolveOutputDir(workdir, override) {
|
|
111671
112016
|
if (override)
|
|
111672
112017
|
return override;
|
|
111673
112018
|
const config2 = await loadConfig(workdir).catch(() => null);
|
|
111674
|
-
const projectKey = config2?.name?.trim() ||
|
|
112019
|
+
const projectKey = config2?.name?.trim() || basename12(workdir);
|
|
111675
112020
|
return projectOutputDir(projectKey, config2?.outputDir);
|
|
111676
112021
|
}
|
|
111677
112022
|
async function parseRunLog(logPath) {
|
|
@@ -113379,7 +113724,7 @@ async function rulesLintCommand(options, deps = _rulesLintDeps) {
|
|
|
113379
113724
|
// src/cli/rules-migrate.ts
|
|
113380
113725
|
init_canonical_loader();
|
|
113381
113726
|
init_errors();
|
|
113382
|
-
import { basename as
|
|
113727
|
+
import { basename as basename19, join as join96 } from "path";
|
|
113383
113728
|
|
|
113384
113729
|
// src/cli/rules-migrate-plan.ts
|
|
113385
113730
|
init_errors();
|
|
@@ -113458,7 +113803,7 @@ async function collectMigrationSources(workdir) {
|
|
|
113458
113803
|
try {
|
|
113459
113804
|
const content = await _rulesCLIDeps.readFile(filePath);
|
|
113460
113805
|
if (content.trim()) {
|
|
113461
|
-
sources.push({ sourcePath: filePath, targetFileName:
|
|
113806
|
+
sources.push({ sourcePath: filePath, targetFileName: basename19(filePath), content });
|
|
113462
113807
|
}
|
|
113463
113808
|
} catch {}
|
|
113464
113809
|
}
|
|
@@ -113959,7 +114304,7 @@ init_runtime();
|
|
|
113959
114304
|
init_json_file();
|
|
113960
114305
|
init_routing();
|
|
113961
114306
|
import { mkdirSync as mkdirSync7 } from "fs";
|
|
113962
|
-
import { basename as
|
|
114307
|
+
import { basename as basename20, join as join100 } from "path";
|
|
113963
114308
|
var _routingCalibrateDeps = {
|
|
113964
114309
|
loadRunMetrics: (outputDir) => loadRunMetrics(outputDir),
|
|
113965
114310
|
readConfig: (workdir) => loadConfig(workdir),
|
|
@@ -114017,7 +114362,7 @@ async function runRoutingCalibrateCli(options, deps = _routingCalibrateDeps) {
|
|
|
114017
114362
|
function resolveOutputDir2(workdir, override, prior) {
|
|
114018
114363
|
if (override)
|
|
114019
114364
|
return override;
|
|
114020
|
-
const key = prior?.name?.trim() ||
|
|
114365
|
+
const key = prior?.name?.trim() || basename20(workdir);
|
|
114021
114366
|
return projectOutputDir(key, prior?.outputDir);
|
|
114022
114367
|
}
|
|
114023
114368
|
function mergeComplexityRouting(prior, adjustments) {
|
|
@@ -114291,7 +114636,7 @@ import { join as join103 } from "path";
|
|
|
114291
114636
|
// src/commands/logs-reader.ts
|
|
114292
114637
|
init_paths3();
|
|
114293
114638
|
import { existsSync as existsSync35, readdirSync as readdirSync7 } from "fs";
|
|
114294
|
-
import { readdir as
|
|
114639
|
+
import { readdir as readdir6 } from "fs/promises";
|
|
114295
114640
|
import { join as join102 } from "path";
|
|
114296
114641
|
var _logsReaderDeps = {
|
|
114297
114642
|
getRunsDir
|
|
@@ -114300,7 +114645,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
114300
114645
|
const runsDir = _logsReaderDeps.getRunsDir();
|
|
114301
114646
|
let entries;
|
|
114302
114647
|
try {
|
|
114303
|
-
entries = await
|
|
114648
|
+
entries = await readdir6(runsDir);
|
|
114304
114649
|
} catch {
|
|
114305
114650
|
throw new Error(`Run not found in registry: ${runId}`);
|
|
114306
114651
|
}
|
|
@@ -114622,12 +114967,12 @@ async function precheckCommand(options) {
|
|
|
114622
114967
|
init_errors();
|
|
114623
114968
|
init_metrics();
|
|
114624
114969
|
import { existsSync as existsSync38 } from "fs";
|
|
114625
|
-
import { dirname as
|
|
114970
|
+
import { dirname as dirname19 } from "path";
|
|
114626
114971
|
|
|
114627
114972
|
// src/replay/discovery.ts
|
|
114628
114973
|
init_errors();
|
|
114629
114974
|
init_paths3();
|
|
114630
|
-
import { readdir as
|
|
114975
|
+
import { readdir as readdir7 } from "fs/promises";
|
|
114631
114976
|
import { join as join106 } from "path";
|
|
114632
114977
|
var _discoveryDeps = {
|
|
114633
114978
|
getRunsDir
|
|
@@ -114635,7 +114980,7 @@ var _discoveryDeps = {
|
|
|
114635
114980
|
async function loadMetas(runsDir) {
|
|
114636
114981
|
let entries;
|
|
114637
114982
|
try {
|
|
114638
|
-
entries = await
|
|
114983
|
+
entries = await readdir7(runsDir);
|
|
114639
114984
|
} catch {
|
|
114640
114985
|
return [];
|
|
114641
114986
|
}
|
|
@@ -114935,7 +115280,7 @@ async function readJsonOrUndefined(path32) {
|
|
|
114935
115280
|
}
|
|
114936
115281
|
async function readMetricsFromProject(meta3) {
|
|
114937
115282
|
const { loadRunMetrics: loadRunMetrics2 } = await Promise.resolve().then(() => (init_tracker(), exports_tracker));
|
|
114938
|
-
const outputDir =
|
|
115283
|
+
const outputDir = dirname19(dirname19(dirname19(meta3.eventsDir)));
|
|
114939
115284
|
const all = await loadRunMetrics2(outputDir);
|
|
114940
115285
|
return all.find((m) => m.runId === meta3.runId);
|
|
114941
115286
|
}
|
|
@@ -115018,7 +115363,7 @@ init_errors();
|
|
|
115018
115363
|
init_checkpoint();
|
|
115019
115364
|
init_runtime();
|
|
115020
115365
|
import { existsSync as existsSync39 } from "fs";
|
|
115021
|
-
import { basename as
|
|
115366
|
+
import { basename as basename21, join as join107 } from "path";
|
|
115022
115367
|
async function defaultCheckpointExists(featureDir2) {
|
|
115023
115368
|
if (!featureDir2 || !existsSync39(featureDir2))
|
|
115024
115369
|
return false;
|
|
@@ -115098,7 +115443,7 @@ function registerResumeCommand(program2) {
|
|
|
115098
115443
|
const globalNaxDir = globalConfigDir();
|
|
115099
115444
|
const hooks = await loadHooksConfig2(naxDir, globalNaxDir);
|
|
115100
115445
|
applyResumeModeDeps2(opts.featureDir ?? "", "auto");
|
|
115101
|
-
const projectKey = config2.name?.trim() ||
|
|
115446
|
+
const projectKey = config2.name?.trim() || basename21(cmdOpts.dir);
|
|
115102
115447
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
115103
115448
|
const statusFilePath = join107(outputDir, "status.json");
|
|
115104
115449
|
const runsDir = join107(outputDir, "features", feature, "runs");
|
|
@@ -115139,7 +115484,7 @@ function registerResumeCommand(program2) {
|
|
|
115139
115484
|
// src/commands/runs.ts
|
|
115140
115485
|
init_source();
|
|
115141
115486
|
init_paths3();
|
|
115142
|
-
import { readdir as
|
|
115487
|
+
import { readdir as readdir8 } from "fs/promises";
|
|
115143
115488
|
import { join as join108 } from "path";
|
|
115144
115489
|
var DEFAULT_LIMIT = 20;
|
|
115145
115490
|
var _runsCmdDeps = {
|
|
@@ -115190,7 +115535,7 @@ async function runsCommand(options = {}) {
|
|
|
115190
115535
|
const runsDir = _runsCmdDeps.getRunsDir();
|
|
115191
115536
|
let entries;
|
|
115192
115537
|
try {
|
|
115193
|
-
entries = await
|
|
115538
|
+
entries = await readdir8(runsDir);
|
|
115194
115539
|
} catch {
|
|
115195
115540
|
console.log("No runs found.");
|
|
115196
115541
|
return;
|
|
@@ -115274,7 +115619,7 @@ async function runsCommand(options = {}) {
|
|
|
115274
115619
|
|
|
115275
115620
|
// src/commands/unlock.ts
|
|
115276
115621
|
init_source();
|
|
115277
|
-
import { unlink as
|
|
115622
|
+
import { unlink as unlink8 } from "fs/promises";
|
|
115278
115623
|
import { join as join109 } from "path";
|
|
115279
115624
|
function formatLockAge(ageMs) {
|
|
115280
115625
|
const minutes = Math.round(ageMs / (60 * 1000));
|
|
@@ -115321,7 +115666,7 @@ async function unlockCommand(options) {
|
|
|
115321
115666
|
}
|
|
115322
115667
|
}
|
|
115323
115668
|
try {
|
|
115324
|
-
await
|
|
115669
|
+
await unlink8(lockPath);
|
|
115325
115670
|
} catch (error48) {
|
|
115326
115671
|
console.error(source_default.red(`Failed to remove lock: ${error48 instanceof Error ? error48.message : String(error48)}`));
|
|
115327
115672
|
process.exit(1);
|
|
@@ -123561,7 +123906,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
123561
123906
|
process.exit(1);
|
|
123562
123907
|
}
|
|
123563
123908
|
resetLogger();
|
|
123564
|
-
const projectKey = config2.name?.trim() ||
|
|
123909
|
+
const projectKey = config2.name?.trim() || basename24(workdir);
|
|
123565
123910
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
123566
123911
|
const runsDir = join115(outputDir, "features", options.feature, "runs");
|
|
123567
123912
|
mkdirSync8(runsDir, { recursive: true });
|