@nathapp/nax 0.46.0 → 0.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,15 +2,17 @@ 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 "./claude/cost";
5
+ export type { ModelCostRates, TokenUsage, CostEstimate, TokenUsageWithConfidence, SessionTokenUsage } from "./cost";
6
6
  export {
7
7
  COST_RATES,
8
+ MODEL_PRICING,
8
9
  parseTokenUsage,
9
10
  estimateCost,
10
11
  estimateCostFromOutput,
11
12
  estimateCostByDuration,
12
13
  formatCostWithConfidence,
13
- } from "./claude/cost";
14
+ estimateCostFromTokenUsage,
15
+ } from "./cost";
14
16
  export { validateAgentForTier, validateAgentFeature, describeAgentCapabilities } from "./shared/validation";
15
17
  export type { AgentVersionInfo } from "./shared/version-detection";
16
18
  export { getAgentVersion, getAgentVersions } from "./shared/version-detection";
@@ -8,6 +8,7 @@
8
8
 
9
9
  import type { NaxConfig } from "../config";
10
10
  import type { ModelDef, ModelTier } from "../config/schema";
11
+ import type { TokenUsage } from "./cost";
11
12
 
12
13
  // Re-export extended types for backward compatibility
13
14
  export type {
@@ -38,6 +39,8 @@ export interface AgentResult {
38
39
  durationMs: number;
39
40
  /** Estimated cost for this run (USD) */
40
41
  estimatedCost: number;
42
+ /** Token usage for this run (when available) */
43
+ tokenUsage?: TokenUsage;
41
44
  /** Process ID of the spawned agent (for cleanup on failure) */
42
45
  pid?: number;
43
46
  }
package/src/cli/init.ts CHANGED
@@ -33,7 +33,21 @@ export interface InitProjectOptions {
33
33
  /**
34
34
  * Gitignore entries added by nax init
35
35
  */
36
- const NAX_GITIGNORE_ENTRIES = [".nax-verifier-verdict.json", "nax.lock", "nax/**/runs/", "nax/metrics.json"];
36
+ const NAX_GITIGNORE_ENTRIES = [
37
+ ".nax-verifier-verdict.json",
38
+ "nax.lock",
39
+ "nax/**/runs/",
40
+ "nax/metrics.json",
41
+ "nax/features/*/status.json",
42
+ "nax/features/*/plan/",
43
+ "nax/features/*/acp-sessions.json",
44
+ "nax/features/*/interactions/",
45
+ "nax/features/*/progress.txt",
46
+ "nax/features/*/acceptance-refined.json",
47
+ ".nax-pids",
48
+ ".nax-wt/",
49
+ "~/",
50
+ ];
37
51
 
38
52
  /**
39
53
  * Add nax-specific entries to .gitignore if not already present.
@@ -106,6 +106,7 @@ export const acceptanceSetupStage: PipelineStage = {
106
106
  const result = await _acceptanceSetupDeps.generate(ctx.prd.userStories, refinedCriteria, {
107
107
  featureName: ctx.prd.feature,
108
108
  workdir: ctx.workdir,
109
+ featureDir: ctx.featureDir,
109
110
  codebaseContext: "",
110
111
  modelTier: ctx.config.acceptance.model ?? "fast",
111
112
  modelDef: resolveModel(ctx.config.models[ctx.config.acceptance.model ?? "fast"]),
@@ -32,6 +32,25 @@ export async function checkGitRepoExists(workdir: string): Promise<Check> {
32
32
  };
33
33
  }
34
34
 
35
+ /**
36
+ * nax runtime files that are allowed to be dirty without blocking the precheck.
37
+ * These are written during nax execution and should be gitignored by `nax init`.
38
+ */
39
+ const NAX_RUNTIME_PATTERNS = [
40
+ /^.{2} nax\.lock$/,
41
+ /^.{2} nax\/metrics\.json$/,
42
+ /^.{2} nax\/features\/[^/]+\/status\.json$/,
43
+ /^.{2} nax\/features\/[^/]+\/runs\//,
44
+ /^.{2} nax\/features\/[^/]+\/plan\//,
45
+ /^.{2} nax\/features\/[^/]+\/acp-sessions\.json$/,
46
+ /^.{2} nax\/features\/[^/]+\/interactions\//,
47
+ /^.{2} nax\/features\/[^/]+\/progress\.txt$/,
48
+ /^.{2} nax\/features\/[^/]+\/acceptance-refined\.json$/,
49
+ /^.{2} \.nax-verifier-verdict\.json$/,
50
+ /^.{2} \.nax-pids$/,
51
+ /^.{2} \.nax-wt\//,
52
+ ];
53
+
35
54
  /** Check if working tree is clean. Uses: git status --porcelain */
36
55
  export async function checkWorkingTreeClean(workdir: string): Promise<Check> {
37
56
  const proc = Bun.spawn(["git", "status", "--porcelain"], {
@@ -42,13 +61,20 @@ export async function checkWorkingTreeClean(workdir: string): Promise<Check> {
42
61
 
43
62
  const output = await new Response(proc.stdout).text();
44
63
  const exitCode = await proc.exited;
45
- const passed = exitCode === 0 && output.trim() === "";
64
+
65
+ // Split without trimming the full output — porcelain lines start with status chars
66
+ // including leading spaces (e.g. " M file.ts"). trim() would corrupt the first line.
67
+ const lines = output.trim() === "" ? [] : output.split("\n").filter(Boolean);
68
+ const nonNaxDirtyFiles = lines.filter((line) => !NAX_RUNTIME_PATTERNS.some((pattern) => pattern.test(line)));
69
+ const passed = exitCode === 0 && nonNaxDirtyFiles.length === 0;
46
70
 
47
71
  return {
48
72
  name: "working-tree-clean",
49
73
  tier: "blocker",
50
74
  passed,
51
- message: passed ? "Working tree is clean" : "Uncommitted changes detected",
75
+ message: passed
76
+ ? "Working tree is clean"
77
+ : `Uncommitted changes detected: ${nonNaxDirtyFiles.map((l) => l.slice(3)).join(", ")}`,
52
78
  };
53
79
  }
54
80
 
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import { existsSync } from "node:fs";
9
+ import { isAbsolute } from "node:path";
9
10
  import type { NaxConfig } from "../config";
10
11
  import type { PRD } from "../prd/types";
11
12
  import type { Check } from "./types";
@@ -126,7 +127,7 @@ async function hasPackageScript(workdir: string, name: string): Promise<boolean>
126
127
 
127
128
  /**
128
129
  * Check if .gitignore covers nax runtime files.
129
- * Patterns: nax.lock, runs/, test/tmp/
130
+ * Patterns: nax.lock, runs/, status.json, .nax-pids, .nax-wt/
130
131
  */
131
132
  export async function checkGitignoreCoversNax(workdir: string): Promise<Check> {
132
133
  const gitignorePath = `${workdir}/.gitignore`;
@@ -143,7 +144,14 @@ export async function checkGitignoreCoversNax(workdir: string): Promise<Check> {
143
144
 
144
145
  const file = Bun.file(gitignorePath);
145
146
  const content = await file.text();
146
- const patterns = ["nax.lock", "runs/", "test/tmp/"];
147
+ const patterns = [
148
+ "nax.lock",
149
+ "nax/**/runs/",
150
+ "nax/metrics.json",
151
+ "nax/features/*/status.json",
152
+ ".nax-pids",
153
+ ".nax-wt/",
154
+ ];
147
155
  const missing = patterns.filter((pattern) => !content.includes(pattern));
148
156
  const passed = missing.length === 0;
149
157
 
@@ -191,3 +199,23 @@ export async function checkPromptOverrideFiles(config: NaxConfig, workdir: strin
191
199
 
192
200
  return checks;
193
201
  }
202
+
203
+ /**
204
+ * Check if HOME env is set and is an absolute path.
205
+ * An unexpanded "~" in HOME causes agent spawns to create a literal ~/
206
+ * directory inside the repo cwd instead of resolving to the user home dir.
207
+ */
208
+ export async function checkHomeEnvValid(): Promise<Check> {
209
+ const home = process.env.HOME ?? "";
210
+ const passed = home !== "" && isAbsolute(home);
211
+ return {
212
+ name: "home-env-valid",
213
+ tier: "warning",
214
+ passed,
215
+ message: passed
216
+ ? `HOME env is valid: ${home}`
217
+ : home === ""
218
+ ? "HOME env is not set — agent may write files to unexpected locations"
219
+ : `HOME env is not an absolute path ("${home}") — may cause literal "~" directories in repo`,
220
+ };
221
+ }
@@ -28,6 +28,7 @@ export {
28
28
  checkPendingStories,
29
29
  checkOptionalCommands,
30
30
  checkGitignoreCoversNax,
31
+ checkHomeEnvValid,
31
32
  checkPromptOverrideFiles,
32
33
  } from "./checks-warnings";
33
34
 
@@ -20,6 +20,7 @@ import {
20
20
  checkGitRepoExists,
21
21
  checkGitUserConfigured,
22
22
  checkGitignoreCoversNax,
23
+ checkHomeEnvValid,
23
24
  checkLintCommand,
24
25
  checkMultiAgentHealth,
25
26
  checkOptionalCommands,
@@ -126,6 +127,7 @@ function getEnvironmentWarnings(config: NaxConfig, workdir: string): CheckFn[] {
126
127
  () => checkDiskSpace(),
127
128
  () => checkOptionalCommands(config, workdir),
128
129
  () => checkGitignoreCoversNax(workdir),
130
+ () => checkHomeEnvValid(),
129
131
  () => checkPromptOverrideFiles(config, workdir),
130
132
  () => checkMultiAgentHealth(),
131
133
  ];