@nathapp/nax 0.40.1 → 0.42.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 (46) hide show
  1. package/README.md +1 -0
  2. package/bin/nax.ts +130 -11
  3. package/dist/nax.js +1520 -424
  4. package/package.json +8 -7
  5. package/src/acceptance/fix-generator.ts +4 -35
  6. package/src/acceptance/generator.ts +4 -27
  7. package/src/agents/acp/adapter.ts +642 -0
  8. package/src/agents/acp/cost.ts +79 -0
  9. package/src/agents/acp/index.ts +9 -0
  10. package/src/agents/acp/interaction-bridge.ts +126 -0
  11. package/src/agents/acp/parser.ts +166 -0
  12. package/src/agents/acp/spawn-client.ts +309 -0
  13. package/src/agents/acp/types.ts +22 -0
  14. package/src/agents/claude-complete.ts +3 -3
  15. package/src/agents/claude.ts +12 -2
  16. package/src/agents/registry.ts +83 -0
  17. package/src/agents/types-extended.ts +23 -0
  18. package/src/agents/types.ts +17 -0
  19. package/src/analyze/scanner.ts +16 -20
  20. package/src/cli/analyze.ts +6 -2
  21. package/src/cli/plan.ts +218 -129
  22. package/src/commands/precheck.ts +1 -1
  23. package/src/config/defaults.ts +1 -0
  24. package/src/config/runtime-types.ts +10 -0
  25. package/src/config/schema.ts +1 -0
  26. package/src/config/schemas.ts +6 -0
  27. package/src/config/types.ts +1 -0
  28. package/src/execution/executor-types.ts +6 -0
  29. package/src/execution/iteration-runner.ts +2 -0
  30. package/src/execution/lifecycle/acceptance-loop.ts +5 -2
  31. package/src/execution/lifecycle/run-initialization.ts +16 -4
  32. package/src/execution/lifecycle/run-setup.ts +4 -0
  33. package/src/execution/runner-completion.ts +11 -1
  34. package/src/execution/runner-execution.ts +8 -0
  35. package/src/execution/runner-setup.ts +4 -0
  36. package/src/execution/runner.ts +10 -0
  37. package/src/interaction/plugins/webhook.ts +10 -1
  38. package/src/pipeline/stages/execution.ts +33 -1
  39. package/src/pipeline/stages/routing.ts +18 -7
  40. package/src/pipeline/types.ts +10 -0
  41. package/src/prd/schema.ts +249 -0
  42. package/src/tdd/orchestrator.ts +7 -0
  43. package/src/tdd/rectification-gate.ts +6 -0
  44. package/src/tdd/session-runner.ts +15 -2
  45. package/src/utils/git.ts +30 -0
  46. package/src/verification/runners.ts +10 -1
@@ -10,8 +10,17 @@ import { resolveModel } from "../config";
10
10
  import { getLogger } from "../logger";
11
11
  import type { UserStory } from "../prd";
12
12
  import { PromptBuilder } from "../prompts";
13
- import { autoCommitIfDirty } from "../utils/git";
13
+ import { autoCommitIfDirty as _autoCommitIfDirtyFn } from "../utils/git";
14
14
  import { cleanupProcessTree } from "./cleanup";
15
+
16
+ /**
17
+ * Injectable dependencies for session-runner — allows tests to mock
18
+ * autoCommitIfDirty without going through internal git deps.
19
+ * @internal
20
+ */
21
+ export const _sessionRunnerDeps = {
22
+ autoCommitIfDirty: _autoCommitIfDirtyFn,
23
+ };
15
24
  import { getChangedFiles, verifyImplementerIsolation, verifyTestWriterIsolation } from "./isolation";
16
25
  import type { IsolationCheck } from "./types";
17
26
  import type { TddSessionResult, TddSessionRole } from "./types";
@@ -84,6 +93,7 @@ export async function runTddSession(
84
93
  lite = false,
85
94
  skipIsolation = false,
86
95
  constitution?: string,
96
+ featureName?: string,
87
97
  ): Promise<TddSessionResult> {
88
98
  const startTime = Date.now();
89
99
 
@@ -130,6 +140,9 @@ export async function runTddSession(
130
140
  modelDef: resolveModel(config.models[modelTier]),
131
141
  timeoutSeconds: config.execution.sessionTimeoutSeconds,
132
142
  dangerouslySkipPermissions: config.execution.dangerouslySkipPermissions,
143
+ featureName,
144
+ storyId: story.id,
145
+ sessionRole: role,
133
146
  });
134
147
 
135
148
  // BUG-21 Fix: Clean up orphaned child processes if agent failed
@@ -154,7 +167,7 @@ export async function runTddSession(
154
167
  }
155
168
 
156
169
  // BUG-058: Auto-commit if agent left uncommitted changes
157
- await autoCommitIfDirty(workdir, "tdd", role, story.id);
170
+ await _sessionRunnerDeps.autoCommitIfDirty(workdir, "tdd", role, story.id);
158
171
 
159
172
  // Check isolation based on role and skipIsolation flag.
160
173
  let isolation: IsolationCheck | undefined;
package/src/utils/git.ts CHANGED
@@ -153,6 +153,36 @@ export function detectMergeConflict(output: string): boolean {
153
153
  export async function autoCommitIfDirty(workdir: string, stage: string, role: string, storyId: string): Promise<void> {
154
154
  const logger = getSafeLogger();
155
155
  try {
156
+ // Guard: only auto-commit if workdir IS the git repository root.
157
+ // Without this, a workdir nested inside another git repo (e.g. a temp dir
158
+ // created inside the nax repo during tests) would cause git to walk up and
159
+ // commit files from the parent repo instead.
160
+ const topLevelProc = _gitDeps.spawn(["git", "rev-parse", "--show-toplevel"], {
161
+ cwd: workdir,
162
+ stdout: "pipe",
163
+ stderr: "pipe",
164
+ });
165
+ const gitRoot = (await new Response(topLevelProc.stdout).text()).trim();
166
+ await topLevelProc.exited;
167
+
168
+ // Normalize paths to handle symlinks (e.g. /tmp → /private/tmp on macOS)
169
+ const { realpathSync } = await import("node:fs");
170
+ const realWorkdir = (() => {
171
+ try {
172
+ return realpathSync(workdir);
173
+ } catch {
174
+ return workdir;
175
+ }
176
+ })();
177
+ const realGitRoot = (() => {
178
+ try {
179
+ return realpathSync(gitRoot);
180
+ } catch {
181
+ return gitRoot;
182
+ }
183
+ })();
184
+ if (realWorkdir !== realGitRoot) return;
185
+
156
186
  const statusProc = _gitDeps.spawn(["git", "status", "--porcelain"], {
157
187
  cwd: workdir,
158
188
  stdout: "pipe",
@@ -113,8 +113,17 @@ export async function scoped(options: VerificationGateOptions): Promise<Verifica
113
113
  return runVerificationCore({ ...options, command: scopedCommand });
114
114
  }
115
115
 
116
+ /**
117
+ * Injectable dependencies for regression() — allows tests to replace
118
+ * the 2s agent-cleanup sleep with a no-op without touching production behaviour.
119
+ * @internal
120
+ */
121
+ export const _regressionRunnerDeps = {
122
+ sleep: (ms: number): Promise<void> => Bun.sleep(ms),
123
+ };
124
+
116
125
  /** Quick smoke test — no asset verification, 2s delay to let agent processes terminate. */
117
126
  export async function regression(options: VerificationGateOptions): Promise<VerificationResult> {
118
- await Bun.sleep(2000);
127
+ await _regressionRunnerDeps.sleep(2000);
119
128
  return runVerificationCore({ ...options, expectedFiles: undefined });
120
129
  }