@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.
- package/.gitlab-ci.yml +96 -0
- package/BRIEF.md +140 -0
- package/CHANGELOG.md +60 -0
- package/CLAUDE.md +159 -0
- package/README.md +373 -0
- package/US-007-IMPLEMENTATION.md +139 -0
- package/bin/nax.ts +930 -0
- package/biome.json +14 -0
- package/bun.lock +168 -0
- package/bunfig.toml +11 -0
- package/docs/20260216-fix-plan-context-review.md +56 -0
- package/docs/20260216-relentless-vs-ngent-comparison.md +208 -0
- package/docs/20260216-v02-plan.md +136 -0
- package/docs/20260216-v02-review.md +685 -0
- package/docs/20260217-dogfood-findings.md +56 -0
- package/docs/20260217-p2-plus-plan.md +117 -0
- package/docs/20260217-partial-fixes-plan.md +62 -0
- package/docs/20260217-plan-analyze-spec.md +117 -0
- package/docs/20260217-post-impl-review.md +1137 -0
- package/docs/20260217-quick-wins-plan.md +66 -0
- package/docs/20260217-split-runner-plan.md +75 -0
- package/docs/20260217-v03-impl-plan.md +80 -0
- package/docs/20260217-v03-post-impl-review.md +589 -0
- package/docs/20260217-v04-impl-plan.md +86 -0
- package/docs/20260217-v05-post-impl-review.md +850 -0
- package/docs/20260217-v06-post-impl-review.md +817 -0
- package/docs/20260218-adr003-port-plan.md +151 -0
- package/docs/20260218-review-adr003-verification.md +175 -0
- package/docs/20260219-fix-plan-bug16-19.md +79 -0
- package/docs/20260219-fix-plan-bug20-22.md +114 -0
- package/docs/20260219-plan-llm-routing.md +116 -0
- package/docs/20260219-review-bug20-22-fixes.md +135 -0
- package/docs/20260219-routing-baseline-keyword.md +63 -0
- package/docs/20260220-plan-structured-logging-p1.md +80 -0
- package/docs/20260220-plan-structured-logging-p2.md +37 -0
- package/docs/20260220-review-llm-routing.md +180 -0
- package/docs/20260220-review-post-fix-llm-routing.md +70 -0
- package/docs/20260221-fix-plan-relevantfiles-split.md +101 -0
- package/docs/20260221-fix-plan-routing-mode.md +125 -0
- package/docs/20260221-review-v0.9-implementation.md +379 -0
- package/docs/20260222-fix-plan-v091-routing-isolation.md +197 -0
- package/docs/20260223-fix-plan-prompt-audit.md +62 -0
- package/docs/20260224-nax-roadmap-phases.md +189 -0
- package/docs/20260225-phase2-llm-service-layer.md +401 -0
- package/docs/20260225-review-v0.10.1.md +187 -0
- package/docs/20260303-v010-implementation-plan.md +165 -0
- package/docs/CLAUDE.md.bak +191 -0
- package/docs/ROADMAP.md +165 -0
- package/docs/SPEC-rectification.md +0 -0
- package/docs/SPEC.md +324 -0
- package/docs/US-001-plugin-loading-verification.md +152 -0
- package/docs/architecture-analysis.md +1076 -0
- package/docs/bugs/BUG-21-escalation-null-attempts.md +48 -0
- package/docs/bugs-from-dogfood-run-c.md +243 -0
- package/docs/code-review-20260228.md +612 -0
- package/docs/code-review-v0.15.0.md +629 -0
- package/docs/hook-lifecycle-test-plan.md +149 -0
- package/docs/releases/v0.11.0-and-earlier.md +20 -0
- package/docs/releases/v0.12.0.md +15 -0
- package/docs/releases/v0.13.0.md +14 -0
- package/docs/releases/v0.14.0.md +20 -0
- package/docs/releases/v0.14.1.md +36 -0
- package/docs/releases/v0.14.2.md +51 -0
- package/docs/releases/v0.14.3.md +174 -0
- package/docs/releases/v0.14.4.md +94 -0
- package/docs/releases/v0.15.0.md +502 -0
- package/docs/releases/v0.15.1.md +170 -0
- package/docs/releases/v0.15.3.md +193 -0
- package/docs/specs/status-file-v0.10.1.md +812 -0
- package/docs/v0.10-global-config.md +206 -0
- package/docs/v0.10-plugin-system.md +415 -0
- package/docs/v0.10-prompt-optimizer.md +234 -0
- package/docs/v0.3-spec.md +244 -0
- package/docs/v0.4-spec.md +140 -0
- package/docs/v0.5-spec.md +237 -0
- package/docs/v0.6-spec.md +371 -0
- package/docs/v0.7-spec.md +177 -0
- package/docs/v0.8-llm-routing.md +206 -0
- package/docs/v0.8-structured-logging.md +132 -0
- package/docs/v0.9.3-prompt-audit.md +112 -0
- package/examples/plugins/console-reporter/index.test.ts +207 -0
- package/examples/plugins/console-reporter/index.ts +110 -0
- package/nax/config.json +147 -0
- package/nax/features/bugfix-v0171/prd.json +52 -0
- package/nax/features/config-management/prd.json +108 -0
- package/nax/features/config-management/progress.txt +5 -0
- package/nax/features/diagnose/acceptance.test.ts +412 -0
- package/nax/features/diagnose/prd.json +41 -0
- package/nax/features/orchestration-fixes/prd.json +89 -0
- package/nax/features/orchestration-fixes/progress.txt +1 -0
- package/nax/features/plugin-integration/US-007-VERIFICATION.md +259 -0
- package/nax/features/plugin-integration/prd.json +208 -0
- package/nax/features/plugin-integration/progress.txt +5 -0
- package/nax/features/precheck/prd.json +205 -0
- package/nax/features/precheck/progress.txt +15 -0
- package/nax/features/structured-logging/prd.json +199 -0
- package/nax/features/unlock/prd.json +36 -0
- package/package.json +47 -0
- package/src/acceptance/fix-generator.ts +348 -0
- package/src/acceptance/generator.ts +282 -0
- package/src/acceptance/index.ts +30 -0
- package/src/acceptance/types.ts +79 -0
- package/src/agents/claude-decompose.ts +169 -0
- package/src/agents/claude-plan.ts +139 -0
- package/src/agents/claude.ts +324 -0
- package/src/agents/cost.ts +268 -0
- package/src/agents/index.ts +13 -0
- package/src/agents/registry.ts +48 -0
- package/src/agents/types-extended.ts +133 -0
- package/src/agents/types.ts +113 -0
- package/src/agents/validation.ts +69 -0
- package/src/analyze/classifier.ts +305 -0
- package/src/analyze/index.ts +16 -0
- package/src/analyze/scanner.ts +175 -0
- package/src/analyze/types.ts +51 -0
- package/src/cli/accept.ts +108 -0
- package/src/cli/analyze-parser.ts +284 -0
- package/src/cli/analyze.ts +207 -0
- package/src/cli/config.ts +561 -0
- package/src/cli/constitution.ts +109 -0
- package/src/cli/diagnose-analysis.ts +159 -0
- package/src/cli/diagnose-formatter.ts +87 -0
- package/src/cli/diagnose.ts +203 -0
- package/src/cli/generate.ts +127 -0
- package/src/cli/index.ts +37 -0
- package/src/cli/init.ts +188 -0
- package/src/cli/interact.ts +295 -0
- package/src/cli/plan.ts +198 -0
- package/src/cli/plugins.ts +111 -0
- package/src/cli/prompts.ts +295 -0
- package/src/cli/runs.ts +174 -0
- package/src/cli/status-cost.ts +151 -0
- package/src/cli/status-features.ts +338 -0
- package/src/cli/status.ts +13 -0
- package/src/commands/common.ts +171 -0
- package/src/commands/diagnose.ts +17 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/logs.ts +384 -0
- package/src/commands/precheck.ts +86 -0
- package/src/commands/unlock.ts +96 -0
- package/src/config/defaults.ts +160 -0
- package/src/config/index.ts +22 -0
- package/src/config/loader.ts +121 -0
- package/src/config/merger.ts +147 -0
- package/src/config/path-security.ts +121 -0
- package/src/config/paths.ts +27 -0
- package/src/config/schema.ts +56 -0
- package/src/config/schemas.ts +286 -0
- package/src/config/types.ts +423 -0
- package/src/config/validate.ts +103 -0
- package/src/constitution/generator.ts +191 -0
- package/src/constitution/generators/aider.ts +41 -0
- package/src/constitution/generators/claude.ts +35 -0
- package/src/constitution/generators/cursor.ts +36 -0
- package/src/constitution/generators/opencode.ts +38 -0
- package/src/constitution/generators/types.ts +33 -0
- package/src/constitution/generators/windsurf.ts +36 -0
- package/src/constitution/index.ts +10 -0
- package/src/constitution/loader.ts +133 -0
- package/src/constitution/types.ts +31 -0
- package/src/context/auto-detect.ts +227 -0
- package/src/context/builder.ts +246 -0
- package/src/context/elements.ts +83 -0
- package/src/context/formatter.ts +107 -0
- package/src/context/generator.ts +129 -0
- package/src/context/generators/aider.ts +34 -0
- package/src/context/generators/claude.ts +28 -0
- package/src/context/generators/cursor.ts +28 -0
- package/src/context/generators/opencode.ts +30 -0
- package/src/context/generators/windsurf.ts +28 -0
- package/src/context/greenfield.ts +114 -0
- package/src/context/index.ts +33 -0
- package/src/context/injector.ts +279 -0
- package/src/context/test-scanner.ts +370 -0
- package/src/context/types.ts +98 -0
- package/src/errors.ts +67 -0
- package/src/execution/batching.ts +157 -0
- package/src/execution/crash-recovery.ts +373 -0
- package/src/execution/escalation/escalation.ts +44 -0
- package/src/execution/escalation/index.ts +13 -0
- package/src/execution/escalation/tier-escalation.ts +295 -0
- package/src/execution/escalation/tier-outcome.ts +158 -0
- package/src/execution/helpers.ts +38 -0
- package/src/execution/index.ts +45 -0
- package/src/execution/lifecycle/acceptance-loop.ts +272 -0
- package/src/execution/lifecycle/headless-formatter.ts +85 -0
- package/src/execution/lifecycle/index.ts +12 -0
- package/src/execution/lifecycle/parallel-lifecycle.ts +101 -0
- package/src/execution/lifecycle/precheck-runner.ts +140 -0
- package/src/execution/lifecycle/run-cleanup.ts +81 -0
- package/src/execution/lifecycle/run-completion.ts +129 -0
- package/src/execution/lifecycle/run-initialization.ts +141 -0
- package/src/execution/lifecycle/run-lifecycle.ts +312 -0
- package/src/execution/lifecycle/run-setup.ts +204 -0
- package/src/execution/lifecycle/story-hooks.ts +38 -0
- package/src/execution/lifecycle/story-size-prompts.ts +123 -0
- package/src/execution/lock.ts +115 -0
- package/src/execution/parallel-executor.ts +216 -0
- package/src/execution/parallel.ts +400 -0
- package/src/execution/pid-registry.ts +280 -0
- package/src/execution/pipeline-result-handler.ts +388 -0
- package/src/execution/post-verify-rectification.ts +188 -0
- package/src/execution/post-verify.ts +274 -0
- package/src/execution/progress.ts +25 -0
- package/src/execution/prompts.ts +127 -0
- package/src/execution/queue-handler.ts +109 -0
- package/src/execution/rectification.ts +13 -0
- package/src/execution/runner.ts +377 -0
- package/src/execution/sequential-executor.ts +388 -0
- package/src/execution/status-file.ts +264 -0
- package/src/execution/status-writer.ts +139 -0
- package/src/execution/story-context.ts +229 -0
- package/src/execution/test-output-parser.ts +14 -0
- package/src/execution/verification.ts +72 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/runner.ts +286 -0
- package/src/hooks/types.ts +67 -0
- package/src/interaction/chain.ts +154 -0
- package/src/interaction/index.ts +60 -0
- package/src/interaction/init.ts +83 -0
- package/src/interaction/plugins/auto.ts +217 -0
- package/src/interaction/plugins/cli.ts +300 -0
- package/src/interaction/plugins/telegram.ts +384 -0
- package/src/interaction/plugins/webhook.ts +258 -0
- package/src/interaction/state.ts +171 -0
- package/src/interaction/triggers.ts +229 -0
- package/src/interaction/types.ts +163 -0
- package/src/logger/formatters.ts +84 -0
- package/src/logger/index.ts +16 -0
- package/src/logger/logger.ts +298 -0
- package/src/logger/types.ts +48 -0
- package/src/logging/formatter.ts +355 -0
- package/src/logging/index.ts +22 -0
- package/src/logging/types.ts +93 -0
- package/src/metrics/aggregator.ts +190 -0
- package/src/metrics/index.ts +14 -0
- package/src/metrics/tracker.ts +200 -0
- package/src/metrics/types.ts +109 -0
- package/src/optimizer/index.ts +62 -0
- package/src/optimizer/noop.optimizer.ts +24 -0
- package/src/optimizer/rule-based.optimizer.ts +248 -0
- package/src/optimizer/types.ts +53 -0
- package/src/pipeline/events.ts +130 -0
- package/src/pipeline/index.ts +19 -0
- package/src/pipeline/runner.ts +161 -0
- package/src/pipeline/stages/acceptance.ts +197 -0
- package/src/pipeline/stages/completion.ts +99 -0
- package/src/pipeline/stages/constitution.ts +63 -0
- package/src/pipeline/stages/context.ts +117 -0
- package/src/pipeline/stages/execution.ts +194 -0
- package/src/pipeline/stages/index.ts +62 -0
- package/src/pipeline/stages/optimizer.ts +74 -0
- package/src/pipeline/stages/prompt.ts +57 -0
- package/src/pipeline/stages/queue-check.ts +103 -0
- package/src/pipeline/stages/review.ts +181 -0
- package/src/pipeline/stages/routing.ts +81 -0
- package/src/pipeline/stages/verify.ts +100 -0
- package/src/pipeline/types.ts +167 -0
- package/src/plugins/index.ts +31 -0
- package/src/plugins/loader.ts +287 -0
- package/src/plugins/registry.ts +168 -0
- package/src/plugins/types.ts +327 -0
- package/src/plugins/validator.ts +352 -0
- package/src/prd/index.ts +172 -0
- package/src/prd/types.ts +202 -0
- package/src/precheck/checks-blockers.ts +391 -0
- package/src/precheck/checks-warnings.ts +142 -0
- package/src/precheck/checks.ts +30 -0
- package/src/precheck/index.ts +247 -0
- package/src/precheck/story-size-gate.ts +144 -0
- package/src/precheck/types.ts +31 -0
- package/src/queue/index.ts +2 -0
- package/src/queue/manager.ts +254 -0
- package/src/queue/types.ts +54 -0
- package/src/review/index.ts +8 -0
- package/src/review/runner.ts +172 -0
- package/src/review/types.ts +66 -0
- package/src/routing/builder.ts +81 -0
- package/src/routing/chain.ts +74 -0
- package/src/routing/index.ts +16 -0
- package/src/routing/loader.ts +58 -0
- package/src/routing/router.ts +303 -0
- package/src/routing/strategies/adaptive.ts +215 -0
- package/src/routing/strategies/index.ts +8 -0
- package/src/routing/strategies/keyword.ts +163 -0
- package/src/routing/strategies/llm-prompts.ts +209 -0
- package/src/routing/strategies/llm.ts +235 -0
- package/src/routing/strategies/manual.ts +50 -0
- package/src/routing/strategy.ts +99 -0
- package/src/tdd/cleanup.ts +111 -0
- package/src/tdd/index.ts +23 -0
- package/src/tdd/isolation.ts +123 -0
- package/src/tdd/orchestrator.ts +383 -0
- package/src/tdd/prompts.ts +270 -0
- package/src/tdd/rectification-gate.ts +183 -0
- package/src/tdd/session-runner.ts +179 -0
- package/src/tdd/types.ts +81 -0
- package/src/tdd/verdict.ts +271 -0
- package/src/tui/App.tsx +265 -0
- package/src/tui/components/AgentPanel.tsx +75 -0
- package/src/tui/components/CostOverlay.tsx +118 -0
- package/src/tui/components/HelpOverlay.tsx +107 -0
- package/src/tui/components/StatusBar.tsx +63 -0
- package/src/tui/components/StoriesPanel.tsx +177 -0
- package/src/tui/hooks/useKeyboard.ts +142 -0
- package/src/tui/hooks/useLayout.ts +137 -0
- package/src/tui/hooks/usePipelineEvents.ts +183 -0
- package/src/tui/hooks/usePty.ts +194 -0
- package/src/tui/index.tsx +38 -0
- package/src/tui/types.ts +76 -0
- package/src/utils/git.ts +83 -0
- package/src/utils/queue-writer.ts +54 -0
- package/src/verification/executor.ts +235 -0
- package/src/verification/gate.ts +207 -0
- package/src/verification/index.ts +12 -0
- package/src/verification/parser.ts +230 -0
- package/src/verification/rectification.ts +108 -0
- package/src/verification/types.ts +113 -0
- package/src/worktree/dispatcher.ts +65 -0
- package/src/worktree/index.ts +2 -0
- package/src/worktree/manager.ts +187 -0
- package/src/worktree/merge.ts +301 -0
- package/src/worktree/types.ts +4 -0
- package/test/TEST_COVERAGE_US001.md +217 -0
- package/test/TEST_COVERAGE_US003.md +84 -0
- package/test/TEST_COVERAGE_US005.md +86 -0
- package/test/US-002-orchestrator.test.ts +246 -0
- package/test/acceptance/cm-003-default-view.test.ts +194 -0
- package/test/execution/pid-registry.test.ts +240 -0
- package/test/execution/post-verify.test.ts +224 -0
- package/test/helpers/timeout.ts +42 -0
- package/test/integration/US-002-TEST-SUMMARY.md +107 -0
- package/test/integration/US-003-TEST-SUMMARY.md +149 -0
- package/test/integration/US-004-TEST-SUMMARY.md +106 -0
- package/test/integration/US-005-TEST-SUMMARY.md +138 -0
- package/test/integration/US-007-TEST-SUMMARY.md +100 -0
- package/test/integration/agent-validation.test.ts +439 -0
- package/test/integration/analyze-integration.test.ts +261 -0
- package/test/integration/analyze-scanner.test.ts +131 -0
- package/test/integration/cli-config-default-edge-cases.test.ts +222 -0
- package/test/integration/cli-config-default-view.test.ts +229 -0
- package/test/integration/cli-config-diff.test.ts +460 -0
- package/test/integration/cli-config.test.ts +736 -0
- package/test/integration/cli-diagnose.test.ts +592 -0
- package/test/integration/cli-logs.test.ts +314 -0
- package/test/integration/cli-plugins.test.ts +678 -0
- package/test/integration/cli-precheck.test.ts +371 -0
- package/test/integration/cli-run-headless.test.ts +173 -0
- package/test/integration/cli.test.ts +75 -0
- package/test/integration/config/merger.test.ts +465 -0
- package/test/integration/config/paths.test.ts +51 -0
- package/test/integration/config-loader.test.ts +265 -0
- package/test/integration/config.test.ts +444 -0
- package/test/integration/context-integration.test.ts +702 -0
- package/test/integration/context-provider-injection.test.ts +506 -0
- package/test/integration/context-verification-integration.test.ts +295 -0
- package/test/integration/e2e.test.ts +896 -0
- package/test/integration/execution.test.ts +625 -0
- package/test/integration/helpers.test.ts +295 -0
- package/test/integration/hooks.test.ts +361 -0
- package/test/integration/interaction-chain-pipeline.test.ts +464 -0
- package/test/integration/isolation.test.ts +143 -0
- package/test/integration/logger.test.ts +461 -0
- package/test/integration/parallel.test.ts +250 -0
- package/test/integration/path-security.test.ts +173 -0
- package/test/integration/pipeline-acceptance.test.ts +302 -0
- package/test/integration/pipeline-events.test.ts +475 -0
- package/test/integration/pipeline.test.ts +658 -0
- package/test/integration/plan.test.ts +157 -0
- package/test/integration/plugin-routing.test.ts +921 -0
- package/test/integration/plugins/config-integration.test.ts +172 -0
- package/test/integration/plugins/config-resolution.test.ts +522 -0
- package/test/integration/plugins/loader.test.ts +641 -0
- package/test/integration/plugins/registry.test.ts +746 -0
- package/test/integration/plugins/validator.test.ts +563 -0
- package/test/integration/prd-pause.test.ts +205 -0
- package/test/integration/prd-resolvers.test.ts +185 -0
- package/test/integration/precheck-integration.test.ts +468 -0
- package/test/integration/precheck.test.ts +805 -0
- package/test/integration/progress.test.ts +34 -0
- package/test/integration/rectification-flow.test.ts +512 -0
- package/test/integration/reporter-lifecycle.test.ts +860 -0
- package/test/integration/review-config-commands.test.ts +319 -0
- package/test/integration/review-config-schema.test.ts +116 -0
- package/test/integration/review-plugin-integration.test.ts +722 -0
- package/test/integration/review.test.ts +149 -0
- package/test/integration/routing-stage-bug-021.test.ts +274 -0
- package/test/integration/routing-stage-greenfield.test.ts +286 -0
- package/test/integration/runner-config-plugins.test.ts +461 -0
- package/test/integration/runner-fixes.test.ts +399 -0
- package/test/integration/runner-plugin-integration.test.ts +543 -0
- package/test/integration/runner.test.ts +1679 -0
- package/test/integration/s5-greenfield-fallback.test.ts +297 -0
- package/test/integration/status-file-integration.test.ts +325 -0
- package/test/integration/status-file.test.ts +379 -0
- package/test/integration/status-writer.test.ts +345 -0
- package/test/integration/story-id-in-events.test.ts +273 -0
- package/test/integration/tdd-cleanup.test.ts +246 -0
- package/test/integration/tdd-orchestrator.test.ts +1762 -0
- package/test/integration/test-scanner.test.ts +403 -0
- package/test/integration/verification-asset-check.test.ts +142 -0
- package/test/integration/verify-stage.test.ts +275 -0
- package/test/integration/worktree/manager.test.ts +218 -0
- package/test/integration/worktree/merge.test.ts +341 -0
- package/test/manual/logging-formatter-demo.ts +158 -0
- package/test/ui/tui-agent-panel.test.tsx +99 -0
- package/test/ui/tui-controls.test.ts +334 -0
- package/test/ui/tui-cost-and-pty.test.ts +189 -0
- package/test/ui/tui-layout.test.ts +378 -0
- package/test/ui/tui-pty-integration.test.tsx +159 -0
- package/test/ui/tui-stories.test.ts +332 -0
- package/test/unit/acceptance.test.ts +186 -0
- package/test/unit/agent-stderr-capture.test.ts +146 -0
- package/test/unit/analyze-classifier.test.ts +215 -0
- package/test/unit/analyze.test.ts +224 -0
- package/test/unit/auto-detect.test.ts +249 -0
- package/test/unit/cli-status.test.ts +417 -0
- package/test/unit/commands/common.test.ts +320 -0
- package/test/unit/commands/logs.test.ts +416 -0
- package/test/unit/commands/unlock.test.ts +319 -0
- package/test/unit/constitution-generators.test.ts +160 -0
- package/test/unit/constitution.test.ts +209 -0
- package/test/unit/context.test.ts +1722 -0
- package/test/unit/cost.test.ts +231 -0
- package/test/unit/crash-recovery.test.ts +308 -0
- package/test/unit/escalation.test.ts +126 -0
- package/test/unit/execution-logging-stderr.test.ts +156 -0
- package/test/unit/execution-stage.test.ts +122 -0
- package/test/unit/fix-generator.test.ts +275 -0
- package/test/unit/formatters.test.ts +469 -0
- package/test/unit/greenfield.test.ts +179 -0
- package/test/unit/helpers.test.ts +317 -0
- package/test/unit/interaction/human-review-trigger.test.ts +164 -0
- package/test/unit/interaction-network-failures.test.ts +389 -0
- package/test/unit/interaction-plugins.test.ts +164 -0
- package/test/unit/isolation.test.ts +134 -0
- package/test/unit/logging/formatter.test.ts +455 -0
- package/test/unit/merge.test.ts +268 -0
- package/test/unit/metrics.test.ts +276 -0
- package/test/unit/optimizer/noop.optimizer.test.ts +125 -0
- package/test/unit/optimizer/rule-based.optimizer.test.ts +358 -0
- package/test/unit/prd-auto-default.test.ts +290 -0
- package/test/unit/prd-failure-category.test.ts +176 -0
- package/test/unit/prd-get-next-story.test.ts +186 -0
- package/test/unit/precheck-checks.test.ts +840 -0
- package/test/unit/precheck-story-size-gate.test.ts +287 -0
- package/test/unit/precheck-types.test.ts +142 -0
- package/test/unit/prompts.test.ts +475 -0
- package/test/unit/queue.test.ts +237 -0
- package/test/unit/rectification.test.ts +284 -0
- package/test/unit/registry.test.ts +287 -0
- package/test/unit/routing.test.ts +937 -0
- package/test/unit/run-lifecycle.test.ts +140 -0
- package/test/unit/storyid-events.test.ts +224 -0
- package/test/unit/tdd-verdict.test.ts +492 -0
- package/test/unit/test-output-parser.test.ts +377 -0
- package/test/unit/verdict.test.ts +324 -0
- package/test/unit/worktree-manager.test.ts +158 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Controls Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests keyboard shortcuts, overlays, focus mode switching,
|
|
5
|
+
* and queue command writing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
9
|
+
import { unlink } from "node:fs/promises";
|
|
10
|
+
import { render } from "ink-testing-library";
|
|
11
|
+
import { createElement } from "react";
|
|
12
|
+
import type { UserStory } from "../../src/prd/types";
|
|
13
|
+
import { CostOverlay } from "../../src/tui/components/CostOverlay";
|
|
14
|
+
import { HelpOverlay } from "../../src/tui/components/HelpOverlay";
|
|
15
|
+
import type { KeyboardAction } from "../../src/tui/hooks/useKeyboard";
|
|
16
|
+
import { PanelFocus } from "../../src/tui/types";
|
|
17
|
+
import type { StoryDisplayState } from "../../src/tui/types";
|
|
18
|
+
import { writeQueueCommand } from "../../src/utils/queue-writer";
|
|
19
|
+
|
|
20
|
+
// Helper to create mock stories
|
|
21
|
+
function createMockStory(id: string, status: StoryDisplayState["status"], cost = 0.01): StoryDisplayState {
|
|
22
|
+
const story: UserStory = {
|
|
23
|
+
id,
|
|
24
|
+
title: `Test story ${id}`,
|
|
25
|
+
description: "Test description",
|
|
26
|
+
acceptanceCriteria: [],
|
|
27
|
+
dependencies: [],
|
|
28
|
+
tags: [],
|
|
29
|
+
passes: status === "passed",
|
|
30
|
+
status: status === "passed" ? "passed" : "pending",
|
|
31
|
+
escalations: [],
|
|
32
|
+
attempts: 0,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
story,
|
|
37
|
+
status,
|
|
38
|
+
routing: {
|
|
39
|
+
complexity: "simple",
|
|
40
|
+
modelTier: "fast",
|
|
41
|
+
testStrategy: "test-after",
|
|
42
|
+
reasoning: "Test routing",
|
|
43
|
+
},
|
|
44
|
+
cost,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe("HelpOverlay", () => {
|
|
49
|
+
test("does not render when visible=false", () => {
|
|
50
|
+
const { lastFrame } = render(createElement(HelpOverlay, { visible: false }));
|
|
51
|
+
expect(lastFrame()).toBe("");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("renders keybindings when visible=true", () => {
|
|
55
|
+
const { lastFrame } = render(createElement(HelpOverlay, { visible: true }));
|
|
56
|
+
const output = lastFrame();
|
|
57
|
+
|
|
58
|
+
expect(output).toContain("Keyboard Shortcuts");
|
|
59
|
+
expect(output).toContain("p"); // Pause
|
|
60
|
+
expect(output).toContain("a"); // Abort
|
|
61
|
+
expect(output).toContain("s"); // Skip
|
|
62
|
+
expect(output).toContain("Tab"); // Toggle focus
|
|
63
|
+
expect(output).toContain("q"); // Quit
|
|
64
|
+
expect(output).toContain("?"); // Show help
|
|
65
|
+
expect(output).toContain("c"); // Show cost
|
|
66
|
+
expect(output).toContain("r"); // Retry
|
|
67
|
+
expect(output).toContain("Esc"); // Close overlay
|
|
68
|
+
expect(output).toContain("Ctrl+]"); // Escape agent panel
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("displays Stories panel keybindings", () => {
|
|
72
|
+
const { lastFrame } = render(createElement(HelpOverlay, { visible: true }));
|
|
73
|
+
const output = lastFrame();
|
|
74
|
+
|
|
75
|
+
expect(output).toContain("Stories Panel");
|
|
76
|
+
expect(output).toContain("Pause after current story");
|
|
77
|
+
expect(output).toContain("Abort run");
|
|
78
|
+
expect(output).toContain("Skip current story");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("displays Agent panel keybindings", () => {
|
|
82
|
+
const { lastFrame } = render(createElement(HelpOverlay, { visible: true }));
|
|
83
|
+
const output = lastFrame();
|
|
84
|
+
|
|
85
|
+
expect(output).toContain("Agent Panel");
|
|
86
|
+
expect(output).toContain("Escape back to Stories panel");
|
|
87
|
+
expect(output).toContain("Forwarded to agent PTY");
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("CostOverlay", () => {
|
|
92
|
+
test("does not render when visible=false", () => {
|
|
93
|
+
const { lastFrame } = render(createElement(CostOverlay, { visible: false }));
|
|
94
|
+
expect(lastFrame()).toBe("");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("renders cost breakdown when visible=true", () => {
|
|
98
|
+
const stories = [
|
|
99
|
+
createMockStory("US-001", "passed", 0.023),
|
|
100
|
+
createMockStory("US-002", "running", 0.015),
|
|
101
|
+
createMockStory("US-003", "pending", 0),
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const { lastFrame } = render(
|
|
105
|
+
createElement(CostOverlay, {
|
|
106
|
+
visible: true,
|
|
107
|
+
stories,
|
|
108
|
+
totalCost: 0.038,
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const output = lastFrame();
|
|
113
|
+
expect(output).toContain("Cost Breakdown");
|
|
114
|
+
expect(output).toContain("Story ID");
|
|
115
|
+
expect(output).toContain("Status");
|
|
116
|
+
expect(output).toContain("Cost");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("displays executed stories with costs", () => {
|
|
120
|
+
const stories = [createMockStory("US-001", "passed", 0.023), createMockStory("US-002", "failed", 0.015)];
|
|
121
|
+
|
|
122
|
+
const { lastFrame } = render(
|
|
123
|
+
createElement(CostOverlay, {
|
|
124
|
+
visible: true,
|
|
125
|
+
stories,
|
|
126
|
+
totalCost: 0.038,
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const output = lastFrame();
|
|
131
|
+
expect(output).toContain("US-001");
|
|
132
|
+
expect(output).toContain("passed");
|
|
133
|
+
expect(output).toContain("$0.0230");
|
|
134
|
+
expect(output).toContain("US-002");
|
|
135
|
+
expect(output).toContain("failed");
|
|
136
|
+
expect(output).toContain("$0.0150");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("displays total cost", () => {
|
|
140
|
+
const stories = [createMockStory("US-001", "passed", 0.023)];
|
|
141
|
+
|
|
142
|
+
const { lastFrame } = render(
|
|
143
|
+
createElement(CostOverlay, {
|
|
144
|
+
visible: true,
|
|
145
|
+
stories,
|
|
146
|
+
totalCost: 0.123456,
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const output = lastFrame();
|
|
151
|
+
expect(output).toContain("Total Cost:");
|
|
152
|
+
expect(output).toContain("$0.1235");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("shows message when no stories executed", () => {
|
|
156
|
+
const stories = [createMockStory("US-001", "pending", 0)];
|
|
157
|
+
|
|
158
|
+
const { lastFrame } = render(
|
|
159
|
+
createElement(CostOverlay, {
|
|
160
|
+
visible: true,
|
|
161
|
+
stories,
|
|
162
|
+
totalCost: 0,
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const output = lastFrame();
|
|
167
|
+
expect(output).toContain("No stories executed yet");
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe("Keyboard action types", () => {
|
|
172
|
+
test("PAUSE action has correct type", () => {
|
|
173
|
+
const action: KeyboardAction = { type: "PAUSE" };
|
|
174
|
+
expect(action.type).toBe("PAUSE");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("ABORT action has correct type", () => {
|
|
178
|
+
const action: KeyboardAction = { type: "ABORT" };
|
|
179
|
+
expect(action.type).toBe("ABORT");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("SKIP action has story ID", () => {
|
|
183
|
+
const action: KeyboardAction = { type: "SKIP", storyId: "US-042" };
|
|
184
|
+
expect(action.type).toBe("SKIP");
|
|
185
|
+
expect(action.storyId).toBe("US-042");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("TOGGLE_FOCUS action has correct type", () => {
|
|
189
|
+
const action: KeyboardAction = { type: "TOGGLE_FOCUS" };
|
|
190
|
+
expect(action.type).toBe("TOGGLE_FOCUS");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("ESCAPE_AGENT action has correct type", () => {
|
|
194
|
+
const action: KeyboardAction = { type: "ESCAPE_AGENT" };
|
|
195
|
+
expect(action.type).toBe("ESCAPE_AGENT");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("QUIT action has correct type", () => {
|
|
199
|
+
const action: KeyboardAction = { type: "QUIT" };
|
|
200
|
+
expect(action.type).toBe("QUIT");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("SHOW_HELP action has correct type", () => {
|
|
204
|
+
const action: KeyboardAction = { type: "SHOW_HELP" };
|
|
205
|
+
expect(action.type).toBe("SHOW_HELP");
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("SHOW_COST action has correct type", () => {
|
|
209
|
+
const action: KeyboardAction = { type: "SHOW_COST" };
|
|
210
|
+
expect(action.type).toBe("SHOW_COST");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("RETRY action has correct type", () => {
|
|
214
|
+
const action: KeyboardAction = { type: "RETRY" };
|
|
215
|
+
expect(action.type).toBe("RETRY");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("CLOSE_OVERLAY action has correct type", () => {
|
|
219
|
+
const action: KeyboardAction = { type: "CLOSE_OVERLAY" };
|
|
220
|
+
expect(action.type).toBe("CLOSE_OVERLAY");
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe("Focus mode", () => {
|
|
225
|
+
test("PanelFocus enum has Stories value", () => {
|
|
226
|
+
expect(PanelFocus.Stories).toBe("stories");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("PanelFocus enum has Agent value", () => {
|
|
230
|
+
expect(PanelFocus.Agent).toBe("agent");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("focus mode toggles between Stories and Agent", () => {
|
|
234
|
+
let focus: PanelFocus = PanelFocus.Stories;
|
|
235
|
+
focus = focus === PanelFocus.Stories ? PanelFocus.Agent : PanelFocus.Stories;
|
|
236
|
+
expect(focus).toBe(PanelFocus.Agent);
|
|
237
|
+
|
|
238
|
+
focus = focus === PanelFocus.Stories ? PanelFocus.Agent : PanelFocus.Stories;
|
|
239
|
+
expect(focus).toBe(PanelFocus.Stories);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe("Queue command writer", () => {
|
|
244
|
+
const tempQueueFile = "/tmp/nax-test-queue.txt";
|
|
245
|
+
|
|
246
|
+
beforeEach(async () => {
|
|
247
|
+
// Clean up any existing test file
|
|
248
|
+
try {
|
|
249
|
+
await unlink(tempQueueFile);
|
|
250
|
+
} catch {
|
|
251
|
+
// Ignore if doesn't exist
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
afterEach(async () => {
|
|
256
|
+
// Clean up after tests
|
|
257
|
+
try {
|
|
258
|
+
await unlink(tempQueueFile);
|
|
259
|
+
} catch {
|
|
260
|
+
// Ignore if doesn't exist
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("writes PAUSE command to queue file", async () => {
|
|
265
|
+
await writeQueueCommand(tempQueueFile, { type: "PAUSE" });
|
|
266
|
+
|
|
267
|
+
const content = await Bun.file(tempQueueFile).text();
|
|
268
|
+
expect(content.trim()).toBe("PAUSE");
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("writes ABORT command to queue file", async () => {
|
|
272
|
+
await writeQueueCommand(tempQueueFile, { type: "ABORT" });
|
|
273
|
+
|
|
274
|
+
const content = await Bun.file(tempQueueFile).text();
|
|
275
|
+
expect(content.trim()).toBe("ABORT");
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("writes SKIP command with story ID to queue file", async () => {
|
|
279
|
+
await writeQueueCommand(tempQueueFile, { type: "SKIP", storyId: "US-042" });
|
|
280
|
+
|
|
281
|
+
const content = await Bun.file(tempQueueFile).text();
|
|
282
|
+
expect(content.trim()).toBe("SKIP US-042");
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("appends multiple commands to queue file", async () => {
|
|
286
|
+
await writeQueueCommand(tempQueueFile, { type: "SKIP", storyId: "US-001" });
|
|
287
|
+
await writeQueueCommand(tempQueueFile, { type: "PAUSE" });
|
|
288
|
+
await writeQueueCommand(tempQueueFile, { type: "ABORT" });
|
|
289
|
+
|
|
290
|
+
const content = await Bun.file(tempQueueFile).text();
|
|
291
|
+
const lines = content.trim().split("\n");
|
|
292
|
+
|
|
293
|
+
expect(lines).toEqual(["SKIP US-001", "PAUSE", "ABORT"]);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("creates queue file if it doesn't exist", async () => {
|
|
297
|
+
// Ensure file doesn't exist
|
|
298
|
+
try {
|
|
299
|
+
await unlink(tempQueueFile);
|
|
300
|
+
} catch {
|
|
301
|
+
// Ignore
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await writeQueueCommand(tempQueueFile, { type: "PAUSE" });
|
|
305
|
+
|
|
306
|
+
const file = Bun.file(tempQueueFile);
|
|
307
|
+
expect(await file.exists()).toBe(true);
|
|
308
|
+
|
|
309
|
+
const content = await file.text();
|
|
310
|
+
expect(content.trim()).toBe("PAUSE");
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe("Ctrl+] escape sequence", () => {
|
|
315
|
+
test("Ctrl+] should escape from agent panel", () => {
|
|
316
|
+
// This would be tested in integration with the useKeyboard hook
|
|
317
|
+
// For now, we verify the concept:
|
|
318
|
+
const input = "]";
|
|
319
|
+
const keyCtrl = true;
|
|
320
|
+
|
|
321
|
+
if (keyCtrl && input === "]") {
|
|
322
|
+
const action: KeyboardAction = { type: "ESCAPE_AGENT" };
|
|
323
|
+
expect(action.type).toBe("ESCAPE_AGENT");
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test("regular ] without Ctrl should not escape", () => {
|
|
328
|
+
const input = "]";
|
|
329
|
+
const keyCtrl = false;
|
|
330
|
+
|
|
331
|
+
// Should not trigger escape
|
|
332
|
+
expect(keyCtrl && input === "]").toBe(false);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Cost Accumulation and PTY Line Length Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for BUG-1 (story cost accumulation) and MEM-1 (PTY line length limits).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, expect, test } from "bun:test";
|
|
8
|
+
import { PipelineEventEmitter } from "../../src/pipeline/events";
|
|
9
|
+
import type { StageResult } from "../../src/pipeline/types";
|
|
10
|
+
import type { UserStory } from "../../src/prd/types";
|
|
11
|
+
|
|
12
|
+
// ── Test Fixtures ────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
const createMockStory = (id: string): UserStory => ({
|
|
15
|
+
id,
|
|
16
|
+
title: `Test story ${id}`,
|
|
17
|
+
description: "Test description",
|
|
18
|
+
acceptanceCriteria: [],
|
|
19
|
+
dependencies: [],
|
|
20
|
+
tags: [],
|
|
21
|
+
passes: false,
|
|
22
|
+
status: "pending",
|
|
23
|
+
escalations: [],
|
|
24
|
+
attempts: 0,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// ── Cost Accumulation Tests (BUG-1) ──────────────────
|
|
28
|
+
|
|
29
|
+
describe("StageResult - Cost field in types (BUG-1)", () => {
|
|
30
|
+
test("should support cost field in continue action", () => {
|
|
31
|
+
const result: StageResult = { action: "continue", cost: 0.05 };
|
|
32
|
+
expect(result.action).toBe("continue");
|
|
33
|
+
expect(result.cost).toBe(0.05);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("should support cost field in fail action", () => {
|
|
37
|
+
const result: StageResult = { action: "fail", reason: "Build failed", cost: 0.02 };
|
|
38
|
+
expect(result.action).toBe("fail");
|
|
39
|
+
expect(result.cost).toBe(0.02);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("should support cost field in skip action", () => {
|
|
43
|
+
const result: StageResult = { action: "skip", reason: "Dependency not met", cost: 0.01 };
|
|
44
|
+
expect(result.action).toBe("skip");
|
|
45
|
+
expect(result.cost).toBe(0.01);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("should support cost field in escalate action", () => {
|
|
49
|
+
const result: StageResult = { action: "escalate", cost: 0.03 };
|
|
50
|
+
expect(result.action).toBe("escalate");
|
|
51
|
+
expect(result.cost).toBe(0.03);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("should support cost field in pause action", () => {
|
|
55
|
+
const result: StageResult = { action: "pause", reason: "User requested", cost: 0.01 };
|
|
56
|
+
expect(result.action).toBe("pause");
|
|
57
|
+
expect(result.cost).toBe(0.01);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("should allow omitting cost field (backward compatibility)", () => {
|
|
61
|
+
const result: StageResult = { action: "continue" };
|
|
62
|
+
expect(result.action).toBe("continue");
|
|
63
|
+
expect(result.cost).toBeUndefined();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("PipelineEventEmitter - Cost in story:complete (BUG-1)", () => {
|
|
68
|
+
test("should emit story:complete with cost field", () => {
|
|
69
|
+
const emitter = new PipelineEventEmitter();
|
|
70
|
+
const story = createMockStory("US-001");
|
|
71
|
+
|
|
72
|
+
const events: Array<{ story: UserStory; result: StageResult }> = [];
|
|
73
|
+
emitter.on("story:complete", (story, result) => {
|
|
74
|
+
events.push({ story, result });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const result: StageResult = { action: "continue", cost: 0.05 };
|
|
78
|
+
emitter.emit("story:complete", story, result);
|
|
79
|
+
|
|
80
|
+
expect(events).toHaveLength(1);
|
|
81
|
+
expect(events[0].story.id).toBe("US-001");
|
|
82
|
+
expect(events[0].result.cost).toBe(0.05);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("should emit story:complete without cost field", () => {
|
|
86
|
+
const emitter = new PipelineEventEmitter();
|
|
87
|
+
const story = createMockStory("US-001");
|
|
88
|
+
|
|
89
|
+
const events: Array<{ story: UserStory; result: StageResult }> = [];
|
|
90
|
+
emitter.on("story:complete", (story, result) => {
|
|
91
|
+
events.push({ story, result });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const result: StageResult = { action: "continue" };
|
|
95
|
+
emitter.emit("story:complete", story, result);
|
|
96
|
+
|
|
97
|
+
expect(events).toHaveLength(1);
|
|
98
|
+
expect(events[0].result.cost).toBeUndefined();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// ── PTY Line Length Tests (MEM-1) ────────────────────
|
|
103
|
+
|
|
104
|
+
describe("usePty - Line Length Limits (MEM-1)", () => {
|
|
105
|
+
test("should truncate lines exceeding MAX_LINE_LENGTH", async () => {
|
|
106
|
+
// This test verifies the line truncation logic exists
|
|
107
|
+
// We'll check the source code directly since node-pty mocking is complex
|
|
108
|
+
|
|
109
|
+
const usePtySource = await Bun.file("src/tui/hooks/usePty.ts").text();
|
|
110
|
+
|
|
111
|
+
// Verify MAX_LINE_LENGTH constant exists
|
|
112
|
+
expect(usePtySource).toContain("const MAX_LINE_LENGTH = 10_000");
|
|
113
|
+
|
|
114
|
+
// Verify truncation logic for complete lines
|
|
115
|
+
expect(usePtySource).toContain("line.length > MAX_LINE_LENGTH");
|
|
116
|
+
expect(usePtySource).toContain("`${line.slice(0, MAX_LINE_LENGTH)}…` : line");
|
|
117
|
+
|
|
118
|
+
// Verify truncation logic for incomplete lines (currentLine)
|
|
119
|
+
expect(usePtySource).toContain("if (currentLine.length > MAX_LINE_LENGTH)");
|
|
120
|
+
expect(usePtySource).toContain("currentLine = currentLine.slice(-MAX_LINE_LENGTH)");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("should have MAX_LINE_LENGTH constant set to 10000", async () => {
|
|
124
|
+
const usePtySource = await Bun.file("src/tui/hooks/usePty.ts").text();
|
|
125
|
+
const match = usePtySource.match(/const MAX_LINE_LENGTH = ([\d_]+)/);
|
|
126
|
+
|
|
127
|
+
expect(match).toBeTruthy();
|
|
128
|
+
expect(match?.[1]).toBe("10_000");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("PTY truncation behavior - unit test for truncation logic", () => {
|
|
132
|
+
const MAX_LINE_LENGTH = 10_000;
|
|
133
|
+
|
|
134
|
+
// Simulate the truncation logic
|
|
135
|
+
const longLine = "x".repeat(15_000);
|
|
136
|
+
const truncatedLine = longLine.length > MAX_LINE_LENGTH ? longLine.slice(0, MAX_LINE_LENGTH) + "…" : longLine;
|
|
137
|
+
|
|
138
|
+
expect(truncatedLine.length).toBe(MAX_LINE_LENGTH + 1); // +1 for ellipsis
|
|
139
|
+
expect(truncatedLine.endsWith("…")).toBe(true);
|
|
140
|
+
expect(truncatedLine.startsWith("x".repeat(100))).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("PTY incomplete line truncation - unit test for currentLine logic", () => {
|
|
144
|
+
const MAX_LINE_LENGTH = 10_000;
|
|
145
|
+
|
|
146
|
+
// Simulate incomplete line accumulation
|
|
147
|
+
let currentLine = "y".repeat(15_000);
|
|
148
|
+
|
|
149
|
+
// Apply truncation (keep last N chars)
|
|
150
|
+
if (currentLine.length > MAX_LINE_LENGTH) {
|
|
151
|
+
currentLine = currentLine.slice(-MAX_LINE_LENGTH);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
expect(currentLine.length).toBe(MAX_LINE_LENGTH);
|
|
155
|
+
expect(currentLine).toBe("y".repeat(MAX_LINE_LENGTH));
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// ── Integration Test: Cost Events ────────────────────
|
|
160
|
+
|
|
161
|
+
describe("Integration - Cost in multiple story:complete events", () => {
|
|
162
|
+
test("should emit multiple story:complete events with different costs", () => {
|
|
163
|
+
const emitter = new PipelineEventEmitter();
|
|
164
|
+
const stories = [createMockStory("US-001"), createMockStory("US-002"), createMockStory("US-003")];
|
|
165
|
+
|
|
166
|
+
const events: Array<{ story: UserStory; result: StageResult }> = [];
|
|
167
|
+
emitter.on("story:complete", (story, result) => {
|
|
168
|
+
events.push({ story, result });
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Story 1: passed ($0.05)
|
|
172
|
+
emitter.emit("story:complete", stories[0], { action: "continue", cost: 0.05 });
|
|
173
|
+
|
|
174
|
+
// Story 2: failed ($0.03)
|
|
175
|
+
emitter.emit("story:complete", stories[1], { action: "fail", reason: "test", cost: 0.03 });
|
|
176
|
+
|
|
177
|
+
// Story 3: skipped ($0.01)
|
|
178
|
+
emitter.emit("story:complete", stories[2], { action: "skip", reason: "test", cost: 0.01 });
|
|
179
|
+
|
|
180
|
+
expect(events).toHaveLength(3);
|
|
181
|
+
expect(events[0].result.cost).toBe(0.05);
|
|
182
|
+
expect(events[1].result.cost).toBe(0.03);
|
|
183
|
+
expect(events[2].result.cost).toBe(0.01);
|
|
184
|
+
|
|
185
|
+
// Verify total cost would be sum
|
|
186
|
+
const totalCost = events.reduce((sum, e) => sum + (e.result.cost || 0), 0);
|
|
187
|
+
expect(totalCost).toBe(0.09);
|
|
188
|
+
});
|
|
189
|
+
});
|