@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,461 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner config plugins integration test (US-007)
|
|
3
|
+
*
|
|
4
|
+
* Verifies that plugins[] from nax/config.json are passed to loadPlugins()
|
|
5
|
+
* when the runner starts. This is the missing integration test that ensures
|
|
6
|
+
* the config loader and plugin loader work together correctly.
|
|
7
|
+
*
|
|
8
|
+
* Focus: Integration between loadConfig() and loadPlugins() in runner.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
12
|
+
import * as fs from "node:fs/promises";
|
|
13
|
+
import * as os from "node:os";
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import { loadConfig } from "../../src/config/loader";
|
|
16
|
+
import { loadPlugins, _setPluginErrorSink, _resetPluginErrorSink } from "../../src/plugins/loader";
|
|
17
|
+
|
|
18
|
+
async function createTempDir(): Promise<string> {
|
|
19
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "nax-runner-config-plugins-"));
|
|
20
|
+
return tmpDir;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function cleanupTempDir(dir: string): Promise<void> {
|
|
24
|
+
try {
|
|
25
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
26
|
+
} catch {
|
|
27
|
+
// Ignore cleanup errors
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe("Runner config plugins integration (US-007)", () => {
|
|
32
|
+
let projectRoot: string;
|
|
33
|
+
let naxDir: string;
|
|
34
|
+
let tempGlobalPluginsDir: string;
|
|
35
|
+
|
|
36
|
+
beforeEach(async () => {
|
|
37
|
+
projectRoot = await createTempDir();
|
|
38
|
+
naxDir = path.join(projectRoot, "nax");
|
|
39
|
+
await fs.mkdir(naxDir, { recursive: true });
|
|
40
|
+
tempGlobalPluginsDir = await fs.mkdtemp(path.join(os.tmpdir(), "nax-test-global-plugins-"));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
afterEach(async () => {
|
|
44
|
+
await cleanupTempDir(projectRoot);
|
|
45
|
+
await cleanupTempDir(tempGlobalPluginsDir);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("config.plugins[] entries are passed to loadPlugins() when runner initializes", async () => {
|
|
49
|
+
// Create a custom plugin directory
|
|
50
|
+
const customPluginsDir = path.join(projectRoot, "custom-plugins");
|
|
51
|
+
await fs.mkdir(customPluginsDir, { recursive: true });
|
|
52
|
+
|
|
53
|
+
// Track plugin initialization
|
|
54
|
+
const initTracker = path.join(projectRoot, "init-tracker.json");
|
|
55
|
+
|
|
56
|
+
// Create a test plugin
|
|
57
|
+
const testPluginCode = `
|
|
58
|
+
export default {
|
|
59
|
+
name: "config-test-plugin",
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
provides: ["optimizer"],
|
|
62
|
+
async setup(config) {
|
|
63
|
+
const fs = await import("node:fs/promises");
|
|
64
|
+
await fs.writeFile("${initTracker}", JSON.stringify({
|
|
65
|
+
initialized: true,
|
|
66
|
+
config: config
|
|
67
|
+
}), "utf-8");
|
|
68
|
+
},
|
|
69
|
+
extensions: {
|
|
70
|
+
optimizer: {
|
|
71
|
+
name: "test",
|
|
72
|
+
async optimize(input) {
|
|
73
|
+
return {
|
|
74
|
+
optimizedPrompt: input.prompt,
|
|
75
|
+
estimatedTokens: input.estimatedTokens,
|
|
76
|
+
tokensSaved: 0,
|
|
77
|
+
appliedStrategies: []
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
`;
|
|
84
|
+
await fs.writeFile(path.join(customPluginsDir, "test-plugin.ts"), testPluginCode, "utf-8");
|
|
85
|
+
|
|
86
|
+
// Create nax/config.json with plugins[] array
|
|
87
|
+
const configContent = {
|
|
88
|
+
plugins: [
|
|
89
|
+
{
|
|
90
|
+
module: "./custom-plugins/test-plugin.ts",
|
|
91
|
+
config: {
|
|
92
|
+
testKey: "test-value-123",
|
|
93
|
+
enabled: true,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
|
|
99
|
+
|
|
100
|
+
// Step 1: Load config (as runner does)
|
|
101
|
+
const config = await loadConfig(naxDir);
|
|
102
|
+
|
|
103
|
+
// Step 2: Verify plugins[] is loaded into config
|
|
104
|
+
expect(config.plugins).toBeDefined();
|
|
105
|
+
expect(config.plugins).toHaveLength(1);
|
|
106
|
+
expect(config.plugins?.[0].module).toBe("./custom-plugins/test-plugin.ts");
|
|
107
|
+
|
|
108
|
+
// Step 3: Simulate what runner does - pass config.plugins to loadPlugins()
|
|
109
|
+
const globalPluginsDir = tempGlobalPluginsDir;
|
|
110
|
+
const projectPluginsDir = path.join(naxDir, "plugins");
|
|
111
|
+
const configPlugins = config.plugins || [];
|
|
112
|
+
|
|
113
|
+
const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
|
|
114
|
+
|
|
115
|
+
// Step 4: Verify plugin was loaded and initialized
|
|
116
|
+
expect(registry.plugins).toHaveLength(1);
|
|
117
|
+
expect(registry.plugins[0].name).toBe("config-test-plugin");
|
|
118
|
+
|
|
119
|
+
// Step 5: Verify plugin's setup() was called with the correct config
|
|
120
|
+
const tracker = JSON.parse(await fs.readFile(initTracker, "utf-8"));
|
|
121
|
+
expect(tracker.initialized).toBe(true);
|
|
122
|
+
expect(tracker.config).toEqual({
|
|
123
|
+
testKey: "test-value-123",
|
|
124
|
+
enabled: true,
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("relative plugin paths in config.plugins[] are resolved relative to project root", async () => {
|
|
129
|
+
// Create plugin in a subdirectory
|
|
130
|
+
const pluginSubdir = path.join(projectRoot, "lib", "plugins");
|
|
131
|
+
await fs.mkdir(pluginSubdir, { recursive: true });
|
|
132
|
+
|
|
133
|
+
const initTracker = path.join(projectRoot, "relative-path-tracker.json");
|
|
134
|
+
|
|
135
|
+
const pluginCode = `
|
|
136
|
+
export default {
|
|
137
|
+
name: "relative-path-plugin",
|
|
138
|
+
version: "1.0.0",
|
|
139
|
+
provides: ["optimizer"],
|
|
140
|
+
async setup(config) {
|
|
141
|
+
const fs = await import("node:fs/promises");
|
|
142
|
+
await fs.writeFile("${initTracker}", JSON.stringify({ loaded: true }), "utf-8");
|
|
143
|
+
},
|
|
144
|
+
extensions: {
|
|
145
|
+
optimizer: {
|
|
146
|
+
name: "test",
|
|
147
|
+
async optimize(input) {
|
|
148
|
+
return {
|
|
149
|
+
optimizedPrompt: input.prompt,
|
|
150
|
+
estimatedTokens: input.estimatedTokens,
|
|
151
|
+
tokensSaved: 0,
|
|
152
|
+
appliedStrategies: []
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
`;
|
|
159
|
+
await fs.writeFile(path.join(pluginSubdir, "plugin.ts"), pluginCode, "utf-8");
|
|
160
|
+
|
|
161
|
+
// Create config with relative path
|
|
162
|
+
const configContent = {
|
|
163
|
+
plugins: [
|
|
164
|
+
{
|
|
165
|
+
module: "./lib/plugins/plugin.ts",
|
|
166
|
+
config: {},
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
};
|
|
170
|
+
await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
|
|
171
|
+
|
|
172
|
+
// Load config
|
|
173
|
+
const config = await loadConfig(naxDir);
|
|
174
|
+
expect(config.plugins?.[0].module).toBe("./lib/plugins/plugin.ts");
|
|
175
|
+
|
|
176
|
+
// Pass to loadPlugins with projectRoot (as runner does)
|
|
177
|
+
const globalPluginsDir = tempGlobalPluginsDir;
|
|
178
|
+
const projectPluginsDir = path.join(naxDir, "plugins");
|
|
179
|
+
const configPlugins = config.plugins || [];
|
|
180
|
+
|
|
181
|
+
const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
|
|
182
|
+
|
|
183
|
+
// Verify plugin was loaded
|
|
184
|
+
expect(registry.plugins).toHaveLength(1);
|
|
185
|
+
expect(registry.plugins[0].name).toBe("relative-path-plugin");
|
|
186
|
+
|
|
187
|
+
// Verify it was actually initialized (proves path resolution worked)
|
|
188
|
+
const tracker = JSON.parse(await fs.readFile(initTracker, "utf-8"));
|
|
189
|
+
expect(tracker.loaded).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("absolute plugin paths in config.plugins[] work without project root resolution", async () => {
|
|
193
|
+
// Create plugin at an absolute path
|
|
194
|
+
const absolutePluginDir = path.join(projectRoot, "absolute-location");
|
|
195
|
+
await fs.mkdir(absolutePluginDir, { recursive: true });
|
|
196
|
+
|
|
197
|
+
const initTracker = path.join(projectRoot, "absolute-tracker.json");
|
|
198
|
+
|
|
199
|
+
const pluginCode = `
|
|
200
|
+
export default {
|
|
201
|
+
name: "absolute-plugin",
|
|
202
|
+
version: "1.0.0",
|
|
203
|
+
provides: ["optimizer"],
|
|
204
|
+
async setup(config) {
|
|
205
|
+
const fs = await import("node:fs/promises");
|
|
206
|
+
await fs.writeFile("${initTracker}", JSON.stringify({ absolutePath: true }), "utf-8");
|
|
207
|
+
},
|
|
208
|
+
extensions: {
|
|
209
|
+
optimizer: {
|
|
210
|
+
name: "test",
|
|
211
|
+
async optimize(input) {
|
|
212
|
+
return {
|
|
213
|
+
optimizedPrompt: input.prompt,
|
|
214
|
+
estimatedTokens: input.estimatedTokens,
|
|
215
|
+
tokensSaved: 0,
|
|
216
|
+
appliedStrategies: []
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
`;
|
|
223
|
+
const absolutePluginPath = path.join(absolutePluginDir, "plugin.ts");
|
|
224
|
+
await fs.writeFile(absolutePluginPath, pluginCode, "utf-8");
|
|
225
|
+
|
|
226
|
+
// Create config with absolute path
|
|
227
|
+
const configContent = {
|
|
228
|
+
plugins: [
|
|
229
|
+
{
|
|
230
|
+
module: absolutePluginPath,
|
|
231
|
+
config: {},
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
};
|
|
235
|
+
await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
|
|
236
|
+
|
|
237
|
+
// Load config
|
|
238
|
+
const config = await loadConfig(naxDir);
|
|
239
|
+
expect(config.plugins?.[0].module).toBe(absolutePluginPath);
|
|
240
|
+
|
|
241
|
+
// Pass to loadPlugins
|
|
242
|
+
const globalPluginsDir = tempGlobalPluginsDir;
|
|
243
|
+
const projectPluginsDir = path.join(naxDir, "plugins");
|
|
244
|
+
const configPlugins = config.plugins || [];
|
|
245
|
+
|
|
246
|
+
const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
|
|
247
|
+
|
|
248
|
+
// Verify plugin was loaded
|
|
249
|
+
expect(registry.plugins).toHaveLength(1);
|
|
250
|
+
expect(registry.plugins[0].name).toBe("absolute-plugin");
|
|
251
|
+
|
|
252
|
+
// Verify it was initialized
|
|
253
|
+
const tracker = JSON.parse(await fs.readFile(initTracker, "utf-8"));
|
|
254
|
+
expect(tracker.absolutePath).toBe(true);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("missing plugin module from config.plugins[] logs clear error (does not crash runner)", async () => {
|
|
258
|
+
// Capture plugin error output via the swappable sink (Bun ESM caches console at import time)
|
|
259
|
+
const errorLogs: string[] = [];
|
|
260
|
+
_setPluginErrorSink((...args: unknown[]) => {
|
|
261
|
+
errorLogs.push(args.map((arg) => String(arg)).join(" "));
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
// Create config with non-existent plugin
|
|
266
|
+
const configContent = {
|
|
267
|
+
plugins: [
|
|
268
|
+
{
|
|
269
|
+
module: "./nonexistent/missing-plugin.ts",
|
|
270
|
+
config: {},
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
|
|
275
|
+
|
|
276
|
+
// Load config
|
|
277
|
+
const config = await loadConfig(naxDir);
|
|
278
|
+
expect(config.plugins).toBeDefined();
|
|
279
|
+
expect(config.plugins).toHaveLength(1);
|
|
280
|
+
|
|
281
|
+
// Pass to loadPlugins (should not throw)
|
|
282
|
+
const globalPluginsDir = tempGlobalPluginsDir;
|
|
283
|
+
const projectPluginsDir = path.join(naxDir, "plugins");
|
|
284
|
+
const configPlugins = config.plugins || [];
|
|
285
|
+
|
|
286
|
+
const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
|
|
287
|
+
|
|
288
|
+
// Should return empty registry (plugin failed to load)
|
|
289
|
+
expect(registry.plugins).toHaveLength(0);
|
|
290
|
+
|
|
291
|
+
// Verify helpful error was logged
|
|
292
|
+
const errorOutput = errorLogs.join("\n");
|
|
293
|
+
expect(errorOutput).toContain("Failed to load plugin module");
|
|
294
|
+
expect(errorOutput).toContain("./nonexistent/missing-plugin.ts");
|
|
295
|
+
expect(errorOutput).toContain("Attempted path:");
|
|
296
|
+
expect(errorOutput).toContain(path.join(projectRoot, "nonexistent/missing-plugin.ts"));
|
|
297
|
+
} finally {
|
|
298
|
+
_resetPluginErrorSink();
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("empty config.plugins[] array results in no config-based plugins loaded", async () => {
|
|
303
|
+
// Create config with empty plugins array
|
|
304
|
+
const configContent = {
|
|
305
|
+
plugins: [],
|
|
306
|
+
};
|
|
307
|
+
await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
|
|
308
|
+
|
|
309
|
+
// Load config
|
|
310
|
+
const config = await loadConfig(naxDir);
|
|
311
|
+
expect(config.plugins).toBeDefined();
|
|
312
|
+
expect(config.plugins).toHaveLength(0);
|
|
313
|
+
|
|
314
|
+
// Pass to loadPlugins
|
|
315
|
+
const globalPluginsDir = tempGlobalPluginsDir;
|
|
316
|
+
const projectPluginsDir = path.join(naxDir, "plugins");
|
|
317
|
+
const configPlugins = config.plugins || [];
|
|
318
|
+
|
|
319
|
+
const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
|
|
320
|
+
|
|
321
|
+
// Should have no plugins (no global, no project, no config)
|
|
322
|
+
expect(registry.plugins).toHaveLength(0);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("undefined config.plugins in project config uses global config plugins if present", async () => {
|
|
326
|
+
// Create config without plugins field
|
|
327
|
+
const configContent = {
|
|
328
|
+
routing: {
|
|
329
|
+
strategy: "keyword",
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
|
|
333
|
+
|
|
334
|
+
// Load config
|
|
335
|
+
const config = await loadConfig(naxDir);
|
|
336
|
+
// config.plugins may be defined from global config or undefined
|
|
337
|
+
// The key is that runner should handle both cases with: config.plugins || []
|
|
338
|
+
|
|
339
|
+
// Simulate runner's fallback: config.plugins || []
|
|
340
|
+
const configPlugins = config.plugins || [];
|
|
341
|
+
expect(Array.isArray(configPlugins)).toBe(true);
|
|
342
|
+
|
|
343
|
+
// Pass to loadPlugins
|
|
344
|
+
const globalPluginsDir = tempGlobalPluginsDir;
|
|
345
|
+
const projectPluginsDir = path.join(naxDir, "plugins");
|
|
346
|
+
|
|
347
|
+
// Should not throw
|
|
348
|
+
const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
|
|
349
|
+
|
|
350
|
+
// Registry should be valid (may have plugins from global config)
|
|
351
|
+
expect(registry).toBeDefined();
|
|
352
|
+
expect(Array.isArray(registry.plugins)).toBe(true);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test("config.plugins[] takes precedence over auto-discovered plugins (name collision)", async () => {
|
|
356
|
+
// Create auto-discovery plugin in project plugins directory
|
|
357
|
+
const projectPluginsDir = path.join(naxDir, "plugins");
|
|
358
|
+
await fs.mkdir(projectPluginsDir, { recursive: true });
|
|
359
|
+
|
|
360
|
+
const initTracker = path.join(projectRoot, "precedence-tracker.json");
|
|
361
|
+
const initOrder: string[] = [];
|
|
362
|
+
|
|
363
|
+
const autoDiscoveredPluginCode = `
|
|
364
|
+
export default {
|
|
365
|
+
name: "collision-plugin",
|
|
366
|
+
version: "1.0.0",
|
|
367
|
+
provides: ["optimizer"],
|
|
368
|
+
async setup(config) {
|
|
369
|
+
const fs = await import("node:fs/promises");
|
|
370
|
+
let tracker = [];
|
|
371
|
+
try {
|
|
372
|
+
tracker = JSON.parse(await fs.readFile("${initTracker}", "utf-8"));
|
|
373
|
+
} catch {}
|
|
374
|
+
tracker.push("auto-discovered");
|
|
375
|
+
await fs.writeFile("${initTracker}", JSON.stringify(tracker), "utf-8");
|
|
376
|
+
},
|
|
377
|
+
extensions: {
|
|
378
|
+
optimizer: {
|
|
379
|
+
name: "auto",
|
|
380
|
+
async optimize(input) {
|
|
381
|
+
return {
|
|
382
|
+
optimizedPrompt: input.prompt,
|
|
383
|
+
estimatedTokens: input.estimatedTokens,
|
|
384
|
+
tokensSaved: 0,
|
|
385
|
+
appliedStrategies: []
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
`;
|
|
392
|
+
await fs.writeFile(path.join(projectPluginsDir, "plugin.ts"), autoDiscoveredPluginCode, "utf-8");
|
|
393
|
+
|
|
394
|
+
// Create config-specified plugin with same name
|
|
395
|
+
const customPluginsDir = path.join(projectRoot, "custom");
|
|
396
|
+
await fs.mkdir(customPluginsDir, { recursive: true });
|
|
397
|
+
|
|
398
|
+
const configPluginCode = `
|
|
399
|
+
export default {
|
|
400
|
+
name: "collision-plugin",
|
|
401
|
+
version: "2.0.0",
|
|
402
|
+
provides: ["optimizer"],
|
|
403
|
+
async setup(config) {
|
|
404
|
+
const fs = await import("node:fs/promises");
|
|
405
|
+
let tracker = [];
|
|
406
|
+
try {
|
|
407
|
+
tracker = JSON.parse(await fs.readFile("${initTracker}", "utf-8"));
|
|
408
|
+
} catch {}
|
|
409
|
+
tracker.push("config-specified");
|
|
410
|
+
await fs.writeFile("${initTracker}", JSON.stringify(tracker), "utf-8");
|
|
411
|
+
},
|
|
412
|
+
extensions: {
|
|
413
|
+
optimizer: {
|
|
414
|
+
name: "config",
|
|
415
|
+
async optimize(input) {
|
|
416
|
+
return {
|
|
417
|
+
optimizedPrompt: input.prompt,
|
|
418
|
+
estimatedTokens: input.estimatedTokens,
|
|
419
|
+
tokensSaved: 0,
|
|
420
|
+
appliedStrategies: []
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
`;
|
|
427
|
+
await fs.writeFile(path.join(customPluginsDir, "plugin.ts"), configPluginCode, "utf-8");
|
|
428
|
+
|
|
429
|
+
// Create config with explicit plugin
|
|
430
|
+
const configContent = {
|
|
431
|
+
plugins: [
|
|
432
|
+
{
|
|
433
|
+
module: "./custom/plugin.ts",
|
|
434
|
+
config: {},
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
};
|
|
438
|
+
await fs.writeFile(path.join(naxDir, "config.json"), JSON.stringify(configContent, null, 2), "utf-8");
|
|
439
|
+
|
|
440
|
+
// Load config
|
|
441
|
+
const config = await loadConfig(naxDir);
|
|
442
|
+
|
|
443
|
+
// Pass to loadPlugins
|
|
444
|
+
const globalPluginsDir = tempGlobalPluginsDir;
|
|
445
|
+
const configPlugins = config.plugins || [];
|
|
446
|
+
|
|
447
|
+
const registry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, projectRoot);
|
|
448
|
+
|
|
449
|
+
// Should have both plugins loaded (name collision allowed, last one wins)
|
|
450
|
+
expect(registry.plugins.length).toBeGreaterThanOrEqual(1);
|
|
451
|
+
|
|
452
|
+
// Verify init order shows auto-discovered loads first, then config
|
|
453
|
+
const tracker = JSON.parse(await fs.readFile(initTracker, "utf-8"));
|
|
454
|
+
expect(tracker).toContain("auto-discovered");
|
|
455
|
+
expect(tracker).toContain("config-specified");
|
|
456
|
+
|
|
457
|
+
// Config plugin should be loaded last (overrides auto-discovered)
|
|
458
|
+
const lastIndex = tracker.lastIndexOf("config-specified");
|
|
459
|
+
expect(lastIndex).toBeGreaterThan(tracker.indexOf("auto-discovered"));
|
|
460
|
+
});
|
|
461
|
+
});
|