@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,103 @@
1
+ /**
2
+ * Configuration Validation
3
+ *
4
+ * @deprecated Use NaxConfigSchema.safeParse() from schema.ts instead.
5
+ * This module is kept for backward compatibility only.
6
+ *
7
+ * Validates NaxConfig structure and constraints.
8
+ */
9
+
10
+ import type { NaxConfig } from "./schema";
11
+
12
+ /** Validation result */
13
+ export interface ValidationResult {
14
+ valid: boolean;
15
+ errors: string[];
16
+ }
17
+
18
+ /**
19
+ * Validate NaxConfig
20
+ *
21
+ * Checks:
22
+ * - version === 1
23
+ * - maxIterations > 0
24
+ * - costLimit > 0
25
+ * - sessionTimeoutSeconds > 0
26
+ * - defaultAgent is non-empty
27
+ * - escalation.tierOrder has at least one tier with valid attempts
28
+ */
29
+ export function validateConfig(config: NaxConfig): ValidationResult {
30
+ const errors: string[] = [];
31
+
32
+ // Version check
33
+ if (config.version !== 1) {
34
+ errors.push(`Invalid version: expected 1, got ${config.version}`);
35
+ }
36
+
37
+ // Models mapping
38
+ const requiredTiers = ["fast", "balanced", "powerful"] as const;
39
+ if (!config.models) {
40
+ errors.push("models mapping is required");
41
+ } else {
42
+ for (const tier of requiredTiers) {
43
+ const entry = config.models[tier];
44
+ if (!entry) {
45
+ errors.push(`models.${tier} is required`);
46
+ } else if (typeof entry === "string") {
47
+ if (entry.trim() === "") {
48
+ errors.push(`models.${tier} must be a non-empty model identifier`);
49
+ }
50
+ } else {
51
+ if (!entry.provider || entry.provider.trim() === "") {
52
+ errors.push(`models.${tier}.provider must be non-empty`);
53
+ }
54
+ if (!entry.model || entry.model.trim() === "") {
55
+ errors.push(`models.${tier}.model must be non-empty`);
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ // Execution limits
62
+ if (config.execution.maxIterations <= 0) {
63
+ errors.push(`maxIterations must be > 0, got ${config.execution.maxIterations}`);
64
+ }
65
+
66
+ if (config.execution.costLimit <= 0) {
67
+ errors.push(`costLimit must be > 0, got ${config.execution.costLimit}`);
68
+ }
69
+
70
+ if (config.execution.sessionTimeoutSeconds <= 0) {
71
+ errors.push(`sessionTimeoutSeconds must be > 0, got ${config.execution.sessionTimeoutSeconds}`);
72
+ }
73
+
74
+ // Auto mode config
75
+ if (!config.autoMode.defaultAgent || config.autoMode.defaultAgent.trim() === "") {
76
+ errors.push("defaultAgent must be non-empty");
77
+ }
78
+
79
+ if (!config.autoMode.escalation.tierOrder || config.autoMode.escalation.tierOrder.length === 0) {
80
+ errors.push("escalation.tierOrder must have at least one tier");
81
+ } else {
82
+ for (const tc of config.autoMode.escalation.tierOrder) {
83
+ if (tc.attempts < 1 || tc.attempts > 20) {
84
+ errors.push(`escalation.tierOrder: tier "${tc.tier}" attempts must be 1-20, got ${tc.attempts}`);
85
+ }
86
+ }
87
+ }
88
+
89
+ // Validate complexityRouting values reference tiers that exist in models config
90
+ const configuredTiers = Object.keys(config.models);
91
+ const complexities = ["simple", "medium", "complex", "expert"] as const;
92
+ for (const complexity of complexities) {
93
+ const tier = config.autoMode.complexityRouting[complexity];
94
+ if (!configuredTiers.includes(tier)) {
95
+ errors.push(`complexityRouting.${complexity} must be one of: ${configuredTiers.join(", ")} (got '${tier}')`);
96
+ }
97
+ }
98
+
99
+ return {
100
+ valid: errors.length === 0,
101
+ errors,
102
+ };
103
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Constitution Generator Orchestrator
3
+ *
4
+ * Generates agent-specific config files from nax/constitution.md.
5
+ */
6
+
7
+ import { existsSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { validateFilePath } from "../config/path-security";
10
+ import { aiderGenerator } from "./generators/aider";
11
+ import { claudeGenerator } from "./generators/claude";
12
+ import { cursorGenerator } from "./generators/cursor";
13
+ import { opencodeGenerator } from "./generators/opencode";
14
+ import type { AgentConfigGenerator, AgentType, ConstitutionContent, GeneratorMap } from "./generators/types";
15
+ import { windsurfGenerator } from "./generators/windsurf";
16
+
17
+ /** Generator registry */
18
+ const GENERATORS: GeneratorMap = {
19
+ claude: claudeGenerator,
20
+ opencode: opencodeGenerator,
21
+ cursor: cursorGenerator,
22
+ windsurf: windsurfGenerator,
23
+ aider: aiderGenerator,
24
+ };
25
+
26
+ /** Generation result for a single agent */
27
+ export interface GenerationResult {
28
+ agent: AgentType;
29
+ outputFile: string;
30
+ content: string;
31
+ written: boolean;
32
+ error?: string;
33
+ }
34
+
35
+ /** Generate options */
36
+ export interface GenerateOptions {
37
+ /** Constitution file path (default: nax/constitution.md) */
38
+ constitutionPath: string;
39
+ /** Output directory (default: project root) */
40
+ outputDir: string;
41
+ /** Dry run mode (don't write files) */
42
+ dryRun?: boolean;
43
+ /** Specific agent to generate for (default: all) */
44
+ agent?: AgentType;
45
+ }
46
+
47
+ /**
48
+ * Load constitution content from file
49
+ */
50
+ async function loadConstitutionContent(constitutionPath: string): Promise<ConstitutionContent> {
51
+ if (!existsSync(constitutionPath)) {
52
+ throw new Error(`Constitution file not found: ${constitutionPath}`);
53
+ }
54
+
55
+ const file = Bun.file(constitutionPath);
56
+ const markdown = await file.text();
57
+
58
+ return {
59
+ markdown,
60
+ sections: {}, // TODO: implement section parsing if needed
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Generate config for a specific agent
66
+ */
67
+ function generateForAgent(
68
+ agent: AgentType,
69
+ constitution: ConstitutionContent,
70
+ ): { content: string; outputFile: string } {
71
+ const generator = GENERATORS[agent];
72
+ if (!generator) {
73
+ throw new Error(`Unknown agent type: ${agent}`);
74
+ }
75
+
76
+ const content = generator.generate(constitution);
77
+ return {
78
+ content,
79
+ outputFile: generator.outputFile,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Write generated content to file
85
+ */
86
+ async function writeGeneratedFile(outputDir: string, filename: string, content: string): Promise<void> {
87
+ const outputPath = join(outputDir, filename);
88
+
89
+ // SEC-5: Validate path before writing
90
+ const validatedPath = validateFilePath(outputPath, outputDir);
91
+
92
+ await Bun.write(validatedPath, content);
93
+ }
94
+
95
+ /**
96
+ * Generate config for a specific agent
97
+ *
98
+ * @param agent - Agent type to generate for
99
+ * @param constitutionPath - Path to constitution file
100
+ * @param outputDir - Directory to write output file
101
+ * @param dryRun - If true, don't write files
102
+ * @returns Generation result
103
+ */
104
+ export async function generateFor(
105
+ agent: AgentType,
106
+ constitutionPath: string,
107
+ outputDir: string,
108
+ dryRun = false,
109
+ ): Promise<GenerationResult> {
110
+ try {
111
+ const constitution = await loadConstitutionContent(constitutionPath);
112
+ const { content, outputFile } = generateForAgent(agent, constitution);
113
+
114
+ if (!dryRun) {
115
+ await writeGeneratedFile(outputDir, outputFile, content);
116
+ }
117
+
118
+ return {
119
+ agent,
120
+ outputFile,
121
+ content,
122
+ written: !dryRun,
123
+ };
124
+ } catch (err) {
125
+ const error = err instanceof Error ? err.message : String(err);
126
+ return {
127
+ agent,
128
+ outputFile: GENERATORS[agent].outputFile,
129
+ content: "",
130
+ written: false,
131
+ error,
132
+ };
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Generate config files for all agents
138
+ *
139
+ * @param constitutionPath - Path to constitution file
140
+ * @param outputDir - Directory to write output files
141
+ * @param dryRun - If true, don't write files
142
+ * @returns Array of generation results
143
+ */
144
+ export async function generateAll(
145
+ constitutionPath: string,
146
+ outputDir: string,
147
+ dryRun = false,
148
+ ): Promise<GenerationResult[]> {
149
+ const agents: AgentType[] = ["claude", "opencode", "cursor", "windsurf", "aider"];
150
+ const results: GenerationResult[] = [];
151
+
152
+ for (const agent of agents) {
153
+ const result = await generateFor(agent, constitutionPath, outputDir, dryRun);
154
+ results.push(result);
155
+ }
156
+
157
+ return results;
158
+ }
159
+
160
+ /**
161
+ * Check if constitution file is newer than generated configs
162
+ */
163
+ export function isConstitutionNewer(constitutionPath: string, outputDir: string): boolean {
164
+ if (!existsSync(constitutionPath)) {
165
+ return false;
166
+ }
167
+
168
+ const constitutionStat = Bun.file(constitutionPath);
169
+ const constitutionMtime = constitutionStat.lastModified;
170
+
171
+ // Check all generated files
172
+ const agents: AgentType[] = ["claude", "opencode", "cursor", "windsurf", "aider"];
173
+ for (const agent of agents) {
174
+ const outputFile = GENERATORS[agent].outputFile;
175
+ const outputPath = join(outputDir, outputFile);
176
+
177
+ if (!existsSync(outputPath)) {
178
+ // If any config file is missing, consider constitution newer
179
+ return true;
180
+ }
181
+
182
+ const outputStat = Bun.file(outputPath);
183
+ const outputMtime = outputStat.lastModified;
184
+
185
+ if (constitutionMtime > outputMtime) {
186
+ return true;
187
+ }
188
+ }
189
+
190
+ return false;
191
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Aider Config Generator
3
+ *
4
+ * Generates .aider.conf.yml from nax/constitution.md.
5
+ * Aider uses YAML format for configuration.
6
+ */
7
+
8
+ import type { AgentConfigGenerator, ConstitutionContent } from "./types";
9
+
10
+ /**
11
+ * Generate .aider.conf.yml from constitution
12
+ */
13
+ function generateAiderConfig(constitution: ConstitutionContent): string {
14
+ const { markdown } = constitution;
15
+
16
+ // Build .aider.conf.yml format
17
+ const header = `# Aider Configuration
18
+ # Auto-generated from nax/constitution.md
19
+ # DO NOT EDIT MANUALLY
20
+
21
+ # Project instructions
22
+ instructions: |
23
+ `;
24
+
25
+ // Indent all lines of markdown for YAML multi-line string
26
+ const indentedMarkdown = markdown
27
+ .split("\n")
28
+ .map((line) => ` ${line}`)
29
+ .join("\n");
30
+
31
+ return `${header}${indentedMarkdown}\n`;
32
+ }
33
+
34
+ /**
35
+ * Aider generator
36
+ */
37
+ export const aiderGenerator: AgentConfigGenerator = {
38
+ name: "aider",
39
+ outputFile: ".aider.conf.yml",
40
+ generate: generateAiderConfig,
41
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Claude Code Config Generator
3
+ *
4
+ * Generates CLAUDE.md from nax/constitution.md.
5
+ */
6
+
7
+ import type { AgentConfigGenerator, ConstitutionContent } from "./types";
8
+
9
+ /**
10
+ * Generate CLAUDE.md from constitution
11
+ */
12
+ function generateClaudeConfig(constitution: ConstitutionContent): string {
13
+ const { markdown } = constitution;
14
+
15
+ // Build CLAUDE.md format
16
+ const header = `# Project Constitution
17
+
18
+ This file is auto-generated from \`nax/constitution.md\`.
19
+ DO NOT EDIT MANUALLY — changes will be overwritten.
20
+
21
+ ---
22
+
23
+ `;
24
+
25
+ return header + markdown;
26
+ }
27
+
28
+ /**
29
+ * Claude Code generator
30
+ */
31
+ export const claudeGenerator: AgentConfigGenerator = {
32
+ name: "claude",
33
+ outputFile: "CLAUDE.md",
34
+ generate: generateClaudeConfig,
35
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Cursor Rules Generator
3
+ *
4
+ * Generates .cursorrules from nax/constitution.md.
5
+ * Cursor uses a simple text format similar to Claude but in a dotfile.
6
+ */
7
+
8
+ import type { AgentConfigGenerator, ConstitutionContent } from "./types";
9
+
10
+ /**
11
+ * Generate .cursorrules from constitution
12
+ */
13
+ function generateCursorRules(constitution: ConstitutionContent): string {
14
+ const { markdown } = constitution;
15
+
16
+ // Build .cursorrules format
17
+ const header = `# Project Rules
18
+
19
+ Auto-generated from nax/constitution.md
20
+ DO NOT EDIT MANUALLY
21
+
22
+ ---
23
+
24
+ `;
25
+
26
+ return header + markdown;
27
+ }
28
+
29
+ /**
30
+ * Cursor generator
31
+ */
32
+ export const cursorGenerator: AgentConfigGenerator = {
33
+ name: "cursor",
34
+ outputFile: ".cursorrules",
35
+ generate: generateCursorRules,
36
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * OpenCode Config Generator
3
+ *
4
+ * Generates AGENTS.md from nax/constitution.md.
5
+ * Format is similar to CLAUDE.md but with OpenCode-specific headers.
6
+ */
7
+
8
+ import type { AgentConfigGenerator, ConstitutionContent } from "./types";
9
+
10
+ /**
11
+ * Generate AGENTS.md from constitution
12
+ */
13
+ function generateOpencodeConfig(constitution: ConstitutionContent): string {
14
+ const { markdown } = constitution;
15
+
16
+ // Build AGENTS.md format (OpenCode/Codex format)
17
+ const header = `# Agent Instructions
18
+
19
+ This file is auto-generated from \`nax/constitution.md\`.
20
+ DO NOT EDIT MANUALLY — changes will be overwritten.
21
+
22
+ These instructions apply to all AI coding agents in this project.
23
+
24
+ ---
25
+
26
+ `;
27
+
28
+ return header + markdown;
29
+ }
30
+
31
+ /**
32
+ * OpenCode generator
33
+ */
34
+ export const opencodeGenerator: AgentConfigGenerator = {
35
+ name: "opencode",
36
+ outputFile: "AGENTS.md",
37
+ generate: generateOpencodeConfig,
38
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Constitution Generator Types
3
+ *
4
+ * Defines the interface for generating agent-specific config files from nax/constitution.md.
5
+ */
6
+
7
+ /** Constitution content structure for generators */
8
+ export interface ConstitutionContent {
9
+ /** Full constitution markdown content */
10
+ markdown: string;
11
+ /** Parsed sections (optional, for structured generation) */
12
+ sections?: Record<string, string>;
13
+ }
14
+
15
+ /** Agent config generator interface */
16
+ export interface AgentConfigGenerator {
17
+ /** Generator name (e.g., 'claude', 'opencode', 'cursor') */
18
+ name: string;
19
+ /** Output filename (e.g., 'CLAUDE.md', '.cursorrules') */
20
+ outputFile: string;
21
+ /**
22
+ * Generate agent-specific config file content from constitution
23
+ * @param constitution - Constitution content
24
+ * @returns Generated config file content
25
+ */
26
+ generate(constitution: ConstitutionContent): string;
27
+ }
28
+
29
+ /** All available generator types */
30
+ export type AgentType = "claude" | "opencode" | "cursor" | "windsurf" | "aider";
31
+
32
+ /** Generator registry map */
33
+ export type GeneratorMap = Record<AgentType, AgentConfigGenerator>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Windsurf Rules Generator
3
+ *
4
+ * Generates .windsurfrules from nax/constitution.md.
5
+ * Windsurf uses a similar format to Cursor.
6
+ */
7
+
8
+ import type { AgentConfigGenerator, ConstitutionContent } from "./types";
9
+
10
+ /**
11
+ * Generate .windsurfrules from constitution
12
+ */
13
+ function generateWindsurfRules(constitution: ConstitutionContent): string {
14
+ const { markdown } = constitution;
15
+
16
+ // Build .windsurfrules format
17
+ const header = `# Windsurf Project Rules
18
+
19
+ Auto-generated from nax/constitution.md
20
+ DO NOT EDIT MANUALLY
21
+
22
+ ---
23
+
24
+ `;
25
+
26
+ return header + markdown;
27
+ }
28
+
29
+ /**
30
+ * Windsurf generator
31
+ */
32
+ export const windsurfGenerator: AgentConfigGenerator = {
33
+ name: "windsurf",
34
+ outputFile: ".windsurfrules",
35
+ generate: generateWindsurfRules,
36
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Constitution system
3
+ *
4
+ * Provides project-level governance by injecting a constitution.md file
5
+ * into every agent session prompt. The constitution defines coding standards,
6
+ * architectural rules, testing requirements, and forbidden patterns.
7
+ */
8
+
9
+ export type { ConstitutionConfig, ConstitutionResult } from "./types";
10
+ export { loadConstitution, estimateTokens, truncateToTokens } from "./loader";
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Constitution loader
3
+ *
4
+ * Loads and processes global + project constitution files.
5
+ */
6
+
7
+ import { existsSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { validateFilePath } from "../config/path-security";
10
+ import { globalConfigDir } from "../config/paths";
11
+ import type { ConstitutionConfig, ConstitutionResult } from "./types";
12
+
13
+ /**
14
+ * Estimate token count for text
15
+ *
16
+ * Uses simple heuristic: 1 token ≈ 3 characters (conservative estimate)
17
+ * This is a rough approximation sufficient for quota management.
18
+ *
19
+ * @param text - Text to estimate tokens for
20
+ * @returns Estimated token count
21
+ */
22
+ export function estimateTokens(text: string): number {
23
+ return Math.ceil(text.length / 3);
24
+ }
25
+
26
+ /**
27
+ * Truncate text to fit within token limit
28
+ *
29
+ * Truncates at word boundaries to avoid cutting mid-word.
30
+ *
31
+ * @param text - Text to truncate
32
+ * @param maxTokens - Maximum tokens allowed
33
+ * @returns Truncated text
34
+ */
35
+ export function truncateToTokens(text: string, maxTokens: number): string {
36
+ const maxChars = maxTokens * 3; // 1 token ≈ 3 chars
37
+
38
+ if (text.length <= maxChars) {
39
+ return text;
40
+ }
41
+
42
+ // Find last word boundary before maxChars
43
+ const truncated = text.slice(0, maxChars);
44
+ const lastSpace = truncated.lastIndexOf(" ");
45
+ const lastNewline = truncated.lastIndexOf("\n");
46
+ const cutPoint = Math.max(lastSpace, lastNewline);
47
+
48
+ if (cutPoint > 0) {
49
+ return truncated.slice(0, cutPoint);
50
+ }
51
+
52
+ // Fallback: hard cut if no word boundary found
53
+ return truncated;
54
+ }
55
+
56
+ /**
57
+ * Load constitution from global and project directories.
58
+ *
59
+ * Prepends global constitution to project constitution with --- separator.
60
+ * Respects skipGlobal flag in config.
61
+ *
62
+ * @param projectDir - Path to project nax/ directory
63
+ * @param config - Constitution configuration
64
+ * @returns Constitution result or null if disabled/missing
65
+ */
66
+ export async function loadConstitution(
67
+ projectDir: string,
68
+ config: ConstitutionConfig,
69
+ ): Promise<ConstitutionResult | null> {
70
+ if (!config.enabled) {
71
+ return null;
72
+ }
73
+
74
+ let combinedContent = "";
75
+
76
+ // Load global constitution (unless skipGlobal is true)
77
+ if (!config.skipGlobal) {
78
+ const globalPath = join(globalConfigDir(), config.path);
79
+ if (existsSync(globalPath)) {
80
+ // SEC-5: Validate path before reading
81
+ const validatedPath = validateFilePath(globalPath, globalConfigDir());
82
+ const globalFile = Bun.file(validatedPath);
83
+ const globalContent = await globalFile.text();
84
+ if (globalContent.trim()) {
85
+ combinedContent = globalContent.trim();
86
+ }
87
+ }
88
+ }
89
+
90
+ // Load project constitution
91
+ const projectPath = join(projectDir, config.path);
92
+ if (existsSync(projectPath)) {
93
+ // SEC-5: Validate path before reading
94
+ const validatedPath = validateFilePath(projectPath, projectDir);
95
+ const projectFile = Bun.file(validatedPath);
96
+ const projectContent = await projectFile.text();
97
+ if (projectContent.trim()) {
98
+ // Concatenate with separator if both exist
99
+ if (combinedContent) {
100
+ combinedContent += `\n\n---\n\n${projectContent.trim()}`;
101
+ } else {
102
+ // If no global content, preserve exact project content (including trailing newline)
103
+ combinedContent = projectContent;
104
+ }
105
+ }
106
+ }
107
+
108
+ // Return null if no content loaded
109
+ if (!combinedContent) {
110
+ return null;
111
+ }
112
+
113
+ const tokens = estimateTokens(combinedContent);
114
+
115
+ if (tokens <= config.maxTokens) {
116
+ return {
117
+ content: combinedContent,
118
+ tokens,
119
+ truncated: false,
120
+ };
121
+ }
122
+
123
+ // Truncate to fit within maxTokens
124
+ const truncatedContent = truncateToTokens(combinedContent, config.maxTokens);
125
+ const truncatedTokens = estimateTokens(truncatedContent);
126
+
127
+ return {
128
+ content: truncatedContent,
129
+ tokens: truncatedTokens,
130
+ truncated: true,
131
+ originalTokens: tokens,
132
+ };
133
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Constitution types
3
+ *
4
+ * The constitution is a project-level governance document that defines coding
5
+ * standards, architectural rules, testing requirements, and forbidden patterns.
6
+ * It gets injected into every agent session prompt.
7
+ */
8
+
9
+ /** Constitution configuration */
10
+ export interface ConstitutionConfig {
11
+ /** Enable constitution loading and injection */
12
+ enabled: boolean;
13
+ /** Path to constitution file relative to nax/ directory */
14
+ path: string;
15
+ /** Maximum tokens allowed for constitution content */
16
+ maxTokens: number;
17
+ /** Skip loading global constitution (default: false) */
18
+ skipGlobal?: boolean;
19
+ }
20
+
21
+ /** Constitution load result */
22
+ export interface ConstitutionResult {
23
+ /** Constitution content (may be truncated) */
24
+ content: string;
25
+ /** Estimated token count */
26
+ tokens: number;
27
+ /** Whether content was truncated */
28
+ truncated: boolean;
29
+ /** Original token count before truncation (if truncated) */
30
+ originalTokens?: number;
31
+ }