@nathapp/nax 0.50.3 → 0.51.2

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 (353) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +177 -104
  3. package/dist/nax.js +417 -213
  4. package/package.json +1 -3
  5. package/bin/nax.ts +0 -1195
  6. package/src/acceptance/fix-generator.ts +0 -322
  7. package/src/acceptance/generator.ts +0 -415
  8. package/src/acceptance/index.ts +0 -42
  9. package/src/acceptance/refinement.ts +0 -224
  10. package/src/acceptance/templates/cli.ts +0 -47
  11. package/src/acceptance/templates/component.ts +0 -78
  12. package/src/acceptance/templates/e2e.ts +0 -43
  13. package/src/acceptance/templates/index.ts +0 -21
  14. package/src/acceptance/templates/snapshot.ts +0 -50
  15. package/src/acceptance/templates/unit.ts +0 -48
  16. package/src/acceptance/types.ts +0 -138
  17. package/src/agents/acp/adapter.ts +0 -888
  18. package/src/agents/acp/cost.ts +0 -9
  19. package/src/agents/acp/index.ts +0 -7
  20. package/src/agents/acp/interaction-bridge.ts +0 -126
  21. package/src/agents/acp/parser.ts +0 -119
  22. package/src/agents/acp/spawn-client.ts +0 -373
  23. package/src/agents/acp/types.ts +0 -22
  24. package/src/agents/aider/adapter.ts +0 -135
  25. package/src/agents/claude/adapter.ts +0 -258
  26. package/src/agents/claude/complete.ts +0 -80
  27. package/src/agents/claude/cost.ts +0 -16
  28. package/src/agents/claude/execution.ts +0 -215
  29. package/src/agents/claude/index.ts +0 -3
  30. package/src/agents/claude/interactive.ts +0 -77
  31. package/src/agents/claude/plan.ts +0 -179
  32. package/src/agents/codex/adapter.ts +0 -153
  33. package/src/agents/cost/calculate.ts +0 -154
  34. package/src/agents/cost/index.ts +0 -10
  35. package/src/agents/cost/parse.ts +0 -97
  36. package/src/agents/cost/pricing.ts +0 -59
  37. package/src/agents/cost/types.ts +0 -45
  38. package/src/agents/gemini/adapter.ts +0 -177
  39. package/src/agents/index.ts +0 -18
  40. package/src/agents/opencode/adapter.ts +0 -106
  41. package/src/agents/registry.ts +0 -136
  42. package/src/agents/shared/decompose.ts +0 -154
  43. package/src/agents/shared/model-resolution.ts +0 -43
  44. package/src/agents/shared/types-extended.ts +0 -164
  45. package/src/agents/shared/validation.ts +0 -69
  46. package/src/agents/shared/version-detection.ts +0 -109
  47. package/src/agents/types.ts +0 -205
  48. package/src/analyze/classifier.ts +0 -282
  49. package/src/analyze/index.ts +0 -16
  50. package/src/analyze/scanner.ts +0 -171
  51. package/src/analyze/types.ts +0 -51
  52. package/src/cli/accept.ts +0 -108
  53. package/src/cli/agents.ts +0 -87
  54. package/src/cli/analyze-parser.ts +0 -291
  55. package/src/cli/analyze.ts +0 -352
  56. package/src/cli/config-descriptions.ts +0 -219
  57. package/src/cli/config-diff.ts +0 -103
  58. package/src/cli/config-display.ts +0 -285
  59. package/src/cli/config-get.ts +0 -55
  60. package/src/cli/config.ts +0 -14
  61. package/src/cli/constitution.ts +0 -17
  62. package/src/cli/diagnose-analysis.ts +0 -159
  63. package/src/cli/diagnose-formatter.ts +0 -87
  64. package/src/cli/diagnose.ts +0 -203
  65. package/src/cli/generate.ts +0 -250
  66. package/src/cli/index.ts +0 -42
  67. package/src/cli/init-context.ts +0 -405
  68. package/src/cli/init-detect.ts +0 -303
  69. package/src/cli/init.ts +0 -296
  70. package/src/cli/interact.ts +0 -295
  71. package/src/cli/plan.ts +0 -509
  72. package/src/cli/plugins.ts +0 -122
  73. package/src/cli/prompts-export.ts +0 -58
  74. package/src/cli/prompts-init.ts +0 -200
  75. package/src/cli/prompts-main.ts +0 -183
  76. package/src/cli/prompts-shared.ts +0 -70
  77. package/src/cli/prompts-tdd.ts +0 -88
  78. package/src/cli/prompts.ts +0 -17
  79. package/src/cli/runs.ts +0 -174
  80. package/src/cli/status-cost.ts +0 -151
  81. package/src/cli/status-features.ts +0 -405
  82. package/src/cli/status.ts +0 -13
  83. package/src/commands/common.ts +0 -171
  84. package/src/commands/diagnose.ts +0 -17
  85. package/src/commands/index.ts +0 -9
  86. package/src/commands/logs-formatter.ts +0 -201
  87. package/src/commands/logs-reader.ts +0 -171
  88. package/src/commands/logs.ts +0 -103
  89. package/src/commands/precheck.ts +0 -86
  90. package/src/commands/runs.ts +0 -220
  91. package/src/commands/unlock.ts +0 -96
  92. package/src/config/defaults.ts +0 -218
  93. package/src/config/index.ts +0 -22
  94. package/src/config/loader.ts +0 -143
  95. package/src/config/merge.ts +0 -106
  96. package/src/config/merger.ts +0 -147
  97. package/src/config/path-security.ts +0 -121
  98. package/src/config/paths.ts +0 -27
  99. package/src/config/permissions.ts +0 -63
  100. package/src/config/runtime-types.ts +0 -522
  101. package/src/config/schema-types.ts +0 -53
  102. package/src/config/schema.ts +0 -60
  103. package/src/config/schemas.ts +0 -426
  104. package/src/config/test-strategy.ts +0 -71
  105. package/src/config/types.ts +0 -57
  106. package/src/config/validate.ts +0 -103
  107. package/src/constitution/generator.ts +0 -158
  108. package/src/constitution/generators/aider.ts +0 -41
  109. package/src/constitution/generators/claude.ts +0 -35
  110. package/src/constitution/generators/cursor.ts +0 -36
  111. package/src/constitution/generators/opencode.ts +0 -38
  112. package/src/constitution/generators/types.ts +0 -33
  113. package/src/constitution/generators/windsurf.ts +0 -36
  114. package/src/constitution/index.ts +0 -11
  115. package/src/constitution/loader.ts +0 -121
  116. package/src/constitution/types.ts +0 -31
  117. package/src/context/auto-detect.ts +0 -228
  118. package/src/context/builder.ts +0 -299
  119. package/src/context/elements.ts +0 -122
  120. package/src/context/formatter.ts +0 -107
  121. package/src/context/generator.ts +0 -343
  122. package/src/context/generators/aider.ts +0 -34
  123. package/src/context/generators/claude.ts +0 -28
  124. package/src/context/generators/codex.ts +0 -28
  125. package/src/context/generators/cursor.ts +0 -28
  126. package/src/context/generators/gemini.ts +0 -28
  127. package/src/context/generators/opencode.ts +0 -30
  128. package/src/context/generators/windsurf.ts +0 -28
  129. package/src/context/greenfield.ts +0 -114
  130. package/src/context/index.ts +0 -34
  131. package/src/context/injector.ts +0 -279
  132. package/src/context/parent-context.ts +0 -39
  133. package/src/context/test-scanner.ts +0 -370
  134. package/src/context/types.ts +0 -98
  135. package/src/decompose/apply.ts +0 -50
  136. package/src/decompose/builder.ts +0 -181
  137. package/src/decompose/index.ts +0 -8
  138. package/src/decompose/sections/codebase.ts +0 -26
  139. package/src/decompose/sections/constraints.ts +0 -32
  140. package/src/decompose/sections/index.ts +0 -4
  141. package/src/decompose/sections/sibling-stories.ts +0 -25
  142. package/src/decompose/sections/target-story.ts +0 -31
  143. package/src/decompose/types.ts +0 -55
  144. package/src/decompose/validators/complexity.ts +0 -45
  145. package/src/decompose/validators/coverage.ts +0 -134
  146. package/src/decompose/validators/dependency.ts +0 -91
  147. package/src/decompose/validators/index.ts +0 -35
  148. package/src/decompose/validators/overlap.ts +0 -128
  149. package/src/errors.ts +0 -67
  150. package/src/execution/batching.ts +0 -157
  151. package/src/execution/crash-heartbeat.ts +0 -77
  152. package/src/execution/crash-recovery.ts +0 -79
  153. package/src/execution/crash-signals.ts +0 -165
  154. package/src/execution/crash-writer.ts +0 -154
  155. package/src/execution/deferred-review.ts +0 -105
  156. package/src/execution/dry-run.ts +0 -81
  157. package/src/execution/escalation/escalation.ts +0 -46
  158. package/src/execution/escalation/index.ts +0 -13
  159. package/src/execution/escalation/tier-escalation.ts +0 -346
  160. package/src/execution/escalation/tier-outcome.ts +0 -143
  161. package/src/execution/executor-types.ts +0 -73
  162. package/src/execution/helpers.ts +0 -38
  163. package/src/execution/index.ts +0 -27
  164. package/src/execution/iteration-runner.ts +0 -160
  165. package/src/execution/lifecycle/acceptance-loop.ts +0 -309
  166. package/src/execution/lifecycle/headless-formatter.ts +0 -83
  167. package/src/execution/lifecycle/index.ts +0 -11
  168. package/src/execution/lifecycle/parallel-lifecycle.ts +0 -101
  169. package/src/execution/lifecycle/precheck-runner.ts +0 -140
  170. package/src/execution/lifecycle/run-cleanup.ts +0 -81
  171. package/src/execution/lifecycle/run-completion.ts +0 -247
  172. package/src/execution/lifecycle/run-initialization.ts +0 -187
  173. package/src/execution/lifecycle/run-regression.ts +0 -305
  174. package/src/execution/lifecycle/run-setup.ts +0 -240
  175. package/src/execution/lifecycle/story-size-prompts.ts +0 -123
  176. package/src/execution/lock.ts +0 -129
  177. package/src/execution/parallel-coordinator.ts +0 -281
  178. package/src/execution/parallel-executor-rectification-pass.ts +0 -117
  179. package/src/execution/parallel-executor-rectify.ts +0 -136
  180. package/src/execution/parallel-executor.ts +0 -330
  181. package/src/execution/parallel-worker.ts +0 -149
  182. package/src/execution/parallel.ts +0 -13
  183. package/src/execution/pid-registry.ts +0 -275
  184. package/src/execution/pipeline-result-handler.ts +0 -221
  185. package/src/execution/progress.ts +0 -27
  186. package/src/execution/queue-handler.ts +0 -109
  187. package/src/execution/runner-completion.ts +0 -171
  188. package/src/execution/runner-execution.ts +0 -243
  189. package/src/execution/runner-setup.ts +0 -86
  190. package/src/execution/runner.ts +0 -265
  191. package/src/execution/sequential-executor.ts +0 -219
  192. package/src/execution/status-file.ts +0 -264
  193. package/src/execution/status-writer.ts +0 -181
  194. package/src/execution/story-context.ts +0 -266
  195. package/src/execution/story-selector.ts +0 -76
  196. package/src/execution/test-output-parser.ts +0 -14
  197. package/src/execution/timeout-handler.ts +0 -100
  198. package/src/hooks/index.ts +0 -2
  199. package/src/hooks/runner.ts +0 -280
  200. package/src/hooks/types.ts +0 -79
  201. package/src/interaction/chain.ts +0 -170
  202. package/src/interaction/index.ts +0 -61
  203. package/src/interaction/init.ts +0 -84
  204. package/src/interaction/plugins/auto.ts +0 -243
  205. package/src/interaction/plugins/cli.ts +0 -300
  206. package/src/interaction/plugins/telegram.ts +0 -384
  207. package/src/interaction/plugins/webhook.ts +0 -286
  208. package/src/interaction/state.ts +0 -171
  209. package/src/interaction/triggers.ts +0 -250
  210. package/src/interaction/types.ts +0 -170
  211. package/src/logger/formatters.ts +0 -84
  212. package/src/logger/index.ts +0 -16
  213. package/src/logger/logger.ts +0 -296
  214. package/src/logger/types.ts +0 -48
  215. package/src/logging/formatter.ts +0 -355
  216. package/src/logging/index.ts +0 -22
  217. package/src/logging/types.ts +0 -93
  218. package/src/metrics/aggregator.ts +0 -191
  219. package/src/metrics/index.ts +0 -14
  220. package/src/metrics/tracker.ts +0 -200
  221. package/src/metrics/types.ts +0 -115
  222. package/src/optimizer/index.ts +0 -63
  223. package/src/optimizer/noop.optimizer.ts +0 -24
  224. package/src/optimizer/rule-based.optimizer.ts +0 -248
  225. package/src/optimizer/types.ts +0 -53
  226. package/src/pipeline/event-bus.ts +0 -297
  227. package/src/pipeline/events.ts +0 -130
  228. package/src/pipeline/index.ts +0 -19
  229. package/src/pipeline/runner.ts +0 -149
  230. package/src/pipeline/stages/acceptance-setup.ts +0 -144
  231. package/src/pipeline/stages/acceptance.ts +0 -215
  232. package/src/pipeline/stages/autofix.ts +0 -262
  233. package/src/pipeline/stages/completion.ts +0 -110
  234. package/src/pipeline/stages/constitution.ts +0 -63
  235. package/src/pipeline/stages/context.ts +0 -122
  236. package/src/pipeline/stages/execution.ts +0 -359
  237. package/src/pipeline/stages/index.ts +0 -86
  238. package/src/pipeline/stages/optimizer.ts +0 -74
  239. package/src/pipeline/stages/prompt.ts +0 -79
  240. package/src/pipeline/stages/queue-check.ts +0 -103
  241. package/src/pipeline/stages/rectify.ts +0 -101
  242. package/src/pipeline/stages/regression.ts +0 -99
  243. package/src/pipeline/stages/review.ts +0 -94
  244. package/src/pipeline/stages/routing.ts +0 -276
  245. package/src/pipeline/stages/verify.ts +0 -286
  246. package/src/pipeline/subscribers/events-writer.ts +0 -135
  247. package/src/pipeline/subscribers/hooks.ts +0 -179
  248. package/src/pipeline/subscribers/interaction.ts +0 -103
  249. package/src/pipeline/subscribers/registry.ts +0 -73
  250. package/src/pipeline/subscribers/reporters.ts +0 -174
  251. package/src/pipeline/types.ts +0 -220
  252. package/src/plugins/extensions.ts +0 -225
  253. package/src/plugins/index.ts +0 -33
  254. package/src/plugins/loader.ts +0 -352
  255. package/src/plugins/plugin-logger.ts +0 -41
  256. package/src/plugins/registry.ts +0 -168
  257. package/src/plugins/types.ts +0 -206
  258. package/src/plugins/validator.ts +0 -352
  259. package/src/prd/index.ts +0 -220
  260. package/src/prd/schema.ts +0 -268
  261. package/src/prd/types.ts +0 -273
  262. package/src/prd/validate.ts +0 -41
  263. package/src/precheck/checks-agents.ts +0 -63
  264. package/src/precheck/checks-blockers.ts +0 -23
  265. package/src/precheck/checks-cli.ts +0 -68
  266. package/src/precheck/checks-config.ts +0 -102
  267. package/src/precheck/checks-git.ts +0 -117
  268. package/src/precheck/checks-system.ts +0 -101
  269. package/src/precheck/checks-warnings.ts +0 -221
  270. package/src/precheck/checks.ts +0 -36
  271. package/src/precheck/index.ts +0 -374
  272. package/src/precheck/story-size-gate.ts +0 -144
  273. package/src/precheck/types.ts +0 -31
  274. package/src/prompts/builder.ts +0 -166
  275. package/src/prompts/index.ts +0 -2
  276. package/src/prompts/loader.ts +0 -43
  277. package/src/prompts/sections/conventions.ts +0 -19
  278. package/src/prompts/sections/hermetic.ts +0 -41
  279. package/src/prompts/sections/index.ts +0 -12
  280. package/src/prompts/sections/isolation.ts +0 -70
  281. package/src/prompts/sections/role-task.ts +0 -182
  282. package/src/prompts/sections/story.ts +0 -55
  283. package/src/prompts/sections/verdict.ts +0 -70
  284. package/src/prompts/types.ts +0 -21
  285. package/src/queue/index.ts +0 -2
  286. package/src/queue/manager.ts +0 -254
  287. package/src/queue/types.ts +0 -54
  288. package/src/review/index.ts +0 -8
  289. package/src/review/orchestrator.ts +0 -154
  290. package/src/review/runner.ts +0 -303
  291. package/src/review/types.ts +0 -70
  292. package/src/routing/batch-route.ts +0 -35
  293. package/src/routing/builder.ts +0 -81
  294. package/src/routing/chain.ts +0 -75
  295. package/src/routing/content-hash.ts +0 -25
  296. package/src/routing/index.ts +0 -20
  297. package/src/routing/loader.ts +0 -62
  298. package/src/routing/router.ts +0 -305
  299. package/src/routing/strategies/adaptive.ts +0 -215
  300. package/src/routing/strategies/index.ts +0 -8
  301. package/src/routing/strategies/keyword.ts +0 -180
  302. package/src/routing/strategies/llm-prompts.ts +0 -224
  303. package/src/routing/strategies/llm.ts +0 -320
  304. package/src/routing/strategies/manual.ts +0 -50
  305. package/src/routing/strategy.ts +0 -102
  306. package/src/tdd/cleanup.ts +0 -120
  307. package/src/tdd/index.ts +0 -22
  308. package/src/tdd/isolation.ts +0 -117
  309. package/src/tdd/orchestrator.ts +0 -406
  310. package/src/tdd/prompts.ts +0 -40
  311. package/src/tdd/rectification-gate.ts +0 -274
  312. package/src/tdd/session-runner.ts +0 -263
  313. package/src/tdd/types.ts +0 -84
  314. package/src/tdd/verdict-reader.ts +0 -266
  315. package/src/tdd/verdict.ts +0 -152
  316. package/src/tui/App.tsx +0 -265
  317. package/src/tui/components/AgentPanel.tsx +0 -75
  318. package/src/tui/components/CostOverlay.tsx +0 -118
  319. package/src/tui/components/HelpOverlay.tsx +0 -107
  320. package/src/tui/components/StatusBar.tsx +0 -63
  321. package/src/tui/components/StoriesPanel.tsx +0 -177
  322. package/src/tui/hooks/useKeyboard.ts +0 -142
  323. package/src/tui/hooks/useLayout.ts +0 -137
  324. package/src/tui/hooks/usePipelineEvents.ts +0 -183
  325. package/src/tui/hooks/usePty.ts +0 -189
  326. package/src/tui/index.tsx +0 -38
  327. package/src/tui/types.ts +0 -76
  328. package/src/utils/errors.ts +0 -12
  329. package/src/utils/git.ts +0 -245
  330. package/src/utils/json-file.ts +0 -72
  331. package/src/utils/log-test-output.ts +0 -25
  332. package/src/utils/path-security.ts +0 -73
  333. package/src/utils/queue-writer.ts +0 -54
  334. package/src/verification/crash-detector.ts +0 -34
  335. package/src/verification/executor.ts +0 -250
  336. package/src/verification/index.ts +0 -12
  337. package/src/verification/orchestrator-types.ts +0 -154
  338. package/src/verification/orchestrator.ts +0 -76
  339. package/src/verification/parser.ts +0 -220
  340. package/src/verification/rectification-loop.ts +0 -172
  341. package/src/verification/rectification.ts +0 -108
  342. package/src/verification/runners.ts +0 -129
  343. package/src/verification/smart-runner.ts +0 -307
  344. package/src/verification/strategies/acceptance.ts +0 -136
  345. package/src/verification/strategies/regression.ts +0 -90
  346. package/src/verification/strategies/scoped.ts +0 -154
  347. package/src/verification/types.ts +0 -117
  348. package/src/version.ts +0 -40
  349. package/src/worktree/dispatcher.ts +0 -6
  350. package/src/worktree/index.ts +0 -2
  351. package/src/worktree/manager.ts +0 -193
  352. package/src/worktree/merge.ts +0 -302
  353. package/src/worktree/types.ts +0 -4
@@ -1,59 +0,0 @@
1
- /**
2
- * Cost rate tables for all supported model tiers and specific models.
3
- */
4
-
5
- import type { ModelTier } from "../../config/schema";
6
- import type { ModelCostRates } from "./types";
7
-
8
- /** Model tier cost rates (as of 2025-01) */
9
- export const COST_RATES: Record<ModelTier, ModelCostRates> = {
10
- fast: {
11
- // Haiku 4.5
12
- inputPer1M: 0.8,
13
- outputPer1M: 4.0,
14
- },
15
- balanced: {
16
- // Sonnet 4.5
17
- inputPer1M: 3.0,
18
- outputPer1M: 15.0,
19
- },
20
- powerful: {
21
- // Opus 4
22
- inputPer1M: 15.0,
23
- outputPer1M: 75.0,
24
- },
25
- };
26
-
27
- /** Per-model pricing in $/1M tokens: { input, output } */
28
- export const MODEL_PRICING: Record<
29
- string,
30
- { input: number; output: number; cacheRead?: number; cacheCreation?: number }
31
- > = {
32
- // Anthropic Claude models (short aliases)
33
- sonnet: { input: 3, output: 15 },
34
- haiku: { input: 0.8, output: 4.0, cacheRead: 0.1, cacheCreation: 1.0 },
35
- opus: { input: 15, output: 75 },
36
-
37
- // Anthropic Claude models (full names)
38
- "claude-sonnet-4": { input: 3, output: 15 },
39
- "claude-sonnet-4-5": { input: 3, output: 15 },
40
- "claude-sonnet-4-6": { input: 3, output: 15 },
41
- "claude-haiku": { input: 0.8, output: 4.0, cacheRead: 0.1, cacheCreation: 1.0 },
42
- "claude-haiku-4-5": { input: 0.8, output: 4.0, cacheRead: 0.1, cacheCreation: 1.0 },
43
- "claude-opus": { input: 15, output: 75 },
44
- "claude-opus-4": { input: 15, output: 75 },
45
- "claude-opus-4-6": { input: 15, output: 75 },
46
-
47
- // OpenAI models
48
- "gpt-4.1": { input: 10, output: 30 },
49
- "gpt-4": { input: 30, output: 60 },
50
- "gpt-3.5-turbo": { input: 0.5, output: 1.5 },
51
-
52
- // Google Gemini
53
- "gemini-2.5-pro": { input: 0.075, output: 0.3 },
54
- "gemini-2-pro": { input: 0.075, output: 0.3 },
55
-
56
- // OpenAI Codex
57
- codex: { input: 0.02, output: 0.06 },
58
- "code-davinci-002": { input: 0.02, output: 0.06 },
59
- };
@@ -1,45 +0,0 @@
1
- /**
2
- * Cost tracking types — shared across all agent adapters.
3
- */
4
-
5
- import type { ModelTier } from "../../config/schema";
6
-
7
- export type { ModelTier };
8
-
9
- /** Cost rates per 1M tokens (USD) */
10
- export interface ModelCostRates {
11
- inputPer1M: number;
12
- outputPer1M: number;
13
- }
14
-
15
- /** Token usage data (camelCase — nax-internal representation) */
16
- export interface TokenUsage {
17
- inputTokens: number;
18
- outputTokens: number;
19
- }
20
-
21
- /** Cost estimate with confidence indicator */
22
- export interface CostEstimate {
23
- cost: number;
24
- confidence: "exact" | "estimated" | "fallback";
25
- }
26
-
27
- /** Token usage with confidence indicator */
28
- export interface TokenUsageWithConfidence {
29
- inputTokens: number;
30
- outputTokens: number;
31
- confidence: "exact" | "estimated";
32
- }
33
-
34
- /**
35
- * Token usage from an ACP session's cumulative_token_usage field.
36
- * Uses snake_case to match the ACP wire format.
37
- */
38
- export interface SessionTokenUsage {
39
- input_tokens: number;
40
- output_tokens: number;
41
- /** Cache read tokens — billed at a reduced rate */
42
- cache_read_input_tokens?: number;
43
- /** Cache creation tokens — billed at a higher creation rate */
44
- cache_creation_input_tokens?: number;
45
- }
@@ -1,177 +0,0 @@
1
- /**
2
- * Gemini CLI Agent Adapter — implements AgentAdapter interface
3
- *
4
- * Provides uniform interface for spawning Gemini CLI processes,
5
- * supporting one-shot completions via 'gemini -p' and Google auth detection.
6
- */
7
-
8
- import type {
9
- AgentAdapter,
10
- AgentCapabilities,
11
- AgentResult,
12
- AgentRunOptions,
13
- CompleteOptions,
14
- DecomposeOptions,
15
- DecomposeResult,
16
- PlanOptions,
17
- PlanResult,
18
- } from "../types";
19
- import { CompleteError } from "../types";
20
-
21
- // ─────────────────────────────────────────────────────────────────────────────
22
- // Injectable dependencies — follows the _deps pattern
23
- // Replaced in unit tests to intercept Bun.spawn/Bun.which calls.
24
- // ─────────────────────────────────────────────────────────────────────────────
25
-
26
- export const _geminiRunDeps = {
27
- which(name: string): string | null {
28
- return Bun.which(name);
29
- },
30
- spawn(
31
- cmd: string[],
32
- opts: { cwd?: string; stdout: "pipe"; stderr: "pipe" | "inherit"; env?: Record<string, string | undefined> },
33
- ): {
34
- stdout: ReadableStream<Uint8Array>;
35
- stderr: ReadableStream<Uint8Array>;
36
- exited: Promise<number>;
37
- pid: number;
38
- kill(signal?: number | NodeJS.Signals): void;
39
- } {
40
- return Bun.spawn(cmd, opts) as unknown as {
41
- stdout: ReadableStream<Uint8Array>;
42
- stderr: ReadableStream<Uint8Array>;
43
- exited: Promise<number>;
44
- pid: number;
45
- kill(signal?: number | NodeJS.Signals): void;
46
- };
47
- },
48
- };
49
-
50
- export const _geminiCompleteDeps = {
51
- spawn(
52
- cmd: string[],
53
- opts: { stdout: "pipe"; stderr: "pipe" | "inherit" },
54
- ): {
55
- stdout: ReadableStream<Uint8Array>;
56
- stderr: ReadableStream<Uint8Array>;
57
- exited: Promise<number>;
58
- pid: number;
59
- } {
60
- return Bun.spawn(cmd, opts) as unknown as {
61
- stdout: ReadableStream<Uint8Array>;
62
- stderr: ReadableStream<Uint8Array>;
63
- exited: Promise<number>;
64
- pid: number;
65
- };
66
- },
67
- };
68
-
69
- // ─────────────────────────────────────────────────────────────────────────────
70
- // GeminiAdapter implementation
71
- // ─────────────────────────────────────────────────────────────────────────────
72
-
73
- const MAX_AGENT_OUTPUT_CHARS = 5000;
74
-
75
- export class GeminiAdapter implements AgentAdapter {
76
- readonly name = "gemini";
77
- readonly displayName = "Gemini CLI";
78
- readonly binary = "gemini";
79
-
80
- readonly capabilities: AgentCapabilities = {
81
- supportedTiers: ["fast", "balanced", "powerful"],
82
- maxContextTokens: 1_000_000,
83
- features: new Set<"tdd" | "review" | "refactor" | "batch">(["tdd", "review", "refactor"]),
84
- };
85
-
86
- async isInstalled(): Promise<boolean> {
87
- const path = _geminiRunDeps.which("gemini");
88
- if (path === null) {
89
- return false;
90
- }
91
-
92
- // Check Google auth — run 'gemini' with a flag that shows auth status
93
- try {
94
- const proc = _geminiRunDeps.spawn(["gemini", "--version"], {
95
- stdout: "pipe",
96
- stderr: "pipe",
97
- });
98
-
99
- const exitCode = await proc.exited;
100
- if (exitCode !== 0) {
101
- return false;
102
- }
103
-
104
- const stdout = await new Response(proc.stdout).text();
105
- const lowerOut = stdout.toLowerCase();
106
-
107
- // If output explicitly says "not logged in", auth has failed
108
- if (lowerOut.includes("not logged in")) {
109
- return false;
110
- }
111
-
112
- return true;
113
- } catch {
114
- return false;
115
- }
116
- }
117
-
118
- buildCommand(options: AgentRunOptions): string[] {
119
- return ["gemini", "-p", options.prompt];
120
- }
121
-
122
- async run(options: AgentRunOptions): Promise<AgentResult> {
123
- const cmd = this.buildCommand(options);
124
- const startTime = Date.now();
125
-
126
- const proc = _geminiRunDeps.spawn(cmd, {
127
- cwd: options.workdir,
128
- stdout: "pipe",
129
- stderr: "inherit",
130
- });
131
-
132
- const exitCode = await proc.exited;
133
- const stdout = await new Response(proc.stdout).text();
134
- const durationMs = Date.now() - startTime;
135
-
136
- return {
137
- success: exitCode === 0,
138
- exitCode,
139
- output: stdout.slice(-MAX_AGENT_OUTPUT_CHARS),
140
- rateLimited: false,
141
- durationMs,
142
- estimatedCost: 0,
143
- pid: proc.pid,
144
- };
145
- }
146
-
147
- async complete(prompt: string, _options?: CompleteOptions): Promise<string> {
148
- const cmd = ["gemini", "-p", prompt];
149
-
150
- const proc = _geminiCompleteDeps.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
151
- const exitCode = await proc.exited;
152
-
153
- const stdout = await new Response(proc.stdout).text();
154
- const stderr = await new Response(proc.stderr).text();
155
- const trimmed = stdout.trim();
156
-
157
- if (exitCode !== 0) {
158
- const errorDetails = stderr.trim() || trimmed;
159
- const errorMessage = errorDetails || `complete() failed with exit code ${exitCode}`;
160
- throw new CompleteError(errorMessage, exitCode);
161
- }
162
-
163
- if (!trimmed) {
164
- throw new CompleteError("complete() returned empty output");
165
- }
166
-
167
- return trimmed;
168
- }
169
-
170
- async plan(_options: PlanOptions): Promise<PlanResult> {
171
- throw new Error("GeminiAdapter.plan() not implemented");
172
- }
173
-
174
- async decompose(_options: DecomposeOptions): Promise<DecomposeResult> {
175
- throw new Error("GeminiAdapter.decompose() not implemented");
176
- }
177
- }
@@ -1,18 +0,0 @@
1
- export type { AgentAdapter, AgentCapabilities, AgentResult, AgentRunOptions, CompleteOptions } from "./types";
2
- export { CompleteError } from "./types";
3
- export { ClaudeCodeAdapter } from "./claude";
4
- export { getAllAgentNames, getAgent, getInstalledAgents, checkAgentHealth } from "./registry";
5
- export type { ModelCostRates, TokenUsage, CostEstimate, TokenUsageWithConfidence, SessionTokenUsage } from "./cost";
6
- export {
7
- COST_RATES,
8
- MODEL_PRICING,
9
- parseTokenUsage,
10
- estimateCost,
11
- estimateCostFromOutput,
12
- estimateCostByDuration,
13
- formatCostWithConfidence,
14
- estimateCostFromTokenUsage,
15
- } from "./cost";
16
- export { validateAgentForTier, validateAgentFeature, describeAgentCapabilities } from "./shared/validation";
17
- export type { AgentVersionInfo } from "./shared/version-detection";
18
- export { getAgentVersion, getAgentVersions } from "./shared/version-detection";
@@ -1,106 +0,0 @@
1
- /**
2
- * OpenCode Agent Adapter — implements AgentAdapter interface
3
- *
4
- * Provides uniform interface for spawning OpenCode agent processes,
5
- * supporting one-shot completions.
6
- */
7
-
8
- import type {
9
- AgentAdapter,
10
- AgentCapabilities,
11
- AgentResult,
12
- AgentRunOptions,
13
- CompleteOptions,
14
- DecomposeOptions,
15
- DecomposeResult,
16
- PlanOptions,
17
- PlanResult,
18
- } from "../types";
19
- import { CompleteError } from "../types";
20
-
21
- // ─────────────────────────────────────────────────────────────────────────────
22
- // Injectable dependencies — matches the _deps pattern used in claude.ts
23
- // These are replaced in unit tests to intercept Bun.spawn calls.
24
- // ─────────────────────────────────────────────────────────────────────────────
25
-
26
- export const _opencodeCompleteDeps = {
27
- which(name: string): string | null {
28
- return Bun.which(name);
29
- },
30
- spawn(
31
- cmd: string[],
32
- opts: { stdout: "pipe"; stderr: "pipe" | "inherit" },
33
- ): {
34
- stdout: ReadableStream<Uint8Array>;
35
- stderr: ReadableStream<Uint8Array>;
36
- exited: Promise<number>;
37
- pid: number;
38
- } {
39
- return Bun.spawn(cmd, opts) as unknown as {
40
- stdout: ReadableStream<Uint8Array>;
41
- stderr: ReadableStream<Uint8Array>;
42
- exited: Promise<number>;
43
- pid: number;
44
- };
45
- },
46
- };
47
-
48
- // ─────────────────────────────────────────────────────────────────────────────
49
- // OpenCodeAdapter implementation
50
- // ─────────────────────────────────────────────────────────────────────────────
51
-
52
- export class OpenCodeAdapter implements AgentAdapter {
53
- readonly name = "opencode";
54
- readonly displayName = "OpenCode";
55
- readonly binary = "opencode";
56
-
57
- readonly capabilities: AgentCapabilities = {
58
- supportedTiers: ["fast", "balanced"],
59
- maxContextTokens: 8_000,
60
- features: new Set<"tdd" | "review" | "refactor" | "batch">(["tdd", "refactor"]),
61
- };
62
-
63
- async isInstalled(): Promise<boolean> {
64
- const path = _opencodeCompleteDeps.which("opencode");
65
- return path !== null;
66
- }
67
-
68
- buildCommand(_options: AgentRunOptions): string[] {
69
- throw new Error("OpenCodeAdapter.buildCommand() not implemented");
70
- }
71
-
72
- async run(_options: AgentRunOptions): Promise<AgentResult> {
73
- throw new Error("OpenCodeAdapter.run() not implemented");
74
- }
75
-
76
- async complete(prompt: string, _options?: CompleteOptions): Promise<string> {
77
- const cmd = ["opencode", "--prompt", prompt];
78
-
79
- const proc = _opencodeCompleteDeps.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
80
- const exitCode = await proc.exited;
81
-
82
- const stdout = await new Response(proc.stdout).text();
83
- const stderr = await new Response(proc.stderr).text();
84
- const trimmed = stdout.trim();
85
-
86
- if (exitCode !== 0) {
87
- const errorDetails = stderr.trim() || trimmed;
88
- const errorMessage = errorDetails || `complete() failed with exit code ${exitCode}`;
89
- throw new CompleteError(errorMessage, exitCode);
90
- }
91
-
92
- if (!trimmed) {
93
- throw new CompleteError("complete() returned empty output");
94
- }
95
-
96
- return trimmed;
97
- }
98
-
99
- async plan(_options: PlanOptions): Promise<PlanResult> {
100
- throw new Error("OpenCodeAdapter.plan() not implemented");
101
- }
102
-
103
- async decompose(_options: DecomposeOptions): Promise<DecomposeResult> {
104
- throw new Error("OpenCodeAdapter.decompose() not implemented");
105
- }
106
- }
@@ -1,136 +0,0 @@
1
- /**
2
- * Agent Registry
3
- *
4
- * Discovers and manages available coding agents.
5
- */
6
-
7
- import type { NaxConfig } from "../config/schema";
8
- import { getLogger } from "../logger";
9
- import { AcpAgentAdapter } from "./acp/adapter";
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
- import type { AgentAdapter } from "./types";
16
-
17
- /** All known agent adapters */
18
- export const ALL_AGENTS: AgentAdapter[] = [
19
- new ClaudeCodeAdapter(),
20
- new CodexAdapter(),
21
- new OpenCodeAdapter(),
22
- new GeminiAdapter(),
23
- new AiderAdapter(),
24
- ];
25
-
26
- /** Get all registered agent names */
27
- export function getAllAgentNames(): string[] {
28
- return ALL_AGENTS.map((a) => a.name);
29
- }
30
-
31
- /** Get a specific agent by name */
32
- export function getAgent(name: string): AgentAdapter | undefined {
33
- return ALL_AGENTS.find((a) => a.name === name);
34
- }
35
-
36
- /** Get all installed agents on this machine */
37
- export async function getInstalledAgents(): Promise<AgentAdapter[]> {
38
- const results = await Promise.all(
39
- ALL_AGENTS.map(async (agent) => ({
40
- agent,
41
- installed: await agent.isInstalled(),
42
- })),
43
- );
44
- return results.filter((r) => r.installed).map((r) => r.agent);
45
- }
46
-
47
- /** Check health of all agents */
48
- export async function checkAgentHealth(): Promise<Array<{ name: string; displayName: string; installed: boolean }>> {
49
- return Promise.all(
50
- ALL_AGENTS.map(async (agent) => ({
51
- name: agent.name,
52
- displayName: agent.displayName,
53
- installed: await agent.isInstalled(),
54
- })),
55
- );
56
- }
57
-
58
- /** Protocol-aware agent registry returned by createAgentRegistry() */
59
- export interface AgentRegistry {
60
- /** Get a specific agent, respecting the configured protocol */
61
- getAgent(name: string): AgentAdapter | undefined;
62
- /** Get all installed agents */
63
- getInstalledAgents(): Promise<AgentAdapter[]>;
64
- /** Check health of all agents */
65
- checkAgentHealth(): Promise<Array<{ name: string; displayName: string; installed: boolean }>>;
66
- /** Active protocol ('acp' | 'cli') */
67
- protocol: "acp" | "cli";
68
- }
69
-
70
- /**
71
- * Create a protocol-aware agent registry.
72
- *
73
- * When config.agent.protocol is 'acp', returns AcpAgentAdapter instances.
74
- * When 'cli' (or unset), returns legacy CLI adapters.
75
- * AcpAgentAdapter instances are cached per agent name for the lifetime of the registry.
76
- */
77
- export function createAgentRegistry(config: NaxConfig): AgentRegistry {
78
- const protocol: "acp" | "cli" = config.agent?.protocol ?? "cli";
79
- const logger = getLogger();
80
- const acpCache = new Map<string, AcpAgentAdapter>();
81
-
82
- // Log which protocol is being used at startup
83
- logger?.info("agents", `Agent protocol: ${protocol}`, { protocol, hasConfig: !!config.agent });
84
-
85
- function getAgent(name: string): AgentAdapter | undefined {
86
- if (protocol === "acp") {
87
- const known = ALL_AGENTS.find((a) => a.name === name);
88
- if (!known) return undefined;
89
- if (!acpCache.has(name)) {
90
- acpCache.set(name, new AcpAgentAdapter(name));
91
- logger?.debug("agents", `Created AcpAgentAdapter for ${name}`, { name, protocol });
92
- }
93
- return acpCache.get(name);
94
- }
95
- const adapter = ALL_AGENTS.find((a) => a.name === name);
96
- if (adapter) {
97
- logger?.debug("agents", `Using CLI adapter for ${name}: ${adapter.constructor.name}`, { name });
98
- }
99
- return adapter;
100
- }
101
-
102
- async function getInstalledAgents(): Promise<AgentAdapter[]> {
103
- const agents =
104
- protocol === "acp"
105
- ? ALL_AGENTS.map((a) => {
106
- if (!acpCache.has(a.name)) {
107
- acpCache.set(a.name, new AcpAgentAdapter(a.name));
108
- }
109
- return acpCache.get(a.name) as AcpAgentAdapter;
110
- })
111
- : ALL_AGENTS;
112
- const results = await Promise.all(agents.map(async (agent) => ({ agent, installed: await agent.isInstalled() })));
113
- return results.filter((r) => r.installed).map((r) => r.agent);
114
- }
115
-
116
- async function checkAgentHealth(): Promise<Array<{ name: string; displayName: string; installed: boolean }>> {
117
- const agents =
118
- protocol === "acp"
119
- ? ALL_AGENTS.map((a) => {
120
- if (!acpCache.has(a.name)) {
121
- acpCache.set(a.name, new AcpAgentAdapter(a.name));
122
- }
123
- return acpCache.get(a.name) as AcpAgentAdapter;
124
- })
125
- : ALL_AGENTS;
126
- return Promise.all(
127
- agents.map(async (agent) => ({
128
- name: agent.name,
129
- displayName: agent.displayName,
130
- installed: await agent.isInstalled(),
131
- })),
132
- );
133
- }
134
-
135
- return { getAgent, getInstalledAgents, checkAgentHealth, protocol };
136
- }
@@ -1,154 +0,0 @@
1
- /**
2
- * Claude Code Decompose Logic
3
- *
4
- * Extracted from claude.ts: decompose(), buildDecomposePrompt(),
5
- * parseDecomposeOutput(), validateComplexity()
6
- */
7
-
8
- import { COMPLEXITY_GUIDE, GROUPING_RULES, TEST_STRATEGY_GUIDE, resolveTestStrategy } from "../../config/test-strategy";
9
- import type { DecomposeOptions, DecomposeResult, DecomposedStory } from "../types";
10
-
11
- /**
12
- * Build the decompose prompt combining spec content and codebase context.
13
- */
14
- export function buildDecomposePrompt(options: DecomposeOptions): string {
15
- return `You are a requirements analyst. Break down the following feature specification into user stories and classify each story's complexity.
16
-
17
- CODEBASE CONTEXT:
18
- ${options.codebaseContext}
19
-
20
- FEATURE SPECIFICATION:
21
- ${options.specContent}
22
-
23
- Decompose this spec into user stories. For each story, provide:
24
- 1. id: Story ID (e.g., "US-001")
25
- 2. title: Concise story title
26
- 3. description: What needs to be implemented
27
- 4. acceptanceCriteria: Array of testable criteria
28
- 5. tags: Array of routing tags (e.g., ["security", "api"])
29
- 6. dependencies: Array of story IDs this depends on (e.g., ["US-001"])
30
- 7. complexity: "simple" | "medium" | "complex" | "expert"
31
- 8. contextFiles: Array of file paths to inject into agent prompt before execution
32
- 9. reasoning: Why this complexity level
33
- 10. estimatedLOC: Estimated lines of code to change
34
- 11. risks: Array of implementation risks
35
- 12. testStrategy: "test-after" | "tdd-simple" | "three-session-tdd" | "three-session-tdd-lite"
36
-
37
- ${COMPLEXITY_GUIDE}
38
-
39
- ${TEST_STRATEGY_GUIDE}
40
-
41
- ${GROUPING_RULES}
42
-
43
- Consider:
44
- 1. Does infrastructure exist? (e.g., "add caching" when no cache layer exists = complex)
45
- 2. How many files will be touched?
46
- 3. Are there cross-cutting concerns (auth, validation, error handling)?
47
- 4. Does it require new dependencies or architectural decisions?
48
-
49
- Respond with ONLY a JSON array (no markdown code fences):
50
- [{
51
- "id": "US-001",
52
- "title": "Story title",
53
- "description": "Story description",
54
- "acceptanceCriteria": ["Criterion 1", "Criterion 2"],
55
- "tags": ["tag1"],
56
- "dependencies": [],
57
- "complexity": "medium",
58
- "contextFiles": ["src/path/to/file.ts"],
59
- "reasoning": "Why this complexity level",
60
- "estimatedLOC": 150,
61
- "risks": ["Risk 1"],
62
- "testStrategy": "test-after"
63
- }]`;
64
- }
65
-
66
- /**
67
- * Parse decompose output from agent stdout.
68
- *
69
- * Extracts JSON array from output, handles markdown code fences,
70
- * and validates structure.
71
- */
72
- export function parseDecomposeOutput(output: string): DecomposedStory[] {
73
- // Extract JSON from output (handles markdown code fences)
74
- const jsonMatch = output.match(/```(?:json)?\s*(\[[\s\S]*?\])\s*```/);
75
- let jsonText = jsonMatch ? jsonMatch[1] : output;
76
-
77
- // Try to find JSON array directly if no code fence
78
- if (!jsonMatch) {
79
- const arrayMatch = output.match(/\[[\s\S]*\]/);
80
- if (arrayMatch) {
81
- jsonText = arrayMatch[0];
82
- }
83
- }
84
-
85
- // Parse JSON
86
- let parsed: unknown;
87
- try {
88
- parsed = JSON.parse(jsonText.trim());
89
- } catch (error) {
90
- throw new Error(
91
- `Failed to parse decompose output as JSON: ${(error as Error).message}\n\nOutput:\n${output.slice(0, 500)}`,
92
- );
93
- }
94
-
95
- // Validate structure
96
- if (!Array.isArray(parsed)) {
97
- throw new Error("Decompose output is not an array");
98
- }
99
-
100
- // Map to DecomposedStory[] with validation
101
- const stories: DecomposedStory[] = parsed.map((item: unknown, index: number) => {
102
- // Type guard: ensure item is an object
103
- if (typeof item !== "object" || item === null) {
104
- throw new Error(`Story at index ${index} is not an object`);
105
- }
106
- const record = item as Record<string, unknown>;
107
- if (!record.id || typeof record.id !== "string") {
108
- throw new Error(`Story at index ${index} missing valid 'id' field`);
109
- }
110
- if (!record.title || typeof record.title !== "string") {
111
- throw new Error(`Story ${record.id} missing valid 'title' field`);
112
- }
113
-
114
- return {
115
- id: record.id,
116
- title: record.title,
117
- description: String(record.description || record.title),
118
- acceptanceCriteria: Array.isArray(record.acceptanceCriteria)
119
- ? record.acceptanceCriteria
120
- : ["Implementation complete"],
121
- tags: Array.isArray(record.tags) ? record.tags : [],
122
- dependencies: Array.isArray(record.dependencies) ? record.dependencies : [],
123
- complexity: coerceComplexity(record.complexity),
124
- // contextFiles: prefer the new field; fall back to legacy relevantFiles from older LLM responses
125
- contextFiles: Array.isArray(record.contextFiles)
126
- ? record.contextFiles
127
- : Array.isArray(record.relevantFiles)
128
- ? record.relevantFiles
129
- : [],
130
- relevantFiles: Array.isArray(record.relevantFiles) ? record.relevantFiles : [],
131
- reasoning: String(record.reasoning || "No reasoning provided"),
132
- estimatedLOC: Number(record.estimatedLOC) || 0,
133
- risks: Array.isArray(record.risks) ? record.risks : [],
134
- testStrategy: resolveTestStrategy(typeof record.testStrategy === "string" ? record.testStrategy : undefined),
135
- };
136
- });
137
-
138
- if (stories.length === 0) {
139
- throw new Error("Decompose returned empty story array");
140
- }
141
-
142
- return stories;
143
- }
144
-
145
- /**
146
- * Coerce complexity value from decompose output.
147
- */
148
- export function coerceComplexity(value: unknown): "simple" | "medium" | "complex" | "expert" {
149
- if (value === "simple" || value === "medium" || value === "complex" || value === "expert") {
150
- return value;
151
- }
152
- // Default to medium if invalid
153
- return "medium";
154
- }