@nathapp/nax 0.50.3 → 0.51.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (353) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +177 -104
  3. package/dist/nax.js +417 -213
  4. package/package.json +1 -3
  5. package/bin/nax.ts +0 -1195
  6. package/src/acceptance/fix-generator.ts +0 -322
  7. package/src/acceptance/generator.ts +0 -415
  8. package/src/acceptance/index.ts +0 -42
  9. package/src/acceptance/refinement.ts +0 -224
  10. package/src/acceptance/templates/cli.ts +0 -47
  11. package/src/acceptance/templates/component.ts +0 -78
  12. package/src/acceptance/templates/e2e.ts +0 -43
  13. package/src/acceptance/templates/index.ts +0 -21
  14. package/src/acceptance/templates/snapshot.ts +0 -50
  15. package/src/acceptance/templates/unit.ts +0 -48
  16. package/src/acceptance/types.ts +0 -138
  17. package/src/agents/acp/adapter.ts +0 -888
  18. package/src/agents/acp/cost.ts +0 -9
  19. package/src/agents/acp/index.ts +0 -7
  20. package/src/agents/acp/interaction-bridge.ts +0 -126
  21. package/src/agents/acp/parser.ts +0 -119
  22. package/src/agents/acp/spawn-client.ts +0 -373
  23. package/src/agents/acp/types.ts +0 -22
  24. package/src/agents/aider/adapter.ts +0 -135
  25. package/src/agents/claude/adapter.ts +0 -258
  26. package/src/agents/claude/complete.ts +0 -80
  27. package/src/agents/claude/cost.ts +0 -16
  28. package/src/agents/claude/execution.ts +0 -215
  29. package/src/agents/claude/index.ts +0 -3
  30. package/src/agents/claude/interactive.ts +0 -77
  31. package/src/agents/claude/plan.ts +0 -179
  32. package/src/agents/codex/adapter.ts +0 -153
  33. package/src/agents/cost/calculate.ts +0 -154
  34. package/src/agents/cost/index.ts +0 -10
  35. package/src/agents/cost/parse.ts +0 -97
  36. package/src/agents/cost/pricing.ts +0 -59
  37. package/src/agents/cost/types.ts +0 -45
  38. package/src/agents/gemini/adapter.ts +0 -177
  39. package/src/agents/index.ts +0 -18
  40. package/src/agents/opencode/adapter.ts +0 -106
  41. package/src/agents/registry.ts +0 -136
  42. package/src/agents/shared/decompose.ts +0 -154
  43. package/src/agents/shared/model-resolution.ts +0 -43
  44. package/src/agents/shared/types-extended.ts +0 -164
  45. package/src/agents/shared/validation.ts +0 -69
  46. package/src/agents/shared/version-detection.ts +0 -109
  47. package/src/agents/types.ts +0 -205
  48. package/src/analyze/classifier.ts +0 -282
  49. package/src/analyze/index.ts +0 -16
  50. package/src/analyze/scanner.ts +0 -171
  51. package/src/analyze/types.ts +0 -51
  52. package/src/cli/accept.ts +0 -108
  53. package/src/cli/agents.ts +0 -87
  54. package/src/cli/analyze-parser.ts +0 -291
  55. package/src/cli/analyze.ts +0 -352
  56. package/src/cli/config-descriptions.ts +0 -219
  57. package/src/cli/config-diff.ts +0 -103
  58. package/src/cli/config-display.ts +0 -285
  59. package/src/cli/config-get.ts +0 -55
  60. package/src/cli/config.ts +0 -14
  61. package/src/cli/constitution.ts +0 -17
  62. package/src/cli/diagnose-analysis.ts +0 -159
  63. package/src/cli/diagnose-formatter.ts +0 -87
  64. package/src/cli/diagnose.ts +0 -203
  65. package/src/cli/generate.ts +0 -250
  66. package/src/cli/index.ts +0 -42
  67. package/src/cli/init-context.ts +0 -405
  68. package/src/cli/init-detect.ts +0 -303
  69. package/src/cli/init.ts +0 -296
  70. package/src/cli/interact.ts +0 -295
  71. package/src/cli/plan.ts +0 -509
  72. package/src/cli/plugins.ts +0 -122
  73. package/src/cli/prompts-export.ts +0 -58
  74. package/src/cli/prompts-init.ts +0 -200
  75. package/src/cli/prompts-main.ts +0 -183
  76. package/src/cli/prompts-shared.ts +0 -70
  77. package/src/cli/prompts-tdd.ts +0 -88
  78. package/src/cli/prompts.ts +0 -17
  79. package/src/cli/runs.ts +0 -174
  80. package/src/cli/status-cost.ts +0 -151
  81. package/src/cli/status-features.ts +0 -405
  82. package/src/cli/status.ts +0 -13
  83. package/src/commands/common.ts +0 -171
  84. package/src/commands/diagnose.ts +0 -17
  85. package/src/commands/index.ts +0 -9
  86. package/src/commands/logs-formatter.ts +0 -201
  87. package/src/commands/logs-reader.ts +0 -171
  88. package/src/commands/logs.ts +0 -103
  89. package/src/commands/precheck.ts +0 -86
  90. package/src/commands/runs.ts +0 -220
  91. package/src/commands/unlock.ts +0 -96
  92. package/src/config/defaults.ts +0 -218
  93. package/src/config/index.ts +0 -22
  94. package/src/config/loader.ts +0 -143
  95. package/src/config/merge.ts +0 -106
  96. package/src/config/merger.ts +0 -147
  97. package/src/config/path-security.ts +0 -121
  98. package/src/config/paths.ts +0 -27
  99. package/src/config/permissions.ts +0 -63
  100. package/src/config/runtime-types.ts +0 -522
  101. package/src/config/schema-types.ts +0 -53
  102. package/src/config/schema.ts +0 -60
  103. package/src/config/schemas.ts +0 -426
  104. package/src/config/test-strategy.ts +0 -71
  105. package/src/config/types.ts +0 -57
  106. package/src/config/validate.ts +0 -103
  107. package/src/constitution/generator.ts +0 -158
  108. package/src/constitution/generators/aider.ts +0 -41
  109. package/src/constitution/generators/claude.ts +0 -35
  110. package/src/constitution/generators/cursor.ts +0 -36
  111. package/src/constitution/generators/opencode.ts +0 -38
  112. package/src/constitution/generators/types.ts +0 -33
  113. package/src/constitution/generators/windsurf.ts +0 -36
  114. package/src/constitution/index.ts +0 -11
  115. package/src/constitution/loader.ts +0 -121
  116. package/src/constitution/types.ts +0 -31
  117. package/src/context/auto-detect.ts +0 -228
  118. package/src/context/builder.ts +0 -299
  119. package/src/context/elements.ts +0 -122
  120. package/src/context/formatter.ts +0 -107
  121. package/src/context/generator.ts +0 -343
  122. package/src/context/generators/aider.ts +0 -34
  123. package/src/context/generators/claude.ts +0 -28
  124. package/src/context/generators/codex.ts +0 -28
  125. package/src/context/generators/cursor.ts +0 -28
  126. package/src/context/generators/gemini.ts +0 -28
  127. package/src/context/generators/opencode.ts +0 -30
  128. package/src/context/generators/windsurf.ts +0 -28
  129. package/src/context/greenfield.ts +0 -114
  130. package/src/context/index.ts +0 -34
  131. package/src/context/injector.ts +0 -279
  132. package/src/context/parent-context.ts +0 -39
  133. package/src/context/test-scanner.ts +0 -370
  134. package/src/context/types.ts +0 -98
  135. package/src/decompose/apply.ts +0 -50
  136. package/src/decompose/builder.ts +0 -181
  137. package/src/decompose/index.ts +0 -8
  138. package/src/decompose/sections/codebase.ts +0 -26
  139. package/src/decompose/sections/constraints.ts +0 -32
  140. package/src/decompose/sections/index.ts +0 -4
  141. package/src/decompose/sections/sibling-stories.ts +0 -25
  142. package/src/decompose/sections/target-story.ts +0 -31
  143. package/src/decompose/types.ts +0 -55
  144. package/src/decompose/validators/complexity.ts +0 -45
  145. package/src/decompose/validators/coverage.ts +0 -134
  146. package/src/decompose/validators/dependency.ts +0 -91
  147. package/src/decompose/validators/index.ts +0 -35
  148. package/src/decompose/validators/overlap.ts +0 -128
  149. package/src/errors.ts +0 -67
  150. package/src/execution/batching.ts +0 -157
  151. package/src/execution/crash-heartbeat.ts +0 -77
  152. package/src/execution/crash-recovery.ts +0 -79
  153. package/src/execution/crash-signals.ts +0 -165
  154. package/src/execution/crash-writer.ts +0 -154
  155. package/src/execution/deferred-review.ts +0 -105
  156. package/src/execution/dry-run.ts +0 -81
  157. package/src/execution/escalation/escalation.ts +0 -46
  158. package/src/execution/escalation/index.ts +0 -13
  159. package/src/execution/escalation/tier-escalation.ts +0 -346
  160. package/src/execution/escalation/tier-outcome.ts +0 -143
  161. package/src/execution/executor-types.ts +0 -73
  162. package/src/execution/helpers.ts +0 -38
  163. package/src/execution/index.ts +0 -27
  164. package/src/execution/iteration-runner.ts +0 -160
  165. package/src/execution/lifecycle/acceptance-loop.ts +0 -309
  166. package/src/execution/lifecycle/headless-formatter.ts +0 -83
  167. package/src/execution/lifecycle/index.ts +0 -11
  168. package/src/execution/lifecycle/parallel-lifecycle.ts +0 -101
  169. package/src/execution/lifecycle/precheck-runner.ts +0 -140
  170. package/src/execution/lifecycle/run-cleanup.ts +0 -81
  171. package/src/execution/lifecycle/run-completion.ts +0 -247
  172. package/src/execution/lifecycle/run-initialization.ts +0 -187
  173. package/src/execution/lifecycle/run-regression.ts +0 -305
  174. package/src/execution/lifecycle/run-setup.ts +0 -240
  175. package/src/execution/lifecycle/story-size-prompts.ts +0 -123
  176. package/src/execution/lock.ts +0 -129
  177. package/src/execution/parallel-coordinator.ts +0 -281
  178. package/src/execution/parallel-executor-rectification-pass.ts +0 -117
  179. package/src/execution/parallel-executor-rectify.ts +0 -136
  180. package/src/execution/parallel-executor.ts +0 -330
  181. package/src/execution/parallel-worker.ts +0 -149
  182. package/src/execution/parallel.ts +0 -13
  183. package/src/execution/pid-registry.ts +0 -275
  184. package/src/execution/pipeline-result-handler.ts +0 -221
  185. package/src/execution/progress.ts +0 -27
  186. package/src/execution/queue-handler.ts +0 -109
  187. package/src/execution/runner-completion.ts +0 -171
  188. package/src/execution/runner-execution.ts +0 -243
  189. package/src/execution/runner-setup.ts +0 -86
  190. package/src/execution/runner.ts +0 -265
  191. package/src/execution/sequential-executor.ts +0 -219
  192. package/src/execution/status-file.ts +0 -264
  193. package/src/execution/status-writer.ts +0 -181
  194. package/src/execution/story-context.ts +0 -266
  195. package/src/execution/story-selector.ts +0 -76
  196. package/src/execution/test-output-parser.ts +0 -14
  197. package/src/execution/timeout-handler.ts +0 -100
  198. package/src/hooks/index.ts +0 -2
  199. package/src/hooks/runner.ts +0 -280
  200. package/src/hooks/types.ts +0 -79
  201. package/src/interaction/chain.ts +0 -170
  202. package/src/interaction/index.ts +0 -61
  203. package/src/interaction/init.ts +0 -84
  204. package/src/interaction/plugins/auto.ts +0 -243
  205. package/src/interaction/plugins/cli.ts +0 -300
  206. package/src/interaction/plugins/telegram.ts +0 -384
  207. package/src/interaction/plugins/webhook.ts +0 -286
  208. package/src/interaction/state.ts +0 -171
  209. package/src/interaction/triggers.ts +0 -250
  210. package/src/interaction/types.ts +0 -170
  211. package/src/logger/formatters.ts +0 -84
  212. package/src/logger/index.ts +0 -16
  213. package/src/logger/logger.ts +0 -296
  214. package/src/logger/types.ts +0 -48
  215. package/src/logging/formatter.ts +0 -355
  216. package/src/logging/index.ts +0 -22
  217. package/src/logging/types.ts +0 -93
  218. package/src/metrics/aggregator.ts +0 -191
  219. package/src/metrics/index.ts +0 -14
  220. package/src/metrics/tracker.ts +0 -200
  221. package/src/metrics/types.ts +0 -115
  222. package/src/optimizer/index.ts +0 -63
  223. package/src/optimizer/noop.optimizer.ts +0 -24
  224. package/src/optimizer/rule-based.optimizer.ts +0 -248
  225. package/src/optimizer/types.ts +0 -53
  226. package/src/pipeline/event-bus.ts +0 -297
  227. package/src/pipeline/events.ts +0 -130
  228. package/src/pipeline/index.ts +0 -19
  229. package/src/pipeline/runner.ts +0 -149
  230. package/src/pipeline/stages/acceptance-setup.ts +0 -144
  231. package/src/pipeline/stages/acceptance.ts +0 -215
  232. package/src/pipeline/stages/autofix.ts +0 -262
  233. package/src/pipeline/stages/completion.ts +0 -110
  234. package/src/pipeline/stages/constitution.ts +0 -63
  235. package/src/pipeline/stages/context.ts +0 -122
  236. package/src/pipeline/stages/execution.ts +0 -359
  237. package/src/pipeline/stages/index.ts +0 -86
  238. package/src/pipeline/stages/optimizer.ts +0 -74
  239. package/src/pipeline/stages/prompt.ts +0 -79
  240. package/src/pipeline/stages/queue-check.ts +0 -103
  241. package/src/pipeline/stages/rectify.ts +0 -101
  242. package/src/pipeline/stages/regression.ts +0 -99
  243. package/src/pipeline/stages/review.ts +0 -94
  244. package/src/pipeline/stages/routing.ts +0 -276
  245. package/src/pipeline/stages/verify.ts +0 -286
  246. package/src/pipeline/subscribers/events-writer.ts +0 -135
  247. package/src/pipeline/subscribers/hooks.ts +0 -179
  248. package/src/pipeline/subscribers/interaction.ts +0 -103
  249. package/src/pipeline/subscribers/registry.ts +0 -73
  250. package/src/pipeline/subscribers/reporters.ts +0 -174
  251. package/src/pipeline/types.ts +0 -220
  252. package/src/plugins/extensions.ts +0 -225
  253. package/src/plugins/index.ts +0 -33
  254. package/src/plugins/loader.ts +0 -352
  255. package/src/plugins/plugin-logger.ts +0 -41
  256. package/src/plugins/registry.ts +0 -168
  257. package/src/plugins/types.ts +0 -206
  258. package/src/plugins/validator.ts +0 -352
  259. package/src/prd/index.ts +0 -220
  260. package/src/prd/schema.ts +0 -268
  261. package/src/prd/types.ts +0 -273
  262. package/src/prd/validate.ts +0 -41
  263. package/src/precheck/checks-agents.ts +0 -63
  264. package/src/precheck/checks-blockers.ts +0 -23
  265. package/src/precheck/checks-cli.ts +0 -68
  266. package/src/precheck/checks-config.ts +0 -102
  267. package/src/precheck/checks-git.ts +0 -117
  268. package/src/precheck/checks-system.ts +0 -101
  269. package/src/precheck/checks-warnings.ts +0 -221
  270. package/src/precheck/checks.ts +0 -36
  271. package/src/precheck/index.ts +0 -374
  272. package/src/precheck/story-size-gate.ts +0 -144
  273. package/src/precheck/types.ts +0 -31
  274. package/src/prompts/builder.ts +0 -166
  275. package/src/prompts/index.ts +0 -2
  276. package/src/prompts/loader.ts +0 -43
  277. package/src/prompts/sections/conventions.ts +0 -19
  278. package/src/prompts/sections/hermetic.ts +0 -41
  279. package/src/prompts/sections/index.ts +0 -12
  280. package/src/prompts/sections/isolation.ts +0 -70
  281. package/src/prompts/sections/role-task.ts +0 -182
  282. package/src/prompts/sections/story.ts +0 -55
  283. package/src/prompts/sections/verdict.ts +0 -70
  284. package/src/prompts/types.ts +0 -21
  285. package/src/queue/index.ts +0 -2
  286. package/src/queue/manager.ts +0 -254
  287. package/src/queue/types.ts +0 -54
  288. package/src/review/index.ts +0 -8
  289. package/src/review/orchestrator.ts +0 -154
  290. package/src/review/runner.ts +0 -303
  291. package/src/review/types.ts +0 -70
  292. package/src/routing/batch-route.ts +0 -35
  293. package/src/routing/builder.ts +0 -81
  294. package/src/routing/chain.ts +0 -75
  295. package/src/routing/content-hash.ts +0 -25
  296. package/src/routing/index.ts +0 -20
  297. package/src/routing/loader.ts +0 -62
  298. package/src/routing/router.ts +0 -305
  299. package/src/routing/strategies/adaptive.ts +0 -215
  300. package/src/routing/strategies/index.ts +0 -8
  301. package/src/routing/strategies/keyword.ts +0 -180
  302. package/src/routing/strategies/llm-prompts.ts +0 -224
  303. package/src/routing/strategies/llm.ts +0 -320
  304. package/src/routing/strategies/manual.ts +0 -50
  305. package/src/routing/strategy.ts +0 -102
  306. package/src/tdd/cleanup.ts +0 -120
  307. package/src/tdd/index.ts +0 -22
  308. package/src/tdd/isolation.ts +0 -117
  309. package/src/tdd/orchestrator.ts +0 -406
  310. package/src/tdd/prompts.ts +0 -40
  311. package/src/tdd/rectification-gate.ts +0 -274
  312. package/src/tdd/session-runner.ts +0 -263
  313. package/src/tdd/types.ts +0 -84
  314. package/src/tdd/verdict-reader.ts +0 -266
  315. package/src/tdd/verdict.ts +0 -152
  316. package/src/tui/App.tsx +0 -265
  317. package/src/tui/components/AgentPanel.tsx +0 -75
  318. package/src/tui/components/CostOverlay.tsx +0 -118
  319. package/src/tui/components/HelpOverlay.tsx +0 -107
  320. package/src/tui/components/StatusBar.tsx +0 -63
  321. package/src/tui/components/StoriesPanel.tsx +0 -177
  322. package/src/tui/hooks/useKeyboard.ts +0 -142
  323. package/src/tui/hooks/useLayout.ts +0 -137
  324. package/src/tui/hooks/usePipelineEvents.ts +0 -183
  325. package/src/tui/hooks/usePty.ts +0 -189
  326. package/src/tui/index.tsx +0 -38
  327. package/src/tui/types.ts +0 -76
  328. package/src/utils/errors.ts +0 -12
  329. package/src/utils/git.ts +0 -245
  330. package/src/utils/json-file.ts +0 -72
  331. package/src/utils/log-test-output.ts +0 -25
  332. package/src/utils/path-security.ts +0 -73
  333. package/src/utils/queue-writer.ts +0 -54
  334. package/src/verification/crash-detector.ts +0 -34
  335. package/src/verification/executor.ts +0 -250
  336. package/src/verification/index.ts +0 -12
  337. package/src/verification/orchestrator-types.ts +0 -154
  338. package/src/verification/orchestrator.ts +0 -76
  339. package/src/verification/parser.ts +0 -220
  340. package/src/verification/rectification-loop.ts +0 -172
  341. package/src/verification/rectification.ts +0 -108
  342. package/src/verification/runners.ts +0 -129
  343. package/src/verification/smart-runner.ts +0 -307
  344. package/src/verification/strategies/acceptance.ts +0 -136
  345. package/src/verification/strategies/regression.ts +0 -90
  346. package/src/verification/strategies/scoped.ts +0 -154
  347. package/src/verification/types.ts +0 -117
  348. package/src/version.ts +0 -40
  349. package/src/worktree/dispatcher.ts +0 -6
  350. package/src/worktree/index.ts +0 -2
  351. package/src/worktree/manager.ts +0 -193
  352. package/src/worktree/merge.ts +0 -302
  353. package/src/worktree/types.ts +0 -4
@@ -1,305 +0,0 @@
1
- /**
2
- * Task Router
3
- *
4
- * Routes stories using pluggable strategy system.
5
- * Falls back to keyword-based classification for backward compatibility.
6
- */
7
-
8
- import type { Complexity, ModelTier, NaxConfig, TddStrategy, TestStrategy } from "../config";
9
- import type { UserStory } from "../prd/types";
10
- import { buildStrategyChain } from "./builder";
11
- import type { RoutingContext } from "./strategy";
12
-
13
- /** Routing decision for a story */
14
- export interface RoutingDecision {
15
- /** Classified complexity */
16
- complexity: Complexity;
17
- /** Model tier to use */
18
- modelTier: ModelTier;
19
- /** Test strategy to apply */
20
- testStrategy: TestStrategy;
21
- /** Reasoning for the classification */
22
- reasoning: string;
23
- }
24
-
25
- /** Keywords that indicate higher complexity */
26
- const COMPLEX_KEYWORDS = [
27
- "refactor",
28
- "redesign",
29
- "architecture",
30
- "migration",
31
- "breaking change",
32
- "public api",
33
- "security",
34
- "auth",
35
- "encryption",
36
- "permission",
37
- "rbac",
38
- "casl",
39
- "jwt",
40
- "grpc",
41
- "microservice",
42
- "event-driven",
43
- "saga",
44
- ];
45
-
46
- const EXPERT_KEYWORDS = [
47
- "cryptograph",
48
- "zero-knowledge",
49
- "distributed consensus",
50
- "real-time",
51
- "websocket",
52
- "streaming",
53
- "performance critical",
54
- ];
55
-
56
- const SECURITY_KEYWORDS = [
57
- "auth",
58
- "security",
59
- "permission",
60
- "jwt",
61
- "oauth",
62
- "token",
63
- "encryption",
64
- "secret",
65
- "credential",
66
- "password",
67
- "rbac",
68
- "casl",
69
- ];
70
-
71
- const PUBLIC_API_KEYWORDS = [
72
- "public api",
73
- "breaking change",
74
- "external",
75
- "consumer",
76
- "sdk",
77
- "npm publish",
78
- "release",
79
- "endpoint",
80
- ];
81
-
82
- /**
83
- * Classify a story's complexity based on keywords and acceptance criteria count.
84
- *
85
- * Classification rules:
86
- * - expert: matches expert keywords (cryptography, distributed consensus, real-time)
87
- * - complex: matches complex keywords or >8 acceptance criteria
88
- * - medium: >4 acceptance criteria
89
- * - simple: default
90
- *
91
- * @param title - Story title
92
- * @param description - Story description
93
- * @param acceptanceCriteria - Array of acceptance criteria
94
- * @param tags - Optional story tags
95
- * @returns Classified complexity level
96
- *
97
- * @example
98
- * ```ts
99
- * classifyComplexity(
100
- * "Add JWT authentication",
101
- * "Implement JWT auth with refresh tokens",
102
- * ["Secure token storage", "Token refresh", "Expiry handling"],
103
- * ["security", "auth"]
104
- * );
105
- * // "complex" (matches security keywords)
106
- *
107
- * classifyComplexity(
108
- * "Fix typo in README",
109
- * "Correct spelling mistake",
110
- * ["Update README.md"],
111
- * []
112
- * );
113
- * // "simple"
114
- * ```
115
- */
116
- export function classifyComplexity(
117
- title: string,
118
- description: string,
119
- acceptanceCriteria: string[],
120
- tags: string[] = [],
121
- ): Complexity {
122
- const text = [title, description, ...(acceptanceCriteria ?? []), ...(tags ?? [])].join(" ").toLowerCase();
123
-
124
- // Expert: matches expert keywords
125
- if (EXPERT_KEYWORDS.some((kw) => text.includes(kw))) {
126
- return "expert";
127
- }
128
-
129
- // Complex: matches complex keywords or has many criteria
130
- if (COMPLEX_KEYWORDS.some((kw) => text.includes(kw)) || acceptanceCriteria.length > 8) {
131
- return "complex";
132
- }
133
-
134
- // Medium: moderate criteria or some structural keywords
135
- if (acceptanceCriteria.length > 4) {
136
- return "medium";
137
- }
138
-
139
- return "simple";
140
- }
141
-
142
- /** Tags that indicate a lite-mode story (UI, layout, CLI, integration, polyglot) */
143
- const LITE_TAGS = ["ui", "layout", "cli", "integration", "polyglot"];
144
-
145
- /**
146
- * Determine test strategy using decision tree logic.
147
- *
148
- * When tddStrategy is provided:
149
- * - 'strict' → always three-session-tdd
150
- * - 'lite' → always three-session-tdd-lite
151
- * - 'off' → always test-after
152
- * - 'auto' → existing heuristic logic, plus:
153
- * if tags include ui/layout/cli/integration/polyglot → three-session-tdd-lite
154
- * if security/public-api/complex/expert → three-session-tdd
155
- * simple → tdd-simple, medium → three-session-tdd-lite
156
- *
157
- * @param complexity - Pre-classified complexity level
158
- * @param title - Story title
159
- * @param description - Story description
160
- * @param tags - Optional story tags
161
- * @param tddStrategy - TDD strategy override from config (default: 'auto')
162
- * @returns Test strategy
163
- *
164
- * @example
165
- * ```ts
166
- * determineTestStrategy("complex", "Add OAuth", "Implement OAuth 2.0", ["security", "auth"], "strict");
167
- * // "three-session-tdd"
168
- *
169
- * determineTestStrategy("simple", "Update button", "Change primary button", ["ui"], "auto");
170
- * // "three-session-tdd-lite"
171
- * ```
172
- */
173
-
174
- export function determineTestStrategy(
175
- complexity: Complexity,
176
- title: string,
177
- description: string,
178
- tags: string[] = [],
179
- tddStrategy: TddStrategy = "auto",
180
- ): TestStrategy {
181
- // Explicit overrides — ignore all heuristics
182
- if (tddStrategy === "strict") return "three-session-tdd";
183
- if (tddStrategy === "lite") return "three-session-tdd-lite";
184
- if (tddStrategy === "simple") return "tdd-simple";
185
- if (tddStrategy === "off") return "test-after";
186
-
187
- // auto mode: apply heuristics
188
- const text = [title, description, ...(tags ?? [])].join(" ").toLowerCase();
189
-
190
- // Public API or security → three-session-tdd (strict)
191
- const isSecurityCritical = SECURITY_KEYWORDS.some((kw) => text.includes(kw));
192
- const isPublicApi = PUBLIC_API_KEYWORDS.some((kw) => text.includes(kw));
193
-
194
- if (isSecurityCritical || isPublicApi) {
195
- return "three-session-tdd";
196
- }
197
-
198
- // Complex/expert heuristic
199
- if (complexity === "complex" || complexity === "expert") {
200
- const normalizedTags = (tags ?? []).map((t) => t.toLowerCase());
201
- const hasLiteTag = LITE_TAGS.some((tag) => normalizedTags.includes(tag));
202
- return hasLiteTag ? "three-session-tdd-lite" : "three-session-tdd";
203
- }
204
-
205
- // TS-001: simple → tdd-simple (TDD discipline, 1 session), medium → tdd-lite (3 sessions)
206
- if (complexity === "simple") return "tdd-simple";
207
- return "three-session-tdd-lite";
208
- }
209
-
210
- /** Map complexity to model tier */
211
- export function complexityToModelTier(complexity: Complexity, config: NaxConfig): ModelTier {
212
- const mapping = config.autoMode.complexityRouting;
213
- return (mapping[complexity] ?? "balanced") as ModelTier;
214
- }
215
-
216
- /**
217
- * Route a story using the pluggable strategy system.
218
- *
219
- * This is the new main entry point for the routing system. It:
220
- * 1. Builds the strategy chain based on config
221
- * 2. Routes the story through the chain
222
- * 3. Returns the first non-null decision
223
- *
224
- * @param story - User story to route
225
- * @param context - Routing context (config, codebase, metrics)
226
- * @param workdir - Working directory for resolving custom strategy paths
227
- * @param plugins - Optional plugin registry for plugin-provided routers
228
- * @returns Routing decision from the strategy chain
229
- *
230
- * @example
231
- * ```ts
232
- * const decision = await routeStory(story, { config }, "/path/to/project", plugins);
233
- * // {
234
- * // complexity: "complex",
235
- * // modelTier: "balanced",
236
- * // testStrategy: "three-session-tdd",
237
- * // reasoning: "three-session-tdd: security-critical, complexity:complex"
238
- * // }
239
- * ```
240
- */
241
- export async function routeStory(
242
- story: UserStory,
243
- context: RoutingContext,
244
- workdir: string,
245
- plugins?: import("../plugins/registry").PluginRegistry,
246
- ): Promise<RoutingDecision> {
247
- const chain = await buildStrategyChain(context.config, workdir, plugins);
248
- return await chain.route(story, context);
249
- }
250
-
251
- /**
252
- * Route a task through complexity classification, model tier selection, and test strategy.
253
- *
254
- * DEPRECATED: Use routeStory() instead. This function is kept for backward compatibility
255
- * and uses only the keyword strategy.
256
- *
257
- * @param title - Story title
258
- * @param description - Story description
259
- * @param acceptanceCriteria - Array of acceptance criteria
260
- * @param tags - Story tags
261
- * @param config - nax configuration with complexity routing mappings
262
- * @returns Routing decision with complexity, model tier, test strategy, and reasoning
263
- *
264
- * @deprecated Use routeStory() with a UserStory object instead
265
- */
266
- export function routeTask(
267
- title: string,
268
- description: string,
269
- acceptanceCriteria: string[],
270
- tags: string[],
271
- config: NaxConfig,
272
- ): RoutingDecision {
273
- const complexity = classifyComplexity(title, description, acceptanceCriteria, tags);
274
- const modelTier = complexityToModelTier(complexity, config);
275
- const tddStrategy: TddStrategy = config.tdd?.strategy ?? "auto";
276
- const testStrategy = determineTestStrategy(complexity, title, description, tags, tddStrategy);
277
-
278
- const reasons: string[] = [];
279
- const text = [title, description, ...(tags ?? [])].join(" ").toLowerCase();
280
- const normalizedTags = (tags ?? []).map((t) => t.toLowerCase());
281
- const hasLiteTag = LITE_TAGS.some((tag) => normalizedTags.includes(tag));
282
-
283
- if (SECURITY_KEYWORDS.some((kw) => text.includes(kw))) reasons.push("security-critical");
284
- if (PUBLIC_API_KEYWORDS.some((kw) => text.includes(kw))) reasons.push("public-api");
285
-
286
- // Only add complexity reason if it's the primary reason for strict/lite TDD
287
- if (complexity === "complex" || complexity === "expert") {
288
- if (reasons.length === 0) {
289
- reasons.push(`complexity:${complexity}`);
290
- }
291
- }
292
-
293
- if (tddStrategy !== "auto") reasons.push(`strategy:${tddStrategy}`);
294
- if (hasLiteTag && (complexity === "complex" || complexity === "expert")) {
295
- reasons.push("lite-tags");
296
- }
297
-
298
- const prefix = testStrategy;
299
- return {
300
- complexity,
301
- modelTier,
302
- testStrategy,
303
- reasoning: reasons.length > 0 ? `${prefix}: ${reasons.join(", ")}` : `test-after: simple task (${complexity})`,
304
- };
305
- }
@@ -1,215 +0,0 @@
1
- /**
2
- * Adaptive Routing Strategy
3
- *
4
- * Uses historical metrics to optimize model tier selection based on cost-effectiveness.
5
- * Routes to the cheapest tier that maintains acceptable success rates, accounting for
6
- * escalation costs.
7
- */
8
-
9
- import type { Complexity, ModelTier } from "../../config";
10
- import type { AggregateMetrics } from "../../metrics/types";
11
- import type { UserStory } from "../../prd/types";
12
- import type { RoutingContext, RoutingDecision, RoutingStrategy } from "../strategy";
13
- import { keywordStrategy } from "./keyword";
14
-
15
- /**
16
- * Estimated costs per model tier (USD per story, approximate).
17
- * These are rough estimates based on typical story complexity.
18
- * Actual costs vary based on input/output tokens.
19
- */
20
- const ESTIMATED_TIER_COSTS: Record<ModelTier, number> = {
21
- fast: 0.005, // ~$0.005 per simple story
22
- balanced: 0.02, // ~$0.02 per medium story
23
- powerful: 0.08, // ~$0.08 per complex story
24
- };
25
-
26
- /**
27
- * Calculate effective cost for a model tier given historical metrics.
28
- *
29
- * effectiveCost = baseCost + (failRate × escalationCost)
30
- *
31
- * Where:
32
- * - baseCost = cost of using this tier
33
- * - failRate = probability of failure (requiring escalation)
34
- * - escalationCost = cost of escalating to next tier
35
- *
36
- * @param tier - Model tier to evaluate
37
- * @param complexity - Story complexity level
38
- * @param metrics - Historical aggregate metrics
39
- * @param tierOrder - Escalation tier order
40
- * @returns Effective cost (USD)
41
- */
42
- function calculateEffectiveCost(
43
- tier: ModelTier,
44
- complexity: Complexity,
45
- metrics: AggregateMetrics,
46
- tierOrder: ModelTier[],
47
- ): number {
48
- const baseCost = ESTIMATED_TIER_COSTS[tier];
49
-
50
- // Get historical pass rate for this tier on this complexity level
51
- const complexityStats = metrics.complexityAccuracy[complexity];
52
- if (!complexityStats || complexityStats.predicted < 1) {
53
- // No data for this complexity level — assume base cost (no escalation)
54
- return baseCost;
55
- }
56
-
57
- // Calculate fail rate (stories that needed escalation)
58
- // mismatchRate = percentage of stories where initial tier != final tier
59
- const failRate = complexityStats.mismatchRate;
60
-
61
- // Find next tier in escalation chain
62
- const currentIndex = tierOrder.indexOf(tier);
63
- const nextTier = currentIndex < tierOrder.length - 1 ? tierOrder[currentIndex + 1] : null;
64
-
65
- if (!nextTier) {
66
- // Already at highest tier — no escalation possible
67
- return baseCost;
68
- }
69
-
70
- // Escalation cost = cost of trying this tier + cost of next tier
71
- const escalationCost = ESTIMATED_TIER_COSTS[nextTier];
72
-
73
- return baseCost + failRate * escalationCost;
74
- }
75
-
76
- /**
77
- * Find the most cost-effective tier for a given complexity level.
78
- *
79
- * Evaluates all tiers in the escalation chain and selects the one with
80
- * the lowest effective cost (including escalation probability).
81
- *
82
- * @param complexity - Story complexity
83
- * @param metrics - Historical metrics
84
- * @param tierOrder - Escalation tier order
85
- * @param costThreshold - Switch threshold (0-1)
86
- * @returns Best tier and reasoning
87
- */
88
- function selectOptimalTier(
89
- complexity: Complexity,
90
- metrics: AggregateMetrics,
91
- tierOrder: ModelTier[],
92
- costThreshold: number,
93
- ): { tier: ModelTier; reasoning: string } {
94
- // Calculate effective cost for each tier
95
- const costs = tierOrder.map((tier) => ({
96
- tier,
97
- effectiveCost: calculateEffectiveCost(tier, complexity, metrics, tierOrder),
98
- }));
99
-
100
- // Sort by effective cost (lowest first)
101
- costs.sort((a, b) => a.effectiveCost - b.effectiveCost);
102
-
103
- const optimal = costs[0];
104
- const complexityStats = metrics.complexityAccuracy[complexity];
105
-
106
- // If the cheapest tier's effective cost is within threshold of next tier, use it
107
- const reasoning = complexityStats
108
- ? `adaptive: ${complexity} → ${optimal.tier} (cost: $${optimal.effectiveCost.toFixed(4)}, ` +
109
- `samples: ${complexityStats.predicted}, mismatch: ${(complexityStats.mismatchRate * 100).toFixed(1)}%)`
110
- : `adaptive: ${complexity} → ${optimal.tier} (insufficient data, using base cost: $${optimal.effectiveCost.toFixed(4)})`;
111
-
112
- return { tier: optimal.tier, reasoning };
113
- }
114
-
115
- /**
116
- * Check if there's sufficient data for adaptive routing.
117
- *
118
- * @param complexity - Story complexity
119
- * @param metrics - Historical metrics
120
- * @param minSamples - Minimum samples required
121
- * @returns True if sufficient data exists
122
- */
123
- function hasSufficientData(complexity: Complexity, metrics: AggregateMetrics, minSamples: number): boolean {
124
- const complexityStats = metrics.complexityAccuracy[complexity];
125
- return Boolean(complexityStats && complexityStats.predicted >= minSamples);
126
- }
127
-
128
- /**
129
- * Adaptive routing strategy.
130
- *
131
- * Uses historical metrics to select the most cost-effective model tier.
132
- * Falls back to configured strategy when insufficient data is available.
133
- *
134
- * Algorithm:
135
- * 1. Check if sufficient historical data exists (>= minSamples)
136
- * 2. If yes: Calculate effective cost for each tier (base + fail × escalation)
137
- * 3. Select tier with lowest effective cost
138
- * 4. If no: Delegate to fallback strategy
139
- *
140
- * @example
141
- * ```ts
142
- * const decision = adaptiveStrategy.route(story, context);
143
- * // With sufficient data:
144
- * // {
145
- * // complexity: "medium",
146
- * // modelTier: "fast",
147
- * // reasoning: "adaptive: medium → fast (cost: $0.0078, samples: 23, mismatch: 12.5%)"
148
- * // }
149
- * //
150
- * // Without sufficient data:
151
- * // {
152
- * // complexity: "medium",
153
- * // modelTier: "balanced",
154
- * // reasoning: "adaptive: insufficient data (7/10 samples) → fallback to llm"
155
- * // }
156
- * ```
157
- */
158
- export const adaptiveStrategy: RoutingStrategy = {
159
- name: "adaptive",
160
-
161
- async route(story: UserStory, context: RoutingContext): Promise<RoutingDecision | null> {
162
- const { config, metrics } = context;
163
-
164
- // Require metrics to be present - use keyword as ultimate fallback
165
- if (!metrics) {
166
- const fallbackStrategy = config.routing.adaptive?.fallbackStrategy || "llm";
167
- const decision = await keywordStrategy.route(story, context); // keyword never returns null
168
- if (!decision) return null;
169
-
170
- return {
171
- ...decision,
172
- reasoning: `adaptive: no metrics available → fallback to ${fallbackStrategy}`,
173
- };
174
- }
175
-
176
- // Get adaptive config
177
- const adaptiveConfig = config.routing.adaptive || {
178
- minSamples: 10,
179
- costThreshold: 0.8,
180
- fallbackStrategy: "llm" as const,
181
- };
182
-
183
- // First, classify complexity using fallback strategy
184
- // (We need to know complexity before checking metrics)
185
- // Always use keyword as the classification source since it never returns null
186
- const fallbackDecision = await keywordStrategy.route(story, context);
187
- if (!fallbackDecision) return null;
188
-
189
- const complexity = fallbackDecision.complexity;
190
-
191
- // Check if we have sufficient historical data for this complexity
192
- if (!hasSufficientData(complexity, metrics, adaptiveConfig.minSamples)) {
193
- const complexityStats = metrics.complexityAccuracy[complexity];
194
- const sampleCount = complexityStats?.predicted || 0;
195
-
196
- return {
197
- ...fallbackDecision,
198
- reasoning:
199
- `adaptive: insufficient data (${sampleCount}/${adaptiveConfig.minSamples} samples) ` +
200
- `→ fallback to ${adaptiveConfig.fallbackStrategy}`,
201
- };
202
- }
203
-
204
- // We have sufficient data — calculate optimal tier
205
- const tierOrder = config.autoMode.escalation.tierOrder.map((t) => t.tier);
206
- const { tier, reasoning } = selectOptimalTier(complexity, metrics, tierOrder, adaptiveConfig.costThreshold);
207
-
208
- return {
209
- complexity,
210
- modelTier: tier,
211
- testStrategy: fallbackDecision.testStrategy, // Use fallback's test strategy decision
212
- reasoning,
213
- };
214
- },
215
- };
@@ -1,8 +0,0 @@
1
- /**
2
- * Built-in Routing Strategies
3
- */
4
-
5
- export { keywordStrategy } from "./keyword";
6
- export { llmStrategy } from "./llm";
7
- export { manualStrategy } from "./manual";
8
- export { adaptiveStrategy } from "./adaptive";
@@ -1,180 +0,0 @@
1
- /**
2
- * Keyword-Based Routing Strategy
3
- *
4
- * Routes stories based on keyword matching and acceptance criteria count.
5
- * This is the default fallback strategy — always returns a decision (never null).
6
- */
7
-
8
- import type { Complexity, ModelTier, TestStrategy } from "../../config";
9
- import type { UserStory } from "../../prd/types";
10
- import type { RoutingContext, RoutingDecision, RoutingStrategy } from "../strategy";
11
-
12
- /** Keywords that indicate higher complexity */
13
- const COMPLEX_KEYWORDS = [
14
- "refactor",
15
- "redesign",
16
- "architecture",
17
- "migration",
18
- "breaking change",
19
- "public api",
20
- "security",
21
- "auth",
22
- "encryption",
23
- "permission",
24
- "rbac",
25
- "casl",
26
- "jwt",
27
- "grpc",
28
- "microservice",
29
- "event-driven",
30
- "saga",
31
- ];
32
-
33
- const EXPERT_KEYWORDS = [
34
- "cryptograph",
35
- "zero-knowledge",
36
- "distributed consensus",
37
- "real-time",
38
- "websocket",
39
- "streaming",
40
- "performance critical",
41
- ];
42
-
43
- const SECURITY_KEYWORDS = [
44
- "auth",
45
- "security",
46
- "permission",
47
- "jwt",
48
- "oauth",
49
- "token",
50
- "encryption",
51
- "secret",
52
- "credential",
53
- "password",
54
- "rbac",
55
- "casl",
56
- ];
57
-
58
- const PUBLIC_API_KEYWORDS = [
59
- "public api",
60
- "breaking change",
61
- "external",
62
- "consumer",
63
- "sdk",
64
- "npm publish",
65
- "release",
66
- "endpoint",
67
- ];
68
-
69
- /**
70
- * Classify complexity based on keywords and criteria count.
71
- */
72
- function classifyComplexity(
73
- title: string,
74
- _description: string,
75
- acceptanceCriteria: string[],
76
- tags: string[] = [],
77
- ): Complexity {
78
- // BUG-031: Exclude description — it accumulates priorErrors across retries and
79
- // causes classification drift. Only use stable, immutable story fields.
80
- const text = [title, ...acceptanceCriteria, ...tags].join(" ").toLowerCase();
81
-
82
- if (EXPERT_KEYWORDS.some((kw) => text.includes(kw))) {
83
- return "expert";
84
- }
85
-
86
- if (COMPLEX_KEYWORDS.some((kw) => text.includes(kw)) || acceptanceCriteria.length > 8) {
87
- return "complex";
88
- }
89
-
90
- if (acceptanceCriteria.length > 4) {
91
- return "medium";
92
- }
93
-
94
- return "simple";
95
- }
96
-
97
- /**
98
- * Determine test strategy using decision tree.
99
- */
100
- function determineTestStrategy(
101
- complexity: Complexity,
102
- title: string,
103
- _description: string,
104
- tags: string[] = [],
105
- ): TestStrategy {
106
- // BUG-031: Exclude description — only use stable, immutable story fields.
107
- const text = [title, ...tags].join(" ").toLowerCase();
108
-
109
- const isSecurityCritical = SECURITY_KEYWORDS.some((kw) => text.includes(kw));
110
- const isPublicApi = PUBLIC_API_KEYWORDS.some((kw) => text.includes(kw));
111
-
112
- if (isSecurityCritical || isPublicApi) {
113
- return "three-session-tdd";
114
- }
115
-
116
- if (complexity === "complex" || complexity === "expert") {
117
- return "three-session-tdd";
118
- }
119
-
120
- // TS-001: simple → tdd-simple (TDD discipline, 1 session), medium → tdd-lite (3 sessions)
121
- if (complexity === "simple") return "tdd-simple";
122
- return "three-session-tdd-lite";
123
- }
124
-
125
- /** Map complexity to model tier */
126
- function complexityToModelTier(complexity: Complexity, context: RoutingContext): ModelTier {
127
- const mapping = context.config.autoMode.complexityRouting;
128
- return (mapping[complexity] ?? "balanced") as ModelTier;
129
- }
130
-
131
- /**
132
- * Keyword-based routing strategy.
133
- *
134
- * This strategy:
135
- * - Classifies complexity based on keywords and criteria count
136
- * - Maps complexity to model tier via config
137
- * - Applies test strategy decision tree
138
- * - ALWAYS returns a decision (never null)
139
- *
140
- * Use as the final fallback strategy in a chain.
141
- */
142
- export const keywordStrategy: RoutingStrategy = {
143
- name: "keyword",
144
-
145
- route(story: UserStory, context: RoutingContext): RoutingDecision {
146
- const { title, description, acceptanceCriteria, tags } = story;
147
-
148
- const complexity = classifyComplexity(title, description, acceptanceCriteria, tags);
149
- const modelTier = complexityToModelTier(complexity, context);
150
- const testStrategy = determineTestStrategy(complexity, title, description, tags);
151
-
152
- const reasons: string[] = [];
153
- const text = [title, description, ...tags].join(" ").toLowerCase();
154
-
155
- if (testStrategy === "three-session-tdd") {
156
- if (SECURITY_KEYWORDS.some((kw) => text.includes(kw))) reasons.push("security-critical");
157
- if (PUBLIC_API_KEYWORDS.some((kw) => text.includes(kw))) reasons.push("public-api");
158
- if (complexity === "complex" || complexity === "expert") reasons.push(`complexity:${complexity}`);
159
- }
160
-
161
- let reasoning = "";
162
- if (testStrategy === "three-session-tdd") {
163
- reasoning =
164
- reasons.length > 0 ? `three-session-tdd: ${reasons.join(", ")}` : `three-session-tdd: ${complexity} task`;
165
- } else if (testStrategy === "three-session-tdd-lite") {
166
- reasoning = `three-session-tdd-lite: simple task (${complexity})`;
167
- } else if (testStrategy === "tdd-simple") {
168
- reasoning = `tdd-simple: simple task (${complexity})`;
169
- } else {
170
- reasoning = `${testStrategy}: ${complexity} task`;
171
- }
172
-
173
- return {
174
- complexity,
175
- modelTier,
176
- testStrategy,
177
- reasoning,
178
- };
179
- },
180
- };