@nathapp/nax 0.53.0-canary.2 → 0.53.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 +339 -229
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -3258,6 +3258,21 @@ async function withProcessTimeout(proc, timeoutMs, opts) {
|
|
|
3258
3258
|
return { exitCode, timedOut };
|
|
3259
3259
|
}
|
|
3260
3260
|
|
|
3261
|
+
// src/utils/bun-deps.ts
|
|
3262
|
+
function typedSpawn(cmd, opts) {
|
|
3263
|
+
return Bun.spawn(cmd, opts);
|
|
3264
|
+
}
|
|
3265
|
+
function which(name) {
|
|
3266
|
+
return Bun.which(name);
|
|
3267
|
+
}
|
|
3268
|
+
function sleep(ms) {
|
|
3269
|
+
return Bun.sleep(ms);
|
|
3270
|
+
}
|
|
3271
|
+
var spawn;
|
|
3272
|
+
var init_bun_deps = __esm(() => {
|
|
3273
|
+
spawn = Bun.spawn;
|
|
3274
|
+
});
|
|
3275
|
+
|
|
3261
3276
|
// src/config/test-strategy.ts
|
|
3262
3277
|
function resolveTestStrategy(raw) {
|
|
3263
3278
|
if (!raw)
|
|
@@ -3479,11 +3494,10 @@ async function executeComplete(binary, prompt, options) {
|
|
|
3479
3494
|
}
|
|
3480
3495
|
var _completeDeps;
|
|
3481
3496
|
var init_complete = __esm(() => {
|
|
3497
|
+
init_bun_deps();
|
|
3482
3498
|
init_types2();
|
|
3483
3499
|
_completeDeps = {
|
|
3484
|
-
spawn
|
|
3485
|
-
return Bun.spawn(cmd, opts);
|
|
3486
|
-
}
|
|
3500
|
+
spawn: typedSpawn
|
|
3487
3501
|
};
|
|
3488
3502
|
});
|
|
3489
3503
|
|
|
@@ -3772,6 +3786,7 @@ async function executeOnce(binary, options, pidRegistry) {
|
|
|
3772
3786
|
var MAX_AGENT_OUTPUT_CHARS = 5000, MAX_AGENT_STDERR_CHARS = 1000, SIGKILL_GRACE_PERIOD_MS = 5000, _runOnceDeps;
|
|
3773
3787
|
var init_execution = __esm(() => {
|
|
3774
3788
|
init_logger2();
|
|
3789
|
+
init_bun_deps();
|
|
3775
3790
|
init_cost2();
|
|
3776
3791
|
_runOnceDeps = {
|
|
3777
3792
|
killProc(proc, signal) {
|
|
@@ -3781,9 +3796,7 @@ var init_execution = __esm(() => {
|
|
|
3781
3796
|
return buildCommand(binary, options);
|
|
3782
3797
|
},
|
|
3783
3798
|
withProcessTimeout,
|
|
3784
|
-
spawn
|
|
3785
|
-
return Bun.spawn(cmd, opts);
|
|
3786
|
-
}
|
|
3799
|
+
spawn: typedSpawn
|
|
3787
3800
|
};
|
|
3788
3801
|
});
|
|
3789
3802
|
|
|
@@ -17793,7 +17806,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17793
17806
|
lintCommand: exports_external.string().nullable().optional(),
|
|
17794
17807
|
typecheckCommand: exports_external.string().nullable().optional(),
|
|
17795
17808
|
dangerouslySkipPermissions: exports_external.boolean().default(true),
|
|
17796
|
-
permissionProfile: exports_external.enum(["unrestricted", "safe", "scoped"]).
|
|
17809
|
+
permissionProfile: exports_external.enum(["unrestricted", "safe", "scoped"]).default("unrestricted"),
|
|
17797
17810
|
permissions: exports_external.record(exports_external.string(), exports_external.object({
|
|
17798
17811
|
mode: exports_external.enum(["approve-all", "approve-reads", "scoped"]),
|
|
17799
17812
|
allowedTools: exports_external.array(exports_external.string()).optional(),
|
|
@@ -17811,7 +17824,8 @@ var init_schemas3 = __esm(() => {
|
|
|
17811
17824
|
test: exports_external.string().optional(),
|
|
17812
17825
|
testScoped: exports_external.string().optional(),
|
|
17813
17826
|
lintFix: exports_external.string().optional(),
|
|
17814
|
-
formatFix: exports_external.string().optional()
|
|
17827
|
+
formatFix: exports_external.string().optional(),
|
|
17828
|
+
build: exports_external.string().optional()
|
|
17815
17829
|
}),
|
|
17816
17830
|
forceExit: exports_external.boolean().default(false),
|
|
17817
17831
|
detectOpenHandles: exports_external.boolean().default(true),
|
|
@@ -17850,7 +17864,6 @@ var init_schemas3 = __esm(() => {
|
|
|
17850
17864
|
"SENTRY_AUTH_TOKEN",
|
|
17851
17865
|
"DATADOG_API_KEY"
|
|
17852
17866
|
]),
|
|
17853
|
-
environmentalEscalationDivisor: exports_external.number().min(1).max(10).default(2),
|
|
17854
17867
|
testing: exports_external.object({
|
|
17855
17868
|
hermetic: exports_external.boolean().default(true),
|
|
17856
17869
|
externalBoundaries: exports_external.array(exports_external.string()).optional(),
|
|
@@ -17885,11 +17898,12 @@ var init_schemas3 = __esm(() => {
|
|
|
17885
17898
|
});
|
|
17886
17899
|
ReviewConfigSchema = exports_external.object({
|
|
17887
17900
|
enabled: exports_external.boolean(),
|
|
17888
|
-
checks: exports_external.array(exports_external.enum(["typecheck", "lint", "test"])),
|
|
17901
|
+
checks: exports_external.array(exports_external.enum(["typecheck", "lint", "test", "build"])),
|
|
17889
17902
|
commands: exports_external.object({
|
|
17890
17903
|
typecheck: exports_external.string().optional(),
|
|
17891
17904
|
lint: exports_external.string().optional(),
|
|
17892
|
-
test: exports_external.string().optional()
|
|
17905
|
+
test: exports_external.string().optional(),
|
|
17906
|
+
build: exports_external.string().optional()
|
|
17893
17907
|
}),
|
|
17894
17908
|
pluginMode: exports_external.enum(["per-story", "deferred"]).default("per-story")
|
|
17895
17909
|
});
|
|
@@ -18038,7 +18052,7 @@ var init_defaults = __esm(() => {
|
|
|
18038
18052
|
autoMode: {
|
|
18039
18053
|
enabled: true,
|
|
18040
18054
|
defaultAgent: "claude",
|
|
18041
|
-
fallbackOrder: ["claude"
|
|
18055
|
+
fallbackOrder: ["claude"],
|
|
18042
18056
|
complexityRouting: {
|
|
18043
18057
|
simple: "fast",
|
|
18044
18058
|
medium: "balanced",
|
|
@@ -18062,26 +18076,26 @@ var init_defaults = __esm(() => {
|
|
|
18062
18076
|
fallbackToKeywords: true,
|
|
18063
18077
|
cacheDecisions: true,
|
|
18064
18078
|
mode: "hybrid",
|
|
18065
|
-
timeoutMs:
|
|
18079
|
+
timeoutMs: 30000
|
|
18066
18080
|
}
|
|
18067
18081
|
},
|
|
18068
18082
|
execution: {
|
|
18069
18083
|
maxIterations: 10,
|
|
18070
18084
|
iterationDelayMs: 2000,
|
|
18071
|
-
costLimit:
|
|
18072
|
-
sessionTimeoutSeconds:
|
|
18073
|
-
verificationTimeoutSeconds:
|
|
18085
|
+
costLimit: 30,
|
|
18086
|
+
sessionTimeoutSeconds: 3600,
|
|
18087
|
+
verificationTimeoutSeconds: 600,
|
|
18074
18088
|
maxStoriesPerFeature: 500,
|
|
18075
18089
|
rectification: {
|
|
18076
18090
|
enabled: true,
|
|
18077
18091
|
maxRetries: 2,
|
|
18078
|
-
fullSuiteTimeoutSeconds:
|
|
18092
|
+
fullSuiteTimeoutSeconds: 300,
|
|
18079
18093
|
maxFailureSummaryChars: 2000,
|
|
18080
18094
|
abortOnIncreasingFailures: true
|
|
18081
18095
|
},
|
|
18082
18096
|
regressionGate: {
|
|
18083
18097
|
enabled: true,
|
|
18084
|
-
timeoutSeconds:
|
|
18098
|
+
timeoutSeconds: 300,
|
|
18085
18099
|
acceptOnTimeout: true,
|
|
18086
18100
|
maxRectificationAttempts: 2
|
|
18087
18101
|
},
|
|
@@ -18130,7 +18144,6 @@ var init_defaults = __esm(() => {
|
|
|
18130
18144
|
"SENTRY_AUTH_TOKEN",
|
|
18131
18145
|
"DATADOG_API_KEY"
|
|
18132
18146
|
],
|
|
18133
|
-
environmentalEscalationDivisor: 2,
|
|
18134
18147
|
testing: {
|
|
18135
18148
|
hermetic: true
|
|
18136
18149
|
}
|
|
@@ -18215,6 +18228,9 @@ var init_defaults = __esm(() => {
|
|
|
18215
18228
|
}
|
|
18216
18229
|
},
|
|
18217
18230
|
prompts: {},
|
|
18231
|
+
agent: {
|
|
18232
|
+
protocol: "acp"
|
|
18233
|
+
},
|
|
18218
18234
|
decompose: {
|
|
18219
18235
|
trigger: "auto",
|
|
18220
18236
|
maxAcceptanceCriteria: 6,
|
|
@@ -18539,19 +18555,18 @@ var _decomposeDeps, _claudeAdapterDeps;
|
|
|
18539
18555
|
var init_adapter = __esm(() => {
|
|
18540
18556
|
init_pid_registry();
|
|
18541
18557
|
init_logger2();
|
|
18558
|
+
init_bun_deps();
|
|
18542
18559
|
init_decompose();
|
|
18543
18560
|
init_complete();
|
|
18544
18561
|
init_execution();
|
|
18545
18562
|
init_interactive();
|
|
18546
18563
|
init_plan();
|
|
18547
18564
|
_decomposeDeps = {
|
|
18548
|
-
spawn
|
|
18549
|
-
return Bun.spawn(cmd, opts);
|
|
18550
|
-
}
|
|
18565
|
+
spawn: typedSpawn
|
|
18551
18566
|
};
|
|
18552
18567
|
_claudeAdapterDeps = {
|
|
18553
|
-
sleep
|
|
18554
|
-
spawn:
|
|
18568
|
+
sleep,
|
|
18569
|
+
spawn: typedSpawn
|
|
18555
18570
|
};
|
|
18556
18571
|
});
|
|
18557
18572
|
|
|
@@ -18728,6 +18743,18 @@ __export(exports_generator, {
|
|
|
18728
18743
|
_generatorPRDDeps: () => _generatorPRDDeps
|
|
18729
18744
|
});
|
|
18730
18745
|
import { join as join2 } from "path";
|
|
18746
|
+
function skeletonImportLine(testFramework) {
|
|
18747
|
+
if (!testFramework)
|
|
18748
|
+
return `import { describe, test, expect } from "bun:test";`;
|
|
18749
|
+
const fw = testFramework.toLowerCase();
|
|
18750
|
+
if (fw === "jest" || fw === "@jest/globals") {
|
|
18751
|
+
return `import { describe, test, expect } from "@jest/globals";`;
|
|
18752
|
+
}
|
|
18753
|
+
if (fw === "vitest") {
|
|
18754
|
+
return `import { describe, test, expect } from "vitest";`;
|
|
18755
|
+
}
|
|
18756
|
+
return `import { describe, test, expect } from "bun:test";`;
|
|
18757
|
+
}
|
|
18731
18758
|
async function generateFromPRD(_stories, refinedCriteria, options) {
|
|
18732
18759
|
const logger = getLogger();
|
|
18733
18760
|
const criteria = refinedCriteria.map((c, i) => ({
|
|
@@ -18775,7 +18802,7 @@ Rules:
|
|
|
18775
18802
|
- **NEVER use placeholder assertions** \u2014 no always-passing or always-failing stubs, no TODO comments as the only content, no empty test bodies
|
|
18776
18803
|
- Every test MUST have real assertions that PASS when the feature is correctly implemented and FAIL when it is broken
|
|
18777
18804
|
- Output raw code only \u2014 no markdown fences, start directly with the language's import or package declaration
|
|
18778
|
-
- **Path anchor (CRITICAL)**: This test file will be saved at \`<repo-root
|
|
18805
|
+
- **Path anchor (CRITICAL)**: This test file will be saved at \`<repo-root>/.nax/features/${options.featureName}/acceptance.test.ts\` and will ALWAYS run from the repo root. The repo root is exactly 4 \`../\` levels above \`__dirname\`: \`join(__dirname, '..', '..', '..', '..')\`. For monorepo projects, navigate into packages from root (e.g. \`join(root, 'apps/api/src')\`).`;
|
|
18779
18806
|
const prompt = basePrompt;
|
|
18780
18807
|
logger.info("acceptance", "Generating tests from PRD refined criteria", { count: refinedCriteria.length });
|
|
18781
18808
|
const rawOutput = await (options.adapter ?? _generatorPRDDeps.adapter).complete(prompt, {
|
|
@@ -18784,7 +18811,18 @@ Rules:
|
|
|
18784
18811
|
timeoutMs: options.config?.acceptance?.timeoutMs ?? 1800000,
|
|
18785
18812
|
workdir: options.workdir
|
|
18786
18813
|
});
|
|
18787
|
-
|
|
18814
|
+
let testCode = extractTestCode(rawOutput);
|
|
18815
|
+
if (!testCode) {
|
|
18816
|
+
const targetPath = join2(options.featureDir, "acceptance.test.ts");
|
|
18817
|
+
try {
|
|
18818
|
+
const existing = await Bun.file(targetPath).text();
|
|
18819
|
+
const recovered = extractTestCode(existing);
|
|
18820
|
+
if (recovered) {
|
|
18821
|
+
logger.info("acceptance", "Recovered acceptance test written to disk by ACP adapter", { targetPath });
|
|
18822
|
+
testCode = recovered;
|
|
18823
|
+
}
|
|
18824
|
+
} catch {}
|
|
18825
|
+
}
|
|
18788
18826
|
if (!testCode) {
|
|
18789
18827
|
logger.warn("acceptance", "LLM returned non-code output for acceptance tests \u2014 falling back to skeleton", {
|
|
18790
18828
|
outputPreview: rawOutput.slice(0, 200)
|
|
@@ -18794,7 +18832,10 @@ Rules:
|
|
|
18794
18832
|
text: c.refined,
|
|
18795
18833
|
lineNumber: i + 1
|
|
18796
18834
|
}));
|
|
18797
|
-
return {
|
|
18835
|
+
return {
|
|
18836
|
+
testCode: generateSkeletonTests(options.featureName, skeletonCriteria, options.testFramework),
|
|
18837
|
+
criteria: skeletonCriteria
|
|
18838
|
+
};
|
|
18798
18839
|
}
|
|
18799
18840
|
const refinedJsonContent = JSON.stringify(refinedCriteria.map((c, i) => ({
|
|
18800
18841
|
acId: `AC-${i + 1}`,
|
|
@@ -18861,7 +18902,7 @@ Rules:
|
|
|
18861
18902
|
- **NEVER use placeholder assertions** \u2014 no always-passing or always-failing stubs, no TODO comments as the only content, no empty test bodies
|
|
18862
18903
|
- Every test MUST have real assertions that PASS when the feature is correctly implemented and FAIL when it is broken
|
|
18863
18904
|
- Output raw code only \u2014 no markdown fences, start directly with the language's import or package declaration
|
|
18864
|
-
- **Path anchor (CRITICAL)**: This test file will be saved at \`<repo-root
|
|
18905
|
+
- **Path anchor (CRITICAL)**: This test file will be saved at \`<repo-root>/.nax/features/${featureName}/acceptance.test.ts\` and will ALWAYS run from the repo root. The repo root is exactly 4 \`../\` levels above \`__dirname\`: \`join(__dirname, '..', '..', '..', '..')\`. For monorepo projects, navigate into packages from root (e.g. \`join(root, 'apps/api/src')\`).`;
|
|
18865
18906
|
}
|
|
18866
18907
|
async function generateAcceptanceTests(adapter, options) {
|
|
18867
18908
|
const logger = getLogger();
|
|
@@ -18869,7 +18910,7 @@ async function generateAcceptanceTests(adapter, options) {
|
|
|
18869
18910
|
if (criteria.length === 0) {
|
|
18870
18911
|
logger.warn("acceptance", "\u26A0 No acceptance criteria found in spec.md");
|
|
18871
18912
|
return {
|
|
18872
|
-
testCode: generateSkeletonTests(options.featureName, []),
|
|
18913
|
+
testCode: generateSkeletonTests(options.featureName, [], options.testFramework),
|
|
18873
18914
|
criteria: []
|
|
18874
18915
|
};
|
|
18875
18916
|
}
|
|
@@ -18888,7 +18929,7 @@ async function generateAcceptanceTests(adapter, options) {
|
|
|
18888
18929
|
outputPreview: output.slice(0, 200)
|
|
18889
18930
|
});
|
|
18890
18931
|
return {
|
|
18891
|
-
testCode: generateSkeletonTests(options.featureName, criteria),
|
|
18932
|
+
testCode: generateSkeletonTests(options.featureName, criteria, options.testFramework),
|
|
18892
18933
|
criteria
|
|
18893
18934
|
};
|
|
18894
18935
|
}
|
|
@@ -18899,7 +18940,7 @@ async function generateAcceptanceTests(adapter, options) {
|
|
|
18899
18940
|
} catch (error48) {
|
|
18900
18941
|
logger.warn("acceptance", "\u26A0 Agent test generation error", { error: error48.message });
|
|
18901
18942
|
return {
|
|
18902
|
-
testCode: generateSkeletonTests(options.featureName, criteria),
|
|
18943
|
+
testCode: generateSkeletonTests(options.featureName, criteria, options.testFramework),
|
|
18903
18944
|
criteria
|
|
18904
18945
|
};
|
|
18905
18946
|
}
|
|
@@ -18930,7 +18971,7 @@ function extractTestCode(output) {
|
|
|
18930
18971
|
}
|
|
18931
18972
|
return code;
|
|
18932
18973
|
}
|
|
18933
|
-
function generateSkeletonTests(featureName, criteria) {
|
|
18974
|
+
function generateSkeletonTests(featureName, criteria, testFramework) {
|
|
18934
18975
|
const tests = criteria.map((ac) => {
|
|
18935
18976
|
return ` test("${ac.id}: ${ac.text}", async () => {
|
|
18936
18977
|
// TODO: Implement acceptance test for ${ac.id}
|
|
@@ -18940,7 +18981,7 @@ function generateSkeletonTests(featureName, criteria) {
|
|
|
18940
18981
|
}).join(`
|
|
18941
18982
|
|
|
18942
18983
|
`);
|
|
18943
|
-
return
|
|
18984
|
+
return `${skeletonImportLine(testFramework)}
|
|
18944
18985
|
|
|
18945
18986
|
describe("${featureName} - Acceptance Tests", () => {
|
|
18946
18987
|
${tests || " // No acceptance criteria found"}
|
|
@@ -19308,8 +19349,8 @@ class SpawnAcpSession {
|
|
|
19308
19349
|
const processPid = proc.pid;
|
|
19309
19350
|
await this.pidRegistry?.register(processPid);
|
|
19310
19351
|
try {
|
|
19311
|
-
proc.stdin
|
|
19312
|
-
proc.stdin
|
|
19352
|
+
proc.stdin?.write(text);
|
|
19353
|
+
proc.stdin?.end();
|
|
19313
19354
|
const exitCode = await proc.exited;
|
|
19314
19355
|
const stdout = await new Response(proc.stdout).text();
|
|
19315
19356
|
const stderr = await new Response(proc.stderr).text();
|
|
@@ -19444,10 +19485,9 @@ function createSpawnAcpClient(cmdStr, cwd, timeoutSeconds, pidRegistry) {
|
|
|
19444
19485
|
var _spawnClientDeps;
|
|
19445
19486
|
var init_spawn_client = __esm(() => {
|
|
19446
19487
|
init_logger2();
|
|
19488
|
+
init_bun_deps();
|
|
19447
19489
|
_spawnClientDeps = {
|
|
19448
|
-
spawn
|
|
19449
|
-
return Bun.spawn(cmd, opts);
|
|
19450
|
-
}
|
|
19490
|
+
spawn: typedSpawn
|
|
19451
19491
|
};
|
|
19452
19492
|
});
|
|
19453
19493
|
|
|
@@ -19994,6 +20034,7 @@ class AcpAgentAdapter {
|
|
|
19994
20034
|
var MAX_AGENT_OUTPUT_CHARS2 = 5000, MAX_RATE_LIMIT_RETRIES = 3, INTERACTION_TIMEOUT_MS, AGENT_REGISTRY, DEFAULT_ENTRY, _acpAdapterDeps, MAX_SESSION_AGE_MS;
|
|
19995
20035
|
var init_adapter2 = __esm(() => {
|
|
19996
20036
|
init_logger2();
|
|
20037
|
+
init_bun_deps();
|
|
19997
20038
|
init_decompose();
|
|
19998
20039
|
init_spawn_client();
|
|
19999
20040
|
init_types2();
|
|
@@ -20026,12 +20067,8 @@ var init_adapter2 = __esm(() => {
|
|
|
20026
20067
|
maxContextTokens: 128000
|
|
20027
20068
|
};
|
|
20028
20069
|
_acpAdapterDeps = {
|
|
20029
|
-
which
|
|
20030
|
-
|
|
20031
|
-
},
|
|
20032
|
-
async sleep(ms) {
|
|
20033
|
-
await Bun.sleep(ms);
|
|
20034
|
-
},
|
|
20070
|
+
which,
|
|
20071
|
+
sleep,
|
|
20035
20072
|
createClient(cmdStr, cwd, timeoutSeconds, pidRegistry) {
|
|
20036
20073
|
return createSpawnAcpClient(cmdStr, cwd, timeoutSeconds, pidRegistry);
|
|
20037
20074
|
}
|
|
@@ -20105,14 +20142,11 @@ class AiderAdapter {
|
|
|
20105
20142
|
}
|
|
20106
20143
|
var _aiderCompleteDeps, MAX_AGENT_OUTPUT_CHARS3 = 5000;
|
|
20107
20144
|
var init_adapter3 = __esm(() => {
|
|
20145
|
+
init_bun_deps();
|
|
20108
20146
|
init_types2();
|
|
20109
20147
|
_aiderCompleteDeps = {
|
|
20110
|
-
which
|
|
20111
|
-
|
|
20112
|
-
},
|
|
20113
|
-
spawn(cmd, opts) {
|
|
20114
|
-
return Bun.spawn(cmd, opts);
|
|
20115
|
-
}
|
|
20148
|
+
which,
|
|
20149
|
+
spawn: typedSpawn
|
|
20116
20150
|
};
|
|
20117
20151
|
});
|
|
20118
20152
|
|
|
@@ -20180,19 +20214,14 @@ class CodexAdapter {
|
|
|
20180
20214
|
}
|
|
20181
20215
|
var _codexRunDeps, _codexCompleteDeps, MAX_AGENT_OUTPUT_CHARS4 = 5000;
|
|
20182
20216
|
var init_adapter4 = __esm(() => {
|
|
20217
|
+
init_bun_deps();
|
|
20183
20218
|
init_types2();
|
|
20184
20219
|
_codexRunDeps = {
|
|
20185
|
-
which
|
|
20186
|
-
|
|
20187
|
-
},
|
|
20188
|
-
spawn(cmd, opts) {
|
|
20189
|
-
return Bun.spawn(cmd, opts);
|
|
20190
|
-
}
|
|
20220
|
+
which,
|
|
20221
|
+
spawn: typedSpawn
|
|
20191
20222
|
};
|
|
20192
20223
|
_codexCompleteDeps = {
|
|
20193
|
-
spawn
|
|
20194
|
-
return Bun.spawn(cmd, opts);
|
|
20195
|
-
}
|
|
20224
|
+
spawn: typedSpawn
|
|
20196
20225
|
};
|
|
20197
20226
|
});
|
|
20198
20227
|
|
|
@@ -20280,19 +20309,14 @@ class GeminiAdapter {
|
|
|
20280
20309
|
}
|
|
20281
20310
|
var _geminiRunDeps, _geminiCompleteDeps, MAX_AGENT_OUTPUT_CHARS5 = 5000;
|
|
20282
20311
|
var init_adapter5 = __esm(() => {
|
|
20312
|
+
init_bun_deps();
|
|
20283
20313
|
init_types2();
|
|
20284
20314
|
_geminiRunDeps = {
|
|
20285
|
-
which
|
|
20286
|
-
|
|
20287
|
-
},
|
|
20288
|
-
spawn(cmd, opts) {
|
|
20289
|
-
return Bun.spawn(cmd, opts);
|
|
20290
|
-
}
|
|
20315
|
+
which,
|
|
20316
|
+
spawn: typedSpawn
|
|
20291
20317
|
};
|
|
20292
20318
|
_geminiCompleteDeps = {
|
|
20293
|
-
spawn
|
|
20294
|
-
return Bun.spawn(cmd, opts);
|
|
20295
|
-
}
|
|
20319
|
+
spawn: typedSpawn
|
|
20296
20320
|
};
|
|
20297
20321
|
});
|
|
20298
20322
|
|
|
@@ -20342,14 +20366,11 @@ class OpenCodeAdapter {
|
|
|
20342
20366
|
}
|
|
20343
20367
|
var _opencodeCompleteDeps;
|
|
20344
20368
|
var init_adapter6 = __esm(() => {
|
|
20369
|
+
init_bun_deps();
|
|
20345
20370
|
init_types2();
|
|
20346
20371
|
_opencodeCompleteDeps = {
|
|
20347
|
-
which
|
|
20348
|
-
|
|
20349
|
-
},
|
|
20350
|
-
spawn(cmd, opts) {
|
|
20351
|
-
return Bun.spawn(cmd, opts);
|
|
20352
|
-
}
|
|
20372
|
+
which,
|
|
20373
|
+
spawn: typedSpawn
|
|
20353
20374
|
};
|
|
20354
20375
|
});
|
|
20355
20376
|
|
|
@@ -20384,7 +20405,7 @@ async function checkAgentHealth() {
|
|
|
20384
20405
|
})));
|
|
20385
20406
|
}
|
|
20386
20407
|
function createAgentRegistry(config2) {
|
|
20387
|
-
const protocol = config2.agent?.protocol ?? "
|
|
20408
|
+
const protocol = config2.agent?.protocol ?? "acp";
|
|
20388
20409
|
const logger = getLogger();
|
|
20389
20410
|
const acpCache = new Map;
|
|
20390
20411
|
logger?.info("agents", `Agent protocol: ${protocol}`, { protocol, hasConfig: !!config2.agent });
|
|
@@ -20634,6 +20655,9 @@ function mergePackageConfig(root, packageOverride) {
|
|
|
20634
20655
|
...packageOverride.quality?.commands?.test !== undefined && {
|
|
20635
20656
|
test: packageOverride.quality.commands.test
|
|
20636
20657
|
},
|
|
20658
|
+
...packageOverride.quality?.commands?.build !== undefined && {
|
|
20659
|
+
build: packageOverride.quality.commands.build
|
|
20660
|
+
},
|
|
20637
20661
|
...packageOverride.review?.commands
|
|
20638
20662
|
}
|
|
20639
20663
|
},
|
|
@@ -21089,7 +21113,7 @@ __export(exports_llm, {
|
|
|
21089
21113
|
classifyWithLlm: () => classifyWithLlm,
|
|
21090
21114
|
buildRoutingPrompt: () => buildRoutingPrompt,
|
|
21091
21115
|
buildBatchPrompt: () => buildBatchRoutingPrompt,
|
|
21092
|
-
|
|
21116
|
+
_llmStrategyDeps: () => _llmStrategyDeps
|
|
21093
21117
|
});
|
|
21094
21118
|
function clearCache() {
|
|
21095
21119
|
cachedDecisions.clear();
|
|
@@ -21160,7 +21184,7 @@ async function routeBatch(stories, context) {
|
|
|
21160
21184
|
if (!llmConfig) {
|
|
21161
21185
|
throw new Error("LLM routing config not found");
|
|
21162
21186
|
}
|
|
21163
|
-
const adapter = context.adapter ??
|
|
21187
|
+
const adapter = context.adapter ?? _llmStrategyDeps.adapter;
|
|
21164
21188
|
if (!adapter) {
|
|
21165
21189
|
throw new Error("No agent adapter available for batch routing (AA-003)");
|
|
21166
21190
|
}
|
|
@@ -21206,7 +21230,7 @@ async function classifyWithLlm(story, config2, adapter) {
|
|
|
21206
21230
|
logger2.info("routing", "One-shot mode cache miss, falling back to keyword", { storyId: story.id });
|
|
21207
21231
|
return null;
|
|
21208
21232
|
}
|
|
21209
|
-
const effectiveAdapter = adapter ??
|
|
21233
|
+
const effectiveAdapter = adapter ?? _llmStrategyDeps.adapter;
|
|
21210
21234
|
if (!effectiveAdapter) {
|
|
21211
21235
|
throw new Error("No agent adapter available for LLM routing (AA-003)");
|
|
21212
21236
|
}
|
|
@@ -21237,16 +21261,17 @@ async function classifyWithLlm(story, config2, adapter) {
|
|
|
21237
21261
|
});
|
|
21238
21262
|
return decision;
|
|
21239
21263
|
}
|
|
21240
|
-
var cachedDecisions, MAX_CACHE_SIZE = 100,
|
|
21264
|
+
var cachedDecisions, MAX_CACHE_SIZE = 100, _llmStrategyDeps;
|
|
21241
21265
|
var init_llm = __esm(() => {
|
|
21242
21266
|
init_config();
|
|
21243
21267
|
init_logger2();
|
|
21268
|
+
init_bun_deps();
|
|
21244
21269
|
init_router();
|
|
21245
21270
|
init_llm_prompts();
|
|
21246
21271
|
init_llm_prompts();
|
|
21247
21272
|
cachedDecisions = new Map;
|
|
21248
|
-
|
|
21249
|
-
spawn:
|
|
21273
|
+
_llmStrategyDeps = {
|
|
21274
|
+
spawn: typedSpawn,
|
|
21250
21275
|
adapter: undefined
|
|
21251
21276
|
};
|
|
21252
21277
|
});
|
|
@@ -21380,11 +21405,11 @@ function routeTask(title, description, acceptanceCriteria, tags, config2) {
|
|
|
21380
21405
|
reasoning: reasons.length > 0 ? `${prefix}: ${reasons.join(", ")}` : `test-after: simple task (${complexity})`
|
|
21381
21406
|
};
|
|
21382
21407
|
}
|
|
21383
|
-
async function tryLlmBatchRoute(config2, stories, label = "routing",
|
|
21408
|
+
async function tryLlmBatchRoute(config2, stories, label = "routing", _deps = _tryLlmBatchRouteDeps) {
|
|
21384
21409
|
const mode = config2.routing.llm?.mode ?? "hybrid";
|
|
21385
21410
|
if (config2.routing.strategy !== "llm" || mode === "per-story" || stories.length === 0)
|
|
21386
21411
|
return;
|
|
21387
|
-
const resolvedAdapter =
|
|
21412
|
+
const resolvedAdapter = _deps.getAgent(config2.execution?.agent ?? "claude");
|
|
21388
21413
|
if (!resolvedAdapter)
|
|
21389
21414
|
return;
|
|
21390
21415
|
const logger = getSafeLogger();
|
|
@@ -22077,7 +22102,7 @@ var package_default;
|
|
|
22077
22102
|
var init_package = __esm(() => {
|
|
22078
22103
|
package_default = {
|
|
22079
22104
|
name: "@nathapp/nax",
|
|
22080
|
-
version: "0.53.0
|
|
22105
|
+
version: "0.53.0",
|
|
22081
22106
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22082
22107
|
type: "module",
|
|
22083
22108
|
bin: {
|
|
@@ -22154,8 +22179,8 @@ var init_version = __esm(() => {
|
|
|
22154
22179
|
NAX_VERSION = package_default.version;
|
|
22155
22180
|
NAX_COMMIT = (() => {
|
|
22156
22181
|
try {
|
|
22157
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22158
|
-
return "
|
|
22182
|
+
if (/^[0-9a-f]{6,10}$/.test("18532ac"))
|
|
22183
|
+
return "18532ac";
|
|
22159
22184
|
} catch {}
|
|
22160
22185
|
try {
|
|
22161
22186
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -23313,6 +23338,7 @@ class WebhookInteractionPlugin {
|
|
|
23313
23338
|
var WebhookConfigSchema, InteractionResponseSchema;
|
|
23314
23339
|
var init_webhook = __esm(() => {
|
|
23315
23340
|
init_zod();
|
|
23341
|
+
init_bun_deps();
|
|
23316
23342
|
WebhookConfigSchema = exports_external.object({
|
|
23317
23343
|
url: exports_external.string().url().optional(),
|
|
23318
23344
|
callbackPort: exports_external.number().int().min(1024).max(65535).optional(),
|
|
@@ -23351,8 +23377,8 @@ class AutoInteractionPlugin {
|
|
|
23351
23377
|
return;
|
|
23352
23378
|
}
|
|
23353
23379
|
try {
|
|
23354
|
-
if (
|
|
23355
|
-
const decision2 = await
|
|
23380
|
+
if (_autoPluginDeps.callLlm) {
|
|
23381
|
+
const decision2 = await _autoPluginDeps.callLlm(request);
|
|
23356
23382
|
if (decision2.confidence < (this.config.confidenceThreshold ?? 0.7)) {
|
|
23357
23383
|
return;
|
|
23358
23384
|
}
|
|
@@ -23381,9 +23407,9 @@ class AutoInteractionPlugin {
|
|
|
23381
23407
|
}
|
|
23382
23408
|
async callLlm(request) {
|
|
23383
23409
|
const prompt = this.buildPrompt(request);
|
|
23384
|
-
const adapter =
|
|
23410
|
+
const adapter = _autoPluginDeps.adapter;
|
|
23385
23411
|
if (!adapter) {
|
|
23386
|
-
throw new Error("Auto plugin requires adapter to be injected via
|
|
23412
|
+
throw new Error("Auto plugin requires adapter to be injected via _autoPluginDeps.adapter");
|
|
23387
23413
|
}
|
|
23388
23414
|
let modelArg;
|
|
23389
23415
|
if (this.config.naxConfig) {
|
|
@@ -23470,7 +23496,7 @@ Respond with ONLY this JSON (no markdown, no explanation):
|
|
|
23470
23496
|
return parsed;
|
|
23471
23497
|
}
|
|
23472
23498
|
}
|
|
23473
|
-
var AutoConfigSchema,
|
|
23499
|
+
var AutoConfigSchema, _autoPluginDeps;
|
|
23474
23500
|
var init_auto = __esm(() => {
|
|
23475
23501
|
init_zod();
|
|
23476
23502
|
init_config();
|
|
@@ -23480,7 +23506,7 @@ var init_auto = __esm(() => {
|
|
|
23480
23506
|
maxCostPerDecision: exports_external.number().positive().optional(),
|
|
23481
23507
|
naxConfig: exports_external.any().optional()
|
|
23482
23508
|
});
|
|
23483
|
-
|
|
23509
|
+
_autoPluginDeps = {
|
|
23484
23510
|
adapter: null,
|
|
23485
23511
|
callLlm: null
|
|
23486
23512
|
};
|
|
@@ -23849,7 +23875,9 @@ var init_acceptance2 = __esm(() => {
|
|
|
23849
23875
|
});
|
|
23850
23876
|
return { action: "continue" };
|
|
23851
23877
|
}
|
|
23852
|
-
const
|
|
23878
|
+
const configuredTestCmd = ctx.config.quality?.commands?.test;
|
|
23879
|
+
const testCmdParts = configuredTestCmd ? [...configuredTestCmd.trim().split(/\s+/), testPath] : ["bun", "test", testPath];
|
|
23880
|
+
const proc = Bun.spawn(testCmdParts, {
|
|
23853
23881
|
cwd: ctx.workdir,
|
|
23854
23882
|
stdout: "pipe",
|
|
23855
23883
|
stderr: "pipe"
|
|
@@ -23963,11 +23991,10 @@ async function getAgentVersions() {
|
|
|
23963
23991
|
}
|
|
23964
23992
|
var _versionDetectionDeps;
|
|
23965
23993
|
var init_version_detection = __esm(() => {
|
|
23994
|
+
init_bun_deps();
|
|
23966
23995
|
init_registry();
|
|
23967
23996
|
_versionDetectionDeps = {
|
|
23968
|
-
spawn
|
|
23969
|
-
return Bun.spawn(cmd, opts);
|
|
23970
|
-
}
|
|
23997
|
+
spawn: typedSpawn
|
|
23971
23998
|
};
|
|
23972
23999
|
});
|
|
23973
24000
|
|
|
@@ -24051,8 +24078,15 @@ var init_acceptance_setup = __esm(() => {
|
|
|
24051
24078
|
writeMeta: async (metaPath, meta3) => {
|
|
24052
24079
|
await Bun.write(metaPath, JSON.stringify(meta3, null, 2));
|
|
24053
24080
|
},
|
|
24054
|
-
runTest: async (_testPath, _workdir) => {
|
|
24055
|
-
|
|
24081
|
+
runTest: async (_testPath, _workdir, _testCmd) => {
|
|
24082
|
+
let cmd;
|
|
24083
|
+
if (_testCmd) {
|
|
24084
|
+
const parts = _testCmd.trim().split(/\s+/);
|
|
24085
|
+
cmd = [...parts, _testPath];
|
|
24086
|
+
} else {
|
|
24087
|
+
cmd = ["bun", "test", _testPath];
|
|
24088
|
+
}
|
|
24089
|
+
const proc = Bun.spawn(cmd, {
|
|
24056
24090
|
cwd: _workdir,
|
|
24057
24091
|
stdout: "pipe",
|
|
24058
24092
|
stderr: "pipe"
|
|
@@ -24147,7 +24181,8 @@ ${stderr}` };
|
|
|
24147
24181
|
ctx.acceptanceSetup = { totalCriteria, testableCount, redFailCount: 0 };
|
|
24148
24182
|
return { action: "continue" };
|
|
24149
24183
|
}
|
|
24150
|
-
const
|
|
24184
|
+
const testCmd = ctx.config.quality?.commands?.test;
|
|
24185
|
+
const { exitCode } = await _acceptanceSetupDeps.runTest(testPath, ctx.workdir, testCmd);
|
|
24151
24186
|
if (exitCode === 0) {
|
|
24152
24187
|
ctx.acceptanceSetup = { totalCriteria, testableCount, redFailCount: 0 };
|
|
24153
24188
|
return {
|
|
@@ -24345,11 +24380,12 @@ async function captureOutputFiles(workdir, baseRef, scopePrefix) {
|
|
|
24345
24380
|
var _gitDeps, GIT_TIMEOUT_MS = 1e4;
|
|
24346
24381
|
var init_git = __esm(() => {
|
|
24347
24382
|
init_logger2();
|
|
24348
|
-
|
|
24383
|
+
init_bun_deps();
|
|
24384
|
+
_gitDeps = { spawn };
|
|
24349
24385
|
});
|
|
24350
24386
|
|
|
24351
24387
|
// src/review/runner.ts
|
|
24352
|
-
var {spawn } = globalThis.Bun;
|
|
24388
|
+
var {spawn: spawn2 } = globalThis.Bun;
|
|
24353
24389
|
async function loadPackageJson(workdir) {
|
|
24354
24390
|
try {
|
|
24355
24391
|
const file2 = _reviewRunnerDeps.file(`${workdir}/package.json`);
|
|
@@ -24383,9 +24419,11 @@ async function resolveCommand(check2, config2, executionConfig, workdir, quality
|
|
|
24383
24419
|
if (qualityCmd) {
|
|
24384
24420
|
return qualityCmd;
|
|
24385
24421
|
}
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24422
|
+
if (check2 !== "build") {
|
|
24423
|
+
const packageJson = await loadPackageJson(workdir);
|
|
24424
|
+
if (hasScript(packageJson, check2)) {
|
|
24425
|
+
return `bun run ${check2}`;
|
|
24426
|
+
}
|
|
24389
24427
|
}
|
|
24390
24428
|
return null;
|
|
24391
24429
|
}
|
|
@@ -24486,7 +24524,7 @@ async function runReview(config2, workdir, executionConfig, qualityCommands, sto
|
|
|
24486
24524
|
const checks3 = [];
|
|
24487
24525
|
let firstFailure;
|
|
24488
24526
|
await autoCommitIfDirty(workdir, "review", "agent", storyId ?? "review");
|
|
24489
|
-
const allUncommittedFiles = await
|
|
24527
|
+
const allUncommittedFiles = await _reviewGitDeps.getUncommittedFiles(workdir);
|
|
24490
24528
|
const NAX_RUNTIME_PATTERNS = [
|
|
24491
24529
|
/nax\.lock$/,
|
|
24492
24530
|
/nax\/metrics\.json$/,
|
|
@@ -24541,18 +24579,18 @@ Stage and commit these files before running review.`
|
|
|
24541
24579
|
failureReason: firstFailure
|
|
24542
24580
|
};
|
|
24543
24581
|
}
|
|
24544
|
-
var _reviewRunnerDeps, REVIEW_CHECK_TIMEOUT_MS = 120000, SIGKILL_GRACE_PERIOD_MS2 = 5000,
|
|
24582
|
+
var _reviewRunnerDeps, REVIEW_CHECK_TIMEOUT_MS = 120000, SIGKILL_GRACE_PERIOD_MS2 = 5000, _reviewGitDeps;
|
|
24545
24583
|
var init_runner2 = __esm(() => {
|
|
24546
24584
|
init_logger2();
|
|
24547
24585
|
init_git();
|
|
24548
|
-
_reviewRunnerDeps = { spawn, file: Bun.file };
|
|
24549
|
-
|
|
24586
|
+
_reviewRunnerDeps = { spawn: spawn2, file: Bun.file };
|
|
24587
|
+
_reviewGitDeps = {
|
|
24550
24588
|
getUncommittedFiles: getUncommittedFilesImpl
|
|
24551
24589
|
};
|
|
24552
24590
|
});
|
|
24553
24591
|
|
|
24554
24592
|
// src/review/orchestrator.ts
|
|
24555
|
-
var {spawn:
|
|
24593
|
+
var {spawn: spawn3 } = globalThis.Bun;
|
|
24556
24594
|
async function getChangedFiles(workdir, baseRef) {
|
|
24557
24595
|
try {
|
|
24558
24596
|
const diffArgs = ["diff", "--name-only"];
|
|
@@ -24650,7 +24688,7 @@ var _orchestratorDeps, reviewOrchestrator;
|
|
|
24650
24688
|
var init_orchestrator = __esm(() => {
|
|
24651
24689
|
init_logger2();
|
|
24652
24690
|
init_runner2();
|
|
24653
|
-
_orchestratorDeps = { spawn:
|
|
24691
|
+
_orchestratorDeps = { spawn: spawn3 };
|
|
24654
24692
|
reviewOrchestrator = new ReviewOrchestrator;
|
|
24655
24693
|
});
|
|
24656
24694
|
|
|
@@ -25802,7 +25840,7 @@ async function addFileElements(elements, storyContext, story) {
|
|
|
25802
25840
|
if (contextFiles.length === 0 && storyContext.config?.context?.autoDetect?.enabled !== false && storyContext.workdir) {
|
|
25803
25841
|
const autoDetectConfig = storyContext.config?.context?.autoDetect;
|
|
25804
25842
|
try {
|
|
25805
|
-
const detected = await
|
|
25843
|
+
const detected = await _contextBuilderDeps.autoDetectContextFiles({
|
|
25806
25844
|
workdir: storyContext.workdir,
|
|
25807
25845
|
storyTitle: story.title,
|
|
25808
25846
|
maxFiles: autoDetectConfig?.maxFiles ?? 5,
|
|
@@ -25860,7 +25898,7 @@ ${content}
|
|
|
25860
25898
|
}
|
|
25861
25899
|
}
|
|
25862
25900
|
}
|
|
25863
|
-
var
|
|
25901
|
+
var _contextBuilderDeps;
|
|
25864
25902
|
var init_builder2 = __esm(() => {
|
|
25865
25903
|
init_logger2();
|
|
25866
25904
|
init_prd();
|
|
@@ -25869,7 +25907,7 @@ var init_builder2 = __esm(() => {
|
|
|
25869
25907
|
init_parent_context();
|
|
25870
25908
|
init_test_scanner();
|
|
25871
25909
|
init_elements();
|
|
25872
|
-
|
|
25910
|
+
_contextBuilderDeps = {
|
|
25873
25911
|
autoDetectContextFiles
|
|
25874
25912
|
};
|
|
25875
25913
|
});
|
|
@@ -26186,7 +26224,8 @@ async function verifyImplementerIsolation(workdir, beforeRef) {
|
|
|
26186
26224
|
}
|
|
26187
26225
|
var _isolationDeps, TEST_PATTERNS, SRC_PATTERNS;
|
|
26188
26226
|
var init_isolation = __esm(() => {
|
|
26189
|
-
|
|
26227
|
+
init_bun_deps();
|
|
26228
|
+
_isolationDeps = { spawn };
|
|
26190
26229
|
TEST_PATTERNS = [/^test\//, /^tests\//, /^__tests__\//, /\.spec\.\w+$/, /\.test\.\w+$/, /\.e2e-spec\.\w+$/];
|
|
26191
26230
|
SRC_PATTERNS = [/^src\//, /^lib\//, /^packages\//];
|
|
26192
26231
|
});
|
|
@@ -26374,12 +26413,69 @@ function buildTestCommand(baseCommand, options) {
|
|
|
26374
26413
|
}
|
|
26375
26414
|
var _executorDeps, DEFAULT_STRIP_ENV_VARS;
|
|
26376
26415
|
var init_executor = __esm(() => {
|
|
26377
|
-
|
|
26416
|
+
init_bun_deps();
|
|
26417
|
+
_executorDeps = { spawn };
|
|
26378
26418
|
DEFAULT_STRIP_ENV_VARS = ["CLAUDECODE", "REPL_ID", "AGENT"];
|
|
26379
26419
|
});
|
|
26380
26420
|
|
|
26381
26421
|
// src/verification/parser.ts
|
|
26382
26422
|
function parseBunTestOutput(output) {
|
|
26423
|
+
if (isJestLikeOutput(output)) {
|
|
26424
|
+
return parseJestOutput(output);
|
|
26425
|
+
}
|
|
26426
|
+
return parseBunOutput(output);
|
|
26427
|
+
}
|
|
26428
|
+
function isJestLikeOutput(output) {
|
|
26429
|
+
return /^\s*Tests:\s+\d+/m.test(output) || /^\s*Test Files\s+\d+/m.test(output);
|
|
26430
|
+
}
|
|
26431
|
+
function parseJestOutput(output) {
|
|
26432
|
+
const failures = [];
|
|
26433
|
+
let passed = 0;
|
|
26434
|
+
let failed = 0;
|
|
26435
|
+
const summaryMatches = Array.from(output.matchAll(/^\s*Tests:\s+(.*)/gm));
|
|
26436
|
+
if (summaryMatches.length > 0) {
|
|
26437
|
+
const summaryLine = summaryMatches[summaryMatches.length - 1][1];
|
|
26438
|
+
const failedMatch = summaryLine.match(/(\d+)\s+failed/);
|
|
26439
|
+
const passedMatch = summaryLine.match(/(\d+)\s+passed/);
|
|
26440
|
+
if (failedMatch)
|
|
26441
|
+
failed = Number.parseInt(failedMatch[1], 10);
|
|
26442
|
+
if (passedMatch)
|
|
26443
|
+
passed = Number.parseInt(passedMatch[1], 10);
|
|
26444
|
+
}
|
|
26445
|
+
let currentFile = "unknown";
|
|
26446
|
+
const lines = output.split(`
|
|
26447
|
+
`);
|
|
26448
|
+
for (let i = 0;i < lines.length; i++) {
|
|
26449
|
+
const line = lines[i];
|
|
26450
|
+
const fileMatch = line.match(/^\s*(?:FAIL|PASS)\s+(\S+\.[jt]sx?)/);
|
|
26451
|
+
if (fileMatch) {
|
|
26452
|
+
currentFile = fileMatch[1];
|
|
26453
|
+
continue;
|
|
26454
|
+
}
|
|
26455
|
+
const bulletMatch = line.match(/^\s+\u25CF\s+(.+)$/);
|
|
26456
|
+
if (bulletMatch) {
|
|
26457
|
+
const testName = bulletMatch[1].trim();
|
|
26458
|
+
let error48 = "";
|
|
26459
|
+
for (let j = i + 1;j < lines.length && j < i + 10; j++) {
|
|
26460
|
+
const next = lines[j].trim();
|
|
26461
|
+
if (!next)
|
|
26462
|
+
continue;
|
|
26463
|
+
if (next.startsWith("\u25CF") || /^(?:FAIL|PASS)\s/.test(next))
|
|
26464
|
+
break;
|
|
26465
|
+
error48 = next;
|
|
26466
|
+
break;
|
|
26467
|
+
}
|
|
26468
|
+
failures.push({
|
|
26469
|
+
file: currentFile,
|
|
26470
|
+
testName,
|
|
26471
|
+
error: error48 || "Unknown error",
|
|
26472
|
+
stackTrace: []
|
|
26473
|
+
});
|
|
26474
|
+
}
|
|
26475
|
+
}
|
|
26476
|
+
return { passed, failed, failures };
|
|
26477
|
+
}
|
|
26478
|
+
function parseBunOutput(output) {
|
|
26383
26479
|
const lines = output.split(`
|
|
26384
26480
|
`);
|
|
26385
26481
|
const failures = [];
|
|
@@ -26596,9 +26692,10 @@ async function regression(options) {
|
|
|
26596
26692
|
}
|
|
26597
26693
|
var _regressionRunnerDeps;
|
|
26598
26694
|
var init_runners = __esm(() => {
|
|
26695
|
+
init_bun_deps();
|
|
26599
26696
|
init_executor();
|
|
26600
26697
|
_regressionRunnerDeps = {
|
|
26601
|
-
sleep
|
|
26698
|
+
sleep
|
|
26602
26699
|
};
|
|
26603
26700
|
});
|
|
26604
26701
|
|
|
@@ -26722,9 +26819,10 @@ async function cleanupProcessTree(pid, gracePeriodMs = 3000) {
|
|
|
26722
26819
|
var _cleanupDeps;
|
|
26723
26820
|
var init_cleanup = __esm(() => {
|
|
26724
26821
|
init_logger2();
|
|
26822
|
+
init_bun_deps();
|
|
26725
26823
|
_cleanupDeps = {
|
|
26726
|
-
spawn
|
|
26727
|
-
sleep
|
|
26824
|
+
spawn,
|
|
26825
|
+
sleep,
|
|
26728
26826
|
kill: process.kill.bind(process)
|
|
26729
26827
|
};
|
|
26730
26828
|
});
|
|
@@ -29145,7 +29243,8 @@ ${stderr}`;
|
|
|
29145
29243
|
var _acceptanceDeps;
|
|
29146
29244
|
var init_acceptance3 = __esm(() => {
|
|
29147
29245
|
init_logger2();
|
|
29148
|
-
|
|
29246
|
+
init_bun_deps();
|
|
29247
|
+
_acceptanceDeps = { spawn };
|
|
29149
29248
|
});
|
|
29150
29249
|
|
|
29151
29250
|
// src/verification/strategies/regression.ts
|
|
@@ -29986,7 +30085,7 @@ __export(exports_init_context, {
|
|
|
29986
30085
|
initContext: () => initContext,
|
|
29987
30086
|
generatePackageContextTemplate: () => generatePackageContextTemplate,
|
|
29988
30087
|
generateContextTemplate: () => generateContextTemplate,
|
|
29989
|
-
|
|
30088
|
+
_initContextDeps: () => _initContextDeps
|
|
29990
30089
|
});
|
|
29991
30090
|
import { existsSync as existsSync20 } from "fs";
|
|
29992
30091
|
import { mkdir } from "fs/promises";
|
|
@@ -30195,7 +30294,7 @@ The context.md should include:
|
|
|
30195
30294
|
Keep it under 2000 tokens. Use markdown formatting. Be specific to the detected stack and structure.
|
|
30196
30295
|
`;
|
|
30197
30296
|
try {
|
|
30198
|
-
const result = await
|
|
30297
|
+
const result = await _initContextDeps.callLLM(prompt);
|
|
30199
30298
|
logger.info("init", "Generated context.md with LLM");
|
|
30200
30299
|
return result;
|
|
30201
30300
|
} catch (err) {
|
|
@@ -30260,10 +30359,10 @@ async function initContext(projectRoot, options = {}) {
|
|
|
30260
30359
|
await Bun.write(contextPath, content);
|
|
30261
30360
|
logger.info("init", "Generated .nax/context.md template from project scan", { path: contextPath });
|
|
30262
30361
|
}
|
|
30263
|
-
var
|
|
30362
|
+
var _initContextDeps;
|
|
30264
30363
|
var init_init_context = __esm(() => {
|
|
30265
30364
|
init_logger2();
|
|
30266
|
-
|
|
30365
|
+
_initContextDeps = {
|
|
30267
30366
|
callLLM: async (_prompt) => {
|
|
30268
30367
|
throw new Error("callLLM not implemented");
|
|
30269
30368
|
}
|
|
@@ -30979,7 +31078,7 @@ var init_checks_config = () => {};
|
|
|
30979
31078
|
async function checkAgentCLI(config2) {
|
|
30980
31079
|
const agent = config2.execution?.agent || "claude";
|
|
30981
31080
|
try {
|
|
30982
|
-
const proc =
|
|
31081
|
+
const proc = _checkCliDeps.spawn([agent, "--version"], {
|
|
30983
31082
|
stdout: "pipe",
|
|
30984
31083
|
stderr: "pipe"
|
|
30985
31084
|
});
|
|
@@ -31000,10 +31099,11 @@ async function checkAgentCLI(config2) {
|
|
|
31000
31099
|
};
|
|
31001
31100
|
}
|
|
31002
31101
|
}
|
|
31003
|
-
var
|
|
31102
|
+
var _checkCliDeps;
|
|
31004
31103
|
var init_checks_cli = __esm(() => {
|
|
31005
|
-
|
|
31006
|
-
|
|
31104
|
+
init_bun_deps();
|
|
31105
|
+
_checkCliDeps = {
|
|
31106
|
+
spawn
|
|
31007
31107
|
};
|
|
31008
31108
|
});
|
|
31009
31109
|
|
|
@@ -31596,19 +31696,19 @@ var init_precheck = __esm(() => {
|
|
|
31596
31696
|
});
|
|
31597
31697
|
|
|
31598
31698
|
// src/hooks/runner.ts
|
|
31599
|
-
import { join as
|
|
31699
|
+
import { join as join45 } from "path";
|
|
31600
31700
|
async function loadHooksConfig(projectDir, globalDir) {
|
|
31601
31701
|
let globalHooks = { hooks: {} };
|
|
31602
31702
|
let projectHooks = { hooks: {} };
|
|
31603
31703
|
let skipGlobal = false;
|
|
31604
|
-
const projectPath =
|
|
31704
|
+
const projectPath = join45(projectDir, "hooks.json");
|
|
31605
31705
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
31606
31706
|
if (projectData) {
|
|
31607
31707
|
projectHooks = projectData;
|
|
31608
31708
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
31609
31709
|
}
|
|
31610
31710
|
if (!skipGlobal && globalDir) {
|
|
31611
|
-
const globalPath =
|
|
31711
|
+
const globalPath = join45(globalDir, "hooks.json");
|
|
31612
31712
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
31613
31713
|
if (globalData) {
|
|
31614
31714
|
globalHooks = globalData;
|
|
@@ -32049,7 +32149,7 @@ __export(exports_acceptance_loop, {
|
|
|
32049
32149
|
isStubTestFile: () => isStubTestFile,
|
|
32050
32150
|
_acceptanceLoopDeps: () => _acceptanceLoopDeps
|
|
32051
32151
|
});
|
|
32052
|
-
import path14, { join as
|
|
32152
|
+
import path14, { join as join46 } from "path";
|
|
32053
32153
|
function isStubTestFile(content) {
|
|
32054
32154
|
return /expect\s*\(\s*true\s*\)\s*\.\s*toBe\s*\(\s*(?:false|true)\s*\)/.test(content);
|
|
32055
32155
|
}
|
|
@@ -32111,7 +32211,7 @@ async function executeFixStory(ctx, story, prd, iterations) {
|
|
|
32111
32211
|
agent: ctx.config.autoMode.defaultAgent,
|
|
32112
32212
|
iteration: iterations
|
|
32113
32213
|
}), ctx.workdir);
|
|
32114
|
-
const fixEffectiveConfig = story.workdir ? await loadConfigForWorkdir(
|
|
32214
|
+
const fixEffectiveConfig = story.workdir ? await loadConfigForWorkdir(join46(ctx.workdir, ".nax", "config.json"), story.workdir) : ctx.config;
|
|
32115
32215
|
const fixContext = {
|
|
32116
32216
|
config: ctx.config,
|
|
32117
32217
|
effectiveConfig: fixEffectiveConfig,
|
|
@@ -32714,12 +32814,12 @@ __export(exports_manager, {
|
|
|
32714
32814
|
WorktreeManager: () => WorktreeManager
|
|
32715
32815
|
});
|
|
32716
32816
|
import { existsSync as existsSync32, symlinkSync } from "fs";
|
|
32717
|
-
import { join as
|
|
32817
|
+
import { join as join47 } from "path";
|
|
32718
32818
|
|
|
32719
32819
|
class WorktreeManager {
|
|
32720
32820
|
async create(projectRoot, storyId) {
|
|
32721
32821
|
validateStoryId(storyId);
|
|
32722
|
-
const worktreePath =
|
|
32822
|
+
const worktreePath = join47(projectRoot, ".nax-wt", storyId);
|
|
32723
32823
|
const branchName = `nax/${storyId}`;
|
|
32724
32824
|
try {
|
|
32725
32825
|
const proc = _managerDeps.spawn(["git", "worktree", "add", worktreePath, "-b", branchName], {
|
|
@@ -32744,9 +32844,9 @@ class WorktreeManager {
|
|
|
32744
32844
|
}
|
|
32745
32845
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
32746
32846
|
}
|
|
32747
|
-
const nodeModulesSource =
|
|
32847
|
+
const nodeModulesSource = join47(projectRoot, "node_modules");
|
|
32748
32848
|
if (existsSync32(nodeModulesSource)) {
|
|
32749
|
-
const nodeModulesTarget =
|
|
32849
|
+
const nodeModulesTarget = join47(worktreePath, "node_modules");
|
|
32750
32850
|
try {
|
|
32751
32851
|
symlinkSync(nodeModulesSource, nodeModulesTarget, "dir");
|
|
32752
32852
|
} catch (error48) {
|
|
@@ -32754,9 +32854,9 @@ class WorktreeManager {
|
|
|
32754
32854
|
throw new Error(`Failed to symlink node_modules: ${errorMessage(error48)}`);
|
|
32755
32855
|
}
|
|
32756
32856
|
}
|
|
32757
|
-
const envSource =
|
|
32857
|
+
const envSource = join47(projectRoot, ".env");
|
|
32758
32858
|
if (existsSync32(envSource)) {
|
|
32759
|
-
const envTarget =
|
|
32859
|
+
const envTarget = join47(worktreePath, ".env");
|
|
32760
32860
|
try {
|
|
32761
32861
|
symlinkSync(envSource, envTarget, "file");
|
|
32762
32862
|
} catch (error48) {
|
|
@@ -32767,7 +32867,7 @@ class WorktreeManager {
|
|
|
32767
32867
|
}
|
|
32768
32868
|
async remove(projectRoot, storyId) {
|
|
32769
32869
|
validateStoryId(storyId);
|
|
32770
|
-
const worktreePath =
|
|
32870
|
+
const worktreePath = join47(projectRoot, ".nax-wt", storyId);
|
|
32771
32871
|
const branchName = `nax/${storyId}`;
|
|
32772
32872
|
try {
|
|
32773
32873
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -32857,8 +32957,9 @@ class WorktreeManager {
|
|
|
32857
32957
|
var _managerDeps;
|
|
32858
32958
|
var init_manager = __esm(() => {
|
|
32859
32959
|
init_logger2();
|
|
32960
|
+
init_bun_deps();
|
|
32860
32961
|
_managerDeps = {
|
|
32861
|
-
spawn
|
|
32962
|
+
spawn
|
|
32862
32963
|
};
|
|
32863
32964
|
});
|
|
32864
32965
|
|
|
@@ -33076,8 +33177,9 @@ ${stderr}`;
|
|
|
33076
33177
|
var _mergeDeps;
|
|
33077
33178
|
var init_merge = __esm(() => {
|
|
33078
33179
|
init_logger2();
|
|
33180
|
+
init_bun_deps();
|
|
33079
33181
|
_mergeDeps = {
|
|
33080
|
-
spawn
|
|
33182
|
+
spawn
|
|
33081
33183
|
};
|
|
33082
33184
|
});
|
|
33083
33185
|
|
|
@@ -33168,7 +33270,7 @@ var init_parallel_worker = __esm(() => {
|
|
|
33168
33270
|
|
|
33169
33271
|
// src/execution/parallel-coordinator.ts
|
|
33170
33272
|
import os3 from "os";
|
|
33171
|
-
import { join as
|
|
33273
|
+
import { join as join48 } from "path";
|
|
33172
33274
|
function groupStoriesByDependencies(stories) {
|
|
33173
33275
|
const batches = [];
|
|
33174
33276
|
const processed = new Set;
|
|
@@ -33247,7 +33349,7 @@ async function executeParallel(stories, prdPath, projectRoot, config2, hooks, pl
|
|
|
33247
33349
|
};
|
|
33248
33350
|
const worktreePaths = new Map;
|
|
33249
33351
|
for (const story of batch) {
|
|
33250
|
-
const worktreePath =
|
|
33352
|
+
const worktreePath = join48(projectRoot, ".nax-wt", story.id);
|
|
33251
33353
|
try {
|
|
33252
33354
|
await worktreeManager.create(projectRoot, story.id);
|
|
33253
33355
|
worktreePaths.set(story.id, worktreePath);
|
|
@@ -33296,7 +33398,7 @@ async function executeParallel(stories, prdPath, projectRoot, config2, hooks, pl
|
|
|
33296
33398
|
});
|
|
33297
33399
|
logger?.warn("parallel", "Worktree preserved for manual conflict resolution", {
|
|
33298
33400
|
storyId: mergeResult.storyId,
|
|
33299
|
-
worktreePath:
|
|
33401
|
+
worktreePath: join48(projectRoot, ".nax-wt", mergeResult.storyId)
|
|
33300
33402
|
});
|
|
33301
33403
|
}
|
|
33302
33404
|
}
|
|
@@ -33755,13 +33857,13 @@ var init_parallel_executor = __esm(() => {
|
|
|
33755
33857
|
|
|
33756
33858
|
// src/pipeline/subscribers/events-writer.ts
|
|
33757
33859
|
import { appendFile as appendFile2, mkdir as mkdir2 } from "fs/promises";
|
|
33758
|
-
import { homedir as
|
|
33759
|
-
import { basename as basename5, join as
|
|
33860
|
+
import { homedir as homedir6 } from "os";
|
|
33861
|
+
import { basename as basename5, join as join49 } from "path";
|
|
33760
33862
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
33761
33863
|
const logger = getSafeLogger();
|
|
33762
33864
|
const project = basename5(workdir);
|
|
33763
|
-
const eventsDir =
|
|
33764
|
-
const eventsFile =
|
|
33865
|
+
const eventsDir = join49(homedir6(), ".nax", "events", project);
|
|
33866
|
+
const eventsFile = join49(eventsDir, "events.jsonl");
|
|
33765
33867
|
let dirReady = false;
|
|
33766
33868
|
const write = (line) => {
|
|
33767
33869
|
(async () => {
|
|
@@ -33934,13 +34036,13 @@ var init_interaction2 = __esm(() => {
|
|
|
33934
34036
|
|
|
33935
34037
|
// src/pipeline/subscribers/registry.ts
|
|
33936
34038
|
import { mkdir as mkdir3, writeFile } from "fs/promises";
|
|
33937
|
-
import { homedir as
|
|
33938
|
-
import { basename as basename6, join as
|
|
34039
|
+
import { homedir as homedir7 } from "os";
|
|
34040
|
+
import { basename as basename6, join as join50 } from "path";
|
|
33939
34041
|
function wireRegistry(bus, feature, runId, workdir) {
|
|
33940
34042
|
const logger = getSafeLogger();
|
|
33941
34043
|
const project = basename6(workdir);
|
|
33942
|
-
const runDir =
|
|
33943
|
-
const metaFile =
|
|
34044
|
+
const runDir = join50(homedir7(), ".nax", "runs", `${project}-${feature}-${runId}`);
|
|
34045
|
+
const metaFile = join50(runDir, "meta.json");
|
|
33944
34046
|
const unsub = bus.on("run:started", (_ev) => {
|
|
33945
34047
|
(async () => {
|
|
33946
34048
|
try {
|
|
@@ -33950,8 +34052,8 @@ function wireRegistry(bus, feature, runId, workdir) {
|
|
|
33950
34052
|
project,
|
|
33951
34053
|
feature,
|
|
33952
34054
|
workdir,
|
|
33953
|
-
statusPath:
|
|
33954
|
-
eventsDir:
|
|
34055
|
+
statusPath: join50(workdir, ".nax", "features", feature, "status.json"),
|
|
34056
|
+
eventsDir: join50(workdir, ".nax", "features", feature, "runs"),
|
|
33955
34057
|
registeredAt: new Date().toISOString()
|
|
33956
34058
|
};
|
|
33957
34059
|
await writeFile(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -34095,7 +34197,7 @@ var init_reporters = __esm(() => {
|
|
|
34095
34197
|
});
|
|
34096
34198
|
|
|
34097
34199
|
// src/execution/deferred-review.ts
|
|
34098
|
-
var {spawn:
|
|
34200
|
+
var {spawn: spawn4 } = globalThis.Bun;
|
|
34099
34201
|
async function captureRunStartRef(workdir) {
|
|
34100
34202
|
try {
|
|
34101
34203
|
const proc = _deferredReviewDeps.spawn({
|
|
@@ -34163,7 +34265,7 @@ async function runDeferredReview(workdir, reviewConfig, plugins, runStartRef) {
|
|
|
34163
34265
|
}
|
|
34164
34266
|
var _deferredReviewDeps;
|
|
34165
34267
|
var init_deferred_review = __esm(() => {
|
|
34166
|
-
_deferredReviewDeps = { spawn:
|
|
34268
|
+
_deferredReviewDeps = { spawn: spawn4 };
|
|
34167
34269
|
});
|
|
34168
34270
|
|
|
34169
34271
|
// src/execution/dry-run.ts
|
|
@@ -34594,7 +34696,7 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
34594
34696
|
});
|
|
34595
34697
|
|
|
34596
34698
|
// src/execution/iteration-runner.ts
|
|
34597
|
-
import { join as
|
|
34699
|
+
import { join as join51 } from "path";
|
|
34598
34700
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
34599
34701
|
const logger = getSafeLogger();
|
|
34600
34702
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
@@ -34620,7 +34722,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
34620
34722
|
const storyStartTime = Date.now();
|
|
34621
34723
|
const storyGitRef = await captureGitRef(ctx.workdir);
|
|
34622
34724
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
34623
|
-
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(
|
|
34725
|
+
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join51(ctx.workdir, ".nax", "config.json"), story.workdir) : ctx.config;
|
|
34624
34726
|
const pipelineContext = {
|
|
34625
34727
|
config: ctx.config,
|
|
34626
34728
|
effectiveConfig,
|
|
@@ -34991,7 +35093,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
34991
35093
|
var init_status_file = () => {};
|
|
34992
35094
|
|
|
34993
35095
|
// src/execution/status-writer.ts
|
|
34994
|
-
import { join as
|
|
35096
|
+
import { join as join52 } from "path";
|
|
34995
35097
|
|
|
34996
35098
|
class StatusWriter {
|
|
34997
35099
|
statusFile;
|
|
@@ -35059,7 +35161,7 @@ class StatusWriter {
|
|
|
35059
35161
|
if (!this._prd)
|
|
35060
35162
|
return;
|
|
35061
35163
|
const safeLogger = getSafeLogger();
|
|
35062
|
-
const featureStatusPath =
|
|
35164
|
+
const featureStatusPath = join52(featureDir, "status.json");
|
|
35063
35165
|
try {
|
|
35064
35166
|
const base = this.getSnapshot(totalCost, iterations);
|
|
35065
35167
|
if (!base) {
|
|
@@ -35267,7 +35369,7 @@ __export(exports_run_initialization, {
|
|
|
35267
35369
|
initializeRun: () => initializeRun,
|
|
35268
35370
|
_reconcileDeps: () => _reconcileDeps
|
|
35269
35371
|
});
|
|
35270
|
-
import { join as
|
|
35372
|
+
import { join as join53 } from "path";
|
|
35271
35373
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
35272
35374
|
const logger = getSafeLogger();
|
|
35273
35375
|
let reconciledCount = 0;
|
|
@@ -35285,7 +35387,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
35285
35387
|
});
|
|
35286
35388
|
continue;
|
|
35287
35389
|
}
|
|
35288
|
-
const effectiveWorkdir = story.workdir ?
|
|
35390
|
+
const effectiveWorkdir = story.workdir ? join53(workdir, story.workdir) : workdir;
|
|
35289
35391
|
try {
|
|
35290
35392
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
35291
35393
|
if (!reviewResult.success) {
|
|
@@ -66419,8 +66521,8 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
66419
66521
|
// bin/nax.ts
|
|
66420
66522
|
init_source();
|
|
66421
66523
|
import { existsSync as existsSync34, mkdirSync as mkdirSync6 } from "fs";
|
|
66422
|
-
import { homedir as
|
|
66423
|
-
import { join as
|
|
66524
|
+
import { homedir as homedir9 } from "os";
|
|
66525
|
+
import { join as join55 } from "path";
|
|
66424
66526
|
|
|
66425
66527
|
// node_modules/commander/esm.mjs
|
|
66426
66528
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -67617,7 +67719,7 @@ function validatePlanOutput(raw, feature, branch) {
|
|
|
67617
67719
|
}
|
|
67618
67720
|
|
|
67619
67721
|
// src/cli/plan.ts
|
|
67620
|
-
var
|
|
67722
|
+
var _planDeps = {
|
|
67621
67723
|
readFile: (path) => Bun.file(path).text(),
|
|
67622
67724
|
writeFile: (path, content) => Bun.write(path, content).then(() => {}),
|
|
67623
67725
|
scanCodebase: (workdir) => scanCodebase(workdir),
|
|
@@ -67640,30 +67742,30 @@ async function planCommand(workdir, config2, options) {
|
|
|
67640
67742
|
}
|
|
67641
67743
|
const logger = getLogger();
|
|
67642
67744
|
logger?.info("plan", "Reading spec", { from: options.from });
|
|
67643
|
-
const specContent = await
|
|
67745
|
+
const specContent = await _planDeps.readFile(options.from);
|
|
67644
67746
|
logger?.info("plan", "Scanning codebase...");
|
|
67645
67747
|
const [scan, discoveredPackages, pkg] = await Promise.all([
|
|
67646
|
-
|
|
67647
|
-
|
|
67648
|
-
|
|
67748
|
+
_planDeps.scanCodebase(workdir),
|
|
67749
|
+
_planDeps.discoverWorkspacePackages(workdir),
|
|
67750
|
+
_planDeps.readPackageJson(workdir)
|
|
67649
67751
|
]);
|
|
67650
67752
|
const codebaseContext = buildCodebaseContext2(scan);
|
|
67651
67753
|
const relativePackages = discoveredPackages.map((p) => p.startsWith("/") ? p.replace(`${workdir}/`, "") : p);
|
|
67652
67754
|
const packageDetails = relativePackages.length > 0 ? await Promise.all(relativePackages.map(async (rel) => {
|
|
67653
|
-
const pkgJson = await
|
|
67755
|
+
const pkgJson = await _planDeps.readPackageJsonAt(join11(workdir, rel, "package.json"));
|
|
67654
67756
|
return buildPackageSummary(rel, pkgJson);
|
|
67655
67757
|
})) : [];
|
|
67656
67758
|
const projectName = detectProjectName(workdir, pkg);
|
|
67657
67759
|
const branchName = options.branch ?? `feat/${options.feature}`;
|
|
67658
67760
|
const outputDir = join11(naxDir, "features", options.feature);
|
|
67659
67761
|
const outputPath = join11(outputDir, "prd.json");
|
|
67660
|
-
await
|
|
67762
|
+
await _planDeps.mkdirp(outputDir);
|
|
67661
67763
|
const agentName = config2?.autoMode?.defaultAgent ?? "claude";
|
|
67662
67764
|
const timeoutSeconds = config2?.execution?.sessionTimeoutSeconds ?? 600;
|
|
67663
67765
|
let rawResponse;
|
|
67664
67766
|
if (options.auto) {
|
|
67665
67767
|
const prompt = buildPlanningPrompt(specContent, codebaseContext, undefined, relativePackages, packageDetails);
|
|
67666
|
-
const cliAdapter =
|
|
67768
|
+
const cliAdapter = _planDeps.getAgent(agentName);
|
|
67667
67769
|
if (!cliAdapter)
|
|
67668
67770
|
throw new Error(`[plan] No agent adapter found for '${agentName}'`);
|
|
67669
67771
|
let autoModel;
|
|
@@ -67684,10 +67786,10 @@ async function planCommand(workdir, config2, options) {
|
|
|
67684
67786
|
} catch {}
|
|
67685
67787
|
} else {
|
|
67686
67788
|
const prompt = buildPlanningPrompt(specContent, codebaseContext, outputPath, relativePackages, packageDetails);
|
|
67687
|
-
const adapter =
|
|
67789
|
+
const adapter = _planDeps.getAgent(agentName, config2);
|
|
67688
67790
|
if (!adapter)
|
|
67689
67791
|
throw new Error(`[plan] No agent adapter found for '${agentName}'`);
|
|
67690
|
-
const interactionBridge =
|
|
67792
|
+
const interactionBridge = _planDeps.createInteractionBridge();
|
|
67691
67793
|
const pidRegistry = new PidRegistry(workdir);
|
|
67692
67794
|
const resolvedPerm = resolvePermissions(config2, "plan");
|
|
67693
67795
|
const resolvedModel = config2?.plan?.model ?? "balanced";
|
|
@@ -67718,14 +67820,14 @@ async function planCommand(workdir, config2, options) {
|
|
|
67718
67820
|
await pidRegistry.killAll().catch(() => {});
|
|
67719
67821
|
logger?.info("plan", "Interactive session ended", { durationMs: Date.now() - planStartTime });
|
|
67720
67822
|
}
|
|
67721
|
-
if (!
|
|
67823
|
+
if (!_planDeps.existsSync(outputPath)) {
|
|
67722
67824
|
throw new Error(`[plan] Agent did not write PRD to ${outputPath}. Check agent logs for errors.`);
|
|
67723
67825
|
}
|
|
67724
|
-
rawResponse = await
|
|
67826
|
+
rawResponse = await _planDeps.readFile(outputPath);
|
|
67725
67827
|
}
|
|
67726
67828
|
const finalPrd = validatePlanOutput(rawResponse, options.feature, branchName);
|
|
67727
67829
|
finalPrd.project = projectName;
|
|
67728
|
-
await
|
|
67830
|
+
await _planDeps.writeFile(outputPath, JSON.stringify(finalPrd, null, 2));
|
|
67729
67831
|
logger?.info("plan", "[OK] PRD written", { outputPath });
|
|
67730
67832
|
return outputPath;
|
|
67731
67833
|
}
|
|
@@ -67756,7 +67858,7 @@ function detectProjectName(workdir, pkg) {
|
|
|
67756
67858
|
if (pkg?.name && typeof pkg.name === "string") {
|
|
67757
67859
|
return pkg.name;
|
|
67758
67860
|
}
|
|
67759
|
-
const result =
|
|
67861
|
+
const result = _planDeps.spawnSync(["git", "remote", "get-url", "origin"], { cwd: workdir });
|
|
67760
67862
|
if (result.exitCode === 0) {
|
|
67761
67863
|
const url2 = result.stdout.toString().trim();
|
|
67762
67864
|
const match = url2.match(/\/([^/]+?)(?:\.git)?$/);
|
|
@@ -69527,6 +69629,7 @@ var FIELD_DESCRIPTIONS = {
|
|
|
69527
69629
|
"quality.commands.typecheck": "Custom typecheck command",
|
|
69528
69630
|
"quality.commands.lint": "Custom lint command",
|
|
69529
69631
|
"quality.commands.test": "Custom test command",
|
|
69632
|
+
"quality.commands.build": "Custom build command",
|
|
69530
69633
|
"quality.forceExit": "Append --forceExit to test command (prevents hangs)",
|
|
69531
69634
|
"quality.detectOpenHandles": "Append --detectOpenHandles on timeout",
|
|
69532
69635
|
"quality.detectOpenHandlesRetries": "Max retries with --detectOpenHandles",
|
|
@@ -69534,7 +69637,6 @@ var FIELD_DESCRIPTIONS = {
|
|
|
69534
69637
|
"quality.drainTimeoutMs": "Deadline in ms to drain stdout/stderr after kill",
|
|
69535
69638
|
"quality.shell": "Shell to use for verification commands",
|
|
69536
69639
|
"quality.stripEnvVars": "Environment variables to strip during verification",
|
|
69537
|
-
"quality.environmentalEscalationDivisor": "Divisor for environmental failure early escalation",
|
|
69538
69640
|
tdd: "Test-driven development configuration",
|
|
69539
69641
|
"tdd.maxRetries": "Max retries per TDD session before escalating",
|
|
69540
69642
|
"tdd.autoVerifyIsolation": "Auto-verify test isolation between sessions",
|
|
@@ -69559,11 +69661,12 @@ var FIELD_DESCRIPTIONS = {
|
|
|
69559
69661
|
"analyze.maxCodebaseSummaryTokens": "Max tokens for codebase summary",
|
|
69560
69662
|
review: "Review phase configuration",
|
|
69561
69663
|
"review.enabled": "Enable review phase",
|
|
69562
|
-
"review.checks": "List of checks to run (typecheck, lint, test)",
|
|
69664
|
+
"review.checks": "List of checks to run (typecheck, lint, test, build)",
|
|
69563
69665
|
"review.commands": "Custom commands per check",
|
|
69564
69666
|
"review.commands.typecheck": "Custom typecheck command for review",
|
|
69565
69667
|
"review.commands.lint": "Custom lint command for review",
|
|
69566
69668
|
"review.commands.test": "Custom test command for review",
|
|
69669
|
+
"review.commands.build": "Custom build command for review",
|
|
69567
69670
|
plan: "Planning phase configuration",
|
|
69568
69671
|
"plan.model": "Model tier for planning",
|
|
69569
69672
|
"plan.outputPath": "Output path for generated spec (relative to nax/)",
|
|
@@ -69940,24 +70043,32 @@ async function diagnose(options) {
|
|
|
69940
70043
|
|
|
69941
70044
|
// src/commands/logs.ts
|
|
69942
70045
|
import { existsSync as existsSync26 } from "fs";
|
|
69943
|
-
import { join as
|
|
70046
|
+
import { join as join41 } from "path";
|
|
69944
70047
|
|
|
69945
70048
|
// src/commands/logs-formatter.ts
|
|
69946
70049
|
init_source();
|
|
69947
70050
|
init_formatter();
|
|
69948
70051
|
import { readdirSync as readdirSync7 } from "fs";
|
|
69949
|
-
import { join as
|
|
70052
|
+
import { join as join40 } from "path";
|
|
69950
70053
|
|
|
69951
70054
|
// src/commands/logs-reader.ts
|
|
69952
70055
|
import { existsSync as existsSync25, readdirSync as readdirSync6 } from "fs";
|
|
69953
70056
|
import { readdir as readdir3 } from "fs/promises";
|
|
70057
|
+
import { join as join39 } from "path";
|
|
70058
|
+
|
|
70059
|
+
// src/utils/paths.ts
|
|
69954
70060
|
import { homedir as homedir5 } from "os";
|
|
69955
70061
|
import { join as join38 } from "path";
|
|
69956
|
-
|
|
69957
|
-
|
|
70062
|
+
function getRunsDir() {
|
|
70063
|
+
return process.env.NAX_RUNS_DIR ?? join38(homedir5(), ".nax", "runs");
|
|
70064
|
+
}
|
|
70065
|
+
|
|
70066
|
+
// src/commands/logs-reader.ts
|
|
70067
|
+
var _logsReaderDeps = {
|
|
70068
|
+
getRunsDir
|
|
69958
70069
|
};
|
|
69959
70070
|
async function resolveRunFileFromRegistry(runId) {
|
|
69960
|
-
const runsDir =
|
|
70071
|
+
const runsDir = _logsReaderDeps.getRunsDir();
|
|
69961
70072
|
let entries;
|
|
69962
70073
|
try {
|
|
69963
70074
|
entries = await readdir3(runsDir);
|
|
@@ -69966,7 +70077,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
69966
70077
|
}
|
|
69967
70078
|
let matched = null;
|
|
69968
70079
|
for (const entry of entries) {
|
|
69969
|
-
const metaPath =
|
|
70080
|
+
const metaPath = join39(runsDir, entry, "meta.json");
|
|
69970
70081
|
try {
|
|
69971
70082
|
const meta3 = await Bun.file(metaPath).json();
|
|
69972
70083
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -69988,14 +70099,14 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
69988
70099
|
return null;
|
|
69989
70100
|
}
|
|
69990
70101
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
69991
|
-
return
|
|
70102
|
+
return join39(matched.eventsDir, specificFile ?? files[0]);
|
|
69992
70103
|
}
|
|
69993
70104
|
async function selectRunFile(runsDir) {
|
|
69994
70105
|
const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
69995
70106
|
if (files.length === 0) {
|
|
69996
70107
|
return null;
|
|
69997
70108
|
}
|
|
69998
|
-
return
|
|
70109
|
+
return join39(runsDir, files[0]);
|
|
69999
70110
|
}
|
|
70000
70111
|
async function extractRunSummary(filePath) {
|
|
70001
70112
|
const file2 = Bun.file(filePath);
|
|
@@ -70080,7 +70191,7 @@ Runs:
|
|
|
70080
70191
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
70081
70192
|
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"));
|
|
70082
70193
|
for (const file2 of files) {
|
|
70083
|
-
const filePath =
|
|
70194
|
+
const filePath = join40(runsDir, file2);
|
|
70084
70195
|
const summary = await extractRunSummary(filePath);
|
|
70085
70196
|
const timestamp = file2.replace(".jsonl", "");
|
|
70086
70197
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -70205,7 +70316,7 @@ async function logsCommand(options) {
|
|
|
70205
70316
|
return;
|
|
70206
70317
|
}
|
|
70207
70318
|
const resolved = resolveProject({ dir: options.dir });
|
|
70208
|
-
const naxDir =
|
|
70319
|
+
const naxDir = join41(resolved.projectDir, ".nax");
|
|
70209
70320
|
const configPath = resolved.configPath;
|
|
70210
70321
|
const configFile = Bun.file(configPath);
|
|
70211
70322
|
const config2 = await configFile.json();
|
|
@@ -70213,8 +70324,8 @@ async function logsCommand(options) {
|
|
|
70213
70324
|
if (!featureName) {
|
|
70214
70325
|
throw new Error("No feature specified in config.json");
|
|
70215
70326
|
}
|
|
70216
|
-
const featureDir =
|
|
70217
|
-
const runsDir =
|
|
70327
|
+
const featureDir = join41(naxDir, "features", featureName);
|
|
70328
|
+
const runsDir = join41(featureDir, "runs");
|
|
70218
70329
|
if (!existsSync26(runsDir)) {
|
|
70219
70330
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
70220
70331
|
}
|
|
@@ -70239,7 +70350,7 @@ init_config();
|
|
|
70239
70350
|
init_prd();
|
|
70240
70351
|
init_precheck();
|
|
70241
70352
|
import { existsSync as existsSync31 } from "fs";
|
|
70242
|
-
import { join as
|
|
70353
|
+
import { join as join42 } from "path";
|
|
70243
70354
|
async function precheckCommand(options) {
|
|
70244
70355
|
const resolved = resolveProject({
|
|
70245
70356
|
dir: options.dir,
|
|
@@ -70255,9 +70366,9 @@ async function precheckCommand(options) {
|
|
|
70255
70366
|
process.exit(1);
|
|
70256
70367
|
}
|
|
70257
70368
|
}
|
|
70258
|
-
const naxDir =
|
|
70259
|
-
const featureDir =
|
|
70260
|
-
const prdPath =
|
|
70369
|
+
const naxDir = join42(resolved.projectDir, ".nax");
|
|
70370
|
+
const featureDir = join42(naxDir, "features", featureName);
|
|
70371
|
+
const prdPath = join42(featureDir, "prd.json");
|
|
70261
70372
|
if (!existsSync31(featureDir)) {
|
|
70262
70373
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
70263
70374
|
process.exit(1);
|
|
@@ -70280,11 +70391,10 @@ async function precheckCommand(options) {
|
|
|
70280
70391
|
// src/commands/runs.ts
|
|
70281
70392
|
init_source();
|
|
70282
70393
|
import { readdir as readdir4 } from "fs/promises";
|
|
70283
|
-
import {
|
|
70284
|
-
import { join as join42 } from "path";
|
|
70394
|
+
import { join as join43 } from "path";
|
|
70285
70395
|
var DEFAULT_LIMIT = 20;
|
|
70286
|
-
var
|
|
70287
|
-
getRunsDir
|
|
70396
|
+
var _runsCmdDeps = {
|
|
70397
|
+
getRunsDir
|
|
70288
70398
|
};
|
|
70289
70399
|
function formatDuration3(ms) {
|
|
70290
70400
|
if (ms <= 0)
|
|
@@ -70326,7 +70436,7 @@ function pad3(str, width) {
|
|
|
70326
70436
|
return str + " ".repeat(padding);
|
|
70327
70437
|
}
|
|
70328
70438
|
async function runsCommand(options = {}) {
|
|
70329
|
-
const runsDir =
|
|
70439
|
+
const runsDir = _runsCmdDeps.getRunsDir();
|
|
70330
70440
|
let entries;
|
|
70331
70441
|
try {
|
|
70332
70442
|
entries = await readdir4(runsDir);
|
|
@@ -70336,7 +70446,7 @@ async function runsCommand(options = {}) {
|
|
|
70336
70446
|
}
|
|
70337
70447
|
const rows = [];
|
|
70338
70448
|
for (const entry of entries) {
|
|
70339
|
-
const metaPath =
|
|
70449
|
+
const metaPath = join43(runsDir, entry, "meta.json");
|
|
70340
70450
|
let meta3;
|
|
70341
70451
|
try {
|
|
70342
70452
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -70413,7 +70523,7 @@ async function runsCommand(options = {}) {
|
|
|
70413
70523
|
|
|
70414
70524
|
// src/commands/unlock.ts
|
|
70415
70525
|
init_source();
|
|
70416
|
-
import { join as
|
|
70526
|
+
import { join as join44 } from "path";
|
|
70417
70527
|
function isProcessAlive3(pid) {
|
|
70418
70528
|
try {
|
|
70419
70529
|
process.kill(pid, 0);
|
|
@@ -70428,7 +70538,7 @@ function formatLockAge(ageMs) {
|
|
|
70428
70538
|
}
|
|
70429
70539
|
async function unlockCommand(options) {
|
|
70430
70540
|
const workdir = options.dir ?? process.cwd();
|
|
70431
|
-
const lockPath =
|
|
70541
|
+
const lockPath = join44(workdir, "nax.lock");
|
|
70432
70542
|
const lockFile = Bun.file(lockPath);
|
|
70433
70543
|
const exists = await lockFile.exists();
|
|
70434
70544
|
if (!exists) {
|
|
@@ -78267,15 +78377,15 @@ Next: nax generate --package ${options.package}`));
|
|
|
78267
78377
|
}
|
|
78268
78378
|
return;
|
|
78269
78379
|
}
|
|
78270
|
-
const naxDir =
|
|
78380
|
+
const naxDir = join55(workdir, "nax");
|
|
78271
78381
|
if (existsSync34(naxDir) && !options.force) {
|
|
78272
78382
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
78273
78383
|
return;
|
|
78274
78384
|
}
|
|
78275
|
-
mkdirSync6(
|
|
78276
|
-
mkdirSync6(
|
|
78277
|
-
await Bun.write(
|
|
78278
|
-
await Bun.write(
|
|
78385
|
+
mkdirSync6(join55(naxDir, "features"), { recursive: true });
|
|
78386
|
+
mkdirSync6(join55(naxDir, "hooks"), { recursive: true });
|
|
78387
|
+
await Bun.write(join55(naxDir, "config.json"), JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
78388
|
+
await Bun.write(join55(naxDir, "hooks.json"), JSON.stringify({
|
|
78279
78389
|
hooks: {
|
|
78280
78390
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
78281
78391
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -78283,12 +78393,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
78283
78393
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
78284
78394
|
}
|
|
78285
78395
|
}, null, 2));
|
|
78286
|
-
await Bun.write(
|
|
78396
|
+
await Bun.write(join55(naxDir, ".gitignore"), `# nax temp files
|
|
78287
78397
|
*.tmp
|
|
78288
78398
|
.paused.json
|
|
78289
78399
|
.nax-verifier-verdict.json
|
|
78290
78400
|
`);
|
|
78291
|
-
await Bun.write(
|
|
78401
|
+
await Bun.write(join55(naxDir, "context.md"), `# Project Context
|
|
78292
78402
|
|
|
78293
78403
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
78294
78404
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -78414,8 +78524,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
78414
78524
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
78415
78525
|
process.exit(1);
|
|
78416
78526
|
}
|
|
78417
|
-
const featureDir =
|
|
78418
|
-
const prdPath =
|
|
78527
|
+
const featureDir = join55(naxDir, "features", options.feature);
|
|
78528
|
+
const prdPath = join55(featureDir, "prd.json");
|
|
78419
78529
|
if (options.plan && options.from) {
|
|
78420
78530
|
if (existsSync34(prdPath) && !options.force) {
|
|
78421
78531
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
@@ -78437,10 +78547,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
78437
78547
|
}
|
|
78438
78548
|
}
|
|
78439
78549
|
try {
|
|
78440
|
-
const planLogDir =
|
|
78550
|
+
const planLogDir = join55(featureDir, "plan");
|
|
78441
78551
|
mkdirSync6(planLogDir, { recursive: true });
|
|
78442
78552
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
78443
|
-
const planLogPath =
|
|
78553
|
+
const planLogPath = join55(planLogDir, `${planLogId}.jsonl`);
|
|
78444
78554
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
78445
78555
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
78446
78556
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -78478,10 +78588,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
78478
78588
|
process.exit(1);
|
|
78479
78589
|
}
|
|
78480
78590
|
resetLogger();
|
|
78481
|
-
const runsDir =
|
|
78591
|
+
const runsDir = join55(featureDir, "runs");
|
|
78482
78592
|
mkdirSync6(runsDir, { recursive: true });
|
|
78483
78593
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
78484
|
-
const logFilePath =
|
|
78594
|
+
const logFilePath = join55(runsDir, `${runId}.jsonl`);
|
|
78485
78595
|
const isTTY = process.stdout.isTTY ?? false;
|
|
78486
78596
|
const headlessFlag = options.headless ?? false;
|
|
78487
78597
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -78497,7 +78607,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
78497
78607
|
config2.autoMode.defaultAgent = options.agent;
|
|
78498
78608
|
}
|
|
78499
78609
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
78500
|
-
const globalNaxDir =
|
|
78610
|
+
const globalNaxDir = join55(homedir9(), ".nax");
|
|
78501
78611
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
78502
78612
|
const eventEmitter = new PipelineEventEmitter;
|
|
78503
78613
|
let tuiInstance;
|
|
@@ -78520,7 +78630,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
78520
78630
|
} else {
|
|
78521
78631
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
78522
78632
|
}
|
|
78523
|
-
const statusFilePath =
|
|
78633
|
+
const statusFilePath = join55(workdir, "nax", "status.json");
|
|
78524
78634
|
let parallel;
|
|
78525
78635
|
if (options.parallel !== undefined) {
|
|
78526
78636
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -78546,7 +78656,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
78546
78656
|
headless: useHeadless,
|
|
78547
78657
|
skipPrecheck: options.skipPrecheck ?? false
|
|
78548
78658
|
});
|
|
78549
|
-
const latestSymlink =
|
|
78659
|
+
const latestSymlink = join55(runsDir, "latest.jsonl");
|
|
78550
78660
|
try {
|
|
78551
78661
|
if (existsSync34(latestSymlink)) {
|
|
78552
78662
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
@@ -78584,9 +78694,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
78584
78694
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
78585
78695
|
process.exit(1);
|
|
78586
78696
|
}
|
|
78587
|
-
const featureDir =
|
|
78697
|
+
const featureDir = join55(naxDir, "features", name);
|
|
78588
78698
|
mkdirSync6(featureDir, { recursive: true });
|
|
78589
|
-
await Bun.write(
|
|
78699
|
+
await Bun.write(join55(featureDir, "spec.md"), `# Feature: ${name}
|
|
78590
78700
|
|
|
78591
78701
|
## Overview
|
|
78592
78702
|
|
|
@@ -78619,7 +78729,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
78619
78729
|
|
|
78620
78730
|
<!-- What this feature explicitly does NOT cover. -->
|
|
78621
78731
|
`);
|
|
78622
|
-
await Bun.write(
|
|
78732
|
+
await Bun.write(join55(featureDir, "progress.txt"), `# Progress: ${name}
|
|
78623
78733
|
|
|
78624
78734
|
Created: ${new Date().toISOString()}
|
|
78625
78735
|
|
|
@@ -78645,7 +78755,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
78645
78755
|
console.error(source_default.red("nax not initialized."));
|
|
78646
78756
|
process.exit(1);
|
|
78647
78757
|
}
|
|
78648
|
-
const featuresDir =
|
|
78758
|
+
const featuresDir = join55(naxDir, "features");
|
|
78649
78759
|
if (!existsSync34(featuresDir)) {
|
|
78650
78760
|
console.log(source_default.dim("No features yet."));
|
|
78651
78761
|
return;
|
|
@@ -78660,7 +78770,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
78660
78770
|
Features:
|
|
78661
78771
|
`));
|
|
78662
78772
|
for (const name of entries) {
|
|
78663
|
-
const prdPath =
|
|
78773
|
+
const prdPath = join55(featuresDir, name, "prd.json");
|
|
78664
78774
|
if (existsSync34(prdPath)) {
|
|
78665
78775
|
const prd = await loadPRD(prdPath);
|
|
78666
78776
|
const c = countStories(prd);
|
|
@@ -78691,10 +78801,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
78691
78801
|
process.exit(1);
|
|
78692
78802
|
}
|
|
78693
78803
|
const config2 = await loadConfig(workdir);
|
|
78694
|
-
const featureLogDir =
|
|
78804
|
+
const featureLogDir = join55(naxDir, "features", options.feature, "plan");
|
|
78695
78805
|
mkdirSync6(featureLogDir, { recursive: true });
|
|
78696
78806
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
78697
|
-
const planLogPath =
|
|
78807
|
+
const planLogPath = join55(featureLogDir, `${planLogId}.jsonl`);
|
|
78698
78808
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
78699
78809
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
78700
78810
|
try {
|
|
@@ -78731,7 +78841,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
78731
78841
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
78732
78842
|
process.exit(1);
|
|
78733
78843
|
}
|
|
78734
|
-
const featureDir =
|
|
78844
|
+
const featureDir = join55(naxDir, "features", options.feature);
|
|
78735
78845
|
if (!existsSync34(featureDir)) {
|
|
78736
78846
|
console.error(source_default.red(`Feature "${options.feature}" not found.`));
|
|
78737
78847
|
process.exit(1);
|
|
@@ -78747,7 +78857,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
78747
78857
|
specPath: options.from,
|
|
78748
78858
|
reclassify: options.reclassify
|
|
78749
78859
|
});
|
|
78750
|
-
const prdPath =
|
|
78860
|
+
const prdPath = join55(featureDir, "prd.json");
|
|
78751
78861
|
await Bun.write(prdPath, JSON.stringify(prd, null, 2));
|
|
78752
78862
|
const c = countStories(prd);
|
|
78753
78863
|
console.log(source_default.green(`
|