@nathapp/nax 0.18.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.
Files changed (459) hide show
  1. package/.gitlab-ci.yml +96 -0
  2. package/BRIEF.md +140 -0
  3. package/CHANGELOG.md +60 -0
  4. package/CLAUDE.md +159 -0
  5. package/README.md +373 -0
  6. package/US-007-IMPLEMENTATION.md +139 -0
  7. package/bin/nax.ts +930 -0
  8. package/biome.json +14 -0
  9. package/bun.lock +168 -0
  10. package/bunfig.toml +11 -0
  11. package/docs/20260216-fix-plan-context-review.md +56 -0
  12. package/docs/20260216-relentless-vs-ngent-comparison.md +208 -0
  13. package/docs/20260216-v02-plan.md +136 -0
  14. package/docs/20260216-v02-review.md +685 -0
  15. package/docs/20260217-dogfood-findings.md +56 -0
  16. package/docs/20260217-p2-plus-plan.md +117 -0
  17. package/docs/20260217-partial-fixes-plan.md +62 -0
  18. package/docs/20260217-plan-analyze-spec.md +117 -0
  19. package/docs/20260217-post-impl-review.md +1137 -0
  20. package/docs/20260217-quick-wins-plan.md +66 -0
  21. package/docs/20260217-split-runner-plan.md +75 -0
  22. package/docs/20260217-v03-impl-plan.md +80 -0
  23. package/docs/20260217-v03-post-impl-review.md +589 -0
  24. package/docs/20260217-v04-impl-plan.md +86 -0
  25. package/docs/20260217-v05-post-impl-review.md +850 -0
  26. package/docs/20260217-v06-post-impl-review.md +817 -0
  27. package/docs/20260218-adr003-port-plan.md +151 -0
  28. package/docs/20260218-review-adr003-verification.md +175 -0
  29. package/docs/20260219-fix-plan-bug16-19.md +79 -0
  30. package/docs/20260219-fix-plan-bug20-22.md +114 -0
  31. package/docs/20260219-plan-llm-routing.md +116 -0
  32. package/docs/20260219-review-bug20-22-fixes.md +135 -0
  33. package/docs/20260219-routing-baseline-keyword.md +63 -0
  34. package/docs/20260220-plan-structured-logging-p1.md +80 -0
  35. package/docs/20260220-plan-structured-logging-p2.md +37 -0
  36. package/docs/20260220-review-llm-routing.md +180 -0
  37. package/docs/20260220-review-post-fix-llm-routing.md +70 -0
  38. package/docs/20260221-fix-plan-relevantfiles-split.md +101 -0
  39. package/docs/20260221-fix-plan-routing-mode.md +125 -0
  40. package/docs/20260221-review-v0.9-implementation.md +379 -0
  41. package/docs/20260222-fix-plan-v091-routing-isolation.md +197 -0
  42. package/docs/20260223-fix-plan-prompt-audit.md +62 -0
  43. package/docs/20260224-nax-roadmap-phases.md +189 -0
  44. package/docs/20260225-phase2-llm-service-layer.md +401 -0
  45. package/docs/20260225-review-v0.10.1.md +187 -0
  46. package/docs/20260303-v010-implementation-plan.md +165 -0
  47. package/docs/CLAUDE.md.bak +191 -0
  48. package/docs/ROADMAP.md +165 -0
  49. package/docs/SPEC-rectification.md +0 -0
  50. package/docs/SPEC.md +324 -0
  51. package/docs/US-001-plugin-loading-verification.md +152 -0
  52. package/docs/architecture-analysis.md +1076 -0
  53. package/docs/bugs/BUG-21-escalation-null-attempts.md +48 -0
  54. package/docs/bugs-from-dogfood-run-c.md +243 -0
  55. package/docs/code-review-20260228.md +612 -0
  56. package/docs/code-review-v0.15.0.md +629 -0
  57. package/docs/hook-lifecycle-test-plan.md +149 -0
  58. package/docs/releases/v0.11.0-and-earlier.md +20 -0
  59. package/docs/releases/v0.12.0.md +15 -0
  60. package/docs/releases/v0.13.0.md +14 -0
  61. package/docs/releases/v0.14.0.md +20 -0
  62. package/docs/releases/v0.14.1.md +36 -0
  63. package/docs/releases/v0.14.2.md +51 -0
  64. package/docs/releases/v0.14.3.md +174 -0
  65. package/docs/releases/v0.14.4.md +94 -0
  66. package/docs/releases/v0.15.0.md +502 -0
  67. package/docs/releases/v0.15.1.md +170 -0
  68. package/docs/releases/v0.15.3.md +193 -0
  69. package/docs/specs/status-file-v0.10.1.md +812 -0
  70. package/docs/v0.10-global-config.md +206 -0
  71. package/docs/v0.10-plugin-system.md +415 -0
  72. package/docs/v0.10-prompt-optimizer.md +234 -0
  73. package/docs/v0.3-spec.md +244 -0
  74. package/docs/v0.4-spec.md +140 -0
  75. package/docs/v0.5-spec.md +237 -0
  76. package/docs/v0.6-spec.md +371 -0
  77. package/docs/v0.7-spec.md +177 -0
  78. package/docs/v0.8-llm-routing.md +206 -0
  79. package/docs/v0.8-structured-logging.md +132 -0
  80. package/docs/v0.9.3-prompt-audit.md +112 -0
  81. package/examples/plugins/console-reporter/index.test.ts +207 -0
  82. package/examples/plugins/console-reporter/index.ts +110 -0
  83. package/nax/config.json +147 -0
  84. package/nax/features/bugfix-v0171/prd.json +52 -0
  85. package/nax/features/config-management/prd.json +108 -0
  86. package/nax/features/config-management/progress.txt +5 -0
  87. package/nax/features/diagnose/acceptance.test.ts +412 -0
  88. package/nax/features/diagnose/prd.json +41 -0
  89. package/nax/features/orchestration-fixes/prd.json +89 -0
  90. package/nax/features/orchestration-fixes/progress.txt +1 -0
  91. package/nax/features/plugin-integration/US-007-VERIFICATION.md +259 -0
  92. package/nax/features/plugin-integration/prd.json +208 -0
  93. package/nax/features/plugin-integration/progress.txt +5 -0
  94. package/nax/features/precheck/prd.json +205 -0
  95. package/nax/features/precheck/progress.txt +15 -0
  96. package/nax/features/structured-logging/prd.json +199 -0
  97. package/nax/features/unlock/prd.json +36 -0
  98. package/package.json +47 -0
  99. package/src/acceptance/fix-generator.ts +348 -0
  100. package/src/acceptance/generator.ts +282 -0
  101. package/src/acceptance/index.ts +30 -0
  102. package/src/acceptance/types.ts +79 -0
  103. package/src/agents/claude-decompose.ts +169 -0
  104. package/src/agents/claude-plan.ts +139 -0
  105. package/src/agents/claude.ts +324 -0
  106. package/src/agents/cost.ts +268 -0
  107. package/src/agents/index.ts +13 -0
  108. package/src/agents/registry.ts +48 -0
  109. package/src/agents/types-extended.ts +133 -0
  110. package/src/agents/types.ts +113 -0
  111. package/src/agents/validation.ts +69 -0
  112. package/src/analyze/classifier.ts +305 -0
  113. package/src/analyze/index.ts +16 -0
  114. package/src/analyze/scanner.ts +175 -0
  115. package/src/analyze/types.ts +51 -0
  116. package/src/cli/accept.ts +108 -0
  117. package/src/cli/analyze-parser.ts +284 -0
  118. package/src/cli/analyze.ts +207 -0
  119. package/src/cli/config.ts +561 -0
  120. package/src/cli/constitution.ts +109 -0
  121. package/src/cli/diagnose-analysis.ts +159 -0
  122. package/src/cli/diagnose-formatter.ts +87 -0
  123. package/src/cli/diagnose.ts +203 -0
  124. package/src/cli/generate.ts +127 -0
  125. package/src/cli/index.ts +37 -0
  126. package/src/cli/init.ts +188 -0
  127. package/src/cli/interact.ts +295 -0
  128. package/src/cli/plan.ts +198 -0
  129. package/src/cli/plugins.ts +111 -0
  130. package/src/cli/prompts.ts +295 -0
  131. package/src/cli/runs.ts +174 -0
  132. package/src/cli/status-cost.ts +151 -0
  133. package/src/cli/status-features.ts +338 -0
  134. package/src/cli/status.ts +13 -0
  135. package/src/commands/common.ts +171 -0
  136. package/src/commands/diagnose.ts +17 -0
  137. package/src/commands/index.ts +8 -0
  138. package/src/commands/logs.ts +384 -0
  139. package/src/commands/precheck.ts +86 -0
  140. package/src/commands/unlock.ts +96 -0
  141. package/src/config/defaults.ts +160 -0
  142. package/src/config/index.ts +22 -0
  143. package/src/config/loader.ts +121 -0
  144. package/src/config/merger.ts +147 -0
  145. package/src/config/path-security.ts +121 -0
  146. package/src/config/paths.ts +27 -0
  147. package/src/config/schema.ts +56 -0
  148. package/src/config/schemas.ts +286 -0
  149. package/src/config/types.ts +423 -0
  150. package/src/config/validate.ts +103 -0
  151. package/src/constitution/generator.ts +191 -0
  152. package/src/constitution/generators/aider.ts +41 -0
  153. package/src/constitution/generators/claude.ts +35 -0
  154. package/src/constitution/generators/cursor.ts +36 -0
  155. package/src/constitution/generators/opencode.ts +38 -0
  156. package/src/constitution/generators/types.ts +33 -0
  157. package/src/constitution/generators/windsurf.ts +36 -0
  158. package/src/constitution/index.ts +10 -0
  159. package/src/constitution/loader.ts +133 -0
  160. package/src/constitution/types.ts +31 -0
  161. package/src/context/auto-detect.ts +227 -0
  162. package/src/context/builder.ts +246 -0
  163. package/src/context/elements.ts +83 -0
  164. package/src/context/formatter.ts +107 -0
  165. package/src/context/generator.ts +129 -0
  166. package/src/context/generators/aider.ts +34 -0
  167. package/src/context/generators/claude.ts +28 -0
  168. package/src/context/generators/cursor.ts +28 -0
  169. package/src/context/generators/opencode.ts +30 -0
  170. package/src/context/generators/windsurf.ts +28 -0
  171. package/src/context/greenfield.ts +114 -0
  172. package/src/context/index.ts +33 -0
  173. package/src/context/injector.ts +279 -0
  174. package/src/context/test-scanner.ts +370 -0
  175. package/src/context/types.ts +98 -0
  176. package/src/errors.ts +67 -0
  177. package/src/execution/batching.ts +157 -0
  178. package/src/execution/crash-recovery.ts +373 -0
  179. package/src/execution/escalation/escalation.ts +44 -0
  180. package/src/execution/escalation/index.ts +13 -0
  181. package/src/execution/escalation/tier-escalation.ts +295 -0
  182. package/src/execution/escalation/tier-outcome.ts +158 -0
  183. package/src/execution/helpers.ts +38 -0
  184. package/src/execution/index.ts +45 -0
  185. package/src/execution/lifecycle/acceptance-loop.ts +272 -0
  186. package/src/execution/lifecycle/headless-formatter.ts +85 -0
  187. package/src/execution/lifecycle/index.ts +12 -0
  188. package/src/execution/lifecycle/parallel-lifecycle.ts +101 -0
  189. package/src/execution/lifecycle/precheck-runner.ts +140 -0
  190. package/src/execution/lifecycle/run-cleanup.ts +81 -0
  191. package/src/execution/lifecycle/run-completion.ts +129 -0
  192. package/src/execution/lifecycle/run-initialization.ts +141 -0
  193. package/src/execution/lifecycle/run-lifecycle.ts +312 -0
  194. package/src/execution/lifecycle/run-setup.ts +204 -0
  195. package/src/execution/lifecycle/story-hooks.ts +38 -0
  196. package/src/execution/lifecycle/story-size-prompts.ts +123 -0
  197. package/src/execution/lock.ts +115 -0
  198. package/src/execution/parallel-executor.ts +216 -0
  199. package/src/execution/parallel.ts +400 -0
  200. package/src/execution/pid-registry.ts +280 -0
  201. package/src/execution/pipeline-result-handler.ts +388 -0
  202. package/src/execution/post-verify-rectification.ts +188 -0
  203. package/src/execution/post-verify.ts +274 -0
  204. package/src/execution/progress.ts +25 -0
  205. package/src/execution/prompts.ts +127 -0
  206. package/src/execution/queue-handler.ts +109 -0
  207. package/src/execution/rectification.ts +13 -0
  208. package/src/execution/runner.ts +377 -0
  209. package/src/execution/sequential-executor.ts +388 -0
  210. package/src/execution/status-file.ts +264 -0
  211. package/src/execution/status-writer.ts +139 -0
  212. package/src/execution/story-context.ts +229 -0
  213. package/src/execution/test-output-parser.ts +14 -0
  214. package/src/execution/verification.ts +72 -0
  215. package/src/hooks/index.ts +2 -0
  216. package/src/hooks/runner.ts +286 -0
  217. package/src/hooks/types.ts +67 -0
  218. package/src/interaction/chain.ts +154 -0
  219. package/src/interaction/index.ts +60 -0
  220. package/src/interaction/init.ts +83 -0
  221. package/src/interaction/plugins/auto.ts +217 -0
  222. package/src/interaction/plugins/cli.ts +300 -0
  223. package/src/interaction/plugins/telegram.ts +384 -0
  224. package/src/interaction/plugins/webhook.ts +258 -0
  225. package/src/interaction/state.ts +171 -0
  226. package/src/interaction/triggers.ts +229 -0
  227. package/src/interaction/types.ts +163 -0
  228. package/src/logger/formatters.ts +84 -0
  229. package/src/logger/index.ts +16 -0
  230. package/src/logger/logger.ts +298 -0
  231. package/src/logger/types.ts +48 -0
  232. package/src/logging/formatter.ts +355 -0
  233. package/src/logging/index.ts +22 -0
  234. package/src/logging/types.ts +93 -0
  235. package/src/metrics/aggregator.ts +190 -0
  236. package/src/metrics/index.ts +14 -0
  237. package/src/metrics/tracker.ts +200 -0
  238. package/src/metrics/types.ts +109 -0
  239. package/src/optimizer/index.ts +62 -0
  240. package/src/optimizer/noop.optimizer.ts +24 -0
  241. package/src/optimizer/rule-based.optimizer.ts +248 -0
  242. package/src/optimizer/types.ts +53 -0
  243. package/src/pipeline/events.ts +130 -0
  244. package/src/pipeline/index.ts +19 -0
  245. package/src/pipeline/runner.ts +161 -0
  246. package/src/pipeline/stages/acceptance.ts +197 -0
  247. package/src/pipeline/stages/completion.ts +99 -0
  248. package/src/pipeline/stages/constitution.ts +63 -0
  249. package/src/pipeline/stages/context.ts +117 -0
  250. package/src/pipeline/stages/execution.ts +194 -0
  251. package/src/pipeline/stages/index.ts +62 -0
  252. package/src/pipeline/stages/optimizer.ts +74 -0
  253. package/src/pipeline/stages/prompt.ts +57 -0
  254. package/src/pipeline/stages/queue-check.ts +103 -0
  255. package/src/pipeline/stages/review.ts +181 -0
  256. package/src/pipeline/stages/routing.ts +81 -0
  257. package/src/pipeline/stages/verify.ts +100 -0
  258. package/src/pipeline/types.ts +167 -0
  259. package/src/plugins/index.ts +31 -0
  260. package/src/plugins/loader.ts +287 -0
  261. package/src/plugins/registry.ts +168 -0
  262. package/src/plugins/types.ts +327 -0
  263. package/src/plugins/validator.ts +352 -0
  264. package/src/prd/index.ts +172 -0
  265. package/src/prd/types.ts +202 -0
  266. package/src/precheck/checks-blockers.ts +391 -0
  267. package/src/precheck/checks-warnings.ts +142 -0
  268. package/src/precheck/checks.ts +30 -0
  269. package/src/precheck/index.ts +247 -0
  270. package/src/precheck/story-size-gate.ts +144 -0
  271. package/src/precheck/types.ts +31 -0
  272. package/src/queue/index.ts +2 -0
  273. package/src/queue/manager.ts +254 -0
  274. package/src/queue/types.ts +54 -0
  275. package/src/review/index.ts +8 -0
  276. package/src/review/runner.ts +172 -0
  277. package/src/review/types.ts +66 -0
  278. package/src/routing/builder.ts +81 -0
  279. package/src/routing/chain.ts +74 -0
  280. package/src/routing/index.ts +16 -0
  281. package/src/routing/loader.ts +58 -0
  282. package/src/routing/router.ts +303 -0
  283. package/src/routing/strategies/adaptive.ts +215 -0
  284. package/src/routing/strategies/index.ts +8 -0
  285. package/src/routing/strategies/keyword.ts +163 -0
  286. package/src/routing/strategies/llm-prompts.ts +209 -0
  287. package/src/routing/strategies/llm.ts +235 -0
  288. package/src/routing/strategies/manual.ts +50 -0
  289. package/src/routing/strategy.ts +99 -0
  290. package/src/tdd/cleanup.ts +111 -0
  291. package/src/tdd/index.ts +23 -0
  292. package/src/tdd/isolation.ts +123 -0
  293. package/src/tdd/orchestrator.ts +383 -0
  294. package/src/tdd/prompts.ts +270 -0
  295. package/src/tdd/rectification-gate.ts +183 -0
  296. package/src/tdd/session-runner.ts +179 -0
  297. package/src/tdd/types.ts +81 -0
  298. package/src/tdd/verdict.ts +271 -0
  299. package/src/tui/App.tsx +265 -0
  300. package/src/tui/components/AgentPanel.tsx +75 -0
  301. package/src/tui/components/CostOverlay.tsx +118 -0
  302. package/src/tui/components/HelpOverlay.tsx +107 -0
  303. package/src/tui/components/StatusBar.tsx +63 -0
  304. package/src/tui/components/StoriesPanel.tsx +177 -0
  305. package/src/tui/hooks/useKeyboard.ts +142 -0
  306. package/src/tui/hooks/useLayout.ts +137 -0
  307. package/src/tui/hooks/usePipelineEvents.ts +183 -0
  308. package/src/tui/hooks/usePty.ts +194 -0
  309. package/src/tui/index.tsx +38 -0
  310. package/src/tui/types.ts +76 -0
  311. package/src/utils/git.ts +83 -0
  312. package/src/utils/queue-writer.ts +54 -0
  313. package/src/verification/executor.ts +235 -0
  314. package/src/verification/gate.ts +207 -0
  315. package/src/verification/index.ts +12 -0
  316. package/src/verification/parser.ts +230 -0
  317. package/src/verification/rectification.ts +108 -0
  318. package/src/verification/types.ts +113 -0
  319. package/src/worktree/dispatcher.ts +65 -0
  320. package/src/worktree/index.ts +2 -0
  321. package/src/worktree/manager.ts +187 -0
  322. package/src/worktree/merge.ts +301 -0
  323. package/src/worktree/types.ts +4 -0
  324. package/test/TEST_COVERAGE_US001.md +217 -0
  325. package/test/TEST_COVERAGE_US003.md +84 -0
  326. package/test/TEST_COVERAGE_US005.md +86 -0
  327. package/test/US-002-orchestrator.test.ts +246 -0
  328. package/test/acceptance/cm-003-default-view.test.ts +194 -0
  329. package/test/execution/pid-registry.test.ts +240 -0
  330. package/test/execution/post-verify.test.ts +224 -0
  331. package/test/helpers/timeout.ts +42 -0
  332. package/test/integration/US-002-TEST-SUMMARY.md +107 -0
  333. package/test/integration/US-003-TEST-SUMMARY.md +149 -0
  334. package/test/integration/US-004-TEST-SUMMARY.md +106 -0
  335. package/test/integration/US-005-TEST-SUMMARY.md +138 -0
  336. package/test/integration/US-007-TEST-SUMMARY.md +100 -0
  337. package/test/integration/agent-validation.test.ts +439 -0
  338. package/test/integration/analyze-integration.test.ts +261 -0
  339. package/test/integration/analyze-scanner.test.ts +131 -0
  340. package/test/integration/cli-config-default-edge-cases.test.ts +222 -0
  341. package/test/integration/cli-config-default-view.test.ts +229 -0
  342. package/test/integration/cli-config-diff.test.ts +460 -0
  343. package/test/integration/cli-config.test.ts +736 -0
  344. package/test/integration/cli-diagnose.test.ts +592 -0
  345. package/test/integration/cli-logs.test.ts +314 -0
  346. package/test/integration/cli-plugins.test.ts +678 -0
  347. package/test/integration/cli-precheck.test.ts +371 -0
  348. package/test/integration/cli-run-headless.test.ts +173 -0
  349. package/test/integration/cli.test.ts +75 -0
  350. package/test/integration/config/merger.test.ts +465 -0
  351. package/test/integration/config/paths.test.ts +51 -0
  352. package/test/integration/config-loader.test.ts +265 -0
  353. package/test/integration/config.test.ts +444 -0
  354. package/test/integration/context-integration.test.ts +702 -0
  355. package/test/integration/context-provider-injection.test.ts +506 -0
  356. package/test/integration/context-verification-integration.test.ts +295 -0
  357. package/test/integration/e2e.test.ts +896 -0
  358. package/test/integration/execution.test.ts +625 -0
  359. package/test/integration/helpers.test.ts +295 -0
  360. package/test/integration/hooks.test.ts +361 -0
  361. package/test/integration/interaction-chain-pipeline.test.ts +464 -0
  362. package/test/integration/isolation.test.ts +143 -0
  363. package/test/integration/logger.test.ts +461 -0
  364. package/test/integration/parallel.test.ts +250 -0
  365. package/test/integration/path-security.test.ts +173 -0
  366. package/test/integration/pipeline-acceptance.test.ts +302 -0
  367. package/test/integration/pipeline-events.test.ts +475 -0
  368. package/test/integration/pipeline.test.ts +658 -0
  369. package/test/integration/plan.test.ts +157 -0
  370. package/test/integration/plugin-routing.test.ts +921 -0
  371. package/test/integration/plugins/config-integration.test.ts +172 -0
  372. package/test/integration/plugins/config-resolution.test.ts +522 -0
  373. package/test/integration/plugins/loader.test.ts +641 -0
  374. package/test/integration/plugins/registry.test.ts +746 -0
  375. package/test/integration/plugins/validator.test.ts +563 -0
  376. package/test/integration/prd-pause.test.ts +205 -0
  377. package/test/integration/prd-resolvers.test.ts +185 -0
  378. package/test/integration/precheck-integration.test.ts +468 -0
  379. package/test/integration/precheck.test.ts +805 -0
  380. package/test/integration/progress.test.ts +34 -0
  381. package/test/integration/rectification-flow.test.ts +512 -0
  382. package/test/integration/reporter-lifecycle.test.ts +860 -0
  383. package/test/integration/review-config-commands.test.ts +319 -0
  384. package/test/integration/review-config-schema.test.ts +116 -0
  385. package/test/integration/review-plugin-integration.test.ts +722 -0
  386. package/test/integration/review.test.ts +149 -0
  387. package/test/integration/routing-stage-bug-021.test.ts +274 -0
  388. package/test/integration/routing-stage-greenfield.test.ts +286 -0
  389. package/test/integration/runner-config-plugins.test.ts +461 -0
  390. package/test/integration/runner-fixes.test.ts +399 -0
  391. package/test/integration/runner-plugin-integration.test.ts +543 -0
  392. package/test/integration/runner.test.ts +1679 -0
  393. package/test/integration/s5-greenfield-fallback.test.ts +297 -0
  394. package/test/integration/status-file-integration.test.ts +325 -0
  395. package/test/integration/status-file.test.ts +379 -0
  396. package/test/integration/status-writer.test.ts +345 -0
  397. package/test/integration/story-id-in-events.test.ts +273 -0
  398. package/test/integration/tdd-cleanup.test.ts +246 -0
  399. package/test/integration/tdd-orchestrator.test.ts +1762 -0
  400. package/test/integration/test-scanner.test.ts +403 -0
  401. package/test/integration/verification-asset-check.test.ts +142 -0
  402. package/test/integration/verify-stage.test.ts +275 -0
  403. package/test/integration/worktree/manager.test.ts +218 -0
  404. package/test/integration/worktree/merge.test.ts +341 -0
  405. package/test/manual/logging-formatter-demo.ts +158 -0
  406. package/test/ui/tui-agent-panel.test.tsx +99 -0
  407. package/test/ui/tui-controls.test.ts +334 -0
  408. package/test/ui/tui-cost-and-pty.test.ts +189 -0
  409. package/test/ui/tui-layout.test.ts +378 -0
  410. package/test/ui/tui-pty-integration.test.tsx +159 -0
  411. package/test/ui/tui-stories.test.ts +332 -0
  412. package/test/unit/acceptance.test.ts +186 -0
  413. package/test/unit/agent-stderr-capture.test.ts +146 -0
  414. package/test/unit/analyze-classifier.test.ts +215 -0
  415. package/test/unit/analyze.test.ts +224 -0
  416. package/test/unit/auto-detect.test.ts +249 -0
  417. package/test/unit/cli-status.test.ts +417 -0
  418. package/test/unit/commands/common.test.ts +320 -0
  419. package/test/unit/commands/logs.test.ts +416 -0
  420. package/test/unit/commands/unlock.test.ts +319 -0
  421. package/test/unit/constitution-generators.test.ts +160 -0
  422. package/test/unit/constitution.test.ts +209 -0
  423. package/test/unit/context.test.ts +1722 -0
  424. package/test/unit/cost.test.ts +231 -0
  425. package/test/unit/crash-recovery.test.ts +308 -0
  426. package/test/unit/escalation.test.ts +126 -0
  427. package/test/unit/execution-logging-stderr.test.ts +156 -0
  428. package/test/unit/execution-stage.test.ts +122 -0
  429. package/test/unit/fix-generator.test.ts +275 -0
  430. package/test/unit/formatters.test.ts +469 -0
  431. package/test/unit/greenfield.test.ts +179 -0
  432. package/test/unit/helpers.test.ts +317 -0
  433. package/test/unit/interaction/human-review-trigger.test.ts +164 -0
  434. package/test/unit/interaction-network-failures.test.ts +389 -0
  435. package/test/unit/interaction-plugins.test.ts +164 -0
  436. package/test/unit/isolation.test.ts +134 -0
  437. package/test/unit/logging/formatter.test.ts +455 -0
  438. package/test/unit/merge.test.ts +268 -0
  439. package/test/unit/metrics.test.ts +276 -0
  440. package/test/unit/optimizer/noop.optimizer.test.ts +125 -0
  441. package/test/unit/optimizer/rule-based.optimizer.test.ts +358 -0
  442. package/test/unit/prd-auto-default.test.ts +290 -0
  443. package/test/unit/prd-failure-category.test.ts +176 -0
  444. package/test/unit/prd-get-next-story.test.ts +186 -0
  445. package/test/unit/precheck-checks.test.ts +840 -0
  446. package/test/unit/precheck-story-size-gate.test.ts +287 -0
  447. package/test/unit/precheck-types.test.ts +142 -0
  448. package/test/unit/prompts.test.ts +475 -0
  449. package/test/unit/queue.test.ts +237 -0
  450. package/test/unit/rectification.test.ts +284 -0
  451. package/test/unit/registry.test.ts +287 -0
  452. package/test/unit/routing.test.ts +937 -0
  453. package/test/unit/run-lifecycle.test.ts +140 -0
  454. package/test/unit/storyid-events.test.ts +224 -0
  455. package/test/unit/tdd-verdict.test.ts +492 -0
  456. package/test/unit/test-output-parser.test.ts +377 -0
  457. package/test/unit/verdict.test.ts +324 -0
  458. package/test/unit/worktree-manager.test.ts +158 -0
  459. package/tsconfig.json +27 -0
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Default Configuration
3
+ *
4
+ * The default NaxConfig used as a base for all projects.
5
+ */
6
+
7
+ import type { NaxConfig } from "./types";
8
+
9
+ /** Default configuration */
10
+ export const DEFAULT_CONFIG: NaxConfig = {
11
+ version: 1,
12
+ models: {
13
+ fast: { provider: "anthropic", model: "haiku" },
14
+ balanced: { provider: "anthropic", model: "sonnet" },
15
+ powerful: { provider: "anthropic", model: "opus" },
16
+ },
17
+ autoMode: {
18
+ enabled: true,
19
+ defaultAgent: "claude",
20
+ fallbackOrder: ["claude", "codex", "opencode", "gemini"],
21
+ complexityRouting: {
22
+ simple: "fast",
23
+ medium: "balanced",
24
+ complex: "powerful",
25
+ expert: "powerful",
26
+ },
27
+ escalation: {
28
+ enabled: true,
29
+ tierOrder: [
30
+ { tier: "fast", attempts: 5 },
31
+ { tier: "balanced", attempts: 3 },
32
+ { tier: "powerful", attempts: 2 },
33
+ ],
34
+ escalateEntireBatch: true,
35
+ },
36
+ },
37
+ routing: {
38
+ strategy: "keyword",
39
+ adaptive: {
40
+ minSamples: 10,
41
+ costThreshold: 0.8,
42
+ fallbackStrategy: "llm",
43
+ },
44
+ llm: {
45
+ model: "fast",
46
+ fallbackToKeywords: true,
47
+ cacheDecisions: true,
48
+ mode: "hybrid",
49
+ timeoutMs: 15000,
50
+ },
51
+ },
52
+ execution: {
53
+ maxIterations: 10, // auto-calculated: sum of tier attempts (5+3+2=10)
54
+ iterationDelayMs: 2000,
55
+ costLimit: 5.0,
56
+ sessionTimeoutSeconds: 600, // 10 minutes
57
+ verificationTimeoutSeconds: 300, // 5 minutes
58
+ maxStoriesPerFeature: 500,
59
+ rectification: {
60
+ enabled: true,
61
+ maxRetries: 2,
62
+ fullSuiteTimeoutSeconds: 120,
63
+ maxFailureSummaryChars: 2000,
64
+ abortOnIncreasingFailures: true,
65
+ },
66
+ regressionGate: {
67
+ enabled: true,
68
+ timeoutSeconds: 120,
69
+ },
70
+ contextProviderTokenBudget: 2000,
71
+ },
72
+ quality: {
73
+ requireTypecheck: true,
74
+ requireLint: true,
75
+ requireTests: true,
76
+ commands: {},
77
+ forceExit: false,
78
+ detectOpenHandles: true,
79
+ detectOpenHandlesRetries: 1,
80
+ gracePeriodMs: 5000,
81
+ drainTimeoutMs: 2000,
82
+ shell: "/bin/sh",
83
+ stripEnvVars: ["CLAUDECODE", "REPL_ID", "AGENT"],
84
+ environmentalEscalationDivisor: 2,
85
+ },
86
+ tdd: {
87
+ maxRetries: 2,
88
+ autoVerifyIsolation: true,
89
+ autoApproveVerifier: true,
90
+ strategy: "auto",
91
+ sessionTiers: {
92
+ testWriter: "balanced",
93
+ // implementer: undefined = uses story's routed tier
94
+ verifier: "fast",
95
+ },
96
+ testWriterAllowedPaths: ["src/index.ts", "src/**/index.ts"],
97
+ rollbackOnFailure: true,
98
+ greenfieldDetection: true,
99
+ },
100
+ constitution: {
101
+ enabled: true,
102
+ path: "constitution.md",
103
+ maxTokens: 2000,
104
+ },
105
+ analyze: {
106
+ llmEnhanced: true,
107
+ model: "balanced",
108
+ fallbackToKeywords: true,
109
+ maxCodebaseSummaryTokens: 5000,
110
+ },
111
+ review: {
112
+ enabled: true,
113
+ checks: ["typecheck", "lint", "test"],
114
+ commands: {},
115
+ },
116
+ plan: {
117
+ model: "balanced",
118
+ outputPath: "spec.md",
119
+ },
120
+ acceptance: {
121
+ enabled: true,
122
+ maxRetries: 2,
123
+ generateTests: true,
124
+ testPath: "acceptance.test.ts",
125
+ },
126
+ context: {
127
+ testCoverage: {
128
+ enabled: true,
129
+ detail: "names-and-counts",
130
+ maxTokens: 500,
131
+ testPattern: "**/*.test.{ts,js,tsx,jsx}",
132
+ scopeToStory: true,
133
+ },
134
+ autoDetect: {
135
+ enabled: true,
136
+ maxFiles: 5,
137
+ traceImports: false,
138
+ },
139
+ },
140
+ interaction: {
141
+ plugin: "cli",
142
+ config: {},
143
+ defaults: {
144
+ timeout: 600000, // 10 minutes
145
+ fallback: "escalate",
146
+ },
147
+ triggers: {
148
+ "security-review": true,
149
+ "cost-warning": true,
150
+ },
151
+ },
152
+ precheck: {
153
+ storySizeGate: {
154
+ enabled: true,
155
+ maxAcCount: 6,
156
+ maxDescriptionLength: 2000,
157
+ maxBulletPoints: 8,
158
+ },
159
+ },
160
+ };
@@ -0,0 +1,22 @@
1
+ export type {
2
+ NaxConfig,
3
+ Complexity,
4
+ TestStrategy,
5
+ TddStrategy,
6
+ ModelTier,
7
+ ModelDef,
8
+ ModelEntry,
9
+ ModelMap,
10
+ AutoModeConfig,
11
+ ExecutionConfig,
12
+ QualityConfig,
13
+ TddConfig,
14
+ TierConfig,
15
+ RectificationConfig,
16
+ } from "./schema";
17
+ export { DEFAULT_CONFIG, resolveModel, NaxConfigSchema } from "./schema";
18
+ export { loadConfig, findProjectDir, globalConfigPath } from "./loader";
19
+ export { validateConfig, type ValidationResult } from "./validate"; // @deprecated: Use NaxConfigSchema.safeParse() instead
20
+ export { validateDirectory, validateFilePath, isWithinDirectory, MAX_DIRECTORY_DEPTH } from "./path-security";
21
+ export { globalConfigDir, projectConfigDir } from "./paths";
22
+ export { deepMergeConfig } from "./merger";
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Configuration Loader
3
+ *
4
+ * Merges global + project config with defaults.
5
+ */
6
+
7
+ import { existsSync } from "node:fs";
8
+ import { join, resolve } from "node:path";
9
+ import { getLogger } from "../logger";
10
+ import { deepMergeConfig } from "./merger";
11
+ import { MAX_DIRECTORY_DEPTH } from "./path-security";
12
+ import { globalConfigDir, projectConfigDir } from "./paths";
13
+ import { DEFAULT_CONFIG, type NaxConfig, NaxConfigSchema } from "./schema";
14
+
15
+ /** Global config path */
16
+ export function globalConfigPath(): string {
17
+ return join(globalConfigDir(), "config.json");
18
+ }
19
+
20
+ /** Find project nax directory (walks up from cwd) */
21
+ export function findProjectDir(startDir: string = process.cwd()): string | null {
22
+ let dir = resolve(startDir);
23
+ let depth = 0;
24
+
25
+ while (depth < MAX_DIRECTORY_DEPTH) {
26
+ const candidate = join(dir, "nax");
27
+ if (existsSync(join(candidate, "config.json"))) {
28
+ return candidate;
29
+ }
30
+ const parent = join(dir, "..");
31
+ if (parent === dir) break; // Root reached
32
+ dir = parent;
33
+ depth++;
34
+ }
35
+
36
+ return null;
37
+ }
38
+
39
+ /** Load and parse a JSON config file */
40
+ async function loadJsonFile<T>(path: string): Promise<T | null> {
41
+ if (!existsSync(path)) return null;
42
+ try {
43
+ return await Bun.file(path).json();
44
+ } catch (err) {
45
+ const logger = getLogger();
46
+ logger.warn("config", "Failed to parse config file", { path, error: String(err) });
47
+ return null;
48
+ }
49
+ }
50
+
51
+ /** @internal Backward compat: map deprecated routing.llm.batchMode to routing.llm.mode.
52
+ * Returns a new object (immutable -- does not mutate the input). */
53
+ function applyBatchModeCompat(conf: Record<string, unknown>): Record<string, unknown> {
54
+ const routing = conf.routing as Record<string, unknown> | undefined;
55
+ const llm = routing?.llm as Record<string, unknown> | undefined;
56
+ if (llm && "batchMode" in llm && !("mode" in llm)) {
57
+ const batchMode = llm.batchMode;
58
+ if (typeof batchMode === "boolean") {
59
+ const mappedMode = batchMode ? "one-shot" : "per-story";
60
+ try {
61
+ getLogger().warn(
62
+ "config",
63
+ `routing.llm.batchMode is deprecated and will be removed in v1.0. Mapped to mode="${mappedMode}". Update your config to use routing.llm.mode instead.`,
64
+ );
65
+ } catch {
66
+ /* logger may not be init yet */
67
+ }
68
+ return {
69
+ ...conf,
70
+ routing: {
71
+ ...routing,
72
+ llm: { ...llm, mode: mappedMode },
73
+ },
74
+ };
75
+ }
76
+ }
77
+ return conf;
78
+ }
79
+
80
+ /** Load merged configuration (defaults < global < project < CLI overrides) */
81
+ export async function loadConfig(projectDir?: string, cliOverrides?: Record<string, unknown>): Promise<NaxConfig> {
82
+ // Start with defaults as a plain object
83
+ let rawConfig: Record<string, unknown> = structuredClone(DEFAULT_CONFIG as unknown as Record<string, unknown>);
84
+
85
+ // Layer 1: Global config (~/.nax/config.json)
86
+ const globalConfRaw = await loadJsonFile<Record<string, unknown>>(globalConfigPath());
87
+ if (globalConfRaw) {
88
+ // Backward compatibility: apply batchMode->mode shim before merge so defaults don't shadow it
89
+ const globalConf = applyBatchModeCompat(globalConfRaw);
90
+ rawConfig = deepMergeConfig(rawConfig, globalConf);
91
+ }
92
+
93
+ // Layer 2: Project config (nax/config.json)
94
+ const projDir = projectDir ?? findProjectDir();
95
+ if (projDir) {
96
+ const projConf = await loadJsonFile<Record<string, unknown>>(join(projDir, "config.json"));
97
+ if (projConf) {
98
+ // Backward compatibility: map deprecated batchMode -> mode on raw user config
99
+ // MUST run before deepMergeConfig so defaults don't shadow the check.
100
+ const resolvedProjConf = applyBatchModeCompat(projConf);
101
+ rawConfig = deepMergeConfig(rawConfig, resolvedProjConf);
102
+ }
103
+ }
104
+
105
+ // Layer 3: CLI overrides (highest priority)
106
+ if (cliOverrides) {
107
+ rawConfig = deepMergeConfig(rawConfig, cliOverrides);
108
+ }
109
+
110
+ // Parse and validate with Zod
111
+ const result = NaxConfigSchema.safeParse(rawConfig);
112
+ if (!result.success) {
113
+ const errors = result.error.issues.map((err) => {
114
+ const path = String(err.path.join("."));
115
+ return path ? `${path}: ${err.message}` : err.message;
116
+ });
117
+ throw new Error(`Invalid configuration:\n${errors.join("\n")}`);
118
+ }
119
+
120
+ return result.data as NaxConfig;
121
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Configuration Merger Utility
3
+ *
4
+ * Deep merge utility for NaxConfig with special handling:
5
+ * - Arrays: replace (not merge)
6
+ * - Null values: remove keys
7
+ * - Hooks: concatenate from both configs
8
+ * - Constitution content: concatenate with newline separator
9
+ */
10
+
11
+ import type { NaxConfig } from "./schema";
12
+
13
+ /**
14
+ * Deep merge two configuration objects.
15
+ *
16
+ * Rules:
17
+ * - Objects are merged recursively
18
+ * - Arrays replace (override completely replaces base)
19
+ * - Null values in override remove the key from result
20
+ * - Undefined values in override are skipped
21
+ * - Hooks are concatenated (both base and override hooks preserved)
22
+ * - Constitution content is concatenated with newline separator
23
+ *
24
+ * @param base - Base configuration object
25
+ * @param override - Override configuration object
26
+ * @returns New merged configuration (immutable - does not mutate inputs)
27
+ */
28
+ export function deepMergeConfig<T = NaxConfig>(base: Record<string, unknown>, override: Record<string, unknown>): T {
29
+ // Start with a clone of base to ensure immutability
30
+ const result: Record<string, unknown> = { ...base };
31
+
32
+ for (const key of Object.keys(override)) {
33
+ const overrideValue = override[key];
34
+
35
+ // Skip undefined values
36
+ if (overrideValue === undefined) {
37
+ continue;
38
+ }
39
+
40
+ // Handle null values - remove key from result
41
+ if (overrideValue === null) {
42
+ delete result[key];
43
+ continue;
44
+ }
45
+
46
+ const baseValue = result[key];
47
+
48
+ // Special case: hooks concatenation
49
+ if (key === "hooks" && isPlainObject(baseValue) && isPlainObject(overrideValue)) {
50
+ const baseHooks = baseValue as Record<string, unknown>;
51
+ const overrideHooks = overrideValue as Record<string, unknown>;
52
+ const merged: Record<string, unknown> = { ...baseHooks };
53
+
54
+ // Merge the nested hooks object
55
+ if (isPlainObject(baseHooks.hooks) && isPlainObject(overrideHooks.hooks)) {
56
+ const baseHookDefs = baseHooks.hooks as Record<string, unknown>;
57
+ const overrideHookDefs = overrideHooks.hooks as Record<string, unknown>;
58
+ const mergedHookDefs: Record<string, unknown> = {};
59
+
60
+ // Collect all hook event names
61
+ const allHookNames = new Set([...Object.keys(baseHookDefs), ...Object.keys(overrideHookDefs)]);
62
+
63
+ // For each hook event, concatenate hooks into an array
64
+ for (const hookName of allHookNames) {
65
+ const baseHook = baseHookDefs[hookName];
66
+ const overrideHook = overrideHookDefs[hookName];
67
+
68
+ if (baseHook && overrideHook) {
69
+ // Both exist - create array with both
70
+ mergedHookDefs[hookName] = [baseHook, overrideHook];
71
+ } else if (overrideHook) {
72
+ // Only override exists
73
+ mergedHookDefs[hookName] = overrideHook;
74
+ } else {
75
+ // Only base exists
76
+ mergedHookDefs[hookName] = baseHook;
77
+ }
78
+ }
79
+
80
+ merged.hooks = mergedHookDefs;
81
+ } else if (overrideHooks.hooks) {
82
+ merged.hooks = overrideHooks.hooks;
83
+ }
84
+
85
+ // Handle other hook config fields (e.g., skipGlobal)
86
+ for (const hookKey of Object.keys(overrideHooks)) {
87
+ if (hookKey !== "hooks") {
88
+ merged[hookKey] = overrideHooks[hookKey];
89
+ }
90
+ }
91
+
92
+ result[key] = merged;
93
+ continue;
94
+ }
95
+
96
+ // Special case: constitution content concatenation
97
+ if (key === "constitution" && isPlainObject(baseValue) && isPlainObject(overrideValue)) {
98
+ const baseConst = baseValue as Record<string, unknown>;
99
+ const overrideConst = overrideValue as Record<string, unknown>;
100
+
101
+ const baseContent = typeof baseConst.content === "string" ? baseConst.content : "";
102
+ const overrideContent = typeof overrideConst.content === "string" ? overrideConst.content : "";
103
+
104
+ // Merge constitution object, but concatenate content field
105
+ const mergedConstitution = deepMergeConfig(baseConst, overrideConst);
106
+
107
+ // Concatenate content if both exist
108
+ if (baseContent && overrideContent) {
109
+ (mergedConstitution as unknown as Record<string, unknown>).content = `${baseContent}\n\n${overrideContent}`;
110
+ } else if (overrideContent) {
111
+ (mergedConstitution as unknown as Record<string, unknown>).content = overrideContent;
112
+ } else if (baseContent) {
113
+ (mergedConstitution as unknown as Record<string, unknown>).content = baseContent;
114
+ }
115
+
116
+ result[key] = mergedConstitution;
117
+ continue;
118
+ }
119
+
120
+ // Arrays replace completely (no merging)
121
+ if (Array.isArray(overrideValue)) {
122
+ result[key] = [...overrideValue];
123
+ continue;
124
+ }
125
+
126
+ // Recursive merge for plain objects
127
+ if (isPlainObject(overrideValue) && isPlainObject(baseValue)) {
128
+ result[key] = deepMergeConfig(baseValue as Record<string, unknown>, overrideValue as Record<string, unknown>);
129
+ continue;
130
+ }
131
+
132
+ // Default: override replaces base
133
+ result[key] = overrideValue;
134
+ }
135
+
136
+ return result as T;
137
+ }
138
+
139
+ /**
140
+ * Check if value is a plain object (not null, not array, not class instance).
141
+ *
142
+ * @param value - Value to check
143
+ * @returns True if value is a plain object
144
+ */
145
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
146
+ return typeof value === "object" && value !== null && !Array.isArray(value) && value.constructor === Object;
147
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Path Security Utilities
3
+ *
4
+ * Prevents path traversal attacks by validating and resolving paths.
5
+ */
6
+
7
+ import { existsSync, lstatSync, realpathSync } from "node:fs";
8
+ import { isAbsolute, normalize, resolve } from "node:path";
9
+
10
+ /** Maximum directory depth to prevent infinite loops */
11
+ export const MAX_DIRECTORY_DEPTH = 10;
12
+
13
+ /**
14
+ * Validate and resolve a directory path safely
15
+ * @param dirPath - The directory path to validate
16
+ * @param baseDir - Optional base directory to check bounds (if provided, dirPath must be within baseDir)
17
+ * @returns Resolved absolute path
18
+ * @throws Error if path is invalid, not a directory, or outside bounds
19
+ */
20
+ export function validateDirectory(dirPath: string, baseDir?: string): string {
21
+ // Resolve to absolute path
22
+ const resolved = resolve(dirPath);
23
+
24
+ // Check if path exists
25
+ if (!existsSync(resolved)) {
26
+ throw new Error(`Directory does not exist: ${dirPath}`);
27
+ }
28
+
29
+ // Get real path (resolves symlinks)
30
+ let realPath: string;
31
+ try {
32
+ realPath = realpathSync(resolved);
33
+ } catch (error) {
34
+ throw new Error(`Failed to resolve path: ${dirPath} (${(error as Error).message})`);
35
+ }
36
+
37
+ // Check if it's a directory
38
+ try {
39
+ const stats = lstatSync(realPath);
40
+ if (!stats.isDirectory()) {
41
+ throw new Error(`Not a directory: ${dirPath}`);
42
+ }
43
+ } catch (error) {
44
+ throw new Error(`Failed to stat path: ${dirPath} (${(error as Error).message})`);
45
+ }
46
+
47
+ // If baseDir provided, ensure realPath is within baseDir
48
+ if (baseDir) {
49
+ const resolvedBase = resolve(baseDir);
50
+ const realBase = existsSync(resolvedBase) ? realpathSync(resolvedBase) : resolvedBase;
51
+
52
+ if (!isWithinDirectory(realPath, realBase)) {
53
+ throw new Error(`Path is outside allowed directory: ${dirPath} (resolved to ${realPath}, base: ${realBase})`);
54
+ }
55
+ }
56
+
57
+ return realPath;
58
+ }
59
+
60
+ /**
61
+ * Check if a path is within a base directory (prevents path traversal)
62
+ * @param targetPath - The path to check (must be absolute)
63
+ * @param basePath - The base directory (must be absolute)
64
+ * @returns true if targetPath is within basePath
65
+ */
66
+ export function isWithinDirectory(targetPath: string, basePath: string): boolean {
67
+ const normalizedTarget = normalize(targetPath);
68
+ const normalizedBase = normalize(basePath);
69
+
70
+ // Ensure both are absolute
71
+ if (!isAbsolute(normalizedTarget) || !isAbsolute(normalizedBase)) {
72
+ return false;
73
+ }
74
+
75
+ // Add trailing slash to base to prevent partial matches
76
+ const baseWithSlash = normalizedBase.endsWith("/") ? normalizedBase : `${normalizedBase}/`;
77
+ const targetWithSlash = normalizedTarget.endsWith("/") ? normalizedTarget : `${normalizedTarget}/`;
78
+
79
+ // Check if target starts with base
80
+ return targetWithSlash.startsWith(baseWithSlash) || normalizedTarget === normalizedBase;
81
+ }
82
+
83
+ /**
84
+ * Validate file path and ensure it's within a base directory
85
+ * @param filePath - The file path to validate
86
+ * @param baseDir - Base directory (filePath must be within this directory)
87
+ * @returns Resolved absolute path
88
+ * @throws Error if path is invalid or outside bounds
89
+ */
90
+ export function validateFilePath(filePath: string, baseDir: string): string {
91
+ const resolved = resolve(filePath);
92
+
93
+ // Get real path (resolves symlinks)
94
+ let realPath: string;
95
+ try {
96
+ // For non-existent files, use parent directory's real path
97
+ if (!existsSync(resolved)) {
98
+ const parent = resolve(resolved, "..");
99
+ if (existsSync(parent)) {
100
+ const realParent = realpathSync(parent);
101
+ realPath = resolve(realParent, filePath.split("/").pop() || "");
102
+ } else {
103
+ realPath = resolved;
104
+ }
105
+ } else {
106
+ realPath = realpathSync(resolved);
107
+ }
108
+ } catch (error) {
109
+ throw new Error(`Failed to resolve path: ${filePath} (${(error as Error).message})`);
110
+ }
111
+
112
+ // Ensure realPath is within baseDir
113
+ const resolvedBase = resolve(baseDir);
114
+ const realBase = existsSync(resolvedBase) ? realpathSync(resolvedBase) : resolvedBase;
115
+
116
+ if (!isWithinDirectory(realPath, realBase)) {
117
+ throw new Error(`Path is outside allowed directory: ${filePath} (resolved to ${realPath}, base: ${realBase})`);
118
+ }
119
+
120
+ return realPath;
121
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Configuration Path Utilities
3
+ *
4
+ * Provides path resolution for global and project-level config directories.
5
+ */
6
+
7
+ import { homedir } from "node:os";
8
+ import { join, resolve } from "node:path";
9
+
10
+ /**
11
+ * Returns the global config directory path (~/.nax).
12
+ *
13
+ * @returns Absolute path to global config directory
14
+ */
15
+ export function globalConfigDir(): string {
16
+ return join(homedir(), ".nax");
17
+ }
18
+
19
+ /**
20
+ * Returns the project config directory path (projectRoot/nax).
21
+ *
22
+ * @param projectRoot - Absolute or relative path to project root
23
+ * @returns Absolute path to project config directory
24
+ */
25
+ export function projectConfigDir(projectRoot: string): string {
26
+ return join(resolve(projectRoot), "nax");
27
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Configuration Schema — Re-export Barrel
3
+ *
4
+ * Backward-compatible re-exports from split modules:
5
+ * - types.ts: All TypeScript interfaces, type aliases, resolveModel
6
+ * - schemas.ts: Zod validation schemas
7
+ * - defaults.ts: DEFAULT_CONFIG constant
8
+ */
9
+
10
+ // Types and resolveModel
11
+ export type {
12
+ Complexity,
13
+ TestStrategy,
14
+ TddStrategy,
15
+ EscalationEntry,
16
+ ModelTier,
17
+ TokenPricing,
18
+ ModelDef,
19
+ ModelEntry,
20
+ ModelMap,
21
+ TierConfig,
22
+ AutoModeConfig,
23
+ RectificationConfig,
24
+ RegressionGateConfig,
25
+ ExecutionConfig,
26
+ QualityConfig,
27
+ TddConfig,
28
+ ConstitutionConfig,
29
+ AnalyzeConfig,
30
+ ReviewConfig,
31
+ PlanConfig,
32
+ AcceptanceConfig,
33
+ OptimizerConfig,
34
+ PluginConfigEntry,
35
+ HooksConfig,
36
+ InteractionConfig,
37
+ TestCoverageConfig,
38
+ ContextAutoDetectConfig,
39
+ ContextConfig,
40
+ RoutingStrategyName,
41
+ AdaptiveRoutingConfig,
42
+ LlmRoutingMode,
43
+ LlmRoutingConfig,
44
+ RoutingConfig,
45
+ StorySizeGateConfig,
46
+ PrecheckConfig,
47
+ NaxConfig,
48
+ } from "./types";
49
+
50
+ export { resolveModel } from "./types";
51
+
52
+ // Zod schemas
53
+ export { NaxConfigSchema } from "./schemas";
54
+
55
+ // Default config
56
+ export { DEFAULT_CONFIG } from "./defaults";