@nathapp/nax 0.42.2 → 0.42.3
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 +35 -19
- package/package.json +1 -1
- package/src/agents/acp/adapter.ts +11 -6
- package/src/agents/acp/spawn-client.ts +0 -2
- package/src/cli/plan.ts +22 -12
package/dist/nax.js
CHANGED
|
@@ -18054,6 +18054,13 @@ var init_defaults = __esm(() => {
|
|
|
18054
18054
|
});
|
|
18055
18055
|
|
|
18056
18056
|
// src/config/schema.ts
|
|
18057
|
+
var exports_schema = {};
|
|
18058
|
+
__export(exports_schema, {
|
|
18059
|
+
resolveModel: () => resolveModel,
|
|
18060
|
+
NaxConfigSchema: () => NaxConfigSchema,
|
|
18061
|
+
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
18062
|
+
AcceptanceConfigSchema: () => AcceptanceConfigSchema
|
|
18063
|
+
});
|
|
18057
18064
|
var init_schema = __esm(() => {
|
|
18058
18065
|
init_types3();
|
|
18059
18066
|
init_schemas3();
|
|
@@ -18968,8 +18975,6 @@ class SpawnAcpSession {
|
|
|
18968
18975
|
"--cwd",
|
|
18969
18976
|
this.cwd,
|
|
18970
18977
|
...this.permissionMode === "approve-all" ? ["--approve-all"] : [],
|
|
18971
|
-
"--format",
|
|
18972
|
-
"json",
|
|
18973
18978
|
"--model",
|
|
18974
18979
|
this.model,
|
|
18975
18980
|
"--timeout",
|
|
@@ -19500,10 +19505,15 @@ class AcpAgentAdapter {
|
|
|
19500
19505
|
async plan(options) {
|
|
19501
19506
|
let modelDef = options.modelDef;
|
|
19502
19507
|
if (!modelDef && options.config?.models) {
|
|
19503
|
-
const
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
19508
|
+
const tier = options.modelTier ?? "balanced";
|
|
19509
|
+
const { resolveModel: resolveModel2 } = await Promise.resolve().then(() => (init_schema(), exports_schema));
|
|
19510
|
+
const models = options.config.models;
|
|
19511
|
+
const entry = models[tier] ?? models.balanced;
|
|
19512
|
+
if (entry) {
|
|
19513
|
+
try {
|
|
19514
|
+
modelDef = resolveModel2(entry);
|
|
19515
|
+
} catch {}
|
|
19516
|
+
}
|
|
19507
19517
|
}
|
|
19508
19518
|
modelDef ??= { provider: "anthropic", model: "claude-sonnet-4-5-20250514" };
|
|
19509
19519
|
const timeoutSeconds = options.timeoutSeconds ?? options.config?.execution?.sessionTimeoutSeconds ?? 600;
|
|
@@ -21849,7 +21859,7 @@ var package_default;
|
|
|
21849
21859
|
var init_package = __esm(() => {
|
|
21850
21860
|
package_default = {
|
|
21851
21861
|
name: "@nathapp/nax",
|
|
21852
|
-
version: "0.42.
|
|
21862
|
+
version: "0.42.3",
|
|
21853
21863
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
21854
21864
|
type: "module",
|
|
21855
21865
|
bin: {
|
|
@@ -21922,8 +21932,8 @@ var init_version = __esm(() => {
|
|
|
21922
21932
|
NAX_VERSION = package_default.version;
|
|
21923
21933
|
NAX_COMMIT = (() => {
|
|
21924
21934
|
try {
|
|
21925
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
21926
|
-
return "
|
|
21935
|
+
if (/^[0-9a-f]{6,10}$/.test("b051dcb"))
|
|
21936
|
+
return "b051dcb";
|
|
21927
21937
|
} catch {}
|
|
21928
21938
|
try {
|
|
21929
21939
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -65743,7 +65753,8 @@ var _deps2 = {
|
|
|
65743
65753
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
|
|
65744
65754
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
65745
65755
|
},
|
|
65746
|
-
mkdirp: (path) => Bun.spawn(["mkdir", "-p", path]).exited.then(() => {})
|
|
65756
|
+
mkdirp: (path) => Bun.spawn(["mkdir", "-p", path]).exited.then(() => {}),
|
|
65757
|
+
existsSync: (path) => existsSync9(path)
|
|
65747
65758
|
};
|
|
65748
65759
|
async function planCommand(workdir, config2, options) {
|
|
65749
65760
|
const naxDir = join10(workdir, "nax");
|
|
@@ -65759,11 +65770,14 @@ async function planCommand(workdir, config2, options) {
|
|
|
65759
65770
|
const pkg = await _deps2.readPackageJson(workdir);
|
|
65760
65771
|
const projectName = detectProjectName(workdir, pkg);
|
|
65761
65772
|
const branchName = options.branch ?? `feat/${options.feature}`;
|
|
65762
|
-
const
|
|
65773
|
+
const outputDir = join10(naxDir, "features", options.feature);
|
|
65774
|
+
const outputPath = join10(outputDir, "prd.json");
|
|
65775
|
+
await _deps2.mkdirp(outputDir);
|
|
65763
65776
|
const agentName = config2?.autoMode?.defaultAgent ?? "claude";
|
|
65764
65777
|
const timeoutSeconds = config2?.execution?.sessionTimeoutSeconds ?? 600;
|
|
65765
65778
|
let rawResponse;
|
|
65766
65779
|
if (options.auto) {
|
|
65780
|
+
const prompt = buildPlanningPrompt(specContent, codebaseContext);
|
|
65767
65781
|
const cliAdapter = _deps2.getAgent(agentName);
|
|
65768
65782
|
if (!cliAdapter)
|
|
65769
65783
|
throw new Error(`[plan] No agent adapter found for '${agentName}'`);
|
|
@@ -65775,33 +65789,34 @@ async function planCommand(workdir, config2, options) {
|
|
|
65775
65789
|
}
|
|
65776
65790
|
} catch {}
|
|
65777
65791
|
} else {
|
|
65792
|
+
const prompt = buildPlanningPrompt(specContent, codebaseContext, outputPath);
|
|
65778
65793
|
const adapter = _deps2.getAgent(agentName, config2);
|
|
65779
65794
|
if (!adapter)
|
|
65780
65795
|
throw new Error(`[plan] No agent adapter found for '${agentName}'`);
|
|
65781
65796
|
const interactionBridge = createCliInteractionBridge();
|
|
65782
65797
|
logger?.info("plan", "Starting interactive planning session...", { agent: agentName });
|
|
65783
65798
|
try {
|
|
65784
|
-
|
|
65799
|
+
await adapter.plan({
|
|
65785
65800
|
prompt,
|
|
65786
65801
|
workdir,
|
|
65787
65802
|
interactive: true,
|
|
65788
65803
|
timeoutSeconds,
|
|
65789
65804
|
interactionBridge,
|
|
65790
65805
|
config: config2,
|
|
65791
|
-
modelTier: "balanced",
|
|
65806
|
+
modelTier: config2?.plan?.model ?? "balanced",
|
|
65792
65807
|
dangerouslySkipPermissions: config2?.execution?.dangerouslySkipPermissions ?? false,
|
|
65793
65808
|
maxInteractionTurns: config2?.agent?.maxInteractionTurns
|
|
65794
65809
|
});
|
|
65795
|
-
rawResponse = result.specContent;
|
|
65796
65810
|
} finally {
|
|
65797
65811
|
logger?.info("plan", "Interactive session ended");
|
|
65798
65812
|
}
|
|
65813
|
+
if (!_deps2.existsSync(outputPath)) {
|
|
65814
|
+
throw new Error(`[plan] Agent did not write PRD to ${outputPath}. Check agent logs for errors.`);
|
|
65815
|
+
}
|
|
65816
|
+
rawResponse = await _deps2.readFile(outputPath);
|
|
65799
65817
|
}
|
|
65800
65818
|
const finalPrd = validatePlanOutput(rawResponse, options.feature, branchName);
|
|
65801
65819
|
finalPrd.project = projectName;
|
|
65802
|
-
const outputDir = join10(naxDir, "features", options.feature);
|
|
65803
|
-
const outputPath = join10(outputDir, "prd.json");
|
|
65804
|
-
await _deps2.mkdirp(outputDir);
|
|
65805
65820
|
await _deps2.writeFile(outputPath, JSON.stringify(finalPrd, null, 2));
|
|
65806
65821
|
logger?.info("plan", "[OK] PRD written", { outputPath });
|
|
65807
65822
|
return outputPath;
|
|
@@ -65868,7 +65883,7 @@ function buildCodebaseContext2(scan) {
|
|
|
65868
65883
|
return sections.join(`
|
|
65869
65884
|
`);
|
|
65870
65885
|
}
|
|
65871
|
-
function buildPlanningPrompt(specContent, codebaseContext) {
|
|
65886
|
+
function buildPlanningPrompt(specContent, codebaseContext, outputFilePath) {
|
|
65872
65887
|
return `You are a senior software architect generating a product requirements document (PRD) as JSON.
|
|
65873
65888
|
|
|
65874
65889
|
## Spec
|
|
@@ -65924,7 +65939,8 @@ Generate a JSON object with this exact structure (no markdown, no explanation \u
|
|
|
65924
65939
|
- three-session-tdd: Complex stories. Full TDD cycle with separate test-writer and implementer sessions.
|
|
65925
65940
|
- three-session-tdd-lite: Expert/high-risk stories. Full TDD with additional verifier session.
|
|
65926
65941
|
|
|
65927
|
-
|
|
65942
|
+
${outputFilePath ? `Write the PRD JSON directly to this file path: ${outputFilePath}
|
|
65943
|
+
Do NOT output the JSON to the conversation. Write the file, then reply with a brief confirmation.` : "Output ONLY the JSON object. Do not wrap in markdown code blocks."}`;
|
|
65928
65944
|
}
|
|
65929
65945
|
// src/cli/accept.ts
|
|
65930
65946
|
init_config();
|
package/package.json
CHANGED
|
@@ -636,14 +636,19 @@ export class AcpAgentAdapter implements AgentAdapter {
|
|
|
636
636
|
}
|
|
637
637
|
|
|
638
638
|
async plan(options: PlanOptions): Promise<PlanResult> {
|
|
639
|
-
// Resolve model: explicit > config.models.balanced > fallback
|
|
639
|
+
// Resolve model: explicit > config.models[tier] > config.models.balanced > fallback
|
|
640
640
|
let modelDef = options.modelDef;
|
|
641
641
|
if (!modelDef && options.config?.models) {
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
642
|
+
const tier = options.modelTier ?? "balanced";
|
|
643
|
+
const { resolveModel } = await import("../../config/schema");
|
|
644
|
+
const models = options.config.models as Record<string, unknown>;
|
|
645
|
+
const entry = models[tier] ?? models.balanced;
|
|
646
|
+
if (entry) {
|
|
647
|
+
try {
|
|
648
|
+
modelDef = resolveModel(entry as Parameters<typeof resolveModel>[0]);
|
|
649
|
+
} catch {
|
|
650
|
+
// resolveModel can throw on malformed entries
|
|
651
|
+
}
|
|
647
652
|
}
|
|
648
653
|
}
|
|
649
654
|
modelDef ??= { provider: "anthropic", model: "claude-sonnet-4-5-20250514" };
|
package/src/cli/plan.ts
CHANGED
|
@@ -37,6 +37,7 @@ export const _deps = {
|
|
|
37
37
|
return { stdout: result.stdout as Buffer, exitCode: result.exitCode };
|
|
38
38
|
},
|
|
39
39
|
mkdirp: (path: string): Promise<void> => Bun.spawn(["mkdir", "-p", path]).exited.then(() => {}),
|
|
40
|
+
existsSync: (path: string): boolean => existsSync(path),
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -88,9 +89,11 @@ export async function planCommand(workdir: string, config: NaxConfig, options: P
|
|
|
88
89
|
const pkg = await _deps.readPackageJson(workdir);
|
|
89
90
|
const projectName = detectProjectName(workdir, pkg);
|
|
90
91
|
|
|
91
|
-
//
|
|
92
|
+
// Compute output path early — needed for interactive file-write prompt
|
|
92
93
|
const branchName = options.branch ?? `feat/${options.feature}`;
|
|
93
|
-
const
|
|
94
|
+
const outputDir = join(naxDir, "features", options.feature);
|
|
95
|
+
const outputPath = join(outputDir, "prd.json");
|
|
96
|
+
await _deps.mkdirp(outputDir);
|
|
94
97
|
|
|
95
98
|
const agentName = config?.autoMode?.defaultAgent ?? "claude";
|
|
96
99
|
|
|
@@ -101,6 +104,7 @@ export async function planCommand(workdir: string, config: NaxConfig, options: P
|
|
|
101
104
|
let rawResponse: string;
|
|
102
105
|
if (options.auto) {
|
|
103
106
|
// One-shot: use CLI adapter directly — simple completion doesn't need ACP session overhead
|
|
107
|
+
const prompt = buildPlanningPrompt(specContent, codebaseContext);
|
|
104
108
|
const cliAdapter = _deps.getAgent(agentName);
|
|
105
109
|
if (!cliAdapter) throw new Error(`[plan] No agent adapter found for '${agentName}'`);
|
|
106
110
|
rawResponse = await cliAdapter.complete(prompt, { jsonMode: true });
|
|
@@ -114,27 +118,32 @@ export async function planCommand(workdir: string, config: NaxConfig, options: P
|
|
|
114
118
|
// Not an envelope — use rawResponse as-is
|
|
115
119
|
}
|
|
116
120
|
} else {
|
|
117
|
-
// Interactive:
|
|
121
|
+
// Interactive: agent writes PRD JSON directly to outputPath (avoids output truncation)
|
|
122
|
+
const prompt = buildPlanningPrompt(specContent, codebaseContext, outputPath);
|
|
118
123
|
const adapter = _deps.getAgent(agentName, config);
|
|
119
124
|
if (!adapter) throw new Error(`[plan] No agent adapter found for '${agentName}'`);
|
|
120
125
|
const interactionBridge = createCliInteractionBridge();
|
|
121
126
|
logger?.info("plan", "Starting interactive planning session...", { agent: agentName });
|
|
122
127
|
try {
|
|
123
|
-
|
|
128
|
+
await adapter.plan({
|
|
124
129
|
prompt,
|
|
125
130
|
workdir,
|
|
126
131
|
interactive: true,
|
|
127
132
|
timeoutSeconds,
|
|
128
133
|
interactionBridge,
|
|
129
134
|
config,
|
|
130
|
-
modelTier: "balanced",
|
|
135
|
+
modelTier: config?.plan?.model ?? "balanced",
|
|
131
136
|
dangerouslySkipPermissions: config?.execution?.dangerouslySkipPermissions ?? false,
|
|
132
137
|
maxInteractionTurns: config?.agent?.maxInteractionTurns,
|
|
133
138
|
});
|
|
134
|
-
rawResponse = result.specContent;
|
|
135
139
|
} finally {
|
|
136
140
|
logger?.info("plan", "Interactive session ended");
|
|
137
141
|
}
|
|
142
|
+
// Read back from file written by agent
|
|
143
|
+
if (!_deps.existsSync(outputPath)) {
|
|
144
|
+
throw new Error(`[plan] Agent did not write PRD to ${outputPath}. Check agent logs for errors.`);
|
|
145
|
+
}
|
|
146
|
+
rawResponse = await _deps.readFile(outputPath);
|
|
138
147
|
}
|
|
139
148
|
|
|
140
149
|
// Validate and normalize: handles markdown extraction, trailing commas, LLM quirks,
|
|
@@ -144,10 +153,7 @@ export async function planCommand(workdir: string, config: NaxConfig, options: P
|
|
|
144
153
|
// Override project with auto-detected name (validatePlanOutput fills feature/branchName already)
|
|
145
154
|
finalPrd.project = projectName;
|
|
146
155
|
|
|
147
|
-
// Write
|
|
148
|
-
const outputDir = join(naxDir, "features", options.feature);
|
|
149
|
-
const outputPath = join(outputDir, "prd.json");
|
|
150
|
-
await _deps.mkdirp(outputDir);
|
|
156
|
+
// Write normalized PRD (overwrites agent-written file with validated/normalized version)
|
|
151
157
|
await _deps.writeFile(outputPath, JSON.stringify(finalPrd, null, 2));
|
|
152
158
|
|
|
153
159
|
logger?.info("plan", "[OK] PRD written", { outputPath });
|
|
@@ -256,7 +262,7 @@ function buildCodebaseContext(scan: CodebaseScan): string {
|
|
|
256
262
|
* - Complexity classification guide
|
|
257
263
|
* - Test strategy guide
|
|
258
264
|
*/
|
|
259
|
-
function buildPlanningPrompt(specContent: string, codebaseContext: string): string {
|
|
265
|
+
function buildPlanningPrompt(specContent: string, codebaseContext: string, outputFilePath?: string): string {
|
|
260
266
|
return `You are a senior software architect generating a product requirements document (PRD) as JSON.
|
|
261
267
|
|
|
262
268
|
## Spec
|
|
@@ -312,5 +318,9 @@ Generate a JSON object with this exact structure (no markdown, no explanation
|
|
|
312
318
|
- three-session-tdd: Complex stories. Full TDD cycle with separate test-writer and implementer sessions.
|
|
313
319
|
- three-session-tdd-lite: Expert/high-risk stories. Full TDD with additional verifier session.
|
|
314
320
|
|
|
315
|
-
|
|
321
|
+
${
|
|
322
|
+
outputFilePath
|
|
323
|
+
? `Write the PRD JSON directly to this file path: ${outputFilePath}\nDo NOT output the JSON to the conversation. Write the file, then reply with a brief confirmation.`
|
|
324
|
+
: "Output ONLY the JSON object. Do not wrap in markdown code blocks."
|
|
325
|
+
}`;
|
|
316
326
|
}
|