@nathapp/nax 0.50.3 → 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 +393 -197
- 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 -415
- 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 -138
- 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 -219
- 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 -218
- 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 -522
- package/src/config/schema-types.ts +0 -53
- package/src/config/schema.ts +0 -60
- package/src/config/schemas.ts +0 -426
- 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 -309
- 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 -144
- 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/utils/git.ts
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git utility functions
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { getSafeLogger } from "../logger";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Injectable dependencies for git subprocess calls — allows tests to intercept
|
|
9
|
-
* Bun.spawn without mock.module().
|
|
10
|
-
*
|
|
11
|
-
* @internal
|
|
12
|
-
*/
|
|
13
|
-
export const _gitDeps = { spawn: Bun.spawn };
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Default timeout for git subprocess calls.
|
|
17
|
-
* Prevents git from hanging indefinitely on locked repos or network mounts.
|
|
18
|
-
*/
|
|
19
|
-
const GIT_TIMEOUT_MS = 10_000;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Spawn a git command with a hard timeout.
|
|
23
|
-
*
|
|
24
|
-
* Kills the process with SIGKILL after GIT_TIMEOUT_MS if it hasn't exited.
|
|
25
|
-
* Returns empty stdout and exit code 1 on timeout.
|
|
26
|
-
*
|
|
27
|
-
* @internal
|
|
28
|
-
*/
|
|
29
|
-
export async function gitWithTimeout(args: string[], workdir: string): Promise<{ stdout: string; exitCode: number }> {
|
|
30
|
-
const proc = _gitDeps.spawn(["git", ...args], {
|
|
31
|
-
cwd: workdir,
|
|
32
|
-
stdout: "pipe",
|
|
33
|
-
stderr: "pipe",
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
let timedOut = false;
|
|
37
|
-
const timerId = setTimeout(() => {
|
|
38
|
-
timedOut = true;
|
|
39
|
-
try {
|
|
40
|
-
proc.kill("SIGKILL");
|
|
41
|
-
} catch {
|
|
42
|
-
// Process may have already exited
|
|
43
|
-
}
|
|
44
|
-
}, GIT_TIMEOUT_MS);
|
|
45
|
-
|
|
46
|
-
const exitCode = await proc.exited;
|
|
47
|
-
clearTimeout(timerId);
|
|
48
|
-
|
|
49
|
-
if (timedOut) {
|
|
50
|
-
return { stdout: "", exitCode: 1 };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const stdout = await new Response(proc.stdout).text();
|
|
54
|
-
return { stdout, exitCode };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Capture current git HEAD ref.
|
|
59
|
-
*
|
|
60
|
-
* Returns the current HEAD commit hash, or undefined if git is not available
|
|
61
|
-
* or the command fails (e.g., not in a git repo).
|
|
62
|
-
*
|
|
63
|
-
* @param workdir - Working directory to run git command in
|
|
64
|
-
* @returns Git HEAD ref or undefined on failure
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```typescript
|
|
68
|
-
* const ref = await captureGitRef("/path/to/repo");
|
|
69
|
-
* if (ref) {
|
|
70
|
-
* console.log(`Current HEAD: ${ref}`);
|
|
71
|
-
* }
|
|
72
|
-
* ```
|
|
73
|
-
*/
|
|
74
|
-
export async function captureGitRef(workdir: string): Promise<string | undefined> {
|
|
75
|
-
try {
|
|
76
|
-
const { stdout, exitCode } = await gitWithTimeout(["rev-parse", "HEAD"], workdir);
|
|
77
|
-
if (exitCode !== 0) return undefined;
|
|
78
|
-
return stdout.trim() || undefined;
|
|
79
|
-
} catch {
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Check if a story ID appears in recent git commit messages.
|
|
86
|
-
*
|
|
87
|
-
* Searches the last N commits for commit messages containing the story ID.
|
|
88
|
-
* Used for state reconciliation: if a failed story has commits in git history,
|
|
89
|
-
* it means the story was partially completed and should be marked as passed.
|
|
90
|
-
*
|
|
91
|
-
* @param workdir - Working directory to run git command in
|
|
92
|
-
* @param storyId - Story ID to search for (e.g., "US-001")
|
|
93
|
-
* @param maxCommits - Maximum number of commits to search (default: 20)
|
|
94
|
-
* @returns true if story ID found in commit messages, false otherwise
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```typescript
|
|
98
|
-
* const hasCommits = await hasCommitsForStory("/path/to/repo", "US-001");
|
|
99
|
-
* if (hasCommits) {
|
|
100
|
-
* console.log("Story US-001 has commits in git history");
|
|
101
|
-
* }
|
|
102
|
-
* ```
|
|
103
|
-
*/
|
|
104
|
-
export async function hasCommitsForStory(workdir: string, storyId: string, maxCommits = 20): Promise<boolean> {
|
|
105
|
-
try {
|
|
106
|
-
const { stdout, exitCode } = await gitWithTimeout(
|
|
107
|
-
["log", `-${maxCommits}`, "--oneline", "--grep", storyId],
|
|
108
|
-
workdir,
|
|
109
|
-
);
|
|
110
|
-
if (exitCode !== 0) return false;
|
|
111
|
-
return stdout.trim().length > 0;
|
|
112
|
-
} catch {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Detect if git operation output contains merge conflict markers.
|
|
119
|
-
*
|
|
120
|
-
* Git outputs "CONFLICT" in uppercase for merge/rebase conflicts.
|
|
121
|
-
* Also checks lowercase "conflict" for edge cases.
|
|
122
|
-
*
|
|
123
|
-
* @param output - Combined stdout/stderr output from a git operation
|
|
124
|
-
* @returns true if output contains CONFLICT markers
|
|
125
|
-
*
|
|
126
|
-
* @example
|
|
127
|
-
* ```typescript
|
|
128
|
-
* const hasConflict = detectMergeConflict(agentOutput);
|
|
129
|
-
* if (hasConflict) {
|
|
130
|
-
* // fire merge-conflict trigger
|
|
131
|
-
* }
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
export function detectMergeConflict(output: string): boolean {
|
|
135
|
-
return output.includes("CONFLICT") || output.includes("conflict");
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Auto-commit safety net.
|
|
140
|
-
*
|
|
141
|
-
* If the agent left uncommitted changes after a session, stage and commit them
|
|
142
|
-
* automatically. Prevents the review stage from failing with "uncommitted
|
|
143
|
-
* changes" errors. No-op when the working tree is clean.
|
|
144
|
-
*
|
|
145
|
-
* Used by session-runner.ts (TDD sessions), rectification-gate.ts, and
|
|
146
|
-
* execution.ts (single-session / test-after).
|
|
147
|
-
*
|
|
148
|
-
* @param workdir - Working directory (git repo root)
|
|
149
|
-
* @param stage - Log stage prefix (e.g. "tdd", "execution")
|
|
150
|
-
* @param role - Session role for the commit message (e.g. "implementer")
|
|
151
|
-
* @param storyId - Story ID for the commit message
|
|
152
|
-
*/
|
|
153
|
-
export async function autoCommitIfDirty(workdir: string, stage: string, role: string, storyId: string): Promise<void> {
|
|
154
|
-
const logger = getSafeLogger();
|
|
155
|
-
try {
|
|
156
|
-
// Guard: only auto-commit if workdir IS the git repository root.
|
|
157
|
-
// Without this, a workdir nested inside another git repo (e.g. a temp dir
|
|
158
|
-
// created inside the nax repo during tests) would cause git to walk up and
|
|
159
|
-
// commit files from the parent repo instead.
|
|
160
|
-
const topLevelProc = _gitDeps.spawn(["git", "rev-parse", "--show-toplevel"], {
|
|
161
|
-
cwd: workdir,
|
|
162
|
-
stdout: "pipe",
|
|
163
|
-
stderr: "pipe",
|
|
164
|
-
});
|
|
165
|
-
const gitRoot = (await new Response(topLevelProc.stdout).text()).trim();
|
|
166
|
-
await topLevelProc.exited;
|
|
167
|
-
|
|
168
|
-
// Normalize paths to handle symlinks (e.g. /tmp → /private/tmp on macOS)
|
|
169
|
-
const { realpathSync } = await import("node:fs");
|
|
170
|
-
const realWorkdir = (() => {
|
|
171
|
-
try {
|
|
172
|
-
return realpathSync(workdir);
|
|
173
|
-
} catch {
|
|
174
|
-
return workdir;
|
|
175
|
-
}
|
|
176
|
-
})();
|
|
177
|
-
const realGitRoot = (() => {
|
|
178
|
-
try {
|
|
179
|
-
return realpathSync(gitRoot);
|
|
180
|
-
} catch {
|
|
181
|
-
return gitRoot;
|
|
182
|
-
}
|
|
183
|
-
})();
|
|
184
|
-
// Allow: workdir IS the git root, or workdir is a subdirectory (monorepo package)
|
|
185
|
-
// Reject: workdir has no git repo at all (realGitRoot would be empty/error)
|
|
186
|
-
const isAtRoot = realWorkdir === realGitRoot;
|
|
187
|
-
const isSubdir = realGitRoot && realWorkdir.startsWith(`${realGitRoot}/`);
|
|
188
|
-
if (!isAtRoot && !isSubdir) return;
|
|
189
|
-
|
|
190
|
-
const statusProc = _gitDeps.spawn(["git", "status", "--porcelain"], {
|
|
191
|
-
cwd: workdir,
|
|
192
|
-
stdout: "pipe",
|
|
193
|
-
stderr: "pipe",
|
|
194
|
-
});
|
|
195
|
-
const statusOutput = await new Response(statusProc.stdout).text();
|
|
196
|
-
await statusProc.exited;
|
|
197
|
-
|
|
198
|
-
if (!statusOutput.trim()) return;
|
|
199
|
-
|
|
200
|
-
logger?.warn(stage, `Agent did not commit after ${role} session — auto-committing`, {
|
|
201
|
-
role,
|
|
202
|
-
storyId,
|
|
203
|
-
dirtyFiles: statusOutput.trim().split("\n").length,
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// Use "git add ." when workdir is a monorepo package subdir — only stages files under
|
|
207
|
-
// that package, preventing accidental cross-package commits.
|
|
208
|
-
// Use "git add -A" at repo root to capture renames/deletions across the full tree.
|
|
209
|
-
const addArgs = isSubdir ? ["git", "add", "."] : ["git", "add", "-A"];
|
|
210
|
-
const addProc = _gitDeps.spawn(addArgs, { cwd: workdir, stdout: "pipe", stderr: "pipe" });
|
|
211
|
-
await addProc.exited;
|
|
212
|
-
|
|
213
|
-
const commitProc = _gitDeps.spawn(["git", "commit", "-m", `chore(${storyId}): auto-commit after ${role} session`], {
|
|
214
|
-
cwd: workdir,
|
|
215
|
-
stdout: "pipe",
|
|
216
|
-
stderr: "pipe",
|
|
217
|
-
});
|
|
218
|
-
await commitProc.exited;
|
|
219
|
-
} catch {
|
|
220
|
-
// Silently ignore — auto-commit is best-effort
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Capture files changed since a given git ref (for context chaining, ENH-005).
|
|
226
|
-
* Scopes to scopePrefix (story.workdir) when set — prevents cross-package bleeding in monorepos.
|
|
227
|
-
* Returns empty array if baseRef is falsy or git fails.
|
|
228
|
-
*/
|
|
229
|
-
export async function captureOutputFiles(
|
|
230
|
-
workdir: string,
|
|
231
|
-
baseRef: string | undefined,
|
|
232
|
-
scopePrefix?: string,
|
|
233
|
-
): Promise<string[]> {
|
|
234
|
-
if (!baseRef) return [];
|
|
235
|
-
try {
|
|
236
|
-
const args = ["diff", "--name-only", `${baseRef}..HEAD`];
|
|
237
|
-
if (scopePrefix) args.push("--", `${scopePrefix}/`);
|
|
238
|
-
const proc = _gitDeps.spawn(["git", ...args], { cwd: workdir, stdout: "pipe", stderr: "pipe" });
|
|
239
|
-
const output = await new Response(proc.stdout).text();
|
|
240
|
-
await proc.exited;
|
|
241
|
-
return output.trim().split("\n").filter(Boolean);
|
|
242
|
-
} catch {
|
|
243
|
-
return [];
|
|
244
|
-
}
|
|
245
|
-
}
|
package/src/utils/json-file.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared JSON File I/O Utility
|
|
3
|
-
*
|
|
4
|
-
* Provides type-safe, error-tolerant helpers for reading and writing JSON files.
|
|
5
|
-
* Encapsulates common patterns: existsSync check, try/catch, logging.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { existsSync } from "node:fs";
|
|
9
|
-
import { getLogger } from "../logger";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Load a JSON file with type safety and error handling.
|
|
13
|
-
*
|
|
14
|
-
* Returns null if the file doesn't exist or cannot be parsed.
|
|
15
|
-
* Logs a warning if parsing fails.
|
|
16
|
-
*
|
|
17
|
-
* @param path - File path to load
|
|
18
|
-
* @param context - Logger context (e.g., "config", "hooks", "metrics")
|
|
19
|
-
* @returns Parsed JSON object, or null if file missing or invalid
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```ts
|
|
23
|
-
* const config = await loadJsonFile<NaxConfig>("nax/config.json", "config");
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export async function loadJsonFile<T>(path: string, context = "json-file"): Promise<T | null> {
|
|
27
|
-
if (!existsSync(path)) {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const content = await Bun.file(path).json();
|
|
33
|
-
return content as T;
|
|
34
|
-
} catch (err) {
|
|
35
|
-
const logger = getLogger();
|
|
36
|
-
logger.warn(context, "Failed to parse JSON file", {
|
|
37
|
-
path,
|
|
38
|
-
error: String(err),
|
|
39
|
-
});
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Save an object as JSON to a file.
|
|
46
|
-
*
|
|
47
|
-
* Writes formatted JSON (2-space indent) for readability.
|
|
48
|
-
* Creates parent directories if they don't exist.
|
|
49
|
-
*
|
|
50
|
-
* @param path - File path to write to
|
|
51
|
-
* @param data - Object to serialize
|
|
52
|
-
* @param context - Logger context (for errors)
|
|
53
|
-
* @throws Error if write fails
|
|
54
|
-
*
|
|
55
|
-
* @example
|
|
56
|
-
* ```ts
|
|
57
|
-
* await saveJsonFile("nax/config.json", config, "config");
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
export async function saveJsonFile<T>(path: string, data: T, context = "json-file"): Promise<void> {
|
|
61
|
-
try {
|
|
62
|
-
const json = JSON.stringify(data, null, 2);
|
|
63
|
-
await Bun.write(path, json);
|
|
64
|
-
} catch (err) {
|
|
65
|
-
const logger = getLogger();
|
|
66
|
-
logger.error(context, "Failed to write JSON file", {
|
|
67
|
-
path,
|
|
68
|
-
error: String(err),
|
|
69
|
-
});
|
|
70
|
-
throw err;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { Logger } from "../logger";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Log test output consistently across all pipeline stages.
|
|
5
|
-
*
|
|
6
|
-
* Summary (exitCode, storyId) is logged at the caller's level (error/warn).
|
|
7
|
-
* Raw output is logged at debug level only — last `tailLines` lines.
|
|
8
|
-
*
|
|
9
|
-
* `storyId` is optional: works for per-story verify/acceptance AND for
|
|
10
|
-
* deferred runs (deferred acceptance, deferred regression) with no story context.
|
|
11
|
-
*/
|
|
12
|
-
export function logTestOutput(
|
|
13
|
-
logger: Logger | null | undefined,
|
|
14
|
-
stage: string,
|
|
15
|
-
output: string | undefined,
|
|
16
|
-
opts: { storyId?: string; tailLines?: number } = {},
|
|
17
|
-
): void {
|
|
18
|
-
if (!logger || !output) return;
|
|
19
|
-
const tailLines = opts.tailLines ?? 20;
|
|
20
|
-
const lines = output.split("\n").slice(-tailLines).join("\n");
|
|
21
|
-
logger.debug(stage, "Test output (tail)", {
|
|
22
|
-
...(opts.storyId !== undefined && { storyId: opts.storyId }),
|
|
23
|
-
output: lines,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Path security utilities for nax (SEC-1, SEC-2).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { realpathSync } from "node:fs";
|
|
6
|
-
import { dirname, isAbsolute, join, normalize, resolve } from "node:path";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Result of a path validation.
|
|
10
|
-
*/
|
|
11
|
-
export interface PathValidationResult {
|
|
12
|
-
/** Whether the path is valid and allowed */
|
|
13
|
-
valid: boolean;
|
|
14
|
-
/** The absolute, normalized path (if valid) */
|
|
15
|
-
absolutePath?: string;
|
|
16
|
-
/** Error message if invalid */
|
|
17
|
-
error?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Validates that a module path is within an allowed root directory.
|
|
22
|
-
*
|
|
23
|
-
* @param modulePath - The user-provided path to validate (relative or absolute)
|
|
24
|
-
* @param allowedRoots - Array of absolute paths that are allowed as roots
|
|
25
|
-
* @returns Validation result
|
|
26
|
-
*/
|
|
27
|
-
/** Resolve symlinks for a path that may not exist yet (fall back to parent dir). */
|
|
28
|
-
function safeRealpath(p: string): string {
|
|
29
|
-
try {
|
|
30
|
-
return realpathSync(p);
|
|
31
|
-
} catch {
|
|
32
|
-
// Path doesn't exist — resolve the parent directory instead
|
|
33
|
-
try {
|
|
34
|
-
const parent = realpathSync(dirname(p));
|
|
35
|
-
return join(parent, p.split("/").pop() ?? "");
|
|
36
|
-
} catch {
|
|
37
|
-
return p;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function validateModulePath(modulePath: string, allowedRoots: string[]): PathValidationResult {
|
|
43
|
-
if (!modulePath) {
|
|
44
|
-
return { valid: false, error: "Module path is empty" };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Resolve symlinks in each root
|
|
48
|
-
const normalizedRoots = allowedRoots.map((r) => safeRealpath(resolve(r)));
|
|
49
|
-
|
|
50
|
-
// If absolute, just check against roots
|
|
51
|
-
if (isAbsolute(modulePath)) {
|
|
52
|
-
const absoluteTarget = safeRealpath(normalize(modulePath));
|
|
53
|
-
const isWithin = normalizedRoots.some((root) => {
|
|
54
|
-
return absoluteTarget.startsWith(`${root}/`) || absoluteTarget === root;
|
|
55
|
-
});
|
|
56
|
-
if (isWithin) {
|
|
57
|
-
return { valid: true, absolutePath: absoluteTarget };
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
// If relative, check if it's within any root when resolved relative to that root
|
|
61
|
-
for (const root of normalizedRoots) {
|
|
62
|
-
const absoluteTarget = safeRealpath(resolve(join(root, modulePath)));
|
|
63
|
-
if (absoluteTarget.startsWith(`${root}/`) || absoluteTarget === root) {
|
|
64
|
-
return { valid: true, absolutePath: absoluteTarget };
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
valid: false,
|
|
71
|
-
error: `Path "${modulePath}" is outside allowed roots`,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Queue Writer Utility
|
|
3
|
-
*
|
|
4
|
-
* Writes queue commands (PAUSE/ABORT/SKIP) to the queue file.
|
|
5
|
-
* Used by the TUI to translate keyboard shortcuts into queue commands.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { QueueCommand } from "../queue/types";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Write a queue command to the queue file.
|
|
12
|
-
*
|
|
13
|
-
* Appends the command to the queue file in the format expected by parseQueueFile:
|
|
14
|
-
* - PAUSE
|
|
15
|
-
* - ABORT
|
|
16
|
-
* - SKIP <story-id>
|
|
17
|
-
*
|
|
18
|
-
* The queue file is checked by the execution runner between stories.
|
|
19
|
-
*
|
|
20
|
-
* @param queueFilePath - Path to the queue file
|
|
21
|
-
* @param command - Queue command to write
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```typescript
|
|
25
|
-
* await writeQueueCommand("/tmp/nax/queue.txt", { type: "PAUSE" });
|
|
26
|
-
* await writeQueueCommand("/tmp/nax/queue.txt", { type: "SKIP", storyId: "US-003" });
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export async function writeQueueCommand(queueFilePath: string, command: QueueCommand): Promise<void> {
|
|
30
|
-
let commandLine: string;
|
|
31
|
-
|
|
32
|
-
switch (command.type) {
|
|
33
|
-
case "PAUSE":
|
|
34
|
-
commandLine = "PAUSE";
|
|
35
|
-
break;
|
|
36
|
-
case "ABORT":
|
|
37
|
-
commandLine = "ABORT";
|
|
38
|
-
break;
|
|
39
|
-
case "SKIP":
|
|
40
|
-
commandLine = `SKIP ${command.storyId}`;
|
|
41
|
-
break;
|
|
42
|
-
default: {
|
|
43
|
-
const _exhaustive: never = command;
|
|
44
|
-
throw new Error(`Unhandled queue command: ${_exhaustive}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Append command to queue file (create if doesn't exist)
|
|
49
|
-
const file = Bun.file(queueFilePath);
|
|
50
|
-
const existingContent = await file.text().catch(() => "");
|
|
51
|
-
const newContent = existingContent ? `${existingContent.trimEnd()}\n${commandLine}\n` : `${commandLine}\n`;
|
|
52
|
-
|
|
53
|
-
await Bun.write(queueFilePath, newContent);
|
|
54
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime Crash Detector — BUG-070
|
|
3
|
-
*
|
|
4
|
-
* Detects Bun runtime crashes in test output so they can be classified as
|
|
5
|
-
* RUNTIME_CRASH rather than TEST_FAILURE, preventing spurious tier escalation.
|
|
6
|
-
*
|
|
7
|
-
* STUB — implementation is intentionally absent. Tests are RED until
|
|
8
|
-
* the real logic is written.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Known patterns emitted by the Bun runtime before any test results
|
|
13
|
-
* when a crash occurs (segfault, panic, etc.).
|
|
14
|
-
*/
|
|
15
|
-
export const CRASH_PATTERNS = [
|
|
16
|
-
"panic(main thread)",
|
|
17
|
-
"Segmentation fault",
|
|
18
|
-
"Bun has crashed",
|
|
19
|
-
"oh no: Bun has crashed",
|
|
20
|
-
] as const;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Detect whether the given test runner output contains a Bun runtime crash.
|
|
24
|
-
*
|
|
25
|
-
* Returns true if any known crash pattern is found in the output.
|
|
26
|
-
* These patterns are emitted by Bun itself before any test result lines.
|
|
27
|
-
*
|
|
28
|
-
* @param output - Raw stdout/stderr from the test runner
|
|
29
|
-
*/
|
|
30
|
-
export function detectRuntimeCrash(output: string | undefined | null): boolean {
|
|
31
|
-
// STUB: not implemented yet — always returns false
|
|
32
|
-
if (!output) return false;
|
|
33
|
-
return CRASH_PATTERNS.some((pattern) => output.includes(pattern));
|
|
34
|
-
}
|