@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,118 @@
1
+ /**
2
+ * CostOverlay — modal-style overlay showing cost breakdown per story.
3
+ *
4
+ * Shown when c is pressed, dismissed with Esc.
5
+ */
6
+
7
+ import { Box, Text } from "ink";
8
+ import type { StoryDisplayState } from "../types";
9
+
10
+ /**
11
+ * Props for CostOverlay component.
12
+ */
13
+ export interface CostOverlayProps {
14
+ /** Whether the overlay is visible */
15
+ visible?: boolean;
16
+ /** All stories with cost data */
17
+ stories?: StoryDisplayState[];
18
+ /** Total accumulated cost */
19
+ totalCost?: number;
20
+ }
21
+
22
+ /**
23
+ * Format cost as USD with 4 decimal places.
24
+ */
25
+ function formatCost(cost: number): string {
26
+ return `$${cost.toFixed(4)}`;
27
+ }
28
+
29
+ /**
30
+ * CostOverlay component.
31
+ *
32
+ * Displays a modal-style overlay with cost breakdown per story.
33
+ * Shows story ID, status, and cost incurred.
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * const [showCost, setShowCost] = useState(false);
38
+ *
39
+ * <CostOverlay
40
+ * visible={showCost}
41
+ * stories={state.stories}
42
+ * totalCost={state.totalCost}
43
+ * />
44
+ * ```
45
+ */
46
+ export function CostOverlay({ visible = false, stories = [], totalCost = 0 }: CostOverlayProps) {
47
+ if (!visible) {
48
+ return null;
49
+ }
50
+
51
+ // Only show stories that have been executed (cost > 0 or status !== pending)
52
+ const executedStories = stories.filter((s) => (s.cost && s.cost > 0) || s.status !== "pending");
53
+
54
+ return (
55
+ <Box flexDirection="column" borderStyle="double" borderColor="cyan" paddingX={2} paddingY={1} minWidth={60}>
56
+ <Box paddingBottom={1}>
57
+ <Text bold color="cyan">
58
+ Cost Breakdown
59
+ </Text>
60
+ </Box>
61
+
62
+ {/* Header row */}
63
+ <Box paddingBottom={1} borderBottom borderColor="gray">
64
+ <Box width={12}>
65
+ <Text bold>Story ID</Text>
66
+ </Box>
67
+ <Box width={12}>
68
+ <Text bold>Status</Text>
69
+ </Box>
70
+ <Box width={12}>
71
+ <Text bold>Cost</Text>
72
+ </Box>
73
+ </Box>
74
+
75
+ {/* Story rows */}
76
+ <Box flexDirection="column" paddingY={1}>
77
+ {executedStories.length > 0 ? (
78
+ executedStories.map((story) => (
79
+ <Box key={story.story.id}>
80
+ <Box width={12}>
81
+ <Text>{story.story.id}</Text>
82
+ </Box>
83
+ <Box width={12}>
84
+ <Text color={story.status === "passed" ? "green" : story.status === "failed" ? "red" : undefined}>
85
+ {story.status}
86
+ </Text>
87
+ </Box>
88
+ <Box width={12}>
89
+ <Text>{formatCost(story.cost || 0)}</Text>
90
+ </Box>
91
+ </Box>
92
+ ))
93
+ ) : (
94
+ <Text dimColor>No stories executed yet</Text>
95
+ )}
96
+ </Box>
97
+
98
+ {/* Total row */}
99
+ <Box paddingTop={1} borderTop borderColor="gray">
100
+ <Box width={24}>
101
+ <Text bold>Total Cost:</Text>
102
+ </Box>
103
+ <Box width={12}>
104
+ <Text bold color="cyan">
105
+ {formatCost(totalCost)}
106
+ </Text>
107
+ </Box>
108
+ </Box>
109
+
110
+ {/* Footer */}
111
+ <Box justifyContent="center" paddingTop={1} borderTop borderColor="gray">
112
+ <Text dimColor>
113
+ Press <Text color="yellow">Esc</Text> to close
114
+ </Text>
115
+ </Box>
116
+ </Box>
117
+ );
118
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * HelpOverlay — modal-style overlay listing all keybindings.
3
+ *
4
+ * Shown when ? is pressed, dismissed with Esc.
5
+ */
6
+
7
+ import { Box, Text } from "ink";
8
+
9
+ /**
10
+ * Props for HelpOverlay component.
11
+ */
12
+ export interface HelpOverlayProps {
13
+ /** Whether the overlay is visible */
14
+ visible?: boolean;
15
+ }
16
+
17
+ /**
18
+ * HelpOverlay component.
19
+ *
20
+ * Displays a modal-style overlay with keybinding reference.
21
+ * Centered on screen with a border.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * const [showHelp, setShowHelp] = useState(false);
26
+ *
27
+ * <HelpOverlay visible={showHelp} />
28
+ *
29
+ * // Close with Esc or by toggling state
30
+ * ```
31
+ */
32
+ export function HelpOverlay({ visible = false }: HelpOverlayProps) {
33
+ if (!visible) {
34
+ return null;
35
+ }
36
+
37
+ return (
38
+ <Box flexDirection="column" borderStyle="double" borderColor="cyan" paddingX={2} paddingY={1}>
39
+ <Box paddingBottom={1}>
40
+ <Text bold color="cyan">
41
+ Keyboard Shortcuts
42
+ </Text>
43
+ </Box>
44
+
45
+ {/* Stories panel shortcuts */}
46
+ <Box flexDirection="column" paddingBottom={1}>
47
+ <Text dimColor>Stories Panel (default):</Text>
48
+ <Text>
49
+ {" "}
50
+ <Text color="yellow">p</Text> — Pause after current story
51
+ </Text>
52
+ <Text>
53
+ {" "}
54
+ <Text color="yellow">a</Text> — Abort run
55
+ </Text>
56
+ <Text>
57
+ {" "}
58
+ <Text color="yellow">s</Text> — Skip current story
59
+ </Text>
60
+ <Text>
61
+ {" "}
62
+ <Text color="yellow">Tab</Text> — Toggle focus to Agent panel
63
+ </Text>
64
+ <Text>
65
+ {" "}
66
+ <Text color="yellow">q</Text> — Quit TUI
67
+ </Text>
68
+ <Text>
69
+ {" "}
70
+ <Text color="yellow">?</Text> — Show this help
71
+ </Text>
72
+ <Text>
73
+ {" "}
74
+ <Text color="yellow">c</Text> — Show cost breakdown
75
+ </Text>
76
+ <Text>
77
+ {" "}
78
+ <Text color="yellow">r</Text> — Retry last failed story
79
+ </Text>
80
+ <Text>
81
+ {" "}
82
+ <Text color="yellow">Esc</Text> — Close overlay
83
+ </Text>
84
+ </Box>
85
+
86
+ {/* Agent panel shortcuts */}
87
+ <Box flexDirection="column" paddingBottom={1}>
88
+ <Text dimColor>Agent Panel (when focused):</Text>
89
+ <Text>
90
+ {" "}
91
+ <Text color="yellow">Ctrl+]</Text> — Escape back to Stories panel
92
+ </Text>
93
+ <Text>
94
+ {" "}
95
+ <Text dimColor>All other keys</Text> — Forwarded to agent PTY
96
+ </Text>
97
+ </Box>
98
+
99
+ {/* Footer */}
100
+ <Box justifyContent="center" paddingTop={1} borderTop borderColor="gray">
101
+ <Text dimColor>
102
+ Press <Text color="yellow">Esc</Text> to close
103
+ </Text>
104
+ </Box>
105
+ </Box>
106
+ );
107
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * StatusBar — displays current story and pipeline stage information.
3
+ */
4
+
5
+ import { Box, Text } from "ink";
6
+ import type { UserStory } from "../../prd/types";
7
+
8
+ /**
9
+ * Props for StatusBar component.
10
+ */
11
+ export interface StatusBarProps {
12
+ /** Current story being executed */
13
+ currentStory?: UserStory;
14
+ /** Current pipeline stage */
15
+ currentStage?: string;
16
+ /** Model tier for current story */
17
+ modelTier?: string;
18
+ /** Test strategy for current story */
19
+ testStrategy?: string;
20
+ }
21
+
22
+ /**
23
+ * StatusBar component.
24
+ *
25
+ * Displays a single line showing the current story ID, stage, model tier, and test strategy.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <StatusBar
30
+ * currentStory={story}
31
+ * currentStage="execution"
32
+ * modelTier="balanced"
33
+ * testStrategy="single-session"
34
+ * />
35
+ * ```
36
+ */
37
+ export function StatusBar({ currentStory, currentStage, modelTier, testStrategy }: StatusBarProps) {
38
+ if (!currentStory) {
39
+ return (
40
+ <Box paddingX={1} borderStyle="single" borderColor="gray">
41
+ <Text dimColor>Idle</Text>
42
+ </Box>
43
+ );
44
+ }
45
+
46
+ const storyInfo = `Story ${currentStory.id}`;
47
+ const stageInfo = currentStage ? ` · ${currentStage}` : "";
48
+ const tierInfo = modelTier ? ` · ${modelTier}` : "";
49
+ const strategyInfo = testStrategy ? ` · ${testStrategy}` : "";
50
+
51
+ return (
52
+ <Box paddingX={1} borderStyle="single" borderColor="gray">
53
+ <Text>
54
+ <Text bold>{storyInfo}</Text>
55
+ <Text dimColor>
56
+ {stageInfo}
57
+ {tierInfo}
58
+ {strategyInfo}
59
+ </Text>
60
+ </Text>
61
+ </Box>
62
+ );
63
+ }
@@ -0,0 +1,177 @@
1
+ /**
2
+ * StoriesPanel — displays story list with status icons, cost, and elapsed time.
3
+ *
4
+ * Supports scrolling for >15 stories and compact mode for single-column layout.
5
+ */
6
+
7
+ import { Box, Text } from "ink";
8
+ import { useEffect, useState } from "react";
9
+ import { COMPACT_MAX_VISIBLE_STORIES, MAX_VISIBLE_STORIES } from "../hooks/useLayout";
10
+ import type { StoryDisplayState } from "../types";
11
+
12
+ /**
13
+ * Props for StoriesPanel component.
14
+ */
15
+ export interface StoriesPanelProps {
16
+ /** Stories to display */
17
+ stories: StoryDisplayState[];
18
+ /** Total cost accumulated */
19
+ totalCost: number;
20
+ /** Elapsed time in milliseconds */
21
+ elapsedMs: number;
22
+ /** Panel width (columns) */
23
+ width?: number;
24
+ /** Compact mode (fewer details, for single-column layout) */
25
+ compact?: boolean;
26
+ /** Maximum height in rows (for single-column mode) */
27
+ maxHeight?: number;
28
+ }
29
+
30
+ /**
31
+ * Get status icon for a story.
32
+ */
33
+ function getStatusIcon(status: StoryDisplayState["status"]): string {
34
+ switch (status) {
35
+ case "pending":
36
+ return "⬚";
37
+ case "running":
38
+ return "🔄";
39
+ case "passed":
40
+ return "✅";
41
+ case "failed":
42
+ return "❌";
43
+ case "skipped":
44
+ return "⏭️";
45
+ case "retrying":
46
+ return "🔁";
47
+ case "paused":
48
+ return "⏸️";
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Format elapsed time as mm:ss.
54
+ */
55
+ function formatElapsedTime(ms: number): string {
56
+ const totalSeconds = Math.floor(ms / 1000);
57
+ const minutes = Math.floor(totalSeconds / 60);
58
+ const seconds = totalSeconds % 60;
59
+ return `${minutes}m ${seconds}s`;
60
+ }
61
+
62
+ /**
63
+ * StoriesPanel component.
64
+ *
65
+ * Displays all stories with status icons, routing info, cost total, and elapsed time.
66
+ * Supports scrolling for >15 stories (or >8 in compact mode) and shows scroll indicators.
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * <StoriesPanel
71
+ * stories={storyStates}
72
+ * totalCost={0.42}
73
+ * elapsedMs={263000}
74
+ * width={30}
75
+ * compact={false}
76
+ * />
77
+ * ```
78
+ */
79
+ export function StoriesPanel({ stories, totalCost, elapsedMs, width, compact = false, maxHeight }: StoriesPanelProps) {
80
+ // Determine max visible stories based on mode
81
+ const maxVisible = compact ? COMPACT_MAX_VISIBLE_STORIES : MAX_VISIBLE_STORIES;
82
+ const needsScrolling = stories.length > maxVisible;
83
+
84
+ // Scroll position (0-indexed offset)
85
+ const [scrollOffset, setScrollOffset] = useState(0);
86
+
87
+ // Auto-scroll to keep the current running story in view
88
+ useEffect(() => {
89
+ const runningIndex = stories.findIndex((s) => s.status === "running");
90
+ if (runningIndex !== -1 && needsScrolling) {
91
+ // If running story is outside the visible window, scroll to it
92
+ if (runningIndex < scrollOffset) {
93
+ setScrollOffset(runningIndex);
94
+ } else if (runningIndex >= scrollOffset + maxVisible) {
95
+ setScrollOffset(runningIndex - maxVisible + 1);
96
+ }
97
+ }
98
+ }, [stories, scrollOffset, maxVisible, needsScrolling]);
99
+
100
+ // Get visible stories (either all or a scrolled window)
101
+ const visibleStories = needsScrolling ? stories.slice(scrollOffset, scrollOffset + maxVisible) : stories;
102
+
103
+ const canScrollUp = scrollOffset > 0;
104
+ const canScrollDown = scrollOffset + maxVisible < stories.length;
105
+
106
+ return (
107
+ <Box flexDirection="column" width={width} height={maxHeight} borderStyle="single" borderColor="gray">
108
+ {/* Header */}
109
+ <Box paddingX={1} borderStyle="single" borderBottom borderColor="gray">
110
+ <Text bold>Stories</Text>
111
+ {needsScrolling && <Text dimColor> ({stories.length} total)</Text>}
112
+ </Box>
113
+
114
+ {/* Scroll indicator (top) */}
115
+ {needsScrolling && canScrollUp && (
116
+ <Box paddingX={1}>
117
+ <Text dimColor>▲ {scrollOffset} more above</Text>
118
+ </Box>
119
+ )}
120
+
121
+ {/* Story list */}
122
+ <Box flexDirection="column" paddingX={1} paddingY={1} flexGrow={1}>
123
+ {visibleStories.map((s) => {
124
+ const icon = getStatusIcon(s.status);
125
+
126
+ if (compact) {
127
+ // Compact mode: just icon and ID
128
+ return (
129
+ <Box key={s.story.id}>
130
+ <Text>
131
+ {icon} {s.story.id}
132
+ </Text>
133
+ </Box>
134
+ );
135
+ }
136
+
137
+ // Normal mode: icon, ID, and routing info
138
+ const routing = s.routing ? ` ${s.routing.complexity.slice(0, 3)} ${s.routing.modelTier}` : "";
139
+ return (
140
+ <Box key={s.story.id}>
141
+ <Text>
142
+ {icon} {s.story.id}
143
+ <Text dimColor>{routing}</Text>
144
+ </Text>
145
+ </Box>
146
+ );
147
+ })}
148
+ </Box>
149
+
150
+ {/* Scroll indicator (bottom) */}
151
+ {needsScrolling && canScrollDown && (
152
+ <Box paddingX={1}>
153
+ <Text dimColor>▼ {stories.length - scrollOffset - maxVisible} more below</Text>
154
+ </Box>
155
+ )}
156
+
157
+ {/* Footer — Cost and time */}
158
+ <Box flexDirection="column" paddingX={1} paddingY={1} borderStyle="single" borderTop borderColor="gray">
159
+ {!compact && (
160
+ <>
161
+ <Text>
162
+ Cost: <Text color="green">${totalCost.toFixed(4)}</Text>
163
+ </Text>
164
+ <Text>
165
+ Time: <Text color="cyan">{formatElapsedTime(elapsedMs)}</Text>
166
+ </Text>
167
+ </>
168
+ )}
169
+ {compact && (
170
+ <Text>
171
+ ${totalCost.toFixed(2)} · {formatElapsedTime(elapsedMs)}
172
+ </Text>
173
+ )}
174
+ </Box>
175
+ </Box>
176
+ );
177
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * useKeyboard hook — handle keyboard shortcuts for TUI controls.
3
+ *
4
+ * Listens for keyboard input when agent panel is NOT focused and dispatches
5
+ * actions like PAUSE/ABORT/SKIP to the queue file.
6
+ *
7
+ * When agent panel IS focused, only Ctrl+] escapes back to TUI controls.
8
+ */
9
+
10
+ import { useInput } from "ink";
11
+ import type { UserStory } from "../../prd/types";
12
+ import { PanelFocus } from "../types";
13
+
14
+ /**
15
+ * Keyboard action types.
16
+ */
17
+ export type KeyboardAction =
18
+ | { type: "PAUSE" }
19
+ | { type: "ABORT" }
20
+ | { type: "SKIP"; storyId: string }
21
+ | { type: "TOGGLE_FOCUS" }
22
+ | { type: "ESCAPE_AGENT" }
23
+ | { type: "QUIT" }
24
+ | { type: "SHOW_HELP" }
25
+ | { type: "SHOW_COST" }
26
+ | { type: "RETRY" }
27
+ | { type: "CLOSE_OVERLAY" };
28
+
29
+ /**
30
+ * Props for useKeyboard hook.
31
+ */
32
+ export interface UseKeyboardProps {
33
+ /** Current panel focus state */
34
+ focus: PanelFocus;
35
+ /** Current story being executed (for SKIP command) */
36
+ currentStory?: UserStory;
37
+ /** Callback when an action is triggered */
38
+ onAction: (action: KeyboardAction) => void;
39
+ /** Disable keyboard handling (e.g., during confirmation dialogs) */
40
+ disabled?: boolean;
41
+ }
42
+
43
+ /**
44
+ * Hook for handling keyboard shortcuts.
45
+ *
46
+ * Keybindings (when Stories panel is focused):
47
+ * - p: PAUSE after current story
48
+ * - a: ABORT run
49
+ * - s: SKIP current story
50
+ * - Tab: Toggle focus between Stories and Agent panels
51
+ * - q: Quit TUI
52
+ * - ?: Show help overlay
53
+ * - c: Show cost breakdown overlay
54
+ * - r: Retry last failed story
55
+ * - Esc: Close overlay
56
+ *
57
+ * When Agent panel is focused:
58
+ * - Ctrl+]: Escape back to TUI controls
59
+ * - All other keys: Forwarded to PTY (handled elsewhere)
60
+ *
61
+ * @example
62
+ * ```tsx
63
+ * const [focus, setFocus] = useState(PanelFocus.Stories);
64
+ * const [showHelp, setShowHelp] = useState(false);
65
+ *
66
+ * useKeyboard({
67
+ * focus,
68
+ * currentStory: state.currentStory,
69
+ * onAction: (action) => {
70
+ * if (action.type === "TOGGLE_FOCUS") {
71
+ * setFocus(prev => prev === PanelFocus.Stories ? PanelFocus.Agent : PanelFocus.Stories);
72
+ * } else if (action.type === "SHOW_HELP") {
73
+ * setShowHelp(true);
74
+ * } else if (action.type === "PAUSE") {
75
+ * writeQueueCommand({ type: "PAUSE" });
76
+ * }
77
+ * }
78
+ * });
79
+ * ```
80
+ */
81
+ export function useKeyboard({ focus, currentStory, onAction, disabled = false }: UseKeyboardProps): void {
82
+ useInput((input, key) => {
83
+ // If disabled, don't process any input
84
+ if (disabled) {
85
+ return;
86
+ }
87
+
88
+ // When Agent panel is focused, only Ctrl+] escapes back to TUI
89
+ if (focus === PanelFocus.Agent) {
90
+ // Ctrl+] is key.ctrl === true and input === ']'
91
+ if (key.ctrl && input === "]") {
92
+ onAction({ type: "ESCAPE_AGENT" });
93
+ }
94
+ // All other input is routed to PTY (handled by caller)
95
+ return;
96
+ }
97
+
98
+ // Stories panel is focused — handle TUI shortcuts
99
+ // Tab key toggles focus
100
+ if (key.tab) {
101
+ onAction({ type: "TOGGLE_FOCUS" });
102
+ return;
103
+ }
104
+
105
+ // Esc closes overlays
106
+ if (key.escape) {
107
+ onAction({ type: "CLOSE_OVERLAY" });
108
+ return;
109
+ }
110
+
111
+ // Character-based shortcuts
112
+ switch (input.toLowerCase()) {
113
+ case "p":
114
+ onAction({ type: "PAUSE" });
115
+ break;
116
+ case "a":
117
+ onAction({ type: "ABORT" });
118
+ break;
119
+ case "s":
120
+ // Skip requires a current story
121
+ if (currentStory) {
122
+ onAction({ type: "SKIP", storyId: currentStory.id });
123
+ }
124
+ break;
125
+ case "q":
126
+ onAction({ type: "QUIT" });
127
+ break;
128
+ case "?":
129
+ onAction({ type: "SHOW_HELP" });
130
+ break;
131
+ case "c":
132
+ onAction({ type: "SHOW_COST" });
133
+ break;
134
+ case "r":
135
+ onAction({ type: "RETRY" });
136
+ break;
137
+ default:
138
+ // Ignore unrecognized keys
139
+ break;
140
+ }
141
+ });
142
+ }