@nathapp/nax 0.45.0 → 0.46.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.
Files changed (33) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/bin/nax.ts +7 -6
  3. package/dist/nax.js +181 -124
  4. package/package.json +1 -1
  5. package/src/agents/acp/adapter.ts +34 -6
  6. package/src/agents/acp/index.ts +0 -2
  7. package/src/agents/acp/parser.ts +57 -104
  8. package/src/agents/acp/spawn-client.ts +2 -1
  9. package/src/agents/{claude.ts → claude/adapter.ts} +15 -12
  10. package/src/agents/{claude-complete.ts → claude/complete.ts} +3 -3
  11. package/src/agents/{cost.ts → claude/cost.ts} +1 -1
  12. package/src/agents/{claude-execution.ts → claude/execution.ts} +5 -5
  13. package/src/agents/claude/index.ts +3 -0
  14. package/src/agents/{claude-interactive.ts → claude/interactive.ts} +4 -4
  15. package/src/agents/{claude-plan.ts → claude/plan.ts} +12 -9
  16. package/src/agents/index.ts +5 -5
  17. package/src/agents/registry.ts +5 -5
  18. package/src/agents/{claude-decompose.ts → shared/decompose.ts} +2 -2
  19. package/src/agents/{model-resolution.ts → shared/model-resolution.ts} +2 -2
  20. package/src/agents/{types-extended.ts → shared/types-extended.ts} +4 -4
  21. package/src/agents/{validation.ts → shared/validation.ts} +2 -2
  22. package/src/agents/{version-detection.ts → shared/version-detection.ts} +3 -3
  23. package/src/agents/types.ts +8 -4
  24. package/src/cli/agents.ts +1 -1
  25. package/src/pipeline/stages/acceptance.ts +5 -8
  26. package/src/pipeline/stages/regression.ts +2 -0
  27. package/src/pipeline/stages/verify.ts +5 -10
  28. package/src/precheck/checks-agents.ts +1 -1
  29. package/src/utils/log-test-output.ts +25 -0
  30. /package/src/agents/{adapters/aider.ts → aider/adapter.ts} +0 -0
  31. /package/src/agents/{adapters/codex.ts → codex/adapter.ts} +0 -0
  32. /package/src/agents/{adapters/gemini.ts → gemini/adapter.ts} +0 -0
  33. /package/src/agents/{adapters/opencode.ts → opencode/adapter.ts} +0 -0
@@ -15,7 +15,7 @@ import { createHash } from "node:crypto";
15
15
  import { join } from "node:path";
16
16
  import { resolvePermissions } from "../../config/permissions";
17
17
  import { getSafeLogger } from "../../logger";
18
- import { buildDecomposePrompt, parseDecomposeOutput } from "../claude-decompose";
18
+ import { buildDecomposePrompt, parseDecomposeOutput } from "../shared/decompose";
19
19
  import { createSpawnAcpClient } from "./spawn-client";
20
20
 
21
21
  import type {
@@ -80,7 +80,14 @@ const DEFAULT_ENTRY: AgentRegistryEntry = {
80
80
  export interface AcpSessionResponse {
81
81
  messages: Array<{ role: string; content: string }>;
82
82
  stopReason: string;
83
- cumulative_token_usage?: { input_tokens: number; output_tokens: number };
83
+ cumulative_token_usage?: {
84
+ input_tokens: number;
85
+ output_tokens: number;
86
+ cache_read_input_tokens?: number;
87
+ cache_creation_input_tokens?: number;
88
+ };
89
+ /** Exact cost in USD from acpx usage_update event. Preferred over token-based estimation. */
90
+ exactCostUsd?: number;
84
91
  }
85
92
 
86
93
  export interface AcpSession {
@@ -555,7 +562,13 @@ export class AcpAgentAdapter implements AgentAdapter {
555
562
  // Tracks whether the run completed successfully — used by finally to decide
556
563
  // whether to close the session (success) or keep it open for retry (failure).
557
564
  const runState = { succeeded: false };
558
- const totalTokenUsage = { input_tokens: 0, output_tokens: 0 };
565
+ const totalTokenUsage = {
566
+ input_tokens: 0,
567
+ output_tokens: 0,
568
+ cache_read_input_tokens: 0,
569
+ cache_creation_input_tokens: 0,
570
+ };
571
+ let totalExactCostUsd: number | undefined;
559
572
 
560
573
  try {
561
574
  // 5. Multi-turn loop
@@ -577,10 +590,16 @@ export class AcpAgentAdapter implements AgentAdapter {
577
590
  lastResponse = turnResult.response;
578
591
  if (!lastResponse) break;
579
592
 
580
- // Accumulate token usage
593
+ // Accumulate token usage and exact cost
581
594
  if (lastResponse.cumulative_token_usage) {
582
595
  totalTokenUsage.input_tokens += lastResponse.cumulative_token_usage.input_tokens ?? 0;
583
596
  totalTokenUsage.output_tokens += lastResponse.cumulative_token_usage.output_tokens ?? 0;
597
+ totalTokenUsage.cache_read_input_tokens += lastResponse.cumulative_token_usage.cache_read_input_tokens ?? 0;
598
+ totalTokenUsage.cache_creation_input_tokens +=
599
+ lastResponse.cumulative_token_usage.cache_creation_input_tokens ?? 0;
600
+ }
601
+ if (lastResponse.exactCostUsd !== undefined) {
602
+ totalExactCostUsd = (totalExactCostUsd ?? 0) + lastResponse.exactCostUsd;
584
603
  }
585
604
 
586
605
  // Check for agent question → route to interaction bridge
@@ -643,10 +662,12 @@ export class AcpAgentAdapter implements AgentAdapter {
643
662
  const success = lastResponse?.stopReason === "end_turn";
644
663
  const output = extractOutput(lastResponse);
645
664
 
665
+ // Prefer exact cost from acpx usage_update; fall back to token-based estimation
646
666
  const estimatedCost =
647
- totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0
667
+ totalExactCostUsd ??
668
+ (totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0
648
669
  ? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model)
649
- : 0;
670
+ : 0);
650
671
 
651
672
  return {
652
673
  success,
@@ -719,6 +740,13 @@ export class AcpAgentAdapter implements AgentAdapter {
719
740
  throw new CompleteError("complete() returned empty output");
720
741
  }
721
742
 
743
+ if (response.exactCostUsd !== undefined) {
744
+ getSafeLogger()?.info("acp-adapter", "complete() cost", {
745
+ costUsd: response.exactCostUsd,
746
+ model,
747
+ });
748
+ }
749
+
722
750
  return unwrapped;
723
751
  } catch (err) {
724
752
  const error = err instanceof Error ? err : new Error(String(err));
@@ -4,6 +4,4 @@
4
4
 
5
5
  export { AcpAgentAdapter, _acpAdapterDeps } from "./adapter";
6
6
  export { createSpawnAcpClient } from "./spawn-client";
7
- export { estimateCostFromTokenUsage } from "./cost";
8
- export type { SessionTokenUsage } from "./cost";
9
7
  export type { AgentRegistryEntry } from "./types";
@@ -2,11 +2,9 @@
2
2
  * ACP adapter — NDJSON and JSON-RPC output parsing helpers.
3
3
  *
4
4
  * Extracted from adapter.ts to keep that file within the 800-line limit.
5
- * Used only by _runOnce() (the spawn-based legacy path).
5
+ * Used by SpawnAcpSession.prompt() to parse acpx stdout.
6
6
  */
7
7
 
8
- import type { AgentRunOptions } from "../types";
9
-
10
8
  // ─────────────────────────────────────────────────────────────────────────────
11
9
  // Types
12
10
  // ─────────────────────────────────────────────────────────────────────────────
@@ -15,131 +13,86 @@ import type { AgentRunOptions } from "../types";
15
13
  export interface AcpxTokenUsage {
16
14
  input_tokens: number;
17
15
  output_tokens: number;
18
- }
19
-
20
- /** JSON-RPC message from acpx --format json --json-strict */
21
- interface JsonRpcMessage {
22
- jsonrpc: "2.0";
23
- method?: string;
24
- params?: {
25
- sessionId: string;
26
- update?: {
27
- sessionUpdate: string;
28
- content?: { type: string; text?: string };
29
- used?: number;
30
- size?: number;
31
- cost?: { amount: number; currency: string };
32
- };
33
- };
34
- id?: number | string;
35
- result?: unknown;
36
- error?: { code: number; message: string };
16
+ cache_read_input_tokens?: number;
17
+ cache_creation_input_tokens?: number;
37
18
  }
38
19
 
39
20
  // ─────────────────────────────────────────────────────────────────────────────
40
- // streamJsonRpcEvents
21
+ // parseAcpxJsonOutput
41
22
  // ─────────────────────────────────────────────────────────────────────────────
42
23
 
43
24
  /**
44
- * Stream stdout line-by-line, parse JSON-RPC, detect questions, call bridge.
25
+ * Parse acpx NDJSON output for assistant text, token usage, and exact cost.
26
+ *
27
+ * Handles the JSON-RPC envelope format emitted by acpx:
28
+ * - session/update agent_message_chunk → text accumulation
29
+ * - session/update usage_update → exact cost (cost.amount) + context size
30
+ * - id/result → token breakdown (inputTokens, outputTokens, cachedWriteTokens, cachedReadTokens)
31
+ *
32
+ * Also handles legacy flat NDJSON format for backward compatibility.
45
33
  */
46
- export async function streamJsonRpcEvents(
47
- stdout: ReadableStream<Uint8Array>,
48
- bridge: AgentRunOptions["interactionBridge"],
49
- _sessionId: string,
50
- ): Promise<{ text: string; tokenUsage?: AcpxTokenUsage }> {
51
- let accumulatedText = "";
34
+ export function parseAcpxJsonOutput(rawOutput: string): {
35
+ text: string;
36
+ tokenUsage?: AcpxTokenUsage;
37
+ exactCostUsd?: number;
38
+ stopReason?: string;
39
+ error?: string;
40
+ } {
41
+ const lines = rawOutput.split("\n").filter((l) => l.trim());
42
+ let text = "";
52
43
  let tokenUsage: AcpxTokenUsage | undefined;
53
- const decoder = new TextDecoder();
54
- let buffer = "";
55
-
56
- const reader = stdout.getReader();
57
-
58
- try {
59
- while (true) {
60
- const { done, value } = await reader.read();
61
- if (done) break;
62
-
63
- buffer += decoder.decode(value, { stream: true });
64
- const lines = buffer.split("\n");
65
- buffer = lines.pop() ?? "";
66
-
67
- for (const line of lines) {
68
- if (!line.trim()) continue;
44
+ let exactCostUsd: number | undefined;
45
+ let stopReason: string | undefined;
46
+ let error: string | undefined;
69
47
 
70
- let msg: JsonRpcMessage;
71
- try {
72
- msg = JSON.parse(line);
73
- } catch {
74
- continue;
75
- }
48
+ for (const line of lines) {
49
+ try {
50
+ const event = JSON.parse(line);
76
51
 
77
- if (msg.method === "session/update" && msg.params?.update) {
78
- const update = msg.params.update;
52
+ // ── JSON-RPC envelope format (acpx v0.3+) ──────────────────────────────
53
+ if (event.jsonrpc === "2.0") {
54
+ // session/update events
55
+ if (event.method === "session/update" && event.params?.update) {
56
+ const update = event.params.update;
79
57
 
58
+ // Text chunks
80
59
  if (
81
60
  update.sessionUpdate === "agent_message_chunk" &&
82
61
  update.content?.type === "text" &&
83
62
  update.content.text
84
63
  ) {
85
- accumulatedText += update.content.text;
86
-
87
- if (bridge?.detectQuestion && bridge.onQuestionDetected) {
88
- const isQuestion = await bridge.detectQuestion(accumulatedText);
89
- if (isQuestion) {
90
- const response = await bridge.onQuestionDetected(accumulatedText);
91
- accumulatedText += `\n\n[Human response: ${response}]`;
92
- }
93
- }
64
+ text += update.content.text;
94
65
  }
95
66
 
96
- if (update.sessionUpdate === "usage_update" && update.used !== undefined) {
97
- const total = update.used;
98
- tokenUsage = {
99
- input_tokens: Math.floor(total * 0.3),
100
- output_tokens: Math.floor(total * 0.7),
101
- };
67
+ // Exact cost from usage_update
68
+ if (update.sessionUpdate === "usage_update" && typeof update.cost?.amount === "number") {
69
+ exactCostUsd = update.cost.amount;
102
70
  }
103
71
  }
104
72
 
105
- if (msg.result) {
106
- const result = msg.result as Record<string, unknown>;
107
- if (typeof result === "string") {
108
- accumulatedText += result;
109
- }
110
- }
111
- }
112
- }
113
- } finally {
114
- reader.releaseLock();
115
- }
116
-
117
- return { text: accumulatedText.trim(), tokenUsage };
118
- }
73
+ // Final result with token breakdown (camelCase from acpx)
74
+ if (event.id !== undefined && event.result && typeof event.result === "object") {
75
+ const result = event.result as Record<string, unknown>;
119
76
 
120
- // ─────────────────────────────────────────────────────────────────────────────
121
- // parseAcpxJsonOutput
122
- // ─────────────────────────────────────────────────────────────────────────────
77
+ if (result.stopReason) stopReason = result.stopReason as string;
78
+ if (result.stop_reason) stopReason = result.stop_reason as string;
123
79
 
124
- /**
125
- * Parse acpx NDJSON output for assistant text and token usage.
126
- */
127
- export function parseAcpxJsonOutput(rawOutput: string): {
128
- text: string;
129
- tokenUsage?: AcpxTokenUsage;
130
- stopReason?: string;
131
- error?: string;
132
- } {
133
- const lines = rawOutput.split("\n").filter((l) => l.trim());
134
- let text = "";
135
- let tokenUsage: AcpxTokenUsage | undefined;
136
- let stopReason: string | undefined;
137
- let error: string | undefined;
80
+ if (result.usage && typeof result.usage === "object") {
81
+ const u = result.usage as Record<string, unknown>;
82
+ tokenUsage = {
83
+ input_tokens: (u.inputTokens as number) ?? (u.input_tokens as number) ?? 0,
84
+ output_tokens: (u.outputTokens as number) ?? (u.output_tokens as number) ?? 0,
85
+ cache_read_input_tokens: (u.cachedReadTokens as number) ?? (u.cache_read_input_tokens as number) ?? 0,
86
+ cache_creation_input_tokens:
87
+ (u.cachedWriteTokens as number) ?? (u.cache_creation_input_tokens as number) ?? 0,
88
+ };
89
+ }
90
+ }
138
91
 
139
- for (const line of lines) {
140
- try {
141
- const event = JSON.parse(line);
92
+ continue;
93
+ }
142
94
 
95
+ // ── Legacy flat NDJSON format ───────────────────────────────────────────
143
96
  if (event.content && typeof event.content === "string") text += event.content;
144
97
  if (event.text && typeof event.text === "string") text += event.text;
145
98
  if (event.result && typeof event.result === "string") text = event.result;
@@ -162,5 +115,5 @@ export function parseAcpxJsonOutput(rawOutput: string): {
162
115
  }
163
116
  }
164
117
 
165
- return { text: text.trim(), tokenUsage, stopReason, error };
118
+ return { text: text.trim(), tokenUsage, exactCostUsd, stopReason, error };
166
119
  }
@@ -180,8 +180,9 @@ class SpawnAcpSession implements AcpSession {
180
180
  const parsed = parseAcpxJsonOutput(stdout);
181
181
  return {
182
182
  messages: [{ role: "assistant", content: parsed.text || "" }],
183
- stopReason: "end_turn",
183
+ stopReason: parsed.stopReason ?? "end_turn",
184
184
  cumulative_token_usage: parsed.tokenUsage,
185
+ exactCostUsd: parsed.exactCostUsd,
185
186
  };
186
187
  } catch (err) {
187
188
  getSafeLogger()?.warn("acp-adapter", "Failed to parse session prompt response", {
@@ -4,15 +4,11 @@
4
4
  * Main adapter class coordinating execution, completion, decomposition, and interactive modes.
5
5
  */
6
6
 
7
- import { resolvePermissions } from "../config/permissions";
8
- import { PidRegistry } from "../execution/pid-registry";
9
- import { withProcessTimeout } from "../execution/timeout-handler";
10
- import { getLogger } from "../logger";
11
- import { _completeDeps, executeComplete } from "./claude-complete";
12
- import { buildDecomposePrompt, parseDecomposeOutput } from "./claude-decompose";
13
- import { _runOnceDeps, buildAllowedEnv, buildCommand, executeOnce } from "./claude-execution";
14
- import { runInteractiveMode } from "./claude-interactive";
15
- import { runPlan } from "./claude-plan";
7
+ import { resolvePermissions } from "../../config/permissions";
8
+ import { PidRegistry } from "../../execution/pid-registry";
9
+ import { withProcessTimeout } from "../../execution/timeout-handler";
10
+ import { getLogger } from "../../logger";
11
+ import { buildDecomposePrompt, parseDecomposeOutput } from "../shared/decompose";
16
12
  import type {
17
13
  AgentAdapter,
18
14
  AgentCapabilities,
@@ -25,7 +21,11 @@ import type {
25
21
  PlanOptions,
26
22
  PlanResult,
27
23
  PtyHandle,
28
- } from "./types";
24
+ } from "../types";
25
+ import { _completeDeps, executeComplete } from "./complete";
26
+ import { _runOnceDeps, buildAllowedEnv, buildCommand, executeOnce } from "./execution";
27
+ import { runInteractiveMode } from "./interactive";
28
+ import { runPlan } from "./plan";
29
29
 
30
30
  /**
31
31
  * Injectable dependencies for decompose() — allows tests to intercept
@@ -174,7 +174,7 @@ export class ClaudeCodeAdapter implements AgentAdapter {
174
174
  }
175
175
 
176
176
  async decompose(options: DecomposeOptions): Promise<DecomposeResult> {
177
- const { resolveBalancedModelDef } = await import("./model-resolution");
177
+ const { resolveBalancedModelDef } = await import("../shared/model-resolution");
178
178
 
179
179
  const prompt = buildDecomposePrompt(options);
180
180
 
@@ -186,7 +186,10 @@ export class ClaudeCodeAdapter implements AgentAdapter {
186
186
  modelDef = resolveBalancedModelDef(options.config);
187
187
  }
188
188
 
189
- const { skipPermissions } = resolvePermissions(options.config as import("../config").NaxConfig | undefined, "run");
189
+ const { skipPermissions } = resolvePermissions(
190
+ options.config as import("../../config").NaxConfig | undefined,
191
+ "run",
192
+ );
190
193
  const cmd = [this.binary, "--model", modelDef.model, "-p", prompt];
191
194
  if (skipPermissions) {
192
195
  cmd.splice(cmd.length - 2, 0, "--dangerously-skip-permissions");
@@ -4,9 +4,9 @@
4
4
  * Standalone completion endpoint for simple prompts.
5
5
  */
6
6
 
7
- import { resolvePermissions } from "../config/permissions";
8
- import type { CompleteOptions } from "./types";
9
- import { CompleteError } from "./types";
7
+ import { resolvePermissions } from "../../config/permissions";
8
+ import type { CompleteOptions } from "../types";
9
+ import { CompleteError } from "../types";
10
10
 
11
11
  /**
12
12
  * Injectable dependencies for complete() — allows tests to intercept
@@ -5,7 +5,7 @@
5
5
  * Parses agent output for token usage and calculates costs.
6
6
  */
7
7
 
8
- import type { ModelTier } from "../config/schema";
8
+ import type { ModelTier } from "../../config/schema";
9
9
 
10
10
  /** Cost rates per 1M tokens (USD) */
11
11
  export interface ModelCostRates {
@@ -4,12 +4,12 @@
4
4
  * Handles building commands, preparing environment, and process execution.
5
5
  */
6
6
 
7
- import { resolvePermissions } from "../config/permissions";
8
- import type { PidRegistry } from "../execution/pid-registry";
9
- import { withProcessTimeout } from "../execution/timeout-handler";
10
- import { getLogger } from "../logger";
7
+ import { resolvePermissions } from "../../config/permissions";
8
+ import type { PidRegistry } from "../../execution/pid-registry";
9
+ import { withProcessTimeout } from "../../execution/timeout-handler";
10
+ import { getLogger } from "../../logger";
11
+ import type { AgentResult, AgentRunOptions } from "../types";
11
12
  import { estimateCostByDuration, estimateCostFromOutput } from "./cost";
12
- import type { AgentResult, AgentRunOptions } from "./types";
13
13
 
14
14
  /**
15
15
  * Maximum characters to capture from agent stdout.
@@ -0,0 +1,3 @@
1
+ // Re-export everything external callers need from claude/
2
+ export { ClaudeCodeAdapter, _completeDeps, _claudeAdapterDeps } from "./adapter";
3
+ export { _runOnceDeps } from "./execution";
@@ -4,10 +4,10 @@
4
4
  * Handles terminal UI interactions with the Claude agent.
5
5
  */
6
6
 
7
- import type { PidRegistry } from "../execution/pid-registry";
8
- import { getLogger } from "../logger";
9
- import { buildAllowedEnv } from "./claude-execution";
10
- import type { AgentRunOptions, InteractiveRunOptions, PtyHandle } from "./types";
7
+ import type { PidRegistry } from "../../execution/pid-registry";
8
+ import { getLogger } from "../../logger";
9
+ import type { AgentRunOptions, InteractiveRunOptions, PtyHandle } from "../types";
10
+ import { buildAllowedEnv } from "./execution";
11
11
 
12
12
  /**
13
13
  * Run Claude agent in interactive (TTY) mode for TUI output.
@@ -7,13 +7,13 @@ import { join } from "node:path";
7
7
  * Extracted from claude.ts: plan(), buildPlanCommand()
8
8
  */
9
9
 
10
- import { resolvePermissions } from "../config/permissions";
11
- import type { PidRegistry } from "../execution/pid-registry";
12
- import { withProcessTimeout } from "../execution/timeout-handler";
13
- import { getLogger } from "../logger";
14
- import { resolveBalancedModelDef } from "./model-resolution";
15
- import type { AgentRunOptions } from "./types";
16
- import type { PlanOptions, PlanResult } from "./types-extended";
10
+ import { resolvePermissions } from "../../config/permissions";
11
+ import type { PidRegistry } from "../../execution/pid-registry";
12
+ import { withProcessTimeout } from "../../execution/timeout-handler";
13
+ import { getLogger } from "../../logger";
14
+ import { resolveBalancedModelDef } from "../shared/model-resolution";
15
+ import type { PlanOptions, PlanResult } from "../shared/types-extended";
16
+ import type { AgentRunOptions } from "../types";
17
17
 
18
18
  /**
19
19
  * Build the CLI command for plan mode.
@@ -32,7 +32,10 @@ export function buildPlanCommand(binary: string, options: PlanOptions): string[]
32
32
  }
33
33
 
34
34
  // Resolve permission mode from config
35
- const { skipPermissions } = resolvePermissions(options.config as import("../config").NaxConfig | undefined, "plan");
35
+ const { skipPermissions } = resolvePermissions(
36
+ options.config as import("../../config").NaxConfig | undefined,
37
+ "plan",
38
+ );
36
39
  if (skipPermissions) {
37
40
  cmd.push("--dangerously-skip-permissions");
38
41
  }
@@ -75,7 +78,7 @@ export async function runPlan(
75
78
  pidRegistry: PidRegistry,
76
79
  buildAllowedEnv: (options: AgentRunOptions) => Record<string, string | undefined>,
77
80
  ): Promise<PlanResult> {
78
- const { resolveBalancedModelDef } = await import("./model-resolution");
81
+ const { resolveBalancedModelDef } = await import("../shared/model-resolution");
79
82
 
80
83
  const cmd = buildPlanCommand(binary, options);
81
84
 
@@ -2,7 +2,7 @@ export type { AgentAdapter, AgentCapabilities, AgentResult, AgentRunOptions, Com
2
2
  export { CompleteError } from "./types";
3
3
  export { ClaudeCodeAdapter } from "./claude";
4
4
  export { getAllAgentNames, getAgent, getInstalledAgents, checkAgentHealth } from "./registry";
5
- export type { ModelCostRates, TokenUsage, CostEstimate, TokenUsageWithConfidence } from "./cost";
5
+ export type { ModelCostRates, TokenUsage, CostEstimate, TokenUsageWithConfidence } from "./claude/cost";
6
6
  export {
7
7
  COST_RATES,
8
8
  parseTokenUsage,
@@ -10,7 +10,7 @@ export {
10
10
  estimateCostFromOutput,
11
11
  estimateCostByDuration,
12
12
  formatCostWithConfidence,
13
- } from "./cost";
14
- export { validateAgentForTier, validateAgentFeature, describeAgentCapabilities } from "./validation";
15
- export type { AgentVersionInfo } from "./version-detection";
16
- export { getAgentVersion, getAgentVersions } from "./version-detection";
13
+ } from "./claude/cost";
14
+ export { validateAgentForTier, validateAgentFeature, describeAgentCapabilities } from "./shared/validation";
15
+ export type { AgentVersionInfo } from "./shared/version-detection";
16
+ export { getAgentVersion, getAgentVersions } from "./shared/version-detection";
@@ -7,11 +7,11 @@
7
7
  import type { NaxConfig } from "../config/schema";
8
8
  import { getLogger } from "../logger";
9
9
  import { AcpAgentAdapter } from "./acp/adapter";
10
- import { AiderAdapter } from "./adapters/aider";
11
- import { CodexAdapter } from "./adapters/codex";
12
- import { GeminiAdapter } from "./adapters/gemini";
13
- import { OpenCodeAdapter } from "./adapters/opencode";
14
- import { ClaudeCodeAdapter } from "./claude";
10
+ import { AiderAdapter } from "./aider/adapter";
11
+ import { ClaudeCodeAdapter } from "./claude/adapter";
12
+ import { CodexAdapter } from "./codex/adapter";
13
+ import { GeminiAdapter } from "./gemini/adapter";
14
+ import { OpenCodeAdapter } from "./opencode/adapter";
15
15
  import type { AgentAdapter } from "./types";
16
16
 
17
17
  /** All known agent adapters */
@@ -5,8 +5,8 @@
5
5
  * parseDecomposeOutput(), validateComplexity()
6
6
  */
7
7
 
8
- import { COMPLEXITY_GUIDE, GROUPING_RULES, TEST_STRATEGY_GUIDE, resolveTestStrategy } from "../config/test-strategy";
9
- import type { DecomposeOptions, DecomposeResult, DecomposedStory } from "./types";
8
+ import { COMPLEXITY_GUIDE, GROUPING_RULES, TEST_STRATEGY_GUIDE, resolveTestStrategy } from "../../config/test-strategy";
9
+ import type { DecomposeOptions, DecomposeResult, DecomposedStory } from "../types";
10
10
 
11
11
  /**
12
12
  * Build the decompose prompt combining spec content and codebase context.
@@ -7,8 +7,8 @@
7
7
  * Implementation placeholder — logic to be filled in by the implementer.
8
8
  */
9
9
 
10
- import { resolveModel } from "../config/schema";
11
- import type { ModelDef, NaxConfig } from "../config/schema";
10
+ import { resolveModel } from "../../config/schema";
11
+ import type { ModelDef, NaxConfig } from "../../config/schema";
12
12
 
13
13
  /**
14
14
  * Resolve the balanced model definition from config, with optional adapter default fallback.
@@ -5,7 +5,7 @@
5
5
  * Separated from core types to keep each file under 400 lines.
6
6
  */
7
7
 
8
- import type { ModelDef, ModelTier, NaxConfig } from "../config/schema";
8
+ import type { ModelDef, ModelTier, NaxConfig } from "../../config/schema";
9
9
 
10
10
  /**
11
11
  * Configuration options for running an agent in plan mode.
@@ -56,7 +56,7 @@ export interface PlanOptions {
56
56
  */
57
57
  onAcpSessionCreated?: (sessionName: string) => Promise<void> | void;
58
58
  /** PID registry for tracking spawned agent processes — cleanup on crash/SIGTERM */
59
- pidRegistry?: import("../execution/pid-registry").PidRegistry;
59
+ pidRegistry?: import("../../execution/pid-registry").PidRegistry;
60
60
  }
61
61
 
62
62
  /**
@@ -117,7 +117,7 @@ export interface DecomposedStory {
117
117
  /** Implementation risks */
118
118
  risks: string[];
119
119
  /** Test strategy recommendation from LLM */
120
- testStrategy?: import("../config/test-strategy").TestStrategy;
120
+ testStrategy?: import("../../config/test-strategy").TestStrategy;
121
121
  }
122
122
 
123
123
  /**
@@ -161,4 +161,4 @@ export interface InteractiveRunOptions extends AgentRunOptions {
161
161
  }
162
162
 
163
163
  // Re-import for the extends clause
164
- import type { AgentRunOptions } from "./types";
164
+ import type { AgentRunOptions } from "../types";
@@ -4,8 +4,8 @@
4
4
  * Runtime validation for agent capabilities and tier compatibility.
5
5
  */
6
6
 
7
- import type { ModelTier } from "../config/schema";
8
- import type { AgentAdapter } from "./types";
7
+ import type { ModelTier } from "../../config/schema";
8
+ import type { AgentAdapter } from "../types";
9
9
 
10
10
  /**
11
11
  * Check if an agent supports a given model tier.
@@ -5,8 +5,8 @@
5
5
  * by running `<agent> --version` and parsing the output.
6
6
  */
7
7
 
8
- import { getInstalledAgents } from "./registry";
9
- import type { AgentAdapter } from "./types";
8
+ import { getInstalledAgents } from "../registry";
9
+ import type { AgentAdapter } from "../types";
10
10
 
11
11
  /**
12
12
  * Information about an installed agent including its version
@@ -90,7 +90,7 @@ export async function getAgentVersions(): Promise<AgentVersionInfo[]> {
90
90
  const agentsByName = new Map(agents.map((a) => [a.name, a]));
91
91
 
92
92
  // Import ALL_AGENTS to include non-installed ones
93
- const { ALL_AGENTS } = await import("./registry");
93
+ const { ALL_AGENTS } = await import("../registry");
94
94
 
95
95
  const versions = await Promise.all(
96
96
  ALL_AGENTS.map(async (agent: AgentAdapter): Promise<AgentVersionInfo> => {
@@ -18,7 +18,7 @@ export type {
18
18
  DecomposedStory,
19
19
  PtyHandle,
20
20
  InteractiveRunOptions,
21
- } from "./types-extended";
21
+ } from "./shared/types-extended";
22
22
 
23
23
  /**
24
24
  * Agent execution result returned after running a coding agent.
@@ -165,10 +165,12 @@ export interface AgentAdapter {
165
165
  buildCommand(options: AgentRunOptions): string[];
166
166
 
167
167
  /** Run the agent in plan mode to generate a feature specification. */
168
- plan(options: import("./types-extended").PlanOptions): Promise<import("./types-extended").PlanResult>;
168
+ plan(options: import("./shared/types-extended").PlanOptions): Promise<import("./shared/types-extended").PlanResult>;
169
169
 
170
170
  /** Run the agent in decompose mode to break spec into classified stories. */
171
- decompose(options: import("./types-extended").DecomposeOptions): Promise<import("./types-extended").DecomposeResult>;
171
+ decompose(
172
+ options: import("./shared/types-extended").DecomposeOptions,
173
+ ): Promise<import("./shared/types-extended").DecomposeResult>;
172
174
 
173
175
  /**
174
176
  * Run a one-shot LLM call and return the plain text response.
@@ -181,5 +183,7 @@ export interface AgentAdapter {
181
183
  * This method is optional — only implemented by agents that support
182
184
  * interactive terminal sessions (e.g., Claude Code).
183
185
  */
184
- runInteractive?(options: import("./types-extended").InteractiveRunOptions): import("./types-extended").PtyHandle;
186
+ runInteractive?(
187
+ options: import("./shared/types-extended").InteractiveRunOptions,
188
+ ): import("./shared/types-extended").PtyHandle;
185
189
  }
package/src/cli/agents.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { ALL_AGENTS } from "../agents/registry";
8
- import { getAgentVersion } from "../agents/version-detection";
8
+ import { getAgentVersion } from "../agents/shared/version-detection";
9
9
  import type { NaxConfig } from "../config/schema";
10
10
 
11
11
  /**