@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
package/src/config/merge.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-Package Config Merge Utility (MW-008, v0.49.0 expansion)
|
|
3
|
-
*
|
|
4
|
-
* Merges a package-level partial config override into a root config.
|
|
5
|
-
* Covers all fields that make sense at the per-package level.
|
|
6
|
-
* Root-only fields (models, autoMode, routing, agent, etc.) are unchanged.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { NaxConfig } from "./schema";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Merge a package-level partial config override into a root config.
|
|
13
|
-
*
|
|
14
|
-
* Mergeable sections:
|
|
15
|
-
* - execution: smartTestRunner, regressionGate (deep), verificationTimeoutSeconds
|
|
16
|
-
* - review: enabled, checks, commands (deep), pluginMode
|
|
17
|
-
* - acceptance: enabled, generateTests, testPath
|
|
18
|
-
* - quality: requireTests, requireTypecheck, requireLint, commands (deep), testing (deep)
|
|
19
|
-
* - context: testCoverage (deep)
|
|
20
|
-
*
|
|
21
|
-
* All other sections (models, autoMode, routing, agent, generate, tdd,
|
|
22
|
-
* decompose, plan, constitution, interaction) remain root-only.
|
|
23
|
-
*
|
|
24
|
-
* @param root - Full root NaxConfig (already validated)
|
|
25
|
-
* @param packageOverride - Partial package-level override
|
|
26
|
-
* @returns New merged NaxConfig (immutable — does not mutate inputs)
|
|
27
|
-
*/
|
|
28
|
-
export function mergePackageConfig(root: NaxConfig, packageOverride: Partial<NaxConfig>): NaxConfig {
|
|
29
|
-
const hasAnyMergeableField =
|
|
30
|
-
packageOverride.execution !== undefined ||
|
|
31
|
-
packageOverride.review !== undefined ||
|
|
32
|
-
packageOverride.acceptance !== undefined ||
|
|
33
|
-
packageOverride.quality !== undefined ||
|
|
34
|
-
packageOverride.context !== undefined;
|
|
35
|
-
|
|
36
|
-
if (!hasAnyMergeableField) {
|
|
37
|
-
return root;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
...root,
|
|
42
|
-
execution: {
|
|
43
|
-
...root.execution,
|
|
44
|
-
...packageOverride.execution,
|
|
45
|
-
smartTestRunner: packageOverride.execution?.smartTestRunner ?? root.execution.smartTestRunner,
|
|
46
|
-
regressionGate: {
|
|
47
|
-
...root.execution.regressionGate,
|
|
48
|
-
...packageOverride.execution?.regressionGate,
|
|
49
|
-
},
|
|
50
|
-
verificationTimeoutSeconds:
|
|
51
|
-
packageOverride.execution?.verificationTimeoutSeconds ?? root.execution.verificationTimeoutSeconds,
|
|
52
|
-
},
|
|
53
|
-
review: {
|
|
54
|
-
...root.review,
|
|
55
|
-
...packageOverride.review,
|
|
56
|
-
commands: {
|
|
57
|
-
...root.review.commands,
|
|
58
|
-
// PKG-006: Bridge quality.commands → review.commands for per-package overrides.
|
|
59
|
-
// Users naturally put per-package commands in quality.commands (the intuitive
|
|
60
|
-
// place), but the review runner reads review.commands. Bridge them here so
|
|
61
|
-
// packages don't need to define the same commands in two places.
|
|
62
|
-
// Explicit review.commands still take precedence (applied after).
|
|
63
|
-
...(packageOverride.quality?.commands?.lint !== undefined && {
|
|
64
|
-
lint: packageOverride.quality.commands.lint,
|
|
65
|
-
}),
|
|
66
|
-
...(packageOverride.quality?.commands?.lintFix !== undefined && {
|
|
67
|
-
lintFix: packageOverride.quality.commands.lintFix,
|
|
68
|
-
}),
|
|
69
|
-
...(packageOverride.quality?.commands?.typecheck !== undefined && {
|
|
70
|
-
typecheck: packageOverride.quality.commands.typecheck,
|
|
71
|
-
}),
|
|
72
|
-
...(packageOverride.quality?.commands?.test !== undefined && {
|
|
73
|
-
test: packageOverride.quality.commands.test,
|
|
74
|
-
}),
|
|
75
|
-
// Explicit review.commands override bridged quality values
|
|
76
|
-
...packageOverride.review?.commands,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
acceptance: {
|
|
80
|
-
...root.acceptance,
|
|
81
|
-
...packageOverride.acceptance,
|
|
82
|
-
},
|
|
83
|
-
quality: {
|
|
84
|
-
...root.quality,
|
|
85
|
-
requireTests: packageOverride.quality?.requireTests ?? root.quality.requireTests,
|
|
86
|
-
requireTypecheck: packageOverride.quality?.requireTypecheck ?? root.quality.requireTypecheck,
|
|
87
|
-
requireLint: packageOverride.quality?.requireLint ?? root.quality.requireLint,
|
|
88
|
-
commands: {
|
|
89
|
-
...root.quality.commands,
|
|
90
|
-
...packageOverride.quality?.commands,
|
|
91
|
-
},
|
|
92
|
-
// ENH-010: deep-merge testing config so per-package overrides work
|
|
93
|
-
testing:
|
|
94
|
-
packageOverride.quality?.testing !== undefined
|
|
95
|
-
? { ...root.quality.testing, ...packageOverride.quality.testing }
|
|
96
|
-
: root.quality.testing,
|
|
97
|
-
},
|
|
98
|
-
context: {
|
|
99
|
-
...root.context,
|
|
100
|
-
testCoverage: {
|
|
101
|
-
...root.context.testCoverage,
|
|
102
|
-
...packageOverride.context?.testCoverage,
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
}
|
package/src/config/merger.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration Merger Utility
|
|
3
|
-
*
|
|
4
|
-
* Deep merge utility for NaxConfig with special handling:
|
|
5
|
-
* - Arrays: replace (not merge)
|
|
6
|
-
* - Null values: remove keys
|
|
7
|
-
* - Hooks: concatenate from both configs
|
|
8
|
-
* - Constitution content: concatenate with newline separator
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { NaxConfig } from "./schema";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Deep merge two configuration objects.
|
|
15
|
-
*
|
|
16
|
-
* Rules:
|
|
17
|
-
* - Objects are merged recursively
|
|
18
|
-
* - Arrays replace (override completely replaces base)
|
|
19
|
-
* - Null values in override remove the key from result
|
|
20
|
-
* - Undefined values in override are skipped
|
|
21
|
-
* - Hooks are concatenated (both base and override hooks preserved)
|
|
22
|
-
* - Constitution content is concatenated with newline separator
|
|
23
|
-
*
|
|
24
|
-
* @param base - Base configuration object
|
|
25
|
-
* @param override - Override configuration object
|
|
26
|
-
* @returns New merged configuration (immutable - does not mutate inputs)
|
|
27
|
-
*/
|
|
28
|
-
export function deepMergeConfig<T = NaxConfig>(base: Record<string, unknown>, override: Record<string, unknown>): T {
|
|
29
|
-
// Start with a clone of base to ensure immutability
|
|
30
|
-
const result: Record<string, unknown> = { ...base };
|
|
31
|
-
|
|
32
|
-
for (const key of Object.keys(override)) {
|
|
33
|
-
const overrideValue = override[key];
|
|
34
|
-
|
|
35
|
-
// Skip undefined values
|
|
36
|
-
if (overrideValue === undefined) {
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Handle null values - remove key from result
|
|
41
|
-
if (overrideValue === null) {
|
|
42
|
-
delete result[key];
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const baseValue = result[key];
|
|
47
|
-
|
|
48
|
-
// Special case: hooks concatenation
|
|
49
|
-
if (key === "hooks" && isPlainObject(baseValue) && isPlainObject(overrideValue)) {
|
|
50
|
-
const baseHooks = baseValue as Record<string, unknown>;
|
|
51
|
-
const overrideHooks = overrideValue as Record<string, unknown>;
|
|
52
|
-
const merged: Record<string, unknown> = { ...baseHooks };
|
|
53
|
-
|
|
54
|
-
// Merge the nested hooks object
|
|
55
|
-
if (isPlainObject(baseHooks.hooks) && isPlainObject(overrideHooks.hooks)) {
|
|
56
|
-
const baseHookDefs = baseHooks.hooks as Record<string, unknown>;
|
|
57
|
-
const overrideHookDefs = overrideHooks.hooks as Record<string, unknown>;
|
|
58
|
-
const mergedHookDefs: Record<string, unknown> = {};
|
|
59
|
-
|
|
60
|
-
// Collect all hook event names
|
|
61
|
-
const allHookNames = new Set([...Object.keys(baseHookDefs), ...Object.keys(overrideHookDefs)]);
|
|
62
|
-
|
|
63
|
-
// For each hook event, concatenate hooks into an array
|
|
64
|
-
for (const hookName of allHookNames) {
|
|
65
|
-
const baseHook = baseHookDefs[hookName];
|
|
66
|
-
const overrideHook = overrideHookDefs[hookName];
|
|
67
|
-
|
|
68
|
-
if (baseHook && overrideHook) {
|
|
69
|
-
// Both exist - create array with both
|
|
70
|
-
mergedHookDefs[hookName] = [baseHook, overrideHook];
|
|
71
|
-
} else if (overrideHook) {
|
|
72
|
-
// Only override exists
|
|
73
|
-
mergedHookDefs[hookName] = overrideHook;
|
|
74
|
-
} else {
|
|
75
|
-
// Only base exists
|
|
76
|
-
mergedHookDefs[hookName] = baseHook;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
merged.hooks = mergedHookDefs;
|
|
81
|
-
} else if (overrideHooks.hooks) {
|
|
82
|
-
merged.hooks = overrideHooks.hooks;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Handle other hook config fields (e.g., skipGlobal)
|
|
86
|
-
for (const hookKey of Object.keys(overrideHooks)) {
|
|
87
|
-
if (hookKey !== "hooks") {
|
|
88
|
-
merged[hookKey] = overrideHooks[hookKey];
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
result[key] = merged;
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Special case: constitution content concatenation
|
|
97
|
-
if (key === "constitution" && isPlainObject(baseValue) && isPlainObject(overrideValue)) {
|
|
98
|
-
const baseConst = baseValue as Record<string, unknown>;
|
|
99
|
-
const overrideConst = overrideValue as Record<string, unknown>;
|
|
100
|
-
|
|
101
|
-
const baseContent = typeof baseConst.content === "string" ? baseConst.content : "";
|
|
102
|
-
const overrideContent = typeof overrideConst.content === "string" ? overrideConst.content : "";
|
|
103
|
-
|
|
104
|
-
// Merge constitution object, but concatenate content field
|
|
105
|
-
const mergedConstitution = deepMergeConfig(baseConst, overrideConst);
|
|
106
|
-
|
|
107
|
-
// Concatenate content if both exist
|
|
108
|
-
if (baseContent && overrideContent) {
|
|
109
|
-
(mergedConstitution as unknown as Record<string, unknown>).content = `${baseContent}\n\n${overrideContent}`;
|
|
110
|
-
} else if (overrideContent) {
|
|
111
|
-
(mergedConstitution as unknown as Record<string, unknown>).content = overrideContent;
|
|
112
|
-
} else if (baseContent) {
|
|
113
|
-
(mergedConstitution as unknown as Record<string, unknown>).content = baseContent;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
result[key] = mergedConstitution;
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Arrays replace completely (no merging)
|
|
121
|
-
if (Array.isArray(overrideValue)) {
|
|
122
|
-
result[key] = [...overrideValue];
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Recursive merge for plain objects
|
|
127
|
-
if (isPlainObject(overrideValue) && isPlainObject(baseValue)) {
|
|
128
|
-
result[key] = deepMergeConfig(baseValue as Record<string, unknown>, overrideValue as Record<string, unknown>);
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Default: override replaces base
|
|
133
|
-
result[key] = overrideValue;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return result as T;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Check if value is a plain object (not null, not array, not class instance).
|
|
141
|
-
*
|
|
142
|
-
* @param value - Value to check
|
|
143
|
-
* @returns True if value is a plain object
|
|
144
|
-
*/
|
|
145
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
146
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && value.constructor === Object;
|
|
147
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Path Security Utilities
|
|
3
|
-
*
|
|
4
|
-
* Prevents path traversal attacks by validating and resolving paths.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { existsSync, lstatSync, realpathSync } from "node:fs";
|
|
8
|
-
import { isAbsolute, normalize, resolve } from "node:path";
|
|
9
|
-
|
|
10
|
-
/** Maximum directory depth to prevent infinite loops */
|
|
11
|
-
export const MAX_DIRECTORY_DEPTH = 10;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Validate and resolve a directory path safely
|
|
15
|
-
* @param dirPath - The directory path to validate
|
|
16
|
-
* @param baseDir - Optional base directory to check bounds (if provided, dirPath must be within baseDir)
|
|
17
|
-
* @returns Resolved absolute path
|
|
18
|
-
* @throws Error if path is invalid, not a directory, or outside bounds
|
|
19
|
-
*/
|
|
20
|
-
export function validateDirectory(dirPath: string, baseDir?: string): string {
|
|
21
|
-
// Resolve to absolute path
|
|
22
|
-
const resolved = resolve(dirPath);
|
|
23
|
-
|
|
24
|
-
// Check if path exists
|
|
25
|
-
if (!existsSync(resolved)) {
|
|
26
|
-
throw new Error(`Directory does not exist: ${dirPath}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Get real path (resolves symlinks)
|
|
30
|
-
let realPath: string;
|
|
31
|
-
try {
|
|
32
|
-
realPath = realpathSync(resolved);
|
|
33
|
-
} catch (error) {
|
|
34
|
-
throw new Error(`Failed to resolve path: ${dirPath} (${(error as Error).message})`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Check if it's a directory
|
|
38
|
-
try {
|
|
39
|
-
const stats = lstatSync(realPath);
|
|
40
|
-
if (!stats.isDirectory()) {
|
|
41
|
-
throw new Error(`Not a directory: ${dirPath}`);
|
|
42
|
-
}
|
|
43
|
-
} catch (error) {
|
|
44
|
-
throw new Error(`Failed to stat path: ${dirPath} (${(error as Error).message})`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// If baseDir provided, ensure realPath is within baseDir
|
|
48
|
-
if (baseDir) {
|
|
49
|
-
const resolvedBase = resolve(baseDir);
|
|
50
|
-
const realBase = existsSync(resolvedBase) ? realpathSync(resolvedBase) : resolvedBase;
|
|
51
|
-
|
|
52
|
-
if (!isWithinDirectory(realPath, realBase)) {
|
|
53
|
-
throw new Error(`Path is outside allowed directory: ${dirPath} (resolved to ${realPath}, base: ${realBase})`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return realPath;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Check if a path is within a base directory (prevents path traversal)
|
|
62
|
-
* @param targetPath - The path to check (must be absolute)
|
|
63
|
-
* @param basePath - The base directory (must be absolute)
|
|
64
|
-
* @returns true if targetPath is within basePath
|
|
65
|
-
*/
|
|
66
|
-
export function isWithinDirectory(targetPath: string, basePath: string): boolean {
|
|
67
|
-
const normalizedTarget = normalize(targetPath);
|
|
68
|
-
const normalizedBase = normalize(basePath);
|
|
69
|
-
|
|
70
|
-
// Ensure both are absolute
|
|
71
|
-
if (!isAbsolute(normalizedTarget) || !isAbsolute(normalizedBase)) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Add trailing slash to base to prevent partial matches
|
|
76
|
-
const baseWithSlash = normalizedBase.endsWith("/") ? normalizedBase : `${normalizedBase}/`;
|
|
77
|
-
const targetWithSlash = normalizedTarget.endsWith("/") ? normalizedTarget : `${normalizedTarget}/`;
|
|
78
|
-
|
|
79
|
-
// Check if target starts with base
|
|
80
|
-
return targetWithSlash.startsWith(baseWithSlash) || normalizedTarget === normalizedBase;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Validate file path and ensure it's within a base directory
|
|
85
|
-
* @param filePath - The file path to validate
|
|
86
|
-
* @param baseDir - Base directory (filePath must be within this directory)
|
|
87
|
-
* @returns Resolved absolute path
|
|
88
|
-
* @throws Error if path is invalid or outside bounds
|
|
89
|
-
*/
|
|
90
|
-
export function validateFilePath(filePath: string, baseDir: string): string {
|
|
91
|
-
const resolved = resolve(filePath);
|
|
92
|
-
|
|
93
|
-
// Get real path (resolves symlinks)
|
|
94
|
-
let realPath: string;
|
|
95
|
-
try {
|
|
96
|
-
// For non-existent files, use parent directory's real path
|
|
97
|
-
if (!existsSync(resolved)) {
|
|
98
|
-
const parent = resolve(resolved, "..");
|
|
99
|
-
if (existsSync(parent)) {
|
|
100
|
-
const realParent = realpathSync(parent);
|
|
101
|
-
realPath = resolve(realParent, filePath.split("/").pop() || "");
|
|
102
|
-
} else {
|
|
103
|
-
realPath = resolved;
|
|
104
|
-
}
|
|
105
|
-
} else {
|
|
106
|
-
realPath = realpathSync(resolved);
|
|
107
|
-
}
|
|
108
|
-
} catch (error) {
|
|
109
|
-
throw new Error(`Failed to resolve path: ${filePath} (${(error as Error).message})`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Ensure realPath is within baseDir
|
|
113
|
-
const resolvedBase = resolve(baseDir);
|
|
114
|
-
const realBase = existsSync(resolvedBase) ? realpathSync(resolvedBase) : resolvedBase;
|
|
115
|
-
|
|
116
|
-
if (!isWithinDirectory(realPath, realBase)) {
|
|
117
|
-
throw new Error(`Path is outside allowed directory: ${filePath} (resolved to ${realPath}, base: ${realBase})`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return realPath;
|
|
121
|
-
}
|
package/src/config/paths.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration Path Utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides path resolution for global and project-level config directories.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { homedir } from "node:os";
|
|
8
|
-
import { join, resolve } from "node:path";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Returns the global config directory path (~/.nax).
|
|
12
|
-
*
|
|
13
|
-
* @returns Absolute path to global config directory
|
|
14
|
-
*/
|
|
15
|
-
export function globalConfigDir(): string {
|
|
16
|
-
return join(homedir(), ".nax");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Returns the project config directory path (projectRoot/nax).
|
|
21
|
-
*
|
|
22
|
-
* @param projectRoot - Absolute or relative path to project root
|
|
23
|
-
* @returns Absolute path to project config directory
|
|
24
|
-
*/
|
|
25
|
-
export function projectConfigDir(projectRoot: string): string {
|
|
26
|
-
return join(resolve(projectRoot), "nax");
|
|
27
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Permission Resolver — Single Source of Truth
|
|
3
|
-
*
|
|
4
|
-
* All adapters call resolvePermissions() to determine permission mode.
|
|
5
|
-
* No local fallbacks allowed elsewhere in the codebase.
|
|
6
|
-
*
|
|
7
|
-
* Phase 1: permissionProfile field + legacy boolean backward compat.
|
|
8
|
-
* Phase 2: per-stage scoped allowlists (stub below).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { NaxConfig } from "./schema";
|
|
12
|
-
|
|
13
|
-
export type PermissionProfile = "unrestricted" | "safe" | "scoped";
|
|
14
|
-
|
|
15
|
-
export type PipelineStage =
|
|
16
|
-
| "plan"
|
|
17
|
-
| "run"
|
|
18
|
-
| "verify"
|
|
19
|
-
| "review"
|
|
20
|
-
| "rectification"
|
|
21
|
-
| "regression"
|
|
22
|
-
| "acceptance"
|
|
23
|
-
| "complete";
|
|
24
|
-
|
|
25
|
-
export interface ResolvedPermissions {
|
|
26
|
-
/** ACP permission mode string */
|
|
27
|
-
mode: "approve-all" | "approve-reads" | "default";
|
|
28
|
-
/** CLI adapter: whether to pass --dangerously-skip-permissions */
|
|
29
|
-
skipPermissions: boolean;
|
|
30
|
-
/** Future: scoped tool allowlist (Phase 2) */
|
|
31
|
-
allowedTools?: string[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Resolve permissions for a given pipeline stage.
|
|
36
|
-
* Single source of truth — all adapters call this.
|
|
37
|
-
*
|
|
38
|
-
* Precedence: permissionProfile > dangerouslySkipPermissions boolean > safe default.
|
|
39
|
-
*/
|
|
40
|
-
export function resolvePermissions(config: NaxConfig | undefined, _stage: PipelineStage): ResolvedPermissions {
|
|
41
|
-
const profile: PermissionProfile =
|
|
42
|
-
config?.execution?.permissionProfile ?? (config?.execution?.dangerouslySkipPermissions ? "unrestricted" : "safe");
|
|
43
|
-
|
|
44
|
-
switch (profile) {
|
|
45
|
-
case "unrestricted":
|
|
46
|
-
return { mode: "approve-all", skipPermissions: true };
|
|
47
|
-
case "safe":
|
|
48
|
-
return { mode: "approve-reads", skipPermissions: false };
|
|
49
|
-
case "scoped":
|
|
50
|
-
return resolveScopedPermissions(config, _stage);
|
|
51
|
-
default:
|
|
52
|
-
return { mode: "approve-reads", skipPermissions: false };
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Phase 2 stub — resolves per-stage permissions from config block.
|
|
58
|
-
* Returns safe defaults until Phase 2 is implemented.
|
|
59
|
-
*/
|
|
60
|
-
function resolveScopedPermissions(_config: NaxConfig | undefined, _stage: PipelineStage): ResolvedPermissions {
|
|
61
|
-
// Phase 2 implementation goes here
|
|
62
|
-
return { mode: "approve-reads", skipPermissions: false };
|
|
63
|
-
}
|