@nathapp/nax 0.54.12 → 0.55.1
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 +386 -222
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -3259,6 +3259,14 @@ async function withProcessTimeout(proc, timeoutMs, opts) {
|
|
|
3259
3259
|
}
|
|
3260
3260
|
|
|
3261
3261
|
// src/utils/bun-deps.ts
|
|
3262
|
+
var exports_bun_deps = {};
|
|
3263
|
+
__export(exports_bun_deps, {
|
|
3264
|
+
which: () => which,
|
|
3265
|
+
typedSpawn: () => typedSpawn,
|
|
3266
|
+
spawn: () => spawn,
|
|
3267
|
+
sleep: () => sleep,
|
|
3268
|
+
file: () => file
|
|
3269
|
+
});
|
|
3262
3270
|
function typedSpawn(cmd, opts) {
|
|
3263
3271
|
return Bun.spawn(cmd, opts);
|
|
3264
3272
|
}
|
|
@@ -3268,6 +3276,9 @@ function which(name) {
|
|
|
3268
3276
|
function sleep(ms) {
|
|
3269
3277
|
return Bun.sleep(ms);
|
|
3270
3278
|
}
|
|
3279
|
+
function file(path) {
|
|
3280
|
+
return Bun.file(path);
|
|
3281
|
+
}
|
|
3271
3282
|
var spawn;
|
|
3272
3283
|
var init_bun_deps = __esm(() => {
|
|
3273
3284
|
spawn = Bun.spawn;
|
|
@@ -14942,26 +14953,26 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
|
|
|
14942
14953
|
_json.pattern = pattern.source;
|
|
14943
14954
|
}, fileProcessor = (schema, _ctx, json, _params) => {
|
|
14944
14955
|
const _json = json;
|
|
14945
|
-
const
|
|
14956
|
+
const file2 = {
|
|
14946
14957
|
type: "string",
|
|
14947
14958
|
format: "binary",
|
|
14948
14959
|
contentEncoding: "binary"
|
|
14949
14960
|
};
|
|
14950
14961
|
const { minimum, maximum, mime } = schema._zod.bag;
|
|
14951
14962
|
if (minimum !== undefined)
|
|
14952
|
-
|
|
14963
|
+
file2.minLength = minimum;
|
|
14953
14964
|
if (maximum !== undefined)
|
|
14954
|
-
|
|
14965
|
+
file2.maxLength = maximum;
|
|
14955
14966
|
if (mime) {
|
|
14956
14967
|
if (mime.length === 1) {
|
|
14957
|
-
|
|
14958
|
-
Object.assign(_json,
|
|
14968
|
+
file2.contentMediaType = mime[0];
|
|
14969
|
+
Object.assign(_json, file2);
|
|
14959
14970
|
} else {
|
|
14960
|
-
Object.assign(_json,
|
|
14971
|
+
Object.assign(_json, file2);
|
|
14961
14972
|
_json.anyOf = mime.map((m) => ({ contentMediaType: m }));
|
|
14962
14973
|
}
|
|
14963
14974
|
} else {
|
|
14964
|
-
Object.assign(_json,
|
|
14975
|
+
Object.assign(_json, file2);
|
|
14965
14976
|
}
|
|
14966
14977
|
}, successProcessor = (_schema, _ctx, json, _params) => {
|
|
14967
14978
|
json.type = "boolean";
|
|
@@ -15833,7 +15844,7 @@ __export(exports_schemas2, {
|
|
|
15833
15844
|
function: () => _function,
|
|
15834
15845
|
float64: () => float64,
|
|
15835
15846
|
float32: () => float32,
|
|
15836
|
-
file: () =>
|
|
15847
|
+
file: () => file2,
|
|
15837
15848
|
exactOptional: () => exactOptional,
|
|
15838
15849
|
enum: () => _enum2,
|
|
15839
15850
|
emoji: () => emoji2,
|
|
@@ -16214,7 +16225,7 @@ function literal(value, params) {
|
|
|
16214
16225
|
...exports_util.normalizeParams(params)
|
|
16215
16226
|
});
|
|
16216
16227
|
}
|
|
16217
|
-
function
|
|
16228
|
+
function file2(params) {
|
|
16218
16229
|
return _file(ZodFile, params);
|
|
16219
16230
|
}
|
|
16220
16231
|
function transform(fn) {
|
|
@@ -17662,7 +17673,7 @@ __export(exports_external, {
|
|
|
17662
17673
|
float64: () => float64,
|
|
17663
17674
|
float32: () => float32,
|
|
17664
17675
|
flattenError: () => flattenError,
|
|
17665
|
-
file: () =>
|
|
17676
|
+
file: () => file2,
|
|
17666
17677
|
exactOptional: () => exactOptional,
|
|
17667
17678
|
enum: () => _enum2,
|
|
17668
17679
|
endsWith: () => _endsWith,
|
|
@@ -19586,7 +19597,8 @@ class SpawnAcpSession {
|
|
|
19586
19597
|
const stderr = await new Response(proc.stderr).text();
|
|
19587
19598
|
if (exitCode !== 0) {
|
|
19588
19599
|
getSafeLogger()?.warn("acp-adapter", `Session prompt exited with code ${exitCode}`, {
|
|
19589
|
-
|
|
19600
|
+
exitCode,
|
|
19601
|
+
stderr: stderr.slice(0, 500)
|
|
19590
19602
|
});
|
|
19591
19603
|
return {
|
|
19592
19604
|
messages: [{ role: "assistant", content: stderr || `Exit code ${exitCode}` }],
|
|
@@ -19912,10 +19924,10 @@ async function sweepFeatureSessions(workdir, featureName) {
|
|
|
19912
19924
|
}
|
|
19913
19925
|
async function sweepStaleFeatureSessions(workdir, featureName, maxAgeMs = MAX_SESSION_AGE_MS) {
|
|
19914
19926
|
const path = acpSessionsPath(workdir, featureName);
|
|
19915
|
-
const
|
|
19916
|
-
if (!await
|
|
19927
|
+
const file3 = Bun.file(path);
|
|
19928
|
+
if (!await file3.exists())
|
|
19917
19929
|
return;
|
|
19918
|
-
const ageMs = Date.now() -
|
|
19930
|
+
const ageMs = Date.now() - file3.lastModified;
|
|
19919
19931
|
if (ageMs < maxAgeMs)
|
|
19920
19932
|
return;
|
|
19921
19933
|
getSafeLogger()?.info("acp-adapter", `[sweep] Sidecar is ${Math.round(ageMs / 60000)}m old \u2014 sweeping stale sessions`, {
|
|
@@ -19994,11 +20006,23 @@ class AcpAgentAdapter {
|
|
|
19994
20006
|
storyId: options.storyId,
|
|
19995
20007
|
sessionRole: options.sessionRole
|
|
19996
20008
|
});
|
|
20009
|
+
let sessionErrorRetried = false;
|
|
19997
20010
|
for (let attempt = 0;attempt < MAX_RATE_LIMIT_RETRIES; attempt++) {
|
|
19998
20011
|
try {
|
|
19999
20012
|
const result = await this._runWithClient(options, startTime);
|
|
20000
20013
|
if (!result.success) {
|
|
20001
20014
|
getSafeLogger()?.warn("acp-adapter", `Run failed for ${this.name}`, { exitCode: result.exitCode });
|
|
20015
|
+
if (result.sessionError && _acpAdapterDeps.shouldRetrySessionError && !sessionErrorRetried) {
|
|
20016
|
+
sessionErrorRetried = true;
|
|
20017
|
+
getSafeLogger()?.warn("acp-adapter", "Session error \u2014 retrying with fresh session", {
|
|
20018
|
+
storyId: options.storyId,
|
|
20019
|
+
featureName: options.featureName
|
|
20020
|
+
});
|
|
20021
|
+
if (options.featureName && options.storyId) {
|
|
20022
|
+
await clearAcpSession(options.workdir, options.featureName, options.storyId);
|
|
20023
|
+
}
|
|
20024
|
+
continue;
|
|
20025
|
+
}
|
|
20002
20026
|
}
|
|
20003
20027
|
return result;
|
|
20004
20028
|
} catch (err) {
|
|
@@ -20127,6 +20151,7 @@ class AcpAgentAdapter {
|
|
|
20127
20151
|
};
|
|
20128
20152
|
}
|
|
20129
20153
|
const success2 = lastResponse?.stopReason === "end_turn";
|
|
20154
|
+
const isSessionError = lastResponse?.stopReason === "error";
|
|
20130
20155
|
const output = extractOutput(lastResponse);
|
|
20131
20156
|
const estimatedCost = totalExactCostUsd ?? (totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0 ? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model) : 0);
|
|
20132
20157
|
return {
|
|
@@ -20134,6 +20159,7 @@ class AcpAgentAdapter {
|
|
|
20134
20159
|
exitCode: success2 ? 0 : 1,
|
|
20135
20160
|
output: output.slice(-MAX_AGENT_OUTPUT_CHARS2),
|
|
20136
20161
|
rateLimited: false,
|
|
20162
|
+
sessionError: isSessionError,
|
|
20137
20163
|
durationMs,
|
|
20138
20164
|
estimatedCost
|
|
20139
20165
|
};
|
|
@@ -20328,6 +20354,7 @@ var init_adapter2 = __esm(() => {
|
|
|
20328
20354
|
_acpAdapterDeps = {
|
|
20329
20355
|
which,
|
|
20330
20356
|
sleep,
|
|
20357
|
+
shouldRetrySessionError: true,
|
|
20331
20358
|
createClient(cmdStr, cwd, timeoutSeconds, pidRegistry) {
|
|
20332
20359
|
return createSpawnAcpClient(cmdStr, cwd, timeoutSeconds, pidRegistry);
|
|
20333
20360
|
}
|
|
@@ -21190,17 +21217,24 @@ ${errors3.join(`
|
|
|
21190
21217
|
return result.data;
|
|
21191
21218
|
}
|
|
21192
21219
|
async function loadConfigForWorkdir(rootConfigPath, packageDir) {
|
|
21220
|
+
const logger = getLogger();
|
|
21193
21221
|
const rootNaxDir = dirname(rootConfigPath);
|
|
21194
21222
|
const rootConfig = await loadConfig(rootNaxDir);
|
|
21195
21223
|
if (!packageDir) {
|
|
21224
|
+
logger.debug("config", "No packageDir \u2014 using root config");
|
|
21196
21225
|
return rootConfig;
|
|
21197
21226
|
}
|
|
21198
21227
|
const repoRoot = dirname(rootNaxDir);
|
|
21199
21228
|
const packageConfigPath = join6(repoRoot, PROJECT_NAX_DIR, "mono", packageDir, "config.json");
|
|
21200
21229
|
const packageOverride = await loadJsonFile(packageConfigPath, "config");
|
|
21201
21230
|
if (!packageOverride) {
|
|
21231
|
+
logger.debug("config", "Per-package config not found \u2014 falling back to root config", {
|
|
21232
|
+
packageConfigPath,
|
|
21233
|
+
packageDir
|
|
21234
|
+
});
|
|
21202
21235
|
return rootConfig;
|
|
21203
21236
|
}
|
|
21237
|
+
logger.debug("config", "Per-package config loaded", { packageConfigPath, packageDir });
|
|
21204
21238
|
return mergePackageConfig(rootConfig, packageOverride);
|
|
21205
21239
|
}
|
|
21206
21240
|
var init_loader = __esm(() => {
|
|
@@ -22370,7 +22404,7 @@ var package_default;
|
|
|
22370
22404
|
var init_package = __esm(() => {
|
|
22371
22405
|
package_default = {
|
|
22372
22406
|
name: "@nathapp/nax",
|
|
22373
|
-
version: "0.
|
|
22407
|
+
version: "0.55.1",
|
|
22374
22408
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22375
22409
|
type: "module",
|
|
22376
22410
|
bin: {
|
|
@@ -22449,8 +22483,8 @@ var init_version = __esm(() => {
|
|
|
22449
22483
|
NAX_VERSION = package_default.version;
|
|
22450
22484
|
NAX_COMMIT = (() => {
|
|
22451
22485
|
try {
|
|
22452
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22453
|
-
return "
|
|
22486
|
+
if (/^[0-9a-f]{6,10}$/.test("a51fce0b"))
|
|
22487
|
+
return "a51fce0b";
|
|
22454
22488
|
} catch {}
|
|
22455
22489
|
try {
|
|
22456
22490
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -23886,12 +23920,12 @@ async function loadPendingInteraction(requestId, featureDir) {
|
|
|
23886
23920
|
const filename = `${requestId}.json`;
|
|
23887
23921
|
const filePath = path3.join(interactionsDir, filename);
|
|
23888
23922
|
try {
|
|
23889
|
-
const
|
|
23890
|
-
const exists = await
|
|
23923
|
+
const file3 = Bun.file(filePath);
|
|
23924
|
+
const exists = await file3.exists();
|
|
23891
23925
|
if (!exists) {
|
|
23892
23926
|
return null;
|
|
23893
23927
|
}
|
|
23894
|
-
const json2 = await
|
|
23928
|
+
const json2 = await file3.text();
|
|
23895
23929
|
const request = JSON.parse(json2);
|
|
23896
23930
|
return request;
|
|
23897
23931
|
} catch {
|
|
@@ -24220,9 +24254,9 @@ var init_acceptance2 = __esm(() => {
|
|
|
24220
24254
|
async execute(ctx) {
|
|
24221
24255
|
const logger = getLogger();
|
|
24222
24256
|
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
24223
|
-
logger.info("acceptance", "Running acceptance tests");
|
|
24257
|
+
logger.info("acceptance", "Running acceptance tests", { storyId: ctx.story.id });
|
|
24224
24258
|
if (!ctx.featureDir) {
|
|
24225
|
-
logger.warn("acceptance", "No feature directory \u2014 skipping acceptance tests");
|
|
24259
|
+
logger.warn("acceptance", "No feature directory \u2014 skipping acceptance tests", { storyId: ctx.story.id });
|
|
24226
24260
|
return { action: "continue" };
|
|
24227
24261
|
}
|
|
24228
24262
|
const testGroups = ctx.acceptanceTestPaths ?? [
|
|
@@ -24239,11 +24273,12 @@ var init_acceptance2 = __esm(() => {
|
|
|
24239
24273
|
const testFile = Bun.file(testPath);
|
|
24240
24274
|
const exists = await testFile.exists();
|
|
24241
24275
|
if (!exists) {
|
|
24242
|
-
logger.warn("acceptance", "Acceptance test file not found \u2014 skipping", { testPath });
|
|
24276
|
+
logger.warn("acceptance", "Acceptance test file not found \u2014 skipping", { storyId: ctx.story.id, testPath });
|
|
24243
24277
|
continue;
|
|
24244
24278
|
}
|
|
24245
24279
|
const testCmdParts = buildAcceptanceRunCommand(testPath, effectiveConfig.project?.testFramework, effectiveConfig.acceptance.command);
|
|
24246
24280
|
logger.info("acceptance", "Running acceptance command", {
|
|
24281
|
+
storyId: ctx.story.id,
|
|
24247
24282
|
cmd: testCmdParts.join(" "),
|
|
24248
24283
|
packageDir
|
|
24249
24284
|
});
|
|
@@ -24266,12 +24301,14 @@ ${stderr}`;
|
|
|
24266
24301
|
const overriddenFailures = failedACs.filter((acId) => overrides[acId]);
|
|
24267
24302
|
if (overriddenFailures.length > 0) {
|
|
24268
24303
|
logger.warn("acceptance", "Skipped failures (overridden)", {
|
|
24304
|
+
storyId: ctx.story.id,
|
|
24269
24305
|
overriddenFailures,
|
|
24270
24306
|
overrides: overriddenFailures.map((acId) => ({ acId, reason: overrides[acId] }))
|
|
24271
24307
|
});
|
|
24272
24308
|
}
|
|
24273
24309
|
if (failedACs.length === 0 && exitCode !== 0) {
|
|
24274
24310
|
logger.error("acceptance", "Tests errored with no AC failures parsed", {
|
|
24311
|
+
storyId: ctx.story.id,
|
|
24275
24312
|
exitCode,
|
|
24276
24313
|
packageDir
|
|
24277
24314
|
});
|
|
@@ -24288,18 +24325,19 @@ ${stderr}`;
|
|
|
24288
24325
|
}
|
|
24289
24326
|
if (actualFailures.length > 0) {
|
|
24290
24327
|
logger.error("acceptance", "Acceptance tests failed", {
|
|
24328
|
+
storyId: ctx.story.id,
|
|
24291
24329
|
failedACs: actualFailures,
|
|
24292
24330
|
packageDir
|
|
24293
24331
|
});
|
|
24294
24332
|
logTestOutput(logger, "acceptance", output);
|
|
24295
24333
|
} else if (exitCode === 0) {
|
|
24296
|
-
logger.info("acceptance", "Package acceptance tests passed", { packageDir });
|
|
24334
|
+
logger.info("acceptance", "Package acceptance tests passed", { storyId: ctx.story.id, packageDir });
|
|
24297
24335
|
}
|
|
24298
24336
|
}
|
|
24299
24337
|
const combinedOutput = allOutputParts.join(`
|
|
24300
24338
|
`);
|
|
24301
24339
|
if (allFailedACs.length === 0) {
|
|
24302
|
-
logger.info("acceptance", "All acceptance tests passed");
|
|
24340
|
+
logger.info("acceptance", "All acceptance tests passed", { storyId: ctx.story.id });
|
|
24303
24341
|
return { action: "continue" };
|
|
24304
24342
|
}
|
|
24305
24343
|
ctx.acceptanceFailures = {
|
|
@@ -24639,6 +24677,99 @@ ${stderr}` };
|
|
|
24639
24677
|
};
|
|
24640
24678
|
});
|
|
24641
24679
|
|
|
24680
|
+
// src/quality/runner.ts
|
|
24681
|
+
var {spawn: spawn2 } = globalThis.Bun;
|
|
24682
|
+
async function runQualityCommand(opts) {
|
|
24683
|
+
const { commandName, command, workdir, storyId, timeoutMs = DEFAULT_TIMEOUT_MS } = opts;
|
|
24684
|
+
const startTime = Date.now();
|
|
24685
|
+
const logger = getSafeLogger();
|
|
24686
|
+
logger?.info("quality", `Running ${commandName}`, { storyId, commandName, command, workdir });
|
|
24687
|
+
try {
|
|
24688
|
+
const parts = command.split(/\s+/);
|
|
24689
|
+
const [executable, ...args] = parts;
|
|
24690
|
+
const proc = _qualityRunnerDeps.spawn({
|
|
24691
|
+
cmd: [executable, ...args],
|
|
24692
|
+
cwd: workdir,
|
|
24693
|
+
stdout: "pipe",
|
|
24694
|
+
stderr: "pipe"
|
|
24695
|
+
});
|
|
24696
|
+
let timedOut = false;
|
|
24697
|
+
const killTimer = setTimeout(() => {
|
|
24698
|
+
timedOut = true;
|
|
24699
|
+
try {
|
|
24700
|
+
proc.kill("SIGTERM");
|
|
24701
|
+
} catch {}
|
|
24702
|
+
setTimeout(() => {
|
|
24703
|
+
try {
|
|
24704
|
+
proc.kill("SIGKILL");
|
|
24705
|
+
} catch {}
|
|
24706
|
+
}, SIGKILL_GRACE_PERIOD_MS2);
|
|
24707
|
+
}, timeoutMs);
|
|
24708
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
24709
|
+
proc.exited,
|
|
24710
|
+
new Response(proc.stdout).text(),
|
|
24711
|
+
new Response(proc.stderr).text()
|
|
24712
|
+
]);
|
|
24713
|
+
clearTimeout(killTimer);
|
|
24714
|
+
const durationMs = Date.now() - startTime;
|
|
24715
|
+
if (timedOut) {
|
|
24716
|
+
logger?.warn("quality", `${commandName} timed out`, {
|
|
24717
|
+
storyId,
|
|
24718
|
+
commandName,
|
|
24719
|
+
command,
|
|
24720
|
+
workdir,
|
|
24721
|
+
durationMs,
|
|
24722
|
+
timedOut: true
|
|
24723
|
+
});
|
|
24724
|
+
return {
|
|
24725
|
+
commandName,
|
|
24726
|
+
command,
|
|
24727
|
+
success: false,
|
|
24728
|
+
exitCode: -1,
|
|
24729
|
+
output: `[nax] ${commandName} timed out after ${timeoutMs / 1000}s`,
|
|
24730
|
+
durationMs,
|
|
24731
|
+
timedOut: true
|
|
24732
|
+
};
|
|
24733
|
+
}
|
|
24734
|
+
const output = [stdout, stderr].filter(Boolean).join(`
|
|
24735
|
+
`);
|
|
24736
|
+
const success2 = exitCode === 0;
|
|
24737
|
+
logger?.info("quality", `${commandName} completed`, {
|
|
24738
|
+
storyId,
|
|
24739
|
+
commandName,
|
|
24740
|
+
command,
|
|
24741
|
+
workdir,
|
|
24742
|
+
exitCode,
|
|
24743
|
+
durationMs,
|
|
24744
|
+
timedOut: false
|
|
24745
|
+
});
|
|
24746
|
+
return { commandName, command, success: success2, exitCode, output, durationMs, timedOut: false };
|
|
24747
|
+
} catch (error48) {
|
|
24748
|
+
const durationMs = Date.now() - startTime;
|
|
24749
|
+
return {
|
|
24750
|
+
commandName,
|
|
24751
|
+
command,
|
|
24752
|
+
success: false,
|
|
24753
|
+
exitCode: -1,
|
|
24754
|
+
output: errorMessage(error48),
|
|
24755
|
+
durationMs,
|
|
24756
|
+
timedOut: false
|
|
24757
|
+
};
|
|
24758
|
+
}
|
|
24759
|
+
}
|
|
24760
|
+
var DEFAULT_TIMEOUT_MS = 120000, SIGKILL_GRACE_PERIOD_MS2 = 5000, _qualityRunnerDeps;
|
|
24761
|
+
var init_runner2 = __esm(() => {
|
|
24762
|
+
init_logger2();
|
|
24763
|
+
_qualityRunnerDeps = {
|
|
24764
|
+
spawn: spawn2
|
|
24765
|
+
};
|
|
24766
|
+
});
|
|
24767
|
+
|
|
24768
|
+
// src/quality/index.ts
|
|
24769
|
+
var init_quality = __esm(() => {
|
|
24770
|
+
init_runner2();
|
|
24771
|
+
});
|
|
24772
|
+
|
|
24642
24773
|
// src/pipeline/event-bus.ts
|
|
24643
24774
|
class PipelineEventBus {
|
|
24644
24775
|
subscribers = new Map;
|
|
@@ -24913,7 +25044,7 @@ var init_language_commands = __esm(() => {
|
|
|
24913
25044
|
});
|
|
24914
25045
|
|
|
24915
25046
|
// src/review/semantic.ts
|
|
24916
|
-
var {spawn:
|
|
25047
|
+
var {spawn: spawn3 } = globalThis.Bun;
|
|
24917
25048
|
async function collectDiff(workdir, storyGitRef, excludePatterns) {
|
|
24918
25049
|
const cmd = ["git", "diff", "--unified=3", `${storyGitRef}..HEAD`];
|
|
24919
25050
|
if (excludePatterns.length > 0) {
|
|
@@ -25016,7 +25147,7 @@ If all ACs are correctly implemented, respond with { "passed": true, "findings":
|
|
|
25016
25147
|
function parseLLMResponse(raw) {
|
|
25017
25148
|
try {
|
|
25018
25149
|
let cleaned = raw.trim();
|
|
25019
|
-
const fenceMatch = cleaned.match(/^```(?:json)?\s*\n
|
|
25150
|
+
const fenceMatch = cleaned.match(/^```(?:json)?\s*\n([\s\S]*?)\n```/);
|
|
25020
25151
|
if (fenceMatch)
|
|
25021
25152
|
cleaned = fenceMatch[1].trim();
|
|
25022
25153
|
const parsed = JSON.parse(cleaned);
|
|
@@ -25198,18 +25329,17 @@ var init_semantic = __esm(() => {
|
|
|
25198
25329
|
init_logger2();
|
|
25199
25330
|
init_git();
|
|
25200
25331
|
_semanticDeps = {
|
|
25201
|
-
spawn:
|
|
25332
|
+
spawn: spawn3,
|
|
25202
25333
|
isGitRefValid,
|
|
25203
25334
|
getMergeBase
|
|
25204
25335
|
};
|
|
25205
25336
|
});
|
|
25206
25337
|
|
|
25207
25338
|
// src/review/runner.ts
|
|
25208
|
-
var {spawn: spawn3 } = globalThis.Bun;
|
|
25209
25339
|
async function loadPackageJson(workdir) {
|
|
25210
25340
|
try {
|
|
25211
|
-
const
|
|
25212
|
-
const content = await
|
|
25341
|
+
const file3 = _reviewRunnerDeps.file(`${workdir}/package.json`);
|
|
25342
|
+
const content = await file3.text();
|
|
25213
25343
|
return JSON.parse(content);
|
|
25214
25344
|
} catch {
|
|
25215
25345
|
return null;
|
|
@@ -25257,81 +25387,20 @@ async function resolveCommand(check2, config2, executionConfig, workdir, quality
|
|
|
25257
25387
|
}
|
|
25258
25388
|
return null;
|
|
25259
25389
|
}
|
|
25260
|
-
async function runCheck(check2, command, workdir) {
|
|
25261
|
-
const
|
|
25262
|
-
|
|
25263
|
-
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
|
|
25268
|
-
|
|
25269
|
-
|
|
25270
|
-
cwd: workdir,
|
|
25271
|
-
stdout: "pipe",
|
|
25272
|
-
stderr: "pipe"
|
|
25273
|
-
});
|
|
25274
|
-
let timedOut = false;
|
|
25275
|
-
const timerId = setTimeout(() => {
|
|
25276
|
-
timedOut = true;
|
|
25277
|
-
try {
|
|
25278
|
-
proc.kill("SIGTERM");
|
|
25279
|
-
} catch {}
|
|
25280
|
-
setTimeout(() => {
|
|
25281
|
-
try {
|
|
25282
|
-
proc.kill("SIGKILL");
|
|
25283
|
-
} catch {}
|
|
25284
|
-
}, SIGKILL_GRACE_PERIOD_MS2);
|
|
25285
|
-
}, REVIEW_CHECK_TIMEOUT_MS);
|
|
25286
|
-
const exitCode = await proc.exited;
|
|
25287
|
-
clearTimeout(timerId);
|
|
25288
|
-
if (timedOut) {
|
|
25289
|
-
return {
|
|
25290
|
-
check: check2,
|
|
25291
|
-
command,
|
|
25292
|
-
success: false,
|
|
25293
|
-
exitCode: -1,
|
|
25294
|
-
output: `[nax] ${check2} timed out after ${REVIEW_CHECK_TIMEOUT_MS / 1000}s`,
|
|
25295
|
-
durationMs: Date.now() - startTime
|
|
25296
|
-
};
|
|
25297
|
-
}
|
|
25298
|
-
const stdout = await new Response(proc.stdout).text();
|
|
25299
|
-
const stderr = await new Response(proc.stderr).text();
|
|
25300
|
-
const output = [stdout, stderr].filter(Boolean).join(`
|
|
25301
|
-
`);
|
|
25302
|
-
if (exitCode !== 0) {
|
|
25303
|
-
logger?.warn("review", `${check2} check failed`, {
|
|
25304
|
-
check: check2,
|
|
25305
|
-
command,
|
|
25306
|
-
workdir,
|
|
25307
|
-
exitCode,
|
|
25308
|
-
output: output.slice(0, 2000)
|
|
25309
|
-
});
|
|
25310
|
-
} else {
|
|
25311
|
-
logger?.debug("review", `${check2} check passed`, { check: check2, command, durationMs: Date.now() - startTime });
|
|
25312
|
-
}
|
|
25313
|
-
return {
|
|
25314
|
-
check: check2,
|
|
25315
|
-
command,
|
|
25316
|
-
success: exitCode === 0,
|
|
25317
|
-
exitCode,
|
|
25318
|
-
output,
|
|
25319
|
-
durationMs: Date.now() - startTime
|
|
25320
|
-
};
|
|
25321
|
-
} catch (error48) {
|
|
25322
|
-
return {
|
|
25323
|
-
check: check2,
|
|
25324
|
-
command,
|
|
25325
|
-
success: false,
|
|
25326
|
-
exitCode: -1,
|
|
25327
|
-
output: errorMessage(error48),
|
|
25328
|
-
durationMs: Date.now() - startTime
|
|
25329
|
-
};
|
|
25330
|
-
}
|
|
25390
|
+
async function runCheck(check2, command, workdir, storyId) {
|
|
25391
|
+
const result = await runQualityCommand({ commandName: check2, command, workdir, storyId });
|
|
25392
|
+
return {
|
|
25393
|
+
check: check2,
|
|
25394
|
+
command: result.command,
|
|
25395
|
+
success: result.success,
|
|
25396
|
+
exitCode: result.exitCode,
|
|
25397
|
+
output: result.output,
|
|
25398
|
+
durationMs: result.durationMs
|
|
25399
|
+
};
|
|
25331
25400
|
}
|
|
25332
25401
|
async function getUncommittedFilesImpl(workdir) {
|
|
25333
25402
|
try {
|
|
25334
|
-
const proc =
|
|
25403
|
+
const proc = Bun.spawn({
|
|
25335
25404
|
cmd: ["git", "diff", "--name-only", "HEAD"],
|
|
25336
25405
|
cwd: workdir,
|
|
25337
25406
|
stdout: "pipe",
|
|
@@ -25348,7 +25417,7 @@ async function getUncommittedFilesImpl(workdir) {
|
|
|
25348
25417
|
return [];
|
|
25349
25418
|
}
|
|
25350
25419
|
}
|
|
25351
|
-
async function runReview(config2, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig) {
|
|
25420
|
+
async function runReview(config2, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks) {
|
|
25352
25421
|
const startTime = Date.now();
|
|
25353
25422
|
const logger = getSafeLogger();
|
|
25354
25423
|
const checks3 = [];
|
|
@@ -25388,6 +25457,12 @@ Stage and commit these files before running review.`
|
|
|
25388
25457
|
};
|
|
25389
25458
|
}
|
|
25390
25459
|
for (const checkName of config2.checks) {
|
|
25460
|
+
if (retrySkipChecks?.has(checkName)) {
|
|
25461
|
+
getSafeLogger()?.debug("review", `Skipping ${checkName} check (already passed in previous review pass)`, {
|
|
25462
|
+
storyId
|
|
25463
|
+
});
|
|
25464
|
+
continue;
|
|
25465
|
+
}
|
|
25391
25466
|
if (checkName === "semantic") {
|
|
25392
25467
|
const semanticStory = {
|
|
25393
25468
|
id: storyId ?? "",
|
|
@@ -25416,7 +25491,7 @@ Stage and commit these files before running review.`
|
|
|
25416
25491
|
getSafeLogger()?.warn("review", `Skipping ${checkName} check (command not configured or disabled)`);
|
|
25417
25492
|
continue;
|
|
25418
25493
|
}
|
|
25419
|
-
const result = await runCheck(checkName, command, workdir);
|
|
25494
|
+
const result = await runCheck(checkName, command, workdir, storyId);
|
|
25420
25495
|
checks3.push(result);
|
|
25421
25496
|
if (!result.success && !firstFailure) {
|
|
25422
25497
|
firstFailure = `${checkName} failed (exit code ${result.exitCode})`;
|
|
@@ -25433,9 +25508,10 @@ Stage and commit these files before running review.`
|
|
|
25433
25508
|
failureReason: firstFailure
|
|
25434
25509
|
};
|
|
25435
25510
|
}
|
|
25436
|
-
var _reviewSemanticDeps, _reviewRunnerDeps,
|
|
25437
|
-
var
|
|
25511
|
+
var _reviewSemanticDeps, _reviewRunnerDeps, _reviewGitDeps;
|
|
25512
|
+
var init_runner3 = __esm(() => {
|
|
25438
25513
|
init_logger2();
|
|
25514
|
+
init_quality();
|
|
25439
25515
|
init_git();
|
|
25440
25516
|
init_language_commands();
|
|
25441
25517
|
init_semantic();
|
|
@@ -25443,7 +25519,6 @@ var init_runner2 = __esm(() => {
|
|
|
25443
25519
|
runSemanticReview
|
|
25444
25520
|
};
|
|
25445
25521
|
_reviewRunnerDeps = {
|
|
25446
|
-
spawn: spawn3,
|
|
25447
25522
|
file: Bun.file,
|
|
25448
25523
|
which: Bun.which
|
|
25449
25524
|
};
|
|
@@ -25483,9 +25558,9 @@ async function getChangedFiles(workdir, baseRef) {
|
|
|
25483
25558
|
}
|
|
25484
25559
|
|
|
25485
25560
|
class ReviewOrchestrator {
|
|
25486
|
-
async review(reviewConfig, workdir, executionConfig, plugins, storyGitRef, scopePrefix, qualityCommands, storyId, story, modelResolver, naxConfig) {
|
|
25561
|
+
async review(reviewConfig, workdir, executionConfig, plugins, storyGitRef, scopePrefix, qualityCommands, storyId, story, modelResolver, naxConfig, retrySkipChecks) {
|
|
25487
25562
|
const logger = getSafeLogger();
|
|
25488
|
-
const builtIn = await runReview(reviewConfig, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig);
|
|
25563
|
+
const builtIn = await runReview(reviewConfig, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks);
|
|
25489
25564
|
if (!builtIn.success) {
|
|
25490
25565
|
return { builtIn, success: false, failureReason: builtIn.failureReason, pluginFailed: false };
|
|
25491
25566
|
}
|
|
@@ -25550,7 +25625,7 @@ class ReviewOrchestrator {
|
|
|
25550
25625
|
var _orchestratorDeps, reviewOrchestrator;
|
|
25551
25626
|
var init_orchestrator = __esm(() => {
|
|
25552
25627
|
init_logger2();
|
|
25553
|
-
|
|
25628
|
+
init_runner3();
|
|
25554
25629
|
_orchestratorDeps = { spawn: spawn4 };
|
|
25555
25630
|
reviewOrchestrator = new ReviewOrchestrator;
|
|
25556
25631
|
});
|
|
@@ -25579,12 +25654,14 @@ var init_review = __esm(() => {
|
|
|
25579
25654
|
const agentResolver = ctx.agentGetFn ?? getAgent;
|
|
25580
25655
|
const agentName = effectiveConfig.autoMode?.defaultAgent;
|
|
25581
25656
|
const modelResolver = (_tier) => agentName ? agentResolver(agentName) ?? null : null;
|
|
25657
|
+
const retrySkipChecks = ctx.retrySkipChecks;
|
|
25658
|
+
ctx.retrySkipChecks = undefined;
|
|
25582
25659
|
const result = await reviewOrchestrator.review(effectiveConfig.review, effectiveWorkdir, effectiveConfig.execution, ctx.plugins, ctx.storyGitRef, ctx.story.workdir, effectiveConfig.quality?.commands, ctx.story.id, {
|
|
25583
25660
|
id: ctx.story.id,
|
|
25584
25661
|
title: ctx.story.title,
|
|
25585
25662
|
description: ctx.story.description,
|
|
25586
25663
|
acceptanceCriteria: ctx.story.acceptanceCriteria
|
|
25587
|
-
}, modelResolver, ctx.config);
|
|
25664
|
+
}, modelResolver, ctx.config, retrySkipChecks);
|
|
25588
25665
|
ctx.reviewResult = result.builtIn;
|
|
25589
25666
|
if (!result.success) {
|
|
25590
25667
|
const pluginFindings = result.builtIn.pluginReviewers?.flatMap((pr) => pr.findings ?? []) ?? [];
|
|
@@ -25626,17 +25703,6 @@ var init_review = __esm(() => {
|
|
|
25626
25703
|
|
|
25627
25704
|
// src/pipeline/stages/autofix.ts
|
|
25628
25705
|
import { join as join17 } from "path";
|
|
25629
|
-
async function runCommand(cmd, cwd) {
|
|
25630
|
-
const parts = cmd.split(/\s+/);
|
|
25631
|
-
const proc = Bun.spawn(parts, { cwd, stdout: "pipe", stderr: "pipe" });
|
|
25632
|
-
const [exitCode, stdout, stderr] = await Promise.all([
|
|
25633
|
-
proc.exited,
|
|
25634
|
-
new Response(proc.stdout).text(),
|
|
25635
|
-
new Response(proc.stderr).text()
|
|
25636
|
-
]);
|
|
25637
|
-
return { exitCode, output: `${stdout}
|
|
25638
|
-
${stderr}` };
|
|
25639
|
-
}
|
|
25640
25706
|
async function recheckReview(ctx) {
|
|
25641
25707
|
const { reviewStage: reviewStage2 } = await Promise.resolve().then(() => (init_review(), exports_review));
|
|
25642
25708
|
if (!reviewStage2.enabled(ctx))
|
|
@@ -25672,20 +25738,35 @@ Commit your fixes when done.${scopeConstraint}`;
|
|
|
25672
25738
|
async function runAgentRectification(ctx) {
|
|
25673
25739
|
const logger = getLogger();
|
|
25674
25740
|
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
25675
|
-
const
|
|
25741
|
+
const maxPerCycle = effectiveConfig.quality.autofix?.maxAttempts ?? 2;
|
|
25742
|
+
const maxTotal = effectiveConfig.quality.autofix?.maxTotalAttempts ?? 10;
|
|
25743
|
+
const consumed = ctx.autofixAttempt ?? 0;
|
|
25676
25744
|
const failedChecks = collectFailedChecks(ctx);
|
|
25677
25745
|
if (failedChecks.length === 0) {
|
|
25678
25746
|
logger.debug("autofix", "No failed checks found \u2014 skipping agent rectification", { storyId: ctx.story.id });
|
|
25679
25747
|
return false;
|
|
25680
25748
|
}
|
|
25749
|
+
if (consumed >= maxTotal) {
|
|
25750
|
+
logger.warn("autofix", "Global autofix budget exhausted \u2014 escalating", {
|
|
25751
|
+
storyId: ctx.story.id,
|
|
25752
|
+
totalAttempts: consumed,
|
|
25753
|
+
maxTotalAttempts: maxTotal
|
|
25754
|
+
});
|
|
25755
|
+
return false;
|
|
25756
|
+
}
|
|
25757
|
+
const remainingBudget = maxTotal - consumed;
|
|
25758
|
+
const maxAttempts = Math.min(maxPerCycle, remainingBudget);
|
|
25681
25759
|
logger.info("autofix", "Starting agent rectification for review failures", {
|
|
25682
25760
|
storyId: ctx.story.id,
|
|
25683
25761
|
failedChecks: failedChecks.map((c) => c.check),
|
|
25684
|
-
maxAttempts
|
|
25762
|
+
maxAttempts,
|
|
25763
|
+
totalUsed: consumed,
|
|
25764
|
+
maxTotalAttempts: maxTotal
|
|
25685
25765
|
});
|
|
25686
25766
|
const agentGetFn = ctx.agentGetFn ?? _autofixDeps.getAgent;
|
|
25687
25767
|
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
25688
|
-
|
|
25768
|
+
ctx.autofixAttempt = consumed + attempt;
|
|
25769
|
+
logger.info("autofix", `Agent rectification attempt ${ctx.autofixAttempt}/${maxTotal}`, { storyId: ctx.story.id });
|
|
25689
25770
|
const agent = agentGetFn(ctx.config.autoMode.defaultAgent);
|
|
25690
25771
|
if (!agent) {
|
|
25691
25772
|
logger.error("autofix", "Agent not found \u2014 cannot run agent rectification", { storyId: ctx.story.id });
|
|
@@ -25732,6 +25813,7 @@ var init_autofix = __esm(() => {
|
|
|
25732
25813
|
init_config();
|
|
25733
25814
|
init_loader();
|
|
25734
25815
|
init_logger2();
|
|
25816
|
+
init_quality();
|
|
25735
25817
|
init_event_bus();
|
|
25736
25818
|
autofixStage = {
|
|
25737
25819
|
name: "autofix",
|
|
@@ -25768,7 +25850,12 @@ var init_autofix = __esm(() => {
|
|
|
25768
25850
|
if (hasLintFailure && (lintFixCmd || formatFixCmd)) {
|
|
25769
25851
|
if (lintFixCmd) {
|
|
25770
25852
|
pipelineEventBus.emit({ type: "autofix:started", storyId: ctx.story.id, command: lintFixCmd });
|
|
25771
|
-
const lintResult = await _autofixDeps.
|
|
25853
|
+
const lintResult = await _autofixDeps.runQualityCommand({
|
|
25854
|
+
commandName: "lintFix",
|
|
25855
|
+
command: lintFixCmd,
|
|
25856
|
+
workdir: effectiveWorkdir,
|
|
25857
|
+
storyId: ctx.story.id
|
|
25858
|
+
});
|
|
25772
25859
|
logger.debug("autofix", `lintFix exit=${lintResult.exitCode}`, { storyId: ctx.story.id, command: lintFixCmd });
|
|
25773
25860
|
if (lintResult.exitCode !== 0) {
|
|
25774
25861
|
logger.warn("autofix", "lintFix command failed \u2014 may not have fixed all issues", {
|
|
@@ -25779,7 +25866,12 @@ var init_autofix = __esm(() => {
|
|
|
25779
25866
|
}
|
|
25780
25867
|
if (formatFixCmd) {
|
|
25781
25868
|
pipelineEventBus.emit({ type: "autofix:started", storyId: ctx.story.id, command: formatFixCmd });
|
|
25782
|
-
const fmtResult = await _autofixDeps.
|
|
25869
|
+
const fmtResult = await _autofixDeps.runQualityCommand({
|
|
25870
|
+
commandName: "formatFix",
|
|
25871
|
+
command: formatFixCmd,
|
|
25872
|
+
workdir: effectiveWorkdir,
|
|
25873
|
+
storyId: ctx.story.id
|
|
25874
|
+
});
|
|
25783
25875
|
logger.debug("autofix", `formatFix exit=${fmtResult.exitCode}`, {
|
|
25784
25876
|
storyId: ctx.story.id,
|
|
25785
25877
|
command: formatFixCmd
|
|
@@ -25805,6 +25897,14 @@ var init_autofix = __esm(() => {
|
|
|
25805
25897
|
if (agentFixed) {
|
|
25806
25898
|
if (ctx.reviewResult)
|
|
25807
25899
|
ctx.reviewResult = { ...ctx.reviewResult, success: true };
|
|
25900
|
+
const passedChecks = (ctx.reviewResult?.checks ?? []).filter((c) => c.success).map((c) => c.check);
|
|
25901
|
+
if (passedChecks.length > 0) {
|
|
25902
|
+
ctx.retrySkipChecks = new Set(passedChecks);
|
|
25903
|
+
logger.debug("autofix", "Skipping already-passed checks on retry", {
|
|
25904
|
+
storyId: ctx.story.id,
|
|
25905
|
+
skippedChecks: passedChecks
|
|
25906
|
+
});
|
|
25907
|
+
}
|
|
25808
25908
|
logger.info("autofix", "Agent rectification succeeded \u2014 retrying review", { storyId: ctx.story.id });
|
|
25809
25909
|
return { action: "retry", fromStage: "review" };
|
|
25810
25910
|
}
|
|
@@ -25814,7 +25914,7 @@ var init_autofix = __esm(() => {
|
|
|
25814
25914
|
};
|
|
25815
25915
|
_autofixDeps = {
|
|
25816
25916
|
getAgent,
|
|
25817
|
-
|
|
25917
|
+
runQualityCommand,
|
|
25818
25918
|
recheckReview,
|
|
25819
25919
|
runAgentRectification,
|
|
25820
25920
|
loadConfigForWorkdir
|
|
@@ -25830,8 +25930,8 @@ async function appendProgress(featureDir, storyId, status, message) {
|
|
|
25830
25930
|
const timestamp = new Date().toISOString();
|
|
25831
25931
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
25832
25932
|
`;
|
|
25833
|
-
const
|
|
25834
|
-
const existing = await
|
|
25933
|
+
const file3 = Bun.file(progressPath);
|
|
25934
|
+
const existing = await file3.exists() ? await file3.text() : "";
|
|
25835
25935
|
await Bun.write(progressPath, existing + entry);
|
|
25836
25936
|
}
|
|
25837
25937
|
var init_progress = () => {};
|
|
@@ -25890,6 +25990,7 @@ var init_completion = __esm(() => {
|
|
|
25890
25990
|
await savePRD(ctx.prd, prdPath);
|
|
25891
25991
|
const updatedCounts = countStories(ctx.prd);
|
|
25892
25992
|
logger.info("completion", "Progress update", {
|
|
25993
|
+
storyId: ctx.story.id,
|
|
25893
25994
|
completed: updatedCounts.passed + updatedCounts.failed,
|
|
25894
25995
|
total: updatedCounts.total,
|
|
25895
25996
|
passed: updatedCounts.passed,
|
|
@@ -26347,7 +26448,7 @@ function deriveTestPatterns(contextFiles) {
|
|
|
26347
26448
|
async function detectTestDir(workdir) {
|
|
26348
26449
|
for (const dir of COMMON_TEST_DIRS) {
|
|
26349
26450
|
const fullPath = path6.join(workdir, dir);
|
|
26350
|
-
const
|
|
26451
|
+
const file3 = Bun.file(path6.join(fullPath, "."));
|
|
26351
26452
|
try {
|
|
26352
26453
|
const dirStat = await Bun.file(fullPath).exists();
|
|
26353
26454
|
const proc = Bun.spawn(["test", "-d", fullPath], { stdout: "pipe", stderr: "pipe" });
|
|
@@ -26410,21 +26511,21 @@ function formatTestSummary(files, detail) {
|
|
|
26410
26511
|
lines.push("The following tests already exist. DO NOT duplicate this coverage.");
|
|
26411
26512
|
lines.push("Focus only on testing NEW behavior introduced by this story.");
|
|
26412
26513
|
lines.push("");
|
|
26413
|
-
for (const
|
|
26514
|
+
for (const file3 of files) {
|
|
26414
26515
|
switch (detail) {
|
|
26415
26516
|
case "names-only":
|
|
26416
|
-
lines.push(`- **${
|
|
26517
|
+
lines.push(`- **${file3.relativePath}** (${file3.testCount} tests)`);
|
|
26417
26518
|
break;
|
|
26418
26519
|
case "names-and-counts":
|
|
26419
|
-
lines.push(`### ${
|
|
26420
|
-
for (const desc of
|
|
26520
|
+
lines.push(`### ${file3.relativePath} (${file3.testCount} tests)`);
|
|
26521
|
+
for (const desc of file3.describes) {
|
|
26421
26522
|
lines.push(`- ${desc.name} (${desc.tests.length} tests)`);
|
|
26422
26523
|
}
|
|
26423
26524
|
lines.push("");
|
|
26424
26525
|
break;
|
|
26425
26526
|
case "describe-blocks":
|
|
26426
|
-
lines.push(`### ${
|
|
26427
|
-
for (const desc of
|
|
26527
|
+
lines.push(`### ${file3.relativePath} (${file3.testCount} tests)`);
|
|
26528
|
+
for (const desc of file3.describes) {
|
|
26428
26529
|
lines.push(`- **${desc.name}** (${desc.tests.length} tests)`);
|
|
26429
26530
|
for (const test of desc.tests) {
|
|
26430
26531
|
lines.push(` - ${test}`);
|
|
@@ -26553,8 +26654,8 @@ function renderErrorSection(sections, byType) {
|
|
|
26553
26654
|
const fileList = match[1].split(",").map((f) => f.trim());
|
|
26554
26655
|
sections.push(`**Required files:**
|
|
26555
26656
|
`);
|
|
26556
|
-
for (const
|
|
26557
|
-
sections.push(`- \`${
|
|
26657
|
+
for (const file3 of fileList) {
|
|
26658
|
+
sections.push(`- \`${file3}\``);
|
|
26558
26659
|
}
|
|
26559
26660
|
sections.push(`
|
|
26560
26661
|
`);
|
|
@@ -26748,24 +26849,24 @@ async function addFileElements(elements, storyContext, story) {
|
|
|
26748
26849
|
for (const relativeFilePath of filesToLoad) {
|
|
26749
26850
|
try {
|
|
26750
26851
|
const absolutePath = path7.resolve(workdir, relativeFilePath);
|
|
26751
|
-
const
|
|
26752
|
-
if (!await
|
|
26852
|
+
const file3 = Bun.file(absolutePath);
|
|
26853
|
+
if (!await file3.exists()) {
|
|
26753
26854
|
const logger = getLogger();
|
|
26754
26855
|
logger.warn("context", "Relevant file not found", { filePath: relativeFilePath, storyId: story.id });
|
|
26755
26856
|
continue;
|
|
26756
26857
|
}
|
|
26757
|
-
if (
|
|
26858
|
+
if (file3.size > MAX_FILE_SIZE_BYTES) {
|
|
26758
26859
|
const logger = getLogger();
|
|
26759
26860
|
logger.warn("context", "File too large for inline \u2014 using path-only", {
|
|
26760
26861
|
filePath: relativeFilePath,
|
|
26761
|
-
sizeKB: Math.round(
|
|
26862
|
+
sizeKB: Math.round(file3.size / 1024),
|
|
26762
26863
|
maxKB: 10,
|
|
26763
26864
|
storyId: story.id
|
|
26764
26865
|
});
|
|
26765
|
-
elements.push(createFileContext(relativeFilePath, `_File too large to inline (${Math.round(
|
|
26866
|
+
elements.push(createFileContext(relativeFilePath, `_File too large to inline (${Math.round(file3.size / 1024)}KB). Path: \`${relativeFilePath}\` \u2014 read it directly if needed._`, 5));
|
|
26766
26867
|
continue;
|
|
26767
26868
|
}
|
|
26768
|
-
const content = await
|
|
26869
|
+
const content = await file3.text();
|
|
26769
26870
|
const ext = path7.extname(relativeFilePath).slice(1) || "txt";
|
|
26770
26871
|
elements.push(createFileContext(relativeFilePath, `\`\`\`${ext}
|
|
26771
26872
|
// File: ${relativeFilePath}
|
|
@@ -26818,10 +26919,10 @@ function hookCtx(feature, opts) {
|
|
|
26818
26919
|
}
|
|
26819
26920
|
async function loadPackageContextMd(packageWorkdir) {
|
|
26820
26921
|
const contextPath = `${packageWorkdir}/.nax/context.md`;
|
|
26821
|
-
const
|
|
26822
|
-
if (!await
|
|
26922
|
+
const file3 = Bun.file(contextPath);
|
|
26923
|
+
if (!await file3.exists())
|
|
26823
26924
|
return null;
|
|
26824
|
-
return
|
|
26925
|
+
return file3.text();
|
|
26825
26926
|
}
|
|
26826
26927
|
async function buildStoryContextFull(prd, story, config2, packageWorkdir) {
|
|
26827
26928
|
try {
|
|
@@ -27073,11 +27174,11 @@ async function verifyTestWriterIsolation(workdir, beforeRef, allowedPaths = ["sr
|
|
|
27073
27174
|
const sourceFiles = changed.filter((f) => isSourceFile(f) && !isTestFile(f));
|
|
27074
27175
|
const softViolations = [];
|
|
27075
27176
|
const violations = [];
|
|
27076
|
-
for (const
|
|
27077
|
-
if (matchesAllowedPath(
|
|
27078
|
-
softViolations.push(
|
|
27177
|
+
for (const file3 of sourceFiles) {
|
|
27178
|
+
if (matchesAllowedPath(file3, allowedPaths)) {
|
|
27179
|
+
softViolations.push(file3);
|
|
27079
27180
|
} else {
|
|
27080
|
-
violations.push(
|
|
27181
|
+
violations.push(file3);
|
|
27081
27182
|
}
|
|
27082
27183
|
}
|
|
27083
27184
|
return {
|
|
@@ -27493,9 +27594,9 @@ async function verifyAssets(workingDirectory, expectedFiles) {
|
|
|
27493
27594
|
if (!expectedFiles || expectedFiles.length === 0)
|
|
27494
27595
|
return { success: true, missingFiles: [] };
|
|
27495
27596
|
const missingFiles = [];
|
|
27496
|
-
for (const
|
|
27497
|
-
if (!existsSync16(join22(workingDirectory,
|
|
27498
|
-
missingFiles.push(
|
|
27597
|
+
for (const file3 of expectedFiles) {
|
|
27598
|
+
if (!existsSync16(join22(workingDirectory, file3)))
|
|
27599
|
+
missingFiles.push(file3);
|
|
27499
27600
|
}
|
|
27500
27601
|
if (missingFiles.length > 0) {
|
|
27501
27602
|
return {
|
|
@@ -27601,7 +27702,7 @@ function createRectificationPrompt(failures, story, config2) {
|
|
|
27601
27702
|
const maxChars = config2?.maxFailureSummaryChars ?? 2000;
|
|
27602
27703
|
const failureSummary = formatFailureSummary(failures, maxChars);
|
|
27603
27704
|
const failingFiles = Array.from(new Set(failures.map((f) => f.file)));
|
|
27604
|
-
const testCommands = failingFiles.map((
|
|
27705
|
+
const testCommands = failingFiles.map((file3) => ` bun test ${file3}`).join(`
|
|
27605
27706
|
`);
|
|
27606
27707
|
return `# Rectification Required
|
|
27607
27708
|
|
|
@@ -27648,7 +27749,7 @@ function createEscalatedRectificationPrompt(failures, story, priorAttempts, orig
|
|
|
27648
27749
|
const maxChars = config2?.maxFailureSummaryChars ?? 2000;
|
|
27649
27750
|
const failureSummary = formatFailureSummary(failures, maxChars);
|
|
27650
27751
|
const failingFiles = Array.from(new Set(failures.map((f) => f.file)));
|
|
27651
|
-
const testCommands = failingFiles.map((
|
|
27752
|
+
const testCommands = failingFiles.map((file3) => ` bun test ${file3}`).join(`
|
|
27652
27753
|
`);
|
|
27653
27754
|
const failingTestNames = failures.map((f) => f.testName);
|
|
27654
27755
|
let failingTestsSection = "";
|
|
@@ -28359,12 +28460,12 @@ async function loadOverride(role, workdir, config2) {
|
|
|
28359
28460
|
return null;
|
|
28360
28461
|
}
|
|
28361
28462
|
const absolutePath = join23(workdir, overridePath);
|
|
28362
|
-
const
|
|
28363
|
-
if (!await
|
|
28463
|
+
const file3 = Bun.file(absolutePath);
|
|
28464
|
+
if (!await file3.exists()) {
|
|
28364
28465
|
return null;
|
|
28365
28466
|
}
|
|
28366
28467
|
try {
|
|
28367
|
-
return await
|
|
28468
|
+
return await file3.text();
|
|
28368
28469
|
} catch (err) {
|
|
28369
28470
|
throw new Error(`Cannot read prompt override for role "${role}" at "${absolutePath}": ${err instanceof Error ? err.message : String(err)}`);
|
|
28370
28471
|
}
|
|
@@ -28486,9 +28587,9 @@ ${this._contextMd}
|
|
|
28486
28587
|
}
|
|
28487
28588
|
if (this._overridePath) {
|
|
28488
28589
|
try {
|
|
28489
|
-
const
|
|
28490
|
-
if (await
|
|
28491
|
-
return await
|
|
28590
|
+
const file3 = Bun.file(this._overridePath);
|
|
28591
|
+
if (await file3.exists()) {
|
|
28592
|
+
return await file3.text();
|
|
28492
28593
|
}
|
|
28493
28594
|
} catch {}
|
|
28494
28595
|
}
|
|
@@ -28824,14 +28925,14 @@ async function readVerdict(workdir) {
|
|
|
28824
28925
|
const logger = getLogger();
|
|
28825
28926
|
const verdictPath = path9.join(workdir, VERDICT_FILE);
|
|
28826
28927
|
try {
|
|
28827
|
-
const
|
|
28828
|
-
const exists = await
|
|
28928
|
+
const file3 = Bun.file(verdictPath);
|
|
28929
|
+
const exists = await file3.exists();
|
|
28829
28930
|
if (!exists) {
|
|
28830
28931
|
return null;
|
|
28831
28932
|
}
|
|
28832
28933
|
let rawText;
|
|
28833
28934
|
try {
|
|
28834
|
-
rawText = await
|
|
28935
|
+
rawText = await file3.text();
|
|
28835
28936
|
} catch (readErr) {
|
|
28836
28937
|
logger.warn("tdd", "Failed to read verifier verdict file", {
|
|
28837
28938
|
path: verdictPath,
|
|
@@ -29756,8 +29857,8 @@ async function readQueueFile(workdir) {
|
|
|
29756
29857
|
const processingPath = path10.join(workdir, ".queue.txt.processing");
|
|
29757
29858
|
const logger = getSafeLogger4();
|
|
29758
29859
|
try {
|
|
29759
|
-
const
|
|
29760
|
-
const exists = await
|
|
29860
|
+
const file3 = Bun.file(queuePath);
|
|
29861
|
+
const exists = await file3.exists();
|
|
29761
29862
|
if (!exists) {
|
|
29762
29863
|
return [];
|
|
29763
29864
|
}
|
|
@@ -29781,8 +29882,8 @@ async function clearQueueFile(workdir) {
|
|
|
29781
29882
|
const processingPath = path10.join(workdir, ".queue.txt.processing");
|
|
29782
29883
|
const logger = getSafeLogger4();
|
|
29783
29884
|
try {
|
|
29784
|
-
const
|
|
29785
|
-
const exists = await
|
|
29885
|
+
const file3 = Bun.file(processingPath);
|
|
29886
|
+
const exists = await file3.exists();
|
|
29786
29887
|
if (exists) {
|
|
29787
29888
|
await Bun.spawn(["rm", processingPath], { stdout: "pipe" }).exited;
|
|
29788
29889
|
}
|
|
@@ -30378,8 +30479,8 @@ async function importGrepFallback(sourceFiles, workdir, testFilePatterns) {
|
|
|
30378
30479
|
const testFilePaths = [];
|
|
30379
30480
|
for (const pattern of testFilePatterns) {
|
|
30380
30481
|
const glob = _bunDeps.glob(pattern);
|
|
30381
|
-
for await (const
|
|
30382
|
-
testFilePaths.push(`${workdir}/${
|
|
30482
|
+
for await (const file3 of glob.scan(workdir)) {
|
|
30483
|
+
testFilePaths.push(`${workdir}/${file3}`);
|
|
30383
30484
|
}
|
|
30384
30485
|
}
|
|
30385
30486
|
const matched = [];
|
|
@@ -31289,8 +31390,8 @@ function generateContextTemplate(scan) {
|
|
|
31289
31390
|
lines.push(`## Project Structure
|
|
31290
31391
|
`);
|
|
31291
31392
|
lines.push("```");
|
|
31292
|
-
for (const
|
|
31293
|
-
lines.push(
|
|
31393
|
+
for (const file3 of scan.fileTree.slice(0, 20)) {
|
|
31394
|
+
lines.push(file3);
|
|
31294
31395
|
}
|
|
31295
31396
|
if (scan.fileTree.length > 20) {
|
|
31296
31397
|
lines.push(`... and ${scan.fileTree.length - 20} more files`);
|
|
@@ -32109,8 +32210,8 @@ async function checkStaleLock(workdir) {
|
|
|
32109
32210
|
};
|
|
32110
32211
|
}
|
|
32111
32212
|
try {
|
|
32112
|
-
const
|
|
32113
|
-
const content = await
|
|
32213
|
+
const file3 = Bun.file(lockPath);
|
|
32214
|
+
const content = await file3.text();
|
|
32114
32215
|
const lockData = JSON.parse(content);
|
|
32115
32216
|
let lockTimeMs;
|
|
32116
32217
|
if (lockData.timestamp) {
|
|
@@ -32396,8 +32497,8 @@ async function checkGitignoreCoversNax(workdir) {
|
|
|
32396
32497
|
message: ".gitignore not found"
|
|
32397
32498
|
};
|
|
32398
32499
|
}
|
|
32399
|
-
const
|
|
32400
|
-
const content = await
|
|
32500
|
+
const file3 = Bun.file(gitignorePath);
|
|
32501
|
+
const content = await file3.text();
|
|
32401
32502
|
const patterns = [
|
|
32402
32503
|
"nax.lock",
|
|
32403
32504
|
".nax/**/runs/",
|
|
@@ -33081,14 +33182,14 @@ async function fireHook(config2, event, ctx, workdir) {
|
|
|
33081
33182
|
}
|
|
33082
33183
|
}
|
|
33083
33184
|
var DEFAULT_TIMEOUT = 5000;
|
|
33084
|
-
var
|
|
33185
|
+
var init_runner4 = __esm(() => {
|
|
33085
33186
|
init_logger2();
|
|
33086
33187
|
init_json_file();
|
|
33087
33188
|
});
|
|
33088
33189
|
|
|
33089
33190
|
// src/hooks/index.ts
|
|
33090
33191
|
var init_hooks = __esm(() => {
|
|
33091
|
-
|
|
33192
|
+
init_runner4();
|
|
33092
33193
|
});
|
|
33093
33194
|
|
|
33094
33195
|
// src/execution/crash-heartbeat.ts
|
|
@@ -33982,7 +34083,7 @@ async function handleRunCompletion(options) {
|
|
|
33982
34083
|
}
|
|
33983
34084
|
var _runCompletionDeps;
|
|
33984
34085
|
var init_run_completion = __esm(() => {
|
|
33985
|
-
|
|
34086
|
+
init_runner4();
|
|
33986
34087
|
init_logger2();
|
|
33987
34088
|
init_metrics();
|
|
33988
34089
|
init_event_bus();
|
|
@@ -35101,13 +35202,23 @@ __export(exports_parallel_worker, {
|
|
|
35101
35202
|
async function executeStoryInWorktree(story, worktreePath, context, routing, eventEmitter) {
|
|
35102
35203
|
const logger = getSafeLogger();
|
|
35103
35204
|
try {
|
|
35205
|
+
let storyGitRef;
|
|
35206
|
+
if (story.storyGitRef && await isGitRefValid(worktreePath, story.storyGitRef)) {
|
|
35207
|
+
storyGitRef = story.storyGitRef;
|
|
35208
|
+
} else {
|
|
35209
|
+
storyGitRef = await captureGitRef(worktreePath);
|
|
35210
|
+
if (storyGitRef) {
|
|
35211
|
+
story.storyGitRef = storyGitRef;
|
|
35212
|
+
}
|
|
35213
|
+
}
|
|
35104
35214
|
const pipelineContext = {
|
|
35105
35215
|
...context,
|
|
35106
35216
|
effectiveConfig: context.effectiveConfig ?? context.config,
|
|
35107
35217
|
story,
|
|
35108
35218
|
stories: [story],
|
|
35109
35219
|
workdir: worktreePath,
|
|
35110
|
-
routing
|
|
35220
|
+
routing,
|
|
35221
|
+
storyGitRef: storyGitRef ?? undefined
|
|
35111
35222
|
};
|
|
35112
35223
|
logger?.debug("parallel", "Executing story in worktree", {
|
|
35113
35224
|
storyId: story.id,
|
|
@@ -35183,6 +35294,7 @@ var init_parallel_worker = __esm(() => {
|
|
|
35183
35294
|
init_runner();
|
|
35184
35295
|
init_stages();
|
|
35185
35296
|
init_routing();
|
|
35297
|
+
init_git();
|
|
35186
35298
|
});
|
|
35187
35299
|
|
|
35188
35300
|
// src/worktree/manager.ts
|
|
@@ -35613,6 +35725,16 @@ __export(exports_merge_conflict_rectify, {
|
|
|
35613
35725
|
rectifyConflictedStory: () => rectifyConflictedStory
|
|
35614
35726
|
});
|
|
35615
35727
|
import path15 from "path";
|
|
35728
|
+
async function closeStaleAcpSession(worktreePath, sessionName) {
|
|
35729
|
+
const logger = getSafeLogger();
|
|
35730
|
+
try {
|
|
35731
|
+
const { typedSpawn: typedSpawn2 } = await Promise.resolve().then(() => (init_bun_deps(), exports_bun_deps));
|
|
35732
|
+
const cmd = ["acpx", "--cwd", worktreePath, "claude", "sessions", "close", sessionName];
|
|
35733
|
+
logger?.debug("parallel", "Closing stale ACP session before rectification", { sessionName });
|
|
35734
|
+
const proc = typedSpawn2(cmd, { stdout: "pipe", stderr: "pipe" });
|
|
35735
|
+
await proc.exited;
|
|
35736
|
+
} catch {}
|
|
35737
|
+
}
|
|
35616
35738
|
async function rectifyConflictedStory(options) {
|
|
35617
35739
|
const { storyId, workdir, config: config2, hooks, pluginRegistry, prd, eventEmitter, agentGetFn } = options;
|
|
35618
35740
|
const logger = getSafeLogger();
|
|
@@ -35630,6 +35752,9 @@ async function rectifyConflictedStory(options) {
|
|
|
35630
35752
|
} catch {}
|
|
35631
35753
|
await worktreeManager.create(workdir, storyId);
|
|
35632
35754
|
const worktreePath = path15.join(workdir, ".nax-wt", storyId);
|
|
35755
|
+
const { buildSessionName: buildSessionName2 } = await Promise.resolve().then(() => (init_adapter2(), exports_adapter));
|
|
35756
|
+
const staleSessionName = buildSessionName2(worktreePath, prd.feature, storyId);
|
|
35757
|
+
await closeStaleAcpSession(worktreePath, staleSessionName);
|
|
35633
35758
|
const story = prd.userStories.find((s) => s.id === storyId);
|
|
35634
35759
|
if (!story) {
|
|
35635
35760
|
return { success: false, storyId, cost: 0, finalConflict: false, pipelineFailure: true };
|
|
@@ -35707,9 +35832,47 @@ async function runParallelBatch(options) {
|
|
|
35707
35832
|
}
|
|
35708
35833
|
worktreePaths.set(story.id, path16.join(workdir, ".nax-wt", story.id));
|
|
35709
35834
|
}
|
|
35710
|
-
const
|
|
35835
|
+
const rootConfigPath = path16.join(workdir, ".nax", "config.json");
|
|
35836
|
+
const storyEffectiveConfigs = new Map;
|
|
35837
|
+
for (const story of stories) {
|
|
35838
|
+
if (story.workdir) {
|
|
35839
|
+
const effectiveConfig = await loadConfigForWorkdir(rootConfigPath, story.workdir);
|
|
35840
|
+
storyEffectiveConfigs.set(story.id, effectiveConfig);
|
|
35841
|
+
}
|
|
35842
|
+
}
|
|
35843
|
+
const workerResult = await _parallelBatchDeps.executeParallelBatch(stories, workdir, config2, pipelineContext, worktreePaths, maxConcurrency, eventEmitter, storyEffectiveConfigs.size > 0 ? storyEffectiveConfigs : undefined);
|
|
35711
35844
|
const batchEndMs = Date.now();
|
|
35712
|
-
const completed =
|
|
35845
|
+
const completed = [];
|
|
35846
|
+
if (workerResult.pipelinePassed.length > 0) {
|
|
35847
|
+
const mergeEngine = await _parallelBatchDeps.createMergeEngine(worktreeManager);
|
|
35848
|
+
const successfulIds = workerResult.pipelinePassed.map((s) => s.id);
|
|
35849
|
+
const deps = {};
|
|
35850
|
+
for (const s of stories)
|
|
35851
|
+
deps[s.id] = s.dependencies ?? [];
|
|
35852
|
+
const mergeResults = await mergeEngine.mergeAll(workdir, successfulIds, deps);
|
|
35853
|
+
for (const mergeResult of mergeResults) {
|
|
35854
|
+
const story = workerResult.pipelinePassed.find((s) => s.id === mergeResult.storyId);
|
|
35855
|
+
if (!story)
|
|
35856
|
+
continue;
|
|
35857
|
+
if (mergeResult.success) {
|
|
35858
|
+
completed.push(story);
|
|
35859
|
+
workerResult.merged.push(story);
|
|
35860
|
+
logger?.info("parallel-batch", "Story merged successfully", {
|
|
35861
|
+
storyId: mergeResult.storyId
|
|
35862
|
+
});
|
|
35863
|
+
} else {
|
|
35864
|
+
workerResult.mergeConflicts.push({
|
|
35865
|
+
storyId: mergeResult.storyId,
|
|
35866
|
+
conflictFiles: mergeResult.conflictFiles || [],
|
|
35867
|
+
originalCost: workerResult.storyCosts.get(mergeResult.storyId) ?? 0
|
|
35868
|
+
});
|
|
35869
|
+
logger?.warn("parallel-batch", "Merge conflict \u2014 will attempt rectification", {
|
|
35870
|
+
storyId: mergeResult.storyId,
|
|
35871
|
+
conflictFiles: mergeResult.conflictFiles
|
|
35872
|
+
});
|
|
35873
|
+
}
|
|
35874
|
+
}
|
|
35875
|
+
}
|
|
35713
35876
|
const failed = workerResult.failed.map((f) => ({
|
|
35714
35877
|
story: f.story,
|
|
35715
35878
|
pipelineResult: f.pipelineResult ?? {
|
|
@@ -35767,11 +35930,12 @@ async function runParallelBatch(options) {
|
|
|
35767
35930
|
}
|
|
35768
35931
|
var _parallelBatchDeps;
|
|
35769
35932
|
var init_parallel_batch = __esm(() => {
|
|
35933
|
+
init_loader();
|
|
35770
35934
|
init_logger2();
|
|
35771
35935
|
_parallelBatchDeps = {
|
|
35772
|
-
executeParallelBatch: async (_stories, _projectRoot, _config, _context, _worktreePaths, _maxConcurrency, _eventEmitter) => {
|
|
35936
|
+
executeParallelBatch: async (_stories, _projectRoot, _config, _context, _worktreePaths, _maxConcurrency, _eventEmitter, _storyEffectiveConfigs) => {
|
|
35773
35937
|
const { executeParallelBatch: executeParallelBatch2 } = await Promise.resolve().then(() => (init_parallel_worker(), exports_parallel_worker));
|
|
35774
|
-
return executeParallelBatch2(_stories, _projectRoot, _config, _context, _worktreePaths, _maxConcurrency, _eventEmitter);
|
|
35938
|
+
return executeParallelBatch2(_stories, _projectRoot, _config, _context, _worktreePaths, _maxConcurrency, _eventEmitter, _storyEffectiveConfigs);
|
|
35775
35939
|
},
|
|
35776
35940
|
createWorktreeManager: async () => {
|
|
35777
35941
|
const { WorktreeManager: WorktreeManager2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
|
|
@@ -36260,15 +36424,15 @@ var _detectorDeps, WEB_DEPS, API_DEPS;
|
|
|
36260
36424
|
var init_detector = __esm(() => {
|
|
36261
36425
|
_detectorDeps = {
|
|
36262
36426
|
async fileExists(path17) {
|
|
36263
|
-
const
|
|
36264
|
-
return
|
|
36427
|
+
const file3 = Bun.file(path17);
|
|
36428
|
+
return file3.exists();
|
|
36265
36429
|
},
|
|
36266
36430
|
async readJson(path17) {
|
|
36267
36431
|
try {
|
|
36268
|
-
const
|
|
36269
|
-
if (!await
|
|
36432
|
+
const file3 = Bun.file(path17);
|
|
36433
|
+
if (!await file3.exists())
|
|
36270
36434
|
return null;
|
|
36271
|
-
const text = await
|
|
36435
|
+
const text = await file3.text();
|
|
36272
36436
|
return JSON.parse(text);
|
|
36273
36437
|
} catch {
|
|
36274
36438
|
return null;
|
|
@@ -36723,7 +36887,7 @@ var init_run_initialization = __esm(() => {
|
|
|
36723
36887
|
init_errors3();
|
|
36724
36888
|
init_logger2();
|
|
36725
36889
|
init_prd();
|
|
36726
|
-
|
|
36890
|
+
init_runner3();
|
|
36727
36891
|
init_git();
|
|
36728
36892
|
_reconcileDeps = {
|
|
36729
36893
|
getAgent,
|
|
@@ -65896,7 +66060,7 @@ var require_stack_utils = __commonJS((exports, module) => {
|
|
|
65896
66060
|
const evalFile = match[4];
|
|
65897
66061
|
const evalLine = Number(match[5]);
|
|
65898
66062
|
const evalCol = Number(match[6]);
|
|
65899
|
-
let
|
|
66063
|
+
let file3 = match[7];
|
|
65900
66064
|
const lnum = match[8];
|
|
65901
66065
|
const col = match[9];
|
|
65902
66066
|
const native = match[10] === "native";
|
|
@@ -65909,17 +66073,17 @@ var require_stack_utils = __commonJS((exports, module) => {
|
|
|
65909
66073
|
if (col) {
|
|
65910
66074
|
res.column = Number(col);
|
|
65911
66075
|
}
|
|
65912
|
-
if (closeParen &&
|
|
66076
|
+
if (closeParen && file3) {
|
|
65913
66077
|
let closes = 0;
|
|
65914
|
-
for (let i =
|
|
65915
|
-
if (
|
|
66078
|
+
for (let i = file3.length - 1;i > 0; i--) {
|
|
66079
|
+
if (file3.charAt(i) === ")") {
|
|
65916
66080
|
closes++;
|
|
65917
|
-
} else if (
|
|
66081
|
+
} else if (file3.charAt(i) === "(" && file3.charAt(i - 1) === " ") {
|
|
65918
66082
|
closes--;
|
|
65919
|
-
if (closes === -1 &&
|
|
65920
|
-
const before2 =
|
|
65921
|
-
const after2 =
|
|
65922
|
-
|
|
66083
|
+
if (closes === -1 && file3.charAt(i - 1) === " ") {
|
|
66084
|
+
const before2 = file3.slice(0, i - 1);
|
|
66085
|
+
const after2 = file3.slice(i + 1);
|
|
66086
|
+
file3 = after2;
|
|
65923
66087
|
fname += ` (${before2}`;
|
|
65924
66088
|
break;
|
|
65925
66089
|
}
|
|
@@ -65933,7 +66097,7 @@ var require_stack_utils = __commonJS((exports, module) => {
|
|
|
65933
66097
|
method2 = methodMatch[2];
|
|
65934
66098
|
}
|
|
65935
66099
|
}
|
|
65936
|
-
setFile(res,
|
|
66100
|
+
setFile(res, file3, this._cwd);
|
|
65937
66101
|
if (ctor) {
|
|
65938
66102
|
Object.defineProperty(res, "constructor", {
|
|
65939
66103
|
value: true,
|
|
@@ -68377,8 +68541,8 @@ async function detectNode(workdir) {
|
|
|
68377
68541
|
if (!existsSync9(pkgPath))
|
|
68378
68542
|
return null;
|
|
68379
68543
|
try {
|
|
68380
|
-
const
|
|
68381
|
-
const pkg = await
|
|
68544
|
+
const file3 = Bun.file(pkgPath);
|
|
68545
|
+
const pkg = await file3.json();
|
|
68382
68546
|
const allDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
68383
68547
|
const notable = [
|
|
68384
68548
|
...new Set(Object.keys(allDeps).filter((dep) => NOTABLE_NODE_DEPS.some((kw) => dep === kw || dep.startsWith(`${kw}/`) || dep.includes(kw))))
|
|
@@ -68454,8 +68618,8 @@ async function detectPhp(workdir) {
|
|
|
68454
68618
|
if (!existsSync9(composerPath))
|
|
68455
68619
|
return null;
|
|
68456
68620
|
try {
|
|
68457
|
-
const
|
|
68458
|
-
const composer = await
|
|
68621
|
+
const file3 = Bun.file(composerPath);
|
|
68622
|
+
const composer = await file3.json();
|
|
68459
68623
|
const deps = Object.keys({ ...composer.require ?? {}, ...composer["require-dev"] ?? {} }).filter((d) => d !== "php").map((d) => d.split("/").pop() ?? d).slice(0, 10);
|
|
68460
68624
|
return { name: composer.name, lang: "PHP", dependencies: deps };
|
|
68461
68625
|
} catch {
|
|
@@ -69970,16 +70134,16 @@ async function runsListCommand(options) {
|
|
|
69970
70134
|
return;
|
|
69971
70135
|
}
|
|
69972
70136
|
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
69973
|
-
for (const
|
|
69974
|
-
const logPath = join15(runsDir,
|
|
70137
|
+
for (const file3 of files.sort().reverse()) {
|
|
70138
|
+
const logPath = join15(runsDir, file3);
|
|
69975
70139
|
const entries = await parseRunLog(logPath);
|
|
69976
70140
|
const startEvent = entries.find((e) => e.message === "run.start");
|
|
69977
70141
|
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
69978
70142
|
if (!startEvent) {
|
|
69979
|
-
logger.warn("cli", "Run log missing run.start event", { file:
|
|
70143
|
+
logger.warn("cli", "Run log missing run.start event", { file: file3 });
|
|
69980
70144
|
continue;
|
|
69981
70145
|
}
|
|
69982
|
-
const runId = startEvent.data?.runId ||
|
|
70146
|
+
const runId = startEvent.data?.runId || file3.replace(".jsonl", "");
|
|
69983
70147
|
const startedAt = startEvent.timestamp;
|
|
69984
70148
|
const status = completeEvent ? "completed" : "in-progress";
|
|
69985
70149
|
const totalCost = completeEvent?.data?.totalCost || 0;
|
|
@@ -71500,8 +71664,8 @@ async function selectRunFile(runsDir) {
|
|
|
71500
71664
|
return join39(runsDir, files[0]);
|
|
71501
71665
|
}
|
|
71502
71666
|
async function extractRunSummary(filePath) {
|
|
71503
|
-
const
|
|
71504
|
-
const content = await
|
|
71667
|
+
const file3 = Bun.file(filePath);
|
|
71668
|
+
const content = await file3.text();
|
|
71505
71669
|
const lines = content.trim().split(`
|
|
71506
71670
|
`);
|
|
71507
71671
|
let total = 0;
|
|
@@ -71581,10 +71745,10 @@ Runs:
|
|
|
71581
71745
|
`));
|
|
71582
71746
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
71583
71747
|
console.log(source_default.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
71584
|
-
for (const
|
|
71585
|
-
const filePath = join40(runsDir,
|
|
71748
|
+
for (const file3 of files) {
|
|
71749
|
+
const filePath = join40(runsDir, file3);
|
|
71586
71750
|
const summary = await extractRunSummary(filePath);
|
|
71587
|
-
const timestamp =
|
|
71751
|
+
const timestamp = file3.replace(".jsonl", "");
|
|
71588
71752
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
71589
71753
|
const duration3 = summary ? formatDuration(summary.durationMs) : "?";
|
|
71590
71754
|
const cost = summary ? `$${summary.totalCost.toFixed(4)}` : "$?.????";
|
|
@@ -71594,8 +71758,8 @@ Runs:
|
|
|
71594
71758
|
console.log();
|
|
71595
71759
|
}
|
|
71596
71760
|
async function displayLogs(filePath, options) {
|
|
71597
|
-
const
|
|
71598
|
-
const content = await
|
|
71761
|
+
const file3 = Bun.file(filePath);
|
|
71762
|
+
const content = await file3.text();
|
|
71599
71763
|
const lines = content.trim().split(`
|
|
71600
71764
|
`);
|
|
71601
71765
|
const mode = options.json ? "json" : "normal";
|
|
@@ -71622,8 +71786,8 @@ async function displayLogs(filePath, options) {
|
|
|
71622
71786
|
}
|
|
71623
71787
|
async function followLogs(filePath, options) {
|
|
71624
71788
|
const mode = options.json ? "json" : "normal";
|
|
71625
|
-
const
|
|
71626
|
-
const content = await
|
|
71789
|
+
const file3 = Bun.file(filePath);
|
|
71790
|
+
const content = await file3.text();
|
|
71627
71791
|
const lines = content.trim().split(`
|
|
71628
71792
|
`);
|
|
71629
71793
|
for (const line of lines) {
|
|
@@ -78552,8 +78716,8 @@ async function writeQueueCommand(queueFilePath, command) {
|
|
|
78552
78716
|
throw new Error(`Unhandled queue command: ${_exhaustive}`);
|
|
78553
78717
|
}
|
|
78554
78718
|
}
|
|
78555
|
-
const
|
|
78556
|
-
const existingContent = await
|
|
78719
|
+
const file3 = Bun.file(queueFilePath);
|
|
78720
|
+
const existingContent = await file3.text().catch(() => "");
|
|
78557
78721
|
const newContent = existingContent ? `${existingContent.trimEnd()}
|
|
78558
78722
|
${commandLine}
|
|
78559
78723
|
` : `${commandLine}
|