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