@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,275 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PID Registry — Track and cleanup spawned agent processes
|
|
3
|
-
*
|
|
4
|
-
* Implements BUG-002:
|
|
5
|
-
* - Track PIDs of spawned Claude Code processes
|
|
6
|
-
* - Write .nax-pids file for persistence across crashes
|
|
7
|
-
* - Support killAll() for crash signal handlers
|
|
8
|
-
* - Support cleanupStale() for startup cleanup
|
|
9
|
-
* - Use process groups (setsid) on Linux, direct kill on macOS
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { existsSync } from "node:fs";
|
|
13
|
-
import { appendFile } from "node:fs/promises";
|
|
14
|
-
import { getSafeLogger } from "../logger";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* PID registry file name
|
|
18
|
-
*/
|
|
19
|
-
const PID_REGISTRY_FILE = ".nax-pids";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* PID registry entry
|
|
23
|
-
*/
|
|
24
|
-
interface PidEntry {
|
|
25
|
-
pid: number;
|
|
26
|
-
spawnedAt: string;
|
|
27
|
-
workdir: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* PID Registry — Track spawned agent processes and cleanup orphans
|
|
32
|
-
*
|
|
33
|
-
* Maintains a .nax-pids file in the workdir to track spawned processes.
|
|
34
|
-
* On crash, signal handlers call killAll() to terminate all tracked PIDs.
|
|
35
|
-
* On startup, runner calls cleanupStale() to kill any orphaned processes from previous runs.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```ts
|
|
39
|
-
* const registry = new PidRegistry("/path/to/project");
|
|
40
|
-
* await registry.register(12345);
|
|
41
|
-
* // ... later, on crash or shutdown
|
|
42
|
-
* await registry.killAll();
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
export class PidRegistry {
|
|
46
|
-
private readonly workdir: string;
|
|
47
|
-
private readonly pidsFilePath: string;
|
|
48
|
-
private readonly pids: Set<number> = new Set();
|
|
49
|
-
private readonly platform: NodeJS.Platform;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Create a new PID registry for the given workdir.
|
|
53
|
-
*
|
|
54
|
-
* @param workdir - Working directory where .nax-pids will be stored
|
|
55
|
-
* @param platform - Optional platform override (for testing)
|
|
56
|
-
*/
|
|
57
|
-
constructor(workdir: string, platform?: NodeJS.Platform) {
|
|
58
|
-
this.workdir = workdir;
|
|
59
|
-
this.pidsFilePath = `${workdir}/${PID_REGISTRY_FILE}`;
|
|
60
|
-
this.platform = platform ?? process.platform;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Register a spawned process PID.
|
|
65
|
-
*
|
|
66
|
-
* Adds the PID to the in-memory set and writes to .nax-pids file.
|
|
67
|
-
*
|
|
68
|
-
* @param pid - Process ID to register
|
|
69
|
-
*/
|
|
70
|
-
async register(pid: number): Promise<void> {
|
|
71
|
-
const logger = getSafeLogger();
|
|
72
|
-
this.pids.add(pid);
|
|
73
|
-
|
|
74
|
-
const entry: PidEntry = {
|
|
75
|
-
pid,
|
|
76
|
-
spawnedAt: new Date().toISOString(),
|
|
77
|
-
workdir: this.workdir,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
// Atomically append to .nax-pids file (one JSON entry per line)
|
|
82
|
-
const line = `${JSON.stringify(entry)}\n`;
|
|
83
|
-
await appendFile(this.pidsFilePath, line);
|
|
84
|
-
logger?.debug("pid-registry", `Registered PID ${pid}`, { pid });
|
|
85
|
-
} catch (err) {
|
|
86
|
-
logger?.warn("pid-registry", `Failed to write PID ${pid} to registry`, {
|
|
87
|
-
error: (err as Error).message,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Unregister a process PID (e.g., after clean exit).
|
|
94
|
-
*
|
|
95
|
-
* Removes the PID from the in-memory set and rewrites .nax-pids file.
|
|
96
|
-
*
|
|
97
|
-
* @param pid - Process ID to unregister
|
|
98
|
-
*/
|
|
99
|
-
async unregister(pid: number): Promise<void> {
|
|
100
|
-
const logger = getSafeLogger();
|
|
101
|
-
this.pids.delete(pid);
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
// Rewrite .nax-pids file without the unregistered PID
|
|
105
|
-
await this.writePidsFile();
|
|
106
|
-
logger?.debug("pid-registry", `Unregistered PID ${pid}`, { pid });
|
|
107
|
-
} catch (err) {
|
|
108
|
-
logger?.warn("pid-registry", `Failed to unregister PID ${pid}`, {
|
|
109
|
-
error: (err as Error).message,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Kill all registered processes.
|
|
116
|
-
*
|
|
117
|
-
* Called by crash signal handlers to cleanup spawned agent processes.
|
|
118
|
-
* Uses process groups (setsid) on Linux, direct kill on macOS.
|
|
119
|
-
*
|
|
120
|
-
* On Linux: kill -TERM -<pid> kills the entire process group
|
|
121
|
-
* On macOS: kill -TERM <pid> kills the process directly
|
|
122
|
-
*/
|
|
123
|
-
async killAll(): Promise<void> {
|
|
124
|
-
const logger = getSafeLogger();
|
|
125
|
-
const pids = Array.from(this.pids);
|
|
126
|
-
|
|
127
|
-
if (pids.length === 0) {
|
|
128
|
-
logger?.debug("pid-registry", "No PIDs to kill");
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
logger?.info("pid-registry", `Killing ${pids.length} registered processes`, { pids });
|
|
133
|
-
|
|
134
|
-
const killPromises = pids.map((pid) => this.killPid(pid));
|
|
135
|
-
await Promise.allSettled(killPromises);
|
|
136
|
-
|
|
137
|
-
// Clear the registry file
|
|
138
|
-
try {
|
|
139
|
-
await Bun.write(this.pidsFilePath, "");
|
|
140
|
-
this.pids.clear();
|
|
141
|
-
logger?.info("pid-registry", "All registered PIDs killed and registry cleared");
|
|
142
|
-
} catch (err) {
|
|
143
|
-
logger?.warn("pid-registry", "Failed to clear registry file", {
|
|
144
|
-
error: (err as Error).message,
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Cleanup stale PIDs from previous runs.
|
|
151
|
-
*
|
|
152
|
-
* Called at runner startup before lock acquisition.
|
|
153
|
-
* Reads .nax-pids file and kills any still-running processes.
|
|
154
|
-
*/
|
|
155
|
-
async cleanupStale(): Promise<void> {
|
|
156
|
-
const logger = getSafeLogger();
|
|
157
|
-
|
|
158
|
-
if (!existsSync(this.pidsFilePath)) {
|
|
159
|
-
logger?.debug("pid-registry", "No stale PIDs file found");
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
const content = await Bun.file(this.pidsFilePath).text();
|
|
165
|
-
const lines = content
|
|
166
|
-
.split("\n")
|
|
167
|
-
.filter((line) => line.trim())
|
|
168
|
-
.map((line) => {
|
|
169
|
-
try {
|
|
170
|
-
return JSON.parse(line) as PidEntry;
|
|
171
|
-
} catch {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
})
|
|
175
|
-
.filter((entry): entry is PidEntry => entry !== null);
|
|
176
|
-
|
|
177
|
-
if (lines.length === 0) {
|
|
178
|
-
logger?.debug("pid-registry", "No stale PIDs to cleanup");
|
|
179
|
-
await Bun.write(this.pidsFilePath, "");
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const stalePids = lines.map((entry) => entry.pid);
|
|
184
|
-
logger?.info("pid-registry", `Cleaning up ${stalePids.length} stale PIDs from previous run`, {
|
|
185
|
-
pids: stalePids,
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
const killPromises = stalePids.map((pid) => this.killPid(pid));
|
|
189
|
-
await Promise.allSettled(killPromises);
|
|
190
|
-
|
|
191
|
-
// Clear the registry file after cleanup
|
|
192
|
-
await Bun.write(this.pidsFilePath, "");
|
|
193
|
-
logger?.info("pid-registry", "Stale PIDs cleanup completed");
|
|
194
|
-
} catch (err) {
|
|
195
|
-
logger?.warn("pid-registry", "Failed to cleanup stale PIDs", {
|
|
196
|
-
error: (err as Error).message,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Kill a single PID.
|
|
203
|
-
*
|
|
204
|
-
* Uses process groups on Linux (kill -TERM -<pid>), direct kill on macOS (kill -TERM <pid>).
|
|
205
|
-
* Ignores ESRCH (process not found) errors.
|
|
206
|
-
*
|
|
207
|
-
* @param pid - Process ID to kill
|
|
208
|
-
*/
|
|
209
|
-
private async killPid(pid: number): Promise<void> {
|
|
210
|
-
const logger = getSafeLogger();
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
// Check if process exists first
|
|
214
|
-
const checkProc = Bun.spawn(["kill", "-0", String(pid)], {
|
|
215
|
-
stdout: "pipe",
|
|
216
|
-
stderr: "pipe",
|
|
217
|
-
});
|
|
218
|
-
const checkCode = await checkProc.exited;
|
|
219
|
-
|
|
220
|
-
if (checkCode !== 0) {
|
|
221
|
-
// Process doesn't exist, skip
|
|
222
|
-
logger?.debug("pid-registry", `PID ${pid} not found (already exited)`, { pid });
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// On Linux, use process groups (kill -TERM -<pid>)
|
|
227
|
-
// On macOS, use direct kill (kill -TERM <pid>)
|
|
228
|
-
const killArgs = this.platform === "linux" ? ["kill", "-TERM", `-${pid}`] : ["kill", "-TERM", String(pid)];
|
|
229
|
-
|
|
230
|
-
const killProc = Bun.spawn(killArgs, {
|
|
231
|
-
stdout: "pipe",
|
|
232
|
-
stderr: "pipe",
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
const killCode = await killProc.exited;
|
|
236
|
-
|
|
237
|
-
if (killCode === 0) {
|
|
238
|
-
logger?.debug("pid-registry", `Killed PID ${pid}`, { pid });
|
|
239
|
-
} else {
|
|
240
|
-
const stderr = await new Response(killProc.stderr).text();
|
|
241
|
-
logger?.warn("pid-registry", `Failed to kill PID ${pid}`, {
|
|
242
|
-
pid,
|
|
243
|
-
exitCode: killCode,
|
|
244
|
-
stderr: stderr.trim(),
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
} catch (err) {
|
|
248
|
-
logger?.warn("pid-registry", `Error killing PID ${pid}`, {
|
|
249
|
-
pid,
|
|
250
|
-
error: (err as Error).message,
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Rewrite .nax-pids file with current in-memory PIDs.
|
|
257
|
-
*/
|
|
258
|
-
private async writePidsFile(): Promise<void> {
|
|
259
|
-
const entries = Array.from(this.pids).map((pid) => ({
|
|
260
|
-
pid,
|
|
261
|
-
spawnedAt: new Date().toISOString(),
|
|
262
|
-
workdir: this.workdir,
|
|
263
|
-
}));
|
|
264
|
-
|
|
265
|
-
const content = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
266
|
-
await Bun.write(this.pidsFilePath, content ? `${content}\n` : "");
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Get all registered PIDs (for testing)
|
|
271
|
-
*/
|
|
272
|
-
getPids(): number[] {
|
|
273
|
-
return Array.from(this.pids);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pipeline Result Handlers (ADR-005, Phase 4)
|
|
3
|
-
*
|
|
4
|
-
* Handles pipeline success, failure outcomes after story execution.
|
|
5
|
-
* Dry-run handling: see execution/dry-run.ts
|
|
6
|
-
* applyCachedRouting: removed (P4-001 — pipeline routing stage is sole source)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { NaxConfig } from "../config";
|
|
10
|
-
import type { LoadedHooksConfig } from "../hooks";
|
|
11
|
-
import type { InteractionChain } from "../interaction/chain";
|
|
12
|
-
import { getSafeLogger } from "../logger";
|
|
13
|
-
import type { StoryMetrics } from "../metrics";
|
|
14
|
-
import { pipelineEventBus } from "../pipeline/event-bus";
|
|
15
|
-
import type { PipelineRunResult } from "../pipeline/runner";
|
|
16
|
-
import type { PluginRegistry } from "../plugins";
|
|
17
|
-
import { countStories, markStoryFailed, markStoryPaused, savePRD } from "../prd";
|
|
18
|
-
import type { PRD, UserStory } from "../prd/types";
|
|
19
|
-
import type { routeTask } from "../routing";
|
|
20
|
-
import { captureOutputFiles } from "../utils/git";
|
|
21
|
-
import { handleTierEscalation } from "./escalation";
|
|
22
|
-
import { appendProgress } from "./progress";
|
|
23
|
-
|
|
24
|
-
/** Filter noise from output files (test files, lock files, nax runtime files) */
|
|
25
|
-
function filterOutputFiles(files: string[]): string[] {
|
|
26
|
-
const NOISE = [
|
|
27
|
-
/\.test\.(ts|js|tsx|jsx)$/,
|
|
28
|
-
/\.spec\.(ts|js|tsx|jsx)$/,
|
|
29
|
-
/package-lock\.json$/,
|
|
30
|
-
/bun\.lock(b?)$/,
|
|
31
|
-
/\.gitignore$/,
|
|
32
|
-
/^nax\//,
|
|
33
|
-
];
|
|
34
|
-
return files.filter((f) => !NOISE.some((p) => p.test(f))).slice(0, 15);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface PipelineHandlerContext {
|
|
38
|
-
config: NaxConfig;
|
|
39
|
-
prd: PRD;
|
|
40
|
-
prdPath: string;
|
|
41
|
-
workdir: string;
|
|
42
|
-
featureDir?: string;
|
|
43
|
-
hooks: LoadedHooksConfig;
|
|
44
|
-
feature: string;
|
|
45
|
-
totalCost: number;
|
|
46
|
-
startTime: number;
|
|
47
|
-
runId: string;
|
|
48
|
-
pluginRegistry: PluginRegistry;
|
|
49
|
-
story: UserStory;
|
|
50
|
-
storiesToExecute: UserStory[];
|
|
51
|
-
routing: ReturnType<typeof routeTask>;
|
|
52
|
-
isBatchExecution: boolean;
|
|
53
|
-
allStoryMetrics: StoryMetrics[];
|
|
54
|
-
storyGitRef: string | null | undefined;
|
|
55
|
-
interactionChain?: InteractionChain | null;
|
|
56
|
-
storyStartTime?: number;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface PipelineSuccessResult {
|
|
60
|
-
storiesCompletedDelta: number;
|
|
61
|
-
costDelta: number;
|
|
62
|
-
prd: PRD;
|
|
63
|
-
prdDirty: boolean;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export async function handlePipelineSuccess(
|
|
67
|
-
ctx: PipelineHandlerContext,
|
|
68
|
-
pipelineResult: PipelineRunResult,
|
|
69
|
-
): Promise<PipelineSuccessResult> {
|
|
70
|
-
const logger = getSafeLogger();
|
|
71
|
-
const costDelta = pipelineResult.context.agentResult?.estimatedCost || 0;
|
|
72
|
-
const prd = ctx.prd;
|
|
73
|
-
|
|
74
|
-
if (pipelineResult.context.storyMetrics) {
|
|
75
|
-
ctx.allStoryMetrics.push(...pipelineResult.context.storyMetrics);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const storiesCompletedDelta = ctx.storiesToExecute.length;
|
|
79
|
-
for (const completedStory of ctx.storiesToExecute) {
|
|
80
|
-
const now = Date.now();
|
|
81
|
-
logger?.info("story.complete", "Story completed successfully", {
|
|
82
|
-
storyId: completedStory.id,
|
|
83
|
-
storyTitle: completedStory.title,
|
|
84
|
-
totalCost: ctx.totalCost + costDelta,
|
|
85
|
-
runElapsedMs: now - ctx.startTime,
|
|
86
|
-
storyDurationMs: ctx.storyStartTime ? now - ctx.storyStartTime : undefined,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
pipelineEventBus.emit({
|
|
90
|
-
type: "story:completed",
|
|
91
|
-
storyId: completedStory.id,
|
|
92
|
-
story: completedStory,
|
|
93
|
-
passed: true,
|
|
94
|
-
runElapsedMs: Date.now() - ctx.startTime,
|
|
95
|
-
cost: costDelta,
|
|
96
|
-
modelTier: ctx.routing.modelTier,
|
|
97
|
-
testStrategy: ctx.routing.testStrategy,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ENH-005: Capture output files for context chaining
|
|
102
|
-
if (ctx.storyGitRef) {
|
|
103
|
-
for (const completedStory of ctx.storiesToExecute) {
|
|
104
|
-
try {
|
|
105
|
-
const rawFiles = await captureOutputFiles(ctx.workdir, ctx.storyGitRef, completedStory.workdir);
|
|
106
|
-
const filtered = filterOutputFiles(rawFiles);
|
|
107
|
-
if (filtered.length > 0) {
|
|
108
|
-
completedStory.outputFiles = filtered;
|
|
109
|
-
}
|
|
110
|
-
} catch {
|
|
111
|
-
// Non-fatal — context chaining is best-effort
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const updatedCounts = countStories(prd);
|
|
117
|
-
logger?.info("progress", "Progress update", {
|
|
118
|
-
totalStories: updatedCounts.total,
|
|
119
|
-
passedStories: updatedCounts.passed,
|
|
120
|
-
failedStories: updatedCounts.failed,
|
|
121
|
-
pendingStories: updatedCounts.pending,
|
|
122
|
-
totalCost: ctx.totalCost + costDelta,
|
|
123
|
-
costLimit: ctx.config.execution.costLimit,
|
|
124
|
-
elapsedMs: Date.now() - ctx.startTime,
|
|
125
|
-
storyDurationMs: ctx.storyStartTime ? Date.now() - ctx.storyStartTime : undefined,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
return { storiesCompletedDelta, costDelta, prd, prdDirty: true };
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export interface PipelineFailureResult {
|
|
132
|
-
prd: PRD;
|
|
133
|
-
prdDirty: boolean;
|
|
134
|
-
costDelta: number;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export async function handlePipelineFailure(
|
|
138
|
-
ctx: PipelineHandlerContext,
|
|
139
|
-
pipelineResult: PipelineRunResult,
|
|
140
|
-
): Promise<PipelineFailureResult> {
|
|
141
|
-
const logger = getSafeLogger();
|
|
142
|
-
let prd = ctx.prd;
|
|
143
|
-
let prdDirty = false;
|
|
144
|
-
// Always capture cost even for failed stories — agent ran and spent tokens
|
|
145
|
-
const costDelta = pipelineResult.context.agentResult?.estimatedCost || 0;
|
|
146
|
-
|
|
147
|
-
switch (pipelineResult.finalAction) {
|
|
148
|
-
case "pause":
|
|
149
|
-
markStoryPaused(prd, ctx.story.id);
|
|
150
|
-
await savePRD(prd, ctx.prdPath);
|
|
151
|
-
prdDirty = true;
|
|
152
|
-
logger?.warn("pipeline", "Story paused", { storyId: ctx.story.id, reason: pipelineResult.reason });
|
|
153
|
-
pipelineEventBus.emit({
|
|
154
|
-
type: "story:paused",
|
|
155
|
-
storyId: ctx.story.id,
|
|
156
|
-
reason: pipelineResult.reason || "Pipeline paused",
|
|
157
|
-
cost: ctx.totalCost,
|
|
158
|
-
});
|
|
159
|
-
break;
|
|
160
|
-
|
|
161
|
-
case "skip":
|
|
162
|
-
logger?.warn("pipeline", "Story skipped", { storyId: ctx.story.id, reason: pipelineResult.reason });
|
|
163
|
-
prdDirty = true;
|
|
164
|
-
break;
|
|
165
|
-
|
|
166
|
-
case "fail":
|
|
167
|
-
markStoryFailed(prd, ctx.story.id, pipelineResult.context.tddFailureCategory, pipelineResult.stoppedAtStage);
|
|
168
|
-
await savePRD(prd, ctx.prdPath);
|
|
169
|
-
prdDirty = true;
|
|
170
|
-
logger?.error("pipeline", "Story failed", { storyId: ctx.story.id, reason: pipelineResult.reason });
|
|
171
|
-
|
|
172
|
-
if (ctx.featureDir) {
|
|
173
|
-
await appendProgress(ctx.featureDir, ctx.story.id, "failed", `${ctx.story.title} — ${pipelineResult.reason}`);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
pipelineEventBus.emit({
|
|
177
|
-
type: "story:failed",
|
|
178
|
-
storyId: ctx.story.id,
|
|
179
|
-
story: ctx.story,
|
|
180
|
-
reason: pipelineResult.reason || "Pipeline failed",
|
|
181
|
-
countsTowardEscalation: true,
|
|
182
|
-
feature: ctx.feature,
|
|
183
|
-
attempts: ctx.story.attempts,
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
if (ctx.story.attempts !== undefined && ctx.story.attempts >= ctx.config.execution.rectification.maxRetries) {
|
|
187
|
-
await pipelineEventBus.emitAsync({
|
|
188
|
-
type: "human-review:requested",
|
|
189
|
-
storyId: ctx.story.id,
|
|
190
|
-
reason: pipelineResult.reason || "Max retries exceeded",
|
|
191
|
-
feature: ctx.feature,
|
|
192
|
-
attempts: ctx.story.attempts,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
break;
|
|
196
|
-
|
|
197
|
-
case "escalate": {
|
|
198
|
-
const escalationResult = await handleTierEscalation({
|
|
199
|
-
story: ctx.story,
|
|
200
|
-
storiesToExecute: ctx.storiesToExecute,
|
|
201
|
-
isBatchExecution: ctx.isBatchExecution,
|
|
202
|
-
routing: ctx.routing,
|
|
203
|
-
pipelineResult,
|
|
204
|
-
config: ctx.config,
|
|
205
|
-
prd,
|
|
206
|
-
prdPath: ctx.prdPath,
|
|
207
|
-
featureDir: ctx.featureDir,
|
|
208
|
-
hooks: ctx.hooks,
|
|
209
|
-
feature: ctx.feature,
|
|
210
|
-
totalCost: ctx.totalCost,
|
|
211
|
-
workdir: ctx.workdir,
|
|
212
|
-
attemptCost: pipelineResult.context.agentResult?.estimatedCost || 0,
|
|
213
|
-
});
|
|
214
|
-
prd = escalationResult.prd;
|
|
215
|
-
prdDirty = escalationResult.prdDirty;
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return { prd, prdDirty, costDelta };
|
|
221
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Progress Logging
|
|
3
|
-
*
|
|
4
|
-
* Append timestamped entries to progress.txt after story completion.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { mkdirSync } from "node:fs";
|
|
8
|
-
import { join } from "node:path";
|
|
9
|
-
import type { StoryStatus } from "../prd";
|
|
10
|
-
|
|
11
|
-
/** Append a progress entry to progress.txt */
|
|
12
|
-
export async function appendProgress(
|
|
13
|
-
featureDir: string,
|
|
14
|
-
storyId: string,
|
|
15
|
-
status: StoryStatus,
|
|
16
|
-
message: string,
|
|
17
|
-
): Promise<void> {
|
|
18
|
-
mkdirSync(featureDir, { recursive: true });
|
|
19
|
-
const progressPath = join(featureDir, "progress.txt");
|
|
20
|
-
const timestamp = new Date().toISOString();
|
|
21
|
-
const entry = `[${timestamp}] ${storyId} — ${status.toUpperCase()} — ${message}\n`;
|
|
22
|
-
|
|
23
|
-
// Append to file (creates if doesn't exist)
|
|
24
|
-
const file = Bun.file(progressPath);
|
|
25
|
-
const existing = (await file.exists()) ? await file.text() : "";
|
|
26
|
-
await Bun.write(progressPath, existing + entry);
|
|
27
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Queue File Handler
|
|
3
|
-
*
|
|
4
|
-
* Provides atomic read/write operations for .queue.txt command files.
|
|
5
|
-
* Uses rename-before-read pattern to prevent race conditions.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import path from "node:path";
|
|
9
|
-
import { getLogger } from "../logger";
|
|
10
|
-
import { parseQueueFile } from "../queue";
|
|
11
|
-
import type { QueueCommand } from "../queue";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Safely get logger instance, returns null if not initialized
|
|
15
|
-
*/
|
|
16
|
-
function getSafeLogger() {
|
|
17
|
-
try {
|
|
18
|
-
return getLogger();
|
|
19
|
-
} catch {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Read and parse queue file atomically.
|
|
26
|
-
* Uses rename-before-read pattern to prevent race conditions:
|
|
27
|
-
* 1. Rename .queue.txt → .queue.txt.processing (atomic operation)
|
|
28
|
-
* 2. Read from .queue.txt.processing
|
|
29
|
-
* 3. Delete .queue.txt.processing after processing
|
|
30
|
-
*
|
|
31
|
-
* This ensures commands written during processing aren't lost.
|
|
32
|
-
*
|
|
33
|
-
* @param workdir - Working directory containing .queue.txt
|
|
34
|
-
* @returns Array of parsed queue commands, or empty array if no queue file
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```typescript
|
|
38
|
-
* const commands = await readQueueFile("/path/to/project");
|
|
39
|
-
* for (const cmd of commands) {
|
|
40
|
-
* if (cmd.type === "PAUSE") {
|
|
41
|
-
* // Handle pause
|
|
42
|
-
* }
|
|
43
|
-
* }
|
|
44
|
-
* await clearQueueFile("/path/to/project");
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export async function readQueueFile(workdir: string): Promise<QueueCommand[]> {
|
|
48
|
-
const queuePath = path.join(workdir, ".queue.txt");
|
|
49
|
-
const processingPath = path.join(workdir, ".queue.txt.processing");
|
|
50
|
-
const logger = getSafeLogger();
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
// Check if queue file exists
|
|
54
|
-
const file = Bun.file(queuePath);
|
|
55
|
-
const exists = await file.exists();
|
|
56
|
-
if (!exists) {
|
|
57
|
-
return [];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Atomically rename to .processing (prevents concurrent reads)
|
|
61
|
-
try {
|
|
62
|
-
await Bun.spawn(["mv", queuePath, processingPath], { stdout: "pipe" }).exited;
|
|
63
|
-
} catch (error) {
|
|
64
|
-
// File was already moved by another process, or doesn't exist anymore
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Read from processing file
|
|
69
|
-
const processingFile = Bun.file(processingPath);
|
|
70
|
-
const content = await processingFile.text();
|
|
71
|
-
const result = parseQueueFile(content);
|
|
72
|
-
|
|
73
|
-
return result.commands;
|
|
74
|
-
} catch (error) {
|
|
75
|
-
logger?.warn("queue", "Failed to read queue file", {
|
|
76
|
-
error: (error as Error).message,
|
|
77
|
-
});
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Clear queue file after processing commands.
|
|
84
|
-
* Deletes .queue.txt.processing file.
|
|
85
|
-
*
|
|
86
|
-
* @param workdir - Working directory containing .queue.txt.processing
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* ```typescript
|
|
90
|
-
* const commands = await readQueueFile("/path/to/project");
|
|
91
|
-
* // Process commands...
|
|
92
|
-
* await clearQueueFile("/path/to/project");
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
export async function clearQueueFile(workdir: string): Promise<void> {
|
|
96
|
-
const processingPath = path.join(workdir, ".queue.txt.processing");
|
|
97
|
-
const logger = getSafeLogger();
|
|
98
|
-
try {
|
|
99
|
-
const file = Bun.file(processingPath);
|
|
100
|
-
const exists = await file.exists();
|
|
101
|
-
if (exists) {
|
|
102
|
-
await Bun.spawn(["rm", processingPath], { stdout: "pipe" }).exited;
|
|
103
|
-
}
|
|
104
|
-
} catch (error) {
|
|
105
|
-
logger?.warn("queue", "Failed to clear queue file", {
|
|
106
|
-
error: (error as Error).message,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|