@nathapp/nax 0.50.2 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (352) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/nax.js +579 -373
  3. package/package.json +1 -3
  4. package/bin/nax.ts +0 -1195
  5. package/src/acceptance/fix-generator.ts +0 -322
  6. package/src/acceptance/generator.ts +0 -423
  7. package/src/acceptance/index.ts +0 -42
  8. package/src/acceptance/refinement.ts +0 -224
  9. package/src/acceptance/templates/cli.ts +0 -47
  10. package/src/acceptance/templates/component.ts +0 -78
  11. package/src/acceptance/templates/e2e.ts +0 -43
  12. package/src/acceptance/templates/index.ts +0 -21
  13. package/src/acceptance/templates/snapshot.ts +0 -50
  14. package/src/acceptance/templates/unit.ts +0 -48
  15. package/src/acceptance/types.ts +0 -135
  16. package/src/agents/acp/adapter.ts +0 -888
  17. package/src/agents/acp/cost.ts +0 -9
  18. package/src/agents/acp/index.ts +0 -7
  19. package/src/agents/acp/interaction-bridge.ts +0 -126
  20. package/src/agents/acp/parser.ts +0 -119
  21. package/src/agents/acp/spawn-client.ts +0 -373
  22. package/src/agents/acp/types.ts +0 -22
  23. package/src/agents/aider/adapter.ts +0 -135
  24. package/src/agents/claude/adapter.ts +0 -258
  25. package/src/agents/claude/complete.ts +0 -80
  26. package/src/agents/claude/cost.ts +0 -16
  27. package/src/agents/claude/execution.ts +0 -215
  28. package/src/agents/claude/index.ts +0 -3
  29. package/src/agents/claude/interactive.ts +0 -77
  30. package/src/agents/claude/plan.ts +0 -179
  31. package/src/agents/codex/adapter.ts +0 -153
  32. package/src/agents/cost/calculate.ts +0 -154
  33. package/src/agents/cost/index.ts +0 -10
  34. package/src/agents/cost/parse.ts +0 -97
  35. package/src/agents/cost/pricing.ts +0 -59
  36. package/src/agents/cost/types.ts +0 -45
  37. package/src/agents/gemini/adapter.ts +0 -177
  38. package/src/agents/index.ts +0 -18
  39. package/src/agents/opencode/adapter.ts +0 -106
  40. package/src/agents/registry.ts +0 -136
  41. package/src/agents/shared/decompose.ts +0 -154
  42. package/src/agents/shared/model-resolution.ts +0 -43
  43. package/src/agents/shared/types-extended.ts +0 -164
  44. package/src/agents/shared/validation.ts +0 -69
  45. package/src/agents/shared/version-detection.ts +0 -109
  46. package/src/agents/types.ts +0 -205
  47. package/src/analyze/classifier.ts +0 -282
  48. package/src/analyze/index.ts +0 -16
  49. package/src/analyze/scanner.ts +0 -171
  50. package/src/analyze/types.ts +0 -51
  51. package/src/cli/accept.ts +0 -108
  52. package/src/cli/agents.ts +0 -87
  53. package/src/cli/analyze-parser.ts +0 -291
  54. package/src/cli/analyze.ts +0 -352
  55. package/src/cli/config-descriptions.ts +0 -218
  56. package/src/cli/config-diff.ts +0 -103
  57. package/src/cli/config-display.ts +0 -285
  58. package/src/cli/config-get.ts +0 -55
  59. package/src/cli/config.ts +0 -14
  60. package/src/cli/constitution.ts +0 -17
  61. package/src/cli/diagnose-analysis.ts +0 -159
  62. package/src/cli/diagnose-formatter.ts +0 -87
  63. package/src/cli/diagnose.ts +0 -203
  64. package/src/cli/generate.ts +0 -250
  65. package/src/cli/index.ts +0 -42
  66. package/src/cli/init-context.ts +0 -405
  67. package/src/cli/init-detect.ts +0 -303
  68. package/src/cli/init.ts +0 -296
  69. package/src/cli/interact.ts +0 -295
  70. package/src/cli/plan.ts +0 -509
  71. package/src/cli/plugins.ts +0 -122
  72. package/src/cli/prompts-export.ts +0 -58
  73. package/src/cli/prompts-init.ts +0 -200
  74. package/src/cli/prompts-main.ts +0 -183
  75. package/src/cli/prompts-shared.ts +0 -70
  76. package/src/cli/prompts-tdd.ts +0 -88
  77. package/src/cli/prompts.ts +0 -17
  78. package/src/cli/runs.ts +0 -174
  79. package/src/cli/status-cost.ts +0 -151
  80. package/src/cli/status-features.ts +0 -405
  81. package/src/cli/status.ts +0 -13
  82. package/src/commands/common.ts +0 -171
  83. package/src/commands/diagnose.ts +0 -17
  84. package/src/commands/index.ts +0 -9
  85. package/src/commands/logs-formatter.ts +0 -201
  86. package/src/commands/logs-reader.ts +0 -171
  87. package/src/commands/logs.ts +0 -103
  88. package/src/commands/precheck.ts +0 -86
  89. package/src/commands/runs.ts +0 -220
  90. package/src/commands/unlock.ts +0 -96
  91. package/src/config/defaults.ts +0 -217
  92. package/src/config/index.ts +0 -22
  93. package/src/config/loader.ts +0 -143
  94. package/src/config/merge.ts +0 -106
  95. package/src/config/merger.ts +0 -147
  96. package/src/config/path-security.ts +0 -121
  97. package/src/config/paths.ts +0 -27
  98. package/src/config/permissions.ts +0 -63
  99. package/src/config/runtime-types.ts +0 -520
  100. package/src/config/schema-types.ts +0 -53
  101. package/src/config/schema.ts +0 -60
  102. package/src/config/schemas.ts +0 -425
  103. package/src/config/test-strategy.ts +0 -71
  104. package/src/config/types.ts +0 -57
  105. package/src/config/validate.ts +0 -103
  106. package/src/constitution/generator.ts +0 -158
  107. package/src/constitution/generators/aider.ts +0 -41
  108. package/src/constitution/generators/claude.ts +0 -35
  109. package/src/constitution/generators/cursor.ts +0 -36
  110. package/src/constitution/generators/opencode.ts +0 -38
  111. package/src/constitution/generators/types.ts +0 -33
  112. package/src/constitution/generators/windsurf.ts +0 -36
  113. package/src/constitution/index.ts +0 -11
  114. package/src/constitution/loader.ts +0 -121
  115. package/src/constitution/types.ts +0 -31
  116. package/src/context/auto-detect.ts +0 -228
  117. package/src/context/builder.ts +0 -299
  118. package/src/context/elements.ts +0 -122
  119. package/src/context/formatter.ts +0 -107
  120. package/src/context/generator.ts +0 -343
  121. package/src/context/generators/aider.ts +0 -34
  122. package/src/context/generators/claude.ts +0 -28
  123. package/src/context/generators/codex.ts +0 -28
  124. package/src/context/generators/cursor.ts +0 -28
  125. package/src/context/generators/gemini.ts +0 -28
  126. package/src/context/generators/opencode.ts +0 -30
  127. package/src/context/generators/windsurf.ts +0 -28
  128. package/src/context/greenfield.ts +0 -114
  129. package/src/context/index.ts +0 -34
  130. package/src/context/injector.ts +0 -279
  131. package/src/context/parent-context.ts +0 -39
  132. package/src/context/test-scanner.ts +0 -370
  133. package/src/context/types.ts +0 -98
  134. package/src/decompose/apply.ts +0 -50
  135. package/src/decompose/builder.ts +0 -181
  136. package/src/decompose/index.ts +0 -8
  137. package/src/decompose/sections/codebase.ts +0 -26
  138. package/src/decompose/sections/constraints.ts +0 -32
  139. package/src/decompose/sections/index.ts +0 -4
  140. package/src/decompose/sections/sibling-stories.ts +0 -25
  141. package/src/decompose/sections/target-story.ts +0 -31
  142. package/src/decompose/types.ts +0 -55
  143. package/src/decompose/validators/complexity.ts +0 -45
  144. package/src/decompose/validators/coverage.ts +0 -134
  145. package/src/decompose/validators/dependency.ts +0 -91
  146. package/src/decompose/validators/index.ts +0 -35
  147. package/src/decompose/validators/overlap.ts +0 -128
  148. package/src/errors.ts +0 -67
  149. package/src/execution/batching.ts +0 -157
  150. package/src/execution/crash-heartbeat.ts +0 -77
  151. package/src/execution/crash-recovery.ts +0 -79
  152. package/src/execution/crash-signals.ts +0 -165
  153. package/src/execution/crash-writer.ts +0 -154
  154. package/src/execution/deferred-review.ts +0 -105
  155. package/src/execution/dry-run.ts +0 -81
  156. package/src/execution/escalation/escalation.ts +0 -46
  157. package/src/execution/escalation/index.ts +0 -13
  158. package/src/execution/escalation/tier-escalation.ts +0 -346
  159. package/src/execution/escalation/tier-outcome.ts +0 -143
  160. package/src/execution/executor-types.ts +0 -73
  161. package/src/execution/helpers.ts +0 -38
  162. package/src/execution/index.ts +0 -27
  163. package/src/execution/iteration-runner.ts +0 -160
  164. package/src/execution/lifecycle/acceptance-loop.ts +0 -280
  165. package/src/execution/lifecycle/headless-formatter.ts +0 -83
  166. package/src/execution/lifecycle/index.ts +0 -11
  167. package/src/execution/lifecycle/parallel-lifecycle.ts +0 -101
  168. package/src/execution/lifecycle/precheck-runner.ts +0 -140
  169. package/src/execution/lifecycle/run-cleanup.ts +0 -81
  170. package/src/execution/lifecycle/run-completion.ts +0 -247
  171. package/src/execution/lifecycle/run-initialization.ts +0 -187
  172. package/src/execution/lifecycle/run-regression.ts +0 -305
  173. package/src/execution/lifecycle/run-setup.ts +0 -240
  174. package/src/execution/lifecycle/story-size-prompts.ts +0 -123
  175. package/src/execution/lock.ts +0 -129
  176. package/src/execution/parallel-coordinator.ts +0 -281
  177. package/src/execution/parallel-executor-rectification-pass.ts +0 -117
  178. package/src/execution/parallel-executor-rectify.ts +0 -136
  179. package/src/execution/parallel-executor.ts +0 -330
  180. package/src/execution/parallel-worker.ts +0 -149
  181. package/src/execution/parallel.ts +0 -13
  182. package/src/execution/pid-registry.ts +0 -275
  183. package/src/execution/pipeline-result-handler.ts +0 -221
  184. package/src/execution/progress.ts +0 -27
  185. package/src/execution/queue-handler.ts +0 -109
  186. package/src/execution/runner-completion.ts +0 -171
  187. package/src/execution/runner-execution.ts +0 -243
  188. package/src/execution/runner-setup.ts +0 -86
  189. package/src/execution/runner.ts +0 -265
  190. package/src/execution/sequential-executor.ts +0 -219
  191. package/src/execution/status-file.ts +0 -264
  192. package/src/execution/status-writer.ts +0 -181
  193. package/src/execution/story-context.ts +0 -266
  194. package/src/execution/story-selector.ts +0 -76
  195. package/src/execution/test-output-parser.ts +0 -14
  196. package/src/execution/timeout-handler.ts +0 -100
  197. package/src/hooks/index.ts +0 -2
  198. package/src/hooks/runner.ts +0 -280
  199. package/src/hooks/types.ts +0 -79
  200. package/src/interaction/chain.ts +0 -170
  201. package/src/interaction/index.ts +0 -61
  202. package/src/interaction/init.ts +0 -84
  203. package/src/interaction/plugins/auto.ts +0 -243
  204. package/src/interaction/plugins/cli.ts +0 -300
  205. package/src/interaction/plugins/telegram.ts +0 -384
  206. package/src/interaction/plugins/webhook.ts +0 -286
  207. package/src/interaction/state.ts +0 -171
  208. package/src/interaction/triggers.ts +0 -250
  209. package/src/interaction/types.ts +0 -170
  210. package/src/logger/formatters.ts +0 -84
  211. package/src/logger/index.ts +0 -16
  212. package/src/logger/logger.ts +0 -296
  213. package/src/logger/types.ts +0 -48
  214. package/src/logging/formatter.ts +0 -355
  215. package/src/logging/index.ts +0 -22
  216. package/src/logging/types.ts +0 -93
  217. package/src/metrics/aggregator.ts +0 -191
  218. package/src/metrics/index.ts +0 -14
  219. package/src/metrics/tracker.ts +0 -200
  220. package/src/metrics/types.ts +0 -115
  221. package/src/optimizer/index.ts +0 -63
  222. package/src/optimizer/noop.optimizer.ts +0 -24
  223. package/src/optimizer/rule-based.optimizer.ts +0 -248
  224. package/src/optimizer/types.ts +0 -53
  225. package/src/pipeline/event-bus.ts +0 -297
  226. package/src/pipeline/events.ts +0 -130
  227. package/src/pipeline/index.ts +0 -19
  228. package/src/pipeline/runner.ts +0 -149
  229. package/src/pipeline/stages/acceptance-setup.ts +0 -140
  230. package/src/pipeline/stages/acceptance.ts +0 -215
  231. package/src/pipeline/stages/autofix.ts +0 -262
  232. package/src/pipeline/stages/completion.ts +0 -110
  233. package/src/pipeline/stages/constitution.ts +0 -63
  234. package/src/pipeline/stages/context.ts +0 -122
  235. package/src/pipeline/stages/execution.ts +0 -359
  236. package/src/pipeline/stages/index.ts +0 -86
  237. package/src/pipeline/stages/optimizer.ts +0 -74
  238. package/src/pipeline/stages/prompt.ts +0 -79
  239. package/src/pipeline/stages/queue-check.ts +0 -103
  240. package/src/pipeline/stages/rectify.ts +0 -101
  241. package/src/pipeline/stages/regression.ts +0 -99
  242. package/src/pipeline/stages/review.ts +0 -94
  243. package/src/pipeline/stages/routing.ts +0 -276
  244. package/src/pipeline/stages/verify.ts +0 -286
  245. package/src/pipeline/subscribers/events-writer.ts +0 -135
  246. package/src/pipeline/subscribers/hooks.ts +0 -179
  247. package/src/pipeline/subscribers/interaction.ts +0 -103
  248. package/src/pipeline/subscribers/registry.ts +0 -73
  249. package/src/pipeline/subscribers/reporters.ts +0 -174
  250. package/src/pipeline/types.ts +0 -220
  251. package/src/plugins/extensions.ts +0 -225
  252. package/src/plugins/index.ts +0 -33
  253. package/src/plugins/loader.ts +0 -352
  254. package/src/plugins/plugin-logger.ts +0 -41
  255. package/src/plugins/registry.ts +0 -168
  256. package/src/plugins/types.ts +0 -206
  257. package/src/plugins/validator.ts +0 -352
  258. package/src/prd/index.ts +0 -220
  259. package/src/prd/schema.ts +0 -268
  260. package/src/prd/types.ts +0 -273
  261. package/src/prd/validate.ts +0 -41
  262. package/src/precheck/checks-agents.ts +0 -63
  263. package/src/precheck/checks-blockers.ts +0 -23
  264. package/src/precheck/checks-cli.ts +0 -68
  265. package/src/precheck/checks-config.ts +0 -102
  266. package/src/precheck/checks-git.ts +0 -117
  267. package/src/precheck/checks-system.ts +0 -101
  268. package/src/precheck/checks-warnings.ts +0 -221
  269. package/src/precheck/checks.ts +0 -36
  270. package/src/precheck/index.ts +0 -374
  271. package/src/precheck/story-size-gate.ts +0 -144
  272. package/src/precheck/types.ts +0 -31
  273. package/src/prompts/builder.ts +0 -166
  274. package/src/prompts/index.ts +0 -2
  275. package/src/prompts/loader.ts +0 -43
  276. package/src/prompts/sections/conventions.ts +0 -19
  277. package/src/prompts/sections/hermetic.ts +0 -41
  278. package/src/prompts/sections/index.ts +0 -12
  279. package/src/prompts/sections/isolation.ts +0 -70
  280. package/src/prompts/sections/role-task.ts +0 -182
  281. package/src/prompts/sections/story.ts +0 -55
  282. package/src/prompts/sections/verdict.ts +0 -70
  283. package/src/prompts/types.ts +0 -21
  284. package/src/queue/index.ts +0 -2
  285. package/src/queue/manager.ts +0 -254
  286. package/src/queue/types.ts +0 -54
  287. package/src/review/index.ts +0 -8
  288. package/src/review/orchestrator.ts +0 -154
  289. package/src/review/runner.ts +0 -303
  290. package/src/review/types.ts +0 -70
  291. package/src/routing/batch-route.ts +0 -35
  292. package/src/routing/builder.ts +0 -81
  293. package/src/routing/chain.ts +0 -75
  294. package/src/routing/content-hash.ts +0 -25
  295. package/src/routing/index.ts +0 -20
  296. package/src/routing/loader.ts +0 -62
  297. package/src/routing/router.ts +0 -305
  298. package/src/routing/strategies/adaptive.ts +0 -215
  299. package/src/routing/strategies/index.ts +0 -8
  300. package/src/routing/strategies/keyword.ts +0 -180
  301. package/src/routing/strategies/llm-prompts.ts +0 -224
  302. package/src/routing/strategies/llm.ts +0 -320
  303. package/src/routing/strategies/manual.ts +0 -50
  304. package/src/routing/strategy.ts +0 -102
  305. package/src/tdd/cleanup.ts +0 -120
  306. package/src/tdd/index.ts +0 -22
  307. package/src/tdd/isolation.ts +0 -117
  308. package/src/tdd/orchestrator.ts +0 -406
  309. package/src/tdd/prompts.ts +0 -40
  310. package/src/tdd/rectification-gate.ts +0 -274
  311. package/src/tdd/session-runner.ts +0 -263
  312. package/src/tdd/types.ts +0 -84
  313. package/src/tdd/verdict-reader.ts +0 -266
  314. package/src/tdd/verdict.ts +0 -152
  315. package/src/tui/App.tsx +0 -265
  316. package/src/tui/components/AgentPanel.tsx +0 -75
  317. package/src/tui/components/CostOverlay.tsx +0 -118
  318. package/src/tui/components/HelpOverlay.tsx +0 -107
  319. package/src/tui/components/StatusBar.tsx +0 -63
  320. package/src/tui/components/StoriesPanel.tsx +0 -177
  321. package/src/tui/hooks/useKeyboard.ts +0 -142
  322. package/src/tui/hooks/useLayout.ts +0 -137
  323. package/src/tui/hooks/usePipelineEvents.ts +0 -183
  324. package/src/tui/hooks/usePty.ts +0 -189
  325. package/src/tui/index.tsx +0 -38
  326. package/src/tui/types.ts +0 -76
  327. package/src/utils/errors.ts +0 -12
  328. package/src/utils/git.ts +0 -245
  329. package/src/utils/json-file.ts +0 -72
  330. package/src/utils/log-test-output.ts +0 -25
  331. package/src/utils/path-security.ts +0 -73
  332. package/src/utils/queue-writer.ts +0 -54
  333. package/src/verification/crash-detector.ts +0 -34
  334. package/src/verification/executor.ts +0 -250
  335. package/src/verification/index.ts +0 -12
  336. package/src/verification/orchestrator-types.ts +0 -154
  337. package/src/verification/orchestrator.ts +0 -76
  338. package/src/verification/parser.ts +0 -220
  339. package/src/verification/rectification-loop.ts +0 -172
  340. package/src/verification/rectification.ts +0 -108
  341. package/src/verification/runners.ts +0 -129
  342. package/src/verification/smart-runner.ts +0 -307
  343. package/src/verification/strategies/acceptance.ts +0 -136
  344. package/src/verification/strategies/regression.ts +0 -90
  345. package/src/verification/strategies/scoped.ts +0 -154
  346. package/src/verification/types.ts +0 -117
  347. package/src/version.ts +0 -40
  348. package/src/worktree/dispatcher.ts +0 -6
  349. package/src/worktree/index.ts +0 -2
  350. package/src/worktree/manager.ts +0 -193
  351. package/src/worktree/merge.ts +0 -302
  352. package/src/worktree/types.ts +0 -4
@@ -1,282 +0,0 @@
1
- /**
2
- * LLM-Enhanced Classifier
3
- *
4
- * Uses a single cheap LLM call (haiku) to classify all stories in one shot.
5
- * Falls back to keyword matching if LLM call fails.
6
- */
7
-
8
- import type { AgentAdapter } from "../agents";
9
- import { ClaudeCodeAdapter } from "../agents/claude";
10
- import type { NaxConfig } from "../config";
11
- import { resolveModel } from "../config/schema";
12
- import { getLogger } from "../logger";
13
- import type { UserStory } from "../prd";
14
- import { classifyComplexity } from "../routing";
15
- import { errorMessage } from "../utils/errors";
16
- import type { ClassificationResult, CodebaseScan, StoryClassification } from "./types";
17
-
18
- /**
19
- * Injectable dependencies for classifier — allows tests to mock adapter.complete()
20
- * without needing the claude binary.
21
- *
22
- * @internal
23
- */
24
- export const _classifyDeps = {
25
- adapter: new ClaudeCodeAdapter() as AgentAdapter,
26
- };
27
-
28
- /**
29
- * Raw LLM classification item (before validation)
30
- */
31
- interface LLMClassificationItem {
32
- storyId: unknown;
33
- complexity: unknown;
34
- relevantFiles: unknown;
35
- reasoning: unknown;
36
- estimatedLOC: unknown;
37
- risks: unknown;
38
- }
39
-
40
- /**
41
- * Classify stories using LLM-enhanced analysis with fallback to keyword matching.
42
- *
43
- * Makes a single Anthropic API call (haiku tier) to classify all stories.
44
- * If the LLM call fails or returns invalid JSON, falls back to keyword matching.
45
- *
46
- * @param stories - User stories to classify
47
- * @param scan - Codebase scan result
48
- * @param config - Ngent configuration
49
- * @returns Classification result with method used
50
- *
51
- * @example
52
- * ```ts
53
- * const result = await classifyStories(stories, scan, config);
54
- * if (result.method === "keyword-fallback") {
55
- * console.warn("LLM classification failed:", result.fallbackReason);
56
- * }
57
- * console.log(result.classifications);
58
- * ```
59
- */
60
- export async function classifyStories(
61
- stories: UserStory[],
62
- scan: CodebaseScan,
63
- config: NaxConfig,
64
- ): Promise<ClassificationResult> {
65
- // Check if LLM-enhanced analysis is enabled
66
- if (!config.analyze?.llmEnhanced) {
67
- return {
68
- classifications: stories.map((story) => fallbackClassification(story)),
69
- method: "keyword-fallback",
70
- fallbackReason: "LLM-enhanced analysis disabled in config",
71
- };
72
- }
73
-
74
- // Try LLM classification
75
- try {
76
- const classifications = await classifyWithLLM(stories, scan, config);
77
- return {
78
- classifications,
79
- method: "llm",
80
- };
81
- } catch (error) {
82
- // Fall back to keyword matching
83
- const reason = errorMessage(error);
84
- const logger = getLogger();
85
- logger.warn("analyze", "LLM classification failed, falling back to keyword matching", { error: reason });
86
-
87
- return {
88
- classifications: stories.map((story) => fallbackClassification(story)),
89
- method: "keyword-fallback",
90
- fallbackReason: reason,
91
- };
92
- }
93
- }
94
-
95
- /**
96
- * Classify stories using LLM with structured JSON output.
97
- *
98
- * @param stories - User stories to classify
99
- * @param scan - Codebase scan result
100
- * @param config - Ngent configuration
101
- * @returns Array of story classifications
102
- */
103
- async function classifyWithLLM(
104
- stories: UserStory[],
105
- scan: CodebaseScan,
106
- config: NaxConfig,
107
- ): Promise<StoryClassification[]> {
108
- // Build prompt
109
- const prompt = buildClassificationPrompt(stories, scan);
110
-
111
- // Resolve model from config.models.fast
112
- const fastModelEntry = config.models.fast;
113
- if (!fastModelEntry) {
114
- throw new Error("config.models.fast not configured");
115
- }
116
- const modelDef = resolveModel(fastModelEntry);
117
-
118
- // Make API call via adapter (uses config.models.fast tier)
119
- const jsonText = await _classifyDeps.adapter.complete(prompt, {
120
- jsonMode: true,
121
- maxTokens: 4096,
122
- model: modelDef.model,
123
- config,
124
- });
125
-
126
- // Parse JSON response
127
- const parsed: unknown = JSON.parse(jsonText);
128
-
129
- // Validate structure
130
- if (!Array.isArray(parsed)) {
131
- throw new Error("LLM response is not an array");
132
- }
133
-
134
- // Map to StoryClassification[]
135
- const classifications: StoryClassification[] = parsed.map((item: unknown) => {
136
- const rawItem = item as LLMClassificationItem;
137
- return {
138
- storyId: String(rawItem.storyId),
139
- complexity: validateComplexity(rawItem.complexity),
140
- contextFiles: Array.isArray(rawItem.relevantFiles) ? rawItem.relevantFiles.map(String) : [],
141
- reasoning: String(rawItem.reasoning || "No reasoning provided"),
142
- estimatedLOC: Number(rawItem.estimatedLOC) || 0,
143
- risks: Array.isArray(rawItem.risks) ? rawItem.risks.map(String) : [],
144
- };
145
- });
146
-
147
- // Ensure all stories are classified
148
- const classifiedIds = new Set(classifications.map((c) => c.storyId));
149
- for (const story of stories) {
150
- if (!classifiedIds.has(story.id)) {
151
- throw new Error(`Story ${story.id} not classified by LLM`);
152
- }
153
- }
154
-
155
- return classifications;
156
- }
157
-
158
- /**
159
- * Build classification prompt for LLM.
160
- *
161
- * @param stories - User stories to classify
162
- * @param scan - Codebase scan result
163
- * @returns Formatted prompt string
164
- */
165
- function buildClassificationPrompt(stories: UserStory[], scan: CodebaseScan): string {
166
- // Format codebase summary
167
- const codebaseSummary = `
168
- FILE TREE:
169
- ${scan.fileTree}
170
-
171
- DEPENDENCIES:
172
- ${Object.entries(scan.dependencies)
173
- .map(([name, version]) => `- ${name}: ${version}`)
174
- .join("\n")}
175
-
176
- DEV DEPENDENCIES:
177
- ${Object.entries(scan.devDependencies)
178
- .map(([name, version]) => `- ${name}: ${version}`)
179
- .join("\n")}
180
-
181
- TEST PATTERNS:
182
- ${scan.testPatterns.map((p) => `- ${p}`).join("\n")}
183
- `.trim();
184
-
185
- // Format stories as JSON for LLM
186
- const storiesJson = JSON.stringify(
187
- stories.map((s) => ({
188
- id: s.id,
189
- title: s.title,
190
- description: s.description,
191
- acceptanceCriteria: s.acceptanceCriteria,
192
- tags: s.tags,
193
- })),
194
- null,
195
- 2,
196
- );
197
-
198
- return `You are a code complexity classifier. Given a codebase summary and user stories,
199
- classify each story's implementation complexity.
200
-
201
- CODEBASE:
202
- ${codebaseSummary}
203
-
204
- STORIES:
205
- ${storiesJson}
206
-
207
- For each story, respond with a JSON array (and ONLY the JSON array, no markdown code fences):
208
- [{
209
- "storyId": "US-001",
210
- "complexity": "simple|medium|complex|expert",
211
- "relevantFiles": ["src/path/to/file.ts"],
212
- "reasoning": "Why this complexity level",
213
- "estimatedLOC": 50,
214
- "risks": ["No existing cache layer"]
215
- }]
216
-
217
- Classification rules:
218
- - simple: 1-3 files, <100 LOC, straightforward implementation, existing patterns
219
- - medium: 3-6 files, 100-300 LOC, moderate logic, some new patterns
220
- - complex: 6+ files, 300-800 LOC, architectural changes, cross-cutting concerns
221
- - expert: Security/crypto/real-time/distributed systems, >800 LOC, new infrastructure
222
-
223
- Consider:
224
- 1. Does infrastructure exist? (e.g., "add caching" when no cache layer exists = complex)
225
- 2. How many files will be touched?
226
- 3. Are there cross-cutting concerns (auth, validation, error handling)?
227
- 4. Does it require new dependencies or architectural decisions?
228
-
229
- Respond with ONLY the JSON array.`;
230
- }
231
-
232
- /**
233
- * Validate complexity value from LLM response.
234
- *
235
- * @param value - Complexity value from LLM
236
- * @returns Valid Complexity type
237
- */
238
- function validateComplexity(value: unknown): "simple" | "medium" | "complex" | "expert" {
239
- if (value === "simple" || value === "medium" || value === "complex" || value === "expert") {
240
- return value;
241
- }
242
- // Default to medium if invalid
243
- return "medium";
244
- }
245
-
246
- /**
247
- * Fallback classification using keyword matching.
248
- *
249
- * @param story - User story to classify
250
- * @returns Story classification using keyword-based complexity
251
- */
252
- function fallbackClassification(story: UserStory): StoryClassification {
253
- const complexity = classifyComplexity(story.title, story.description, story.acceptanceCriteria, story.tags);
254
-
255
- return {
256
- storyId: story.id,
257
- complexity,
258
- contextFiles: [],
259
- reasoning: `Keyword-based classification: ${complexity}`,
260
- estimatedLOC: estimateLOCFromComplexity(complexity),
261
- risks: [],
262
- };
263
- }
264
-
265
- /**
266
- * Estimate LOC from complexity level (rough heuristic).
267
- *
268
- * @param complexity - Complexity level
269
- * @returns Estimated lines of code
270
- */
271
- function estimateLOCFromComplexity(complexity: "simple" | "medium" | "complex" | "expert"): number {
272
- switch (complexity) {
273
- case "simple":
274
- return 50;
275
- case "medium":
276
- return 150;
277
- case "complex":
278
- return 400;
279
- case "expert":
280
- return 800;
281
- }
282
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * Analyze Module
3
- *
4
- * LLM-enhanced story classification with codebase scanning.
5
- */
6
-
7
- export type {
8
- CodebaseScan,
9
- StoryClassification,
10
- ClassifierResponse,
11
- ClassificationMethod,
12
- ClassificationResult,
13
- } from "./types";
14
-
15
- export { scanCodebase } from "./scanner";
16
- export { classifyStories } from "./classifier";
@@ -1,171 +0,0 @@
1
- /**
2
- * Codebase Scanner
3
- *
4
- * Scans the project directory to generate a summary for LLM classification.
5
- */
6
-
7
- import { existsSync, readdirSync } from "node:fs";
8
- import { join } from "node:path";
9
- import type { CodebaseScan } from "./types";
10
-
11
- /**
12
- * Scan codebase to generate summary for LLM classification.
13
- *
14
- * Generates:
15
- * - File tree (src/ directory, max depth 3)
16
- * - Package.json dependencies
17
- * - Test pattern detection
18
- *
19
- * @param workdir - Project root directory
20
- * @returns Codebase scan result
21
- *
22
- * @example
23
- * ```ts
24
- * const scan = await scanCodebase("/path/to/project");
25
- * console.log(scan.fileTree);
26
- * console.log(scan.dependencies);
27
- * ```
28
- */
29
- export async function scanCodebase(workdir: string): Promise<CodebaseScan> {
30
- const srcPath = join(workdir, "src");
31
- const packageJsonPath = join(workdir, "package.json");
32
-
33
- // Generate file tree (src/ only, max depth 3)
34
- const fileTree = existsSync(srcPath) ? await generateFileTree(srcPath, 3) : "No src/ directory";
35
-
36
- // Extract dependencies from package.json
37
- let dependencies: Record<string, string> = {};
38
- let devDependencies: Record<string, string> = {};
39
-
40
- if (existsSync(packageJsonPath)) {
41
- try {
42
- const pkg = await Bun.file(packageJsonPath).json();
43
- dependencies = pkg.dependencies || {};
44
- devDependencies = pkg.devDependencies || {};
45
- } catch {
46
- // Invalid package.json, use empty deps
47
- }
48
- }
49
-
50
- // Detect test patterns
51
- const testPatterns = detectTestPatterns(workdir, dependencies, devDependencies);
52
-
53
- return {
54
- fileTree,
55
- dependencies,
56
- devDependencies,
57
- testPatterns,
58
- };
59
- }
60
-
61
- /**
62
- * Generate file tree for a directory with depth limit.
63
- *
64
- * @param dir - Directory path
65
- * @param maxDepth - Maximum depth to traverse
66
- * @param currentDepth - Current depth (internal)
67
- * @param prefix - Line prefix for formatting (internal)
68
- * @returns Formatted file tree string
69
- */
70
- async function generateFileTree(dir: string, maxDepth: number, currentDepth = 0, prefix = ""): Promise<string> {
71
- if (currentDepth >= maxDepth) {
72
- return "";
73
- }
74
-
75
- const entries: string[] = [];
76
-
77
- try {
78
- // readdirSync with withFileTypes avoids a separate stat() call per entry —
79
- // directory info comes from the single readdir syscall (much faster on large trees).
80
- const dirEntries = readdirSync(dir, { withFileTypes: true });
81
-
82
- // Sort: directories first, then files
83
- dirEntries.sort((a, b) => {
84
- if (a.isDirectory() && !b.isDirectory()) return -1;
85
- if (!a.isDirectory() && b.isDirectory()) return 1;
86
- return a.name.localeCompare(b.name);
87
- });
88
-
89
- for (let i = 0; i < dirEntries.length; i++) {
90
- const dirent = dirEntries[i];
91
- const isLast = i === dirEntries.length - 1;
92
- const connector = isLast ? "└── " : "├── ";
93
- const childPrefix = isLast ? " " : "│ ";
94
- const isDir = dirent.isDirectory();
95
-
96
- entries.push(`${prefix}${connector}${dirent.name}${isDir ? "/" : ""}`);
97
-
98
- // Recurse into directories
99
- if (isDir) {
100
- const subtree = await generateFileTree(
101
- join(dir, dirent.name),
102
- maxDepth,
103
- currentDepth + 1,
104
- prefix + childPrefix,
105
- );
106
- if (subtree) {
107
- entries.push(subtree);
108
- }
109
- }
110
- }
111
- } catch {
112
- // Directory not accessible, skip
113
- }
114
-
115
- return entries.join("\n");
116
- }
117
-
118
- /**
119
- * Detect test patterns from directory structure and dependencies.
120
- *
121
- * Checks for:
122
- * - Test framework (vitest, jest, bun:test, mocha, etc.)
123
- * - Test directory structure (test/, __tests__/)
124
- * - Test file patterns (*.test.ts, *.spec.ts)
125
- *
126
- * @param workdir - Project root directory
127
- * @param dependencies - Production dependencies
128
- * @param devDependencies - Dev dependencies
129
- * @returns Array of detected patterns
130
- */
131
- function detectTestPatterns(
132
- workdir: string,
133
- dependencies: Record<string, string>,
134
- devDependencies: Record<string, string>,
135
- ): string[] {
136
- const patterns: string[] = [];
137
- const allDeps = { ...dependencies, ...devDependencies };
138
-
139
- // Detect test framework
140
- if (allDeps.vitest) {
141
- patterns.push("Test framework: vitest");
142
- } else if (allDeps.jest || allDeps["@jest/globals"]) {
143
- patterns.push("Test framework: jest");
144
- } else if (allDeps.mocha) {
145
- patterns.push("Test framework: mocha");
146
- } else if (allDeps.ava) {
147
- patterns.push("Test framework: ava");
148
- } else {
149
- // Check for bun:test (no package.json entry)
150
- patterns.push("Test framework: likely bun:test (no framework dependency)");
151
- }
152
-
153
- // Detect test directory
154
- if (existsSync(join(workdir, "test"))) {
155
- patterns.push("Test directory: test/");
156
- }
157
- if (existsSync(join(workdir, "__tests__"))) {
158
- patterns.push("Test directory: __tests__/");
159
- }
160
- if (existsSync(join(workdir, "tests"))) {
161
- patterns.push("Test directory: tests/");
162
- }
163
-
164
- // Detect test file patterns
165
- const hasTestFiles = existsSync(join(workdir, "test")) || existsSync(join(workdir, "src"));
166
- if (hasTestFiles) {
167
- patterns.push("Test files: *.test.ts, *.spec.ts");
168
- }
169
-
170
- return patterns;
171
- }
@@ -1,51 +0,0 @@
1
- /**
2
- * Analyze Module Types
3
- *
4
- * Types for codebase scanning and LLM-enhanced classification.
5
- */
6
-
7
- import type { Complexity } from "../config";
8
-
9
- /** Codebase scan result */
10
- export interface CodebaseScan {
11
- /** File tree (src/ directory, max depth 3) */
12
- fileTree: string;
13
- /** Package dependencies */
14
- dependencies: Record<string, string>;
15
- /** Dev dependencies */
16
- devDependencies: Record<string, string>;
17
- /** Detected test patterns */
18
- testPatterns: string[];
19
- }
20
-
21
- /** LLM classification result for a single story */
22
- export interface StoryClassification {
23
- /** Story ID (e.g., "US-001") */
24
- storyId: string;
25
- /** Classified complexity */
26
- complexity: Complexity;
27
- /** Context files to inject into agent prompt before execution */
28
- contextFiles: string[];
29
- /** Reasoning for the classification */
30
- reasoning: string;
31
- /** Estimated lines of code to change */
32
- estimatedLOC: number;
33
- /** Potential implementation risks */
34
- risks: string[];
35
- }
36
-
37
- /** LLM classifier response (array of classifications) */
38
- export type ClassifierResponse = StoryClassification[];
39
-
40
- /** Classification method used */
41
- export type ClassificationMethod = "llm" | "keyword-fallback";
42
-
43
- /** Classification result with metadata */
44
- export interface ClassificationResult {
45
- /** Classification data */
46
- classifications: StoryClassification[];
47
- /** Method used (llm or keyword-fallback) */
48
- method: ClassificationMethod;
49
- /** Error message if LLM failed and fallback was used */
50
- fallbackReason?: string;
51
- }
package/src/cli/accept.ts DELETED
@@ -1,108 +0,0 @@
1
- /**
2
- * Accept Command
3
- *
4
- * Allows manual override of failed acceptance criteria.
5
- * Stores override in prd.json with reason.
6
- *
7
- * Usage:
8
- * nax accept --override AC-2 "intentional: using lazy expiry instead of exact timing"
9
- */
10
-
11
- import path from "node:path";
12
- import { findProjectDir, validateDirectory } from "../config";
13
- import { NaxError } from "../errors";
14
- import { getLogger } from "../logger";
15
- import { loadPRD, savePRD } from "../prd";
16
-
17
- /**
18
- * Accept command options.
19
- */
20
- export interface AcceptOptions {
21
- /** Feature name */
22
- feature: string;
23
- /** AC ID to override (e.g., "AC-2") */
24
- override: string;
25
- /** Reason for accepting despite test failure */
26
- reason: string;
27
- }
28
-
29
- /**
30
- * Execute the accept command.
31
- *
32
- * Loads the PRD, adds the AC override with reason, and saves.
33
- *
34
- * @param options - Command options
35
- *
36
- * @example
37
- * ```bash
38
- * nax accept --feature auth-system --override AC-2 "intentional: lazy expiry"
39
- * ```
40
- */
41
- export async function acceptCommand(options: AcceptOptions): Promise<void> {
42
- const logger = getLogger();
43
- const { feature, override, reason } = options;
44
-
45
- // Validate AC ID format
46
- if (!override.match(/^AC-\d+$/i)) {
47
- logger.error("cli", "Invalid AC ID format", { override, expected: "AC-1, AC-2, etc." });
48
- throw new NaxError("Invalid AC ID format", "INVALID_AC_ID", { override, expected: "AC-1, AC-2, etc." });
49
- }
50
-
51
- // Normalize AC ID to uppercase
52
- const acId = override.toUpperCase();
53
-
54
- // Find project directory
55
- const projectDirResult = findProjectDir(process.cwd());
56
- if (!projectDirResult) {
57
- logger.error("cli", "Not in a nax project directory", { hint: "Run 'nax init' first" });
58
- throw new NaxError("Not in a nax project directory", "PROJECT_NOT_FOUND", { hint: "Run 'nax init' first" });
59
- }
60
- const projectDir = projectDirResult;
61
-
62
- // Validate directory
63
- try {
64
- validateDirectory(projectDir);
65
- } catch (err) {
66
- logger.error("cli", "Invalid project directory", { error: (err as Error).message });
67
- throw new NaxError("Invalid project directory", "INVALID_DIRECTORY", { error: (err as Error).message });
68
- }
69
-
70
- // Build path to feature PRD
71
- const featureDir = path.join(projectDir, "nax", "features", feature);
72
- const prdPath = path.join(featureDir, "prd.json");
73
-
74
- // Check if feature exists
75
- const prdFile = Bun.file(prdPath);
76
- if (!(await prdFile.exists())) {
77
- logger.error("cli", "Feature not found", { feature, prdPath });
78
- throw new NaxError("Feature not found", "FEATURE_NOT_FOUND", { feature, prdPath });
79
- }
80
-
81
- // Load PRD
82
- const prd = await loadPRD(prdPath);
83
-
84
- // Add override
85
- if (!prd.acceptanceOverrides) {
86
- prd.acceptanceOverrides = {};
87
- }
88
-
89
- if (prd.acceptanceOverrides[acId]) {
90
- logger.warn("cli", "Override already exists", {
91
- acId,
92
- previous: prd.acceptanceOverrides[acId],
93
- new: reason,
94
- });
95
- }
96
-
97
- prd.acceptanceOverrides[acId] = reason;
98
-
99
- // Save PRD
100
- await savePRD(prd, prdPath);
101
-
102
- logger.info("cli", "✓ Override added", {
103
- acId,
104
- reason,
105
- prdPath,
106
- hint: `Re-run acceptance tests: bun test ${path.join(featureDir, "acceptance.test.ts")}`,
107
- });
108
- }