ralph-cursor 0.1.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/README.md +724 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +60 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +69 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +162 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/logs.d.ts +3 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +28 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/loop.d.ts +3 -0
- package/dist/commands/loop.d.ts.map +1 -0
- package/dist/commands/loop.js +186 -0
- package/dist/commands/loop.js.map +1 -0
- package/dist/commands/once.d.ts +3 -0
- package/dist/commands/once.d.ts.map +1 -0
- package/dist/commands/once.js +187 -0
- package/dist/commands/once.js.map +1 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +328 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +33 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/task.d.ts +3 -0
- package/dist/commands/task.d.ts.map +1 -0
- package/dist/commands/task.js +161 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/lib/config.d.ts +8 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +48 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/last-run.d.ts +12 -0
- package/dist/lib/last-run.d.ts.map +1 -0
- package/dist/lib/last-run.js +33 -0
- package/dist/lib/last-run.js.map +1 -0
- package/dist/lib/ralph-dir.d.ts +6 -0
- package/dist/lib/ralph-dir.d.ts.map +1 -0
- package/dist/lib/ralph-dir.js +96 -0
- package/dist/lib/ralph-dir.js.map +1 -0
- package/dist/lib/spawn.d.ts +11 -0
- package/dist/lib/spawn.d.ts.map +1 -0
- package/dist/lib/spawn.js +29 -0
- package/dist/lib/spawn.js.map +1 -0
- package/dist/lib/tail.d.ts +9 -0
- package/dist/lib/tail.d.ts.map +1 -0
- package/dist/lib/tail.js +58 -0
- package/dist/lib/tail.js.map +1 -0
- package/dist/loop/index.d.ts +32 -0
- package/dist/loop/index.d.ts.map +1 -0
- package/dist/loop/index.js +165 -0
- package/dist/loop/index.js.map +1 -0
- package/dist/loop/prompt.d.ts +5 -0
- package/dist/loop/prompt.d.ts.map +1 -0
- package/dist/loop/prompt.js +77 -0
- package/dist/loop/prompt.js.map +1 -0
- package/dist/loop/retry.d.ts +22 -0
- package/dist/loop/retry.d.ts.map +1 -0
- package/dist/loop/retry.js +65 -0
- package/dist/loop/retry.js.map +1 -0
- package/dist/parallel/lock.d.ts +7 -0
- package/dist/parallel/lock.d.ts.map +1 -0
- package/dist/parallel/lock.js +110 -0
- package/dist/parallel/lock.js.map +1 -0
- package/dist/parallel/merge.d.ts +5 -0
- package/dist/parallel/merge.d.ts.map +1 -0
- package/dist/parallel/merge.js +39 -0
- package/dist/parallel/merge.js.map +1 -0
- package/dist/parallel/run.d.ts +18 -0
- package/dist/parallel/run.d.ts.map +1 -0
- package/dist/parallel/run.js +407 -0
- package/dist/parallel/run.js.map +1 -0
- package/dist/parallel/worktree.d.ts +12 -0
- package/dist/parallel/worktree.d.ts.map +1 -0
- package/dist/parallel/worktree.js +69 -0
- package/dist/parallel/worktree.js.map +1 -0
- package/dist/stream-parser/index.d.ts +18 -0
- package/dist/stream-parser/index.d.ts.map +1 -0
- package/dist/stream-parser/index.js +248 -0
- package/dist/stream-parser/index.js.map +1 -0
- package/dist/task-parser/index.d.ts +21 -0
- package/dist/task-parser/index.d.ts.map +1 -0
- package/dist/task-parser/index.js +195 -0
- package/dist/task-parser/index.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { createInterface } from "readline";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import { ensureRalphDir, getIteration, setIteration, logProgress } from "../lib/ralph-dir.js";
|
|
4
|
+
import { buildPrompt } from "./prompt.js";
|
|
5
|
+
import { spawnCursorAgent } from "../lib/spawn.js";
|
|
6
|
+
import { createStreamParser } from "../stream-parser/index.js";
|
|
7
|
+
import { checkTaskComplete } from "../task-parser/index.js";
|
|
8
|
+
import { calculateBackoffDelay, sleepMs } from "./retry.js";
|
|
9
|
+
function tryOpenPr(workspace, branch) {
|
|
10
|
+
try {
|
|
11
|
+
execSync(`git push -u origin ${branch}`, { cwd: workspace, stdio: "pipe" });
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
try {
|
|
15
|
+
execSync("git push", { cwd: workspace, stdio: "pipe" });
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// push failed
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
execSync("gh pr create --fill", { cwd: workspace, stdio: "inherit" });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
console.error("⚠️ gh CLI not found or PR create failed. Push complete; create PR manually.");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Run a single agent iteration. Returns the signal (ROTATE, GUTTER, COMPLETE, DEFER) or null when agent exits without signal.
|
|
30
|
+
*/
|
|
31
|
+
export async function runIteration(options) {
|
|
32
|
+
const { workspace, iteration, model, sessionId, warnThreshold = 70_000, rotateThreshold = 80_000, } = options;
|
|
33
|
+
ensureRalphDir(workspace);
|
|
34
|
+
const prompt = buildPrompt(workspace, iteration);
|
|
35
|
+
logProgress(workspace, `**Session ${iteration} started** (model: ${model})`);
|
|
36
|
+
const parser = createStreamParser(workspace, { warnThreshold, rotateThreshold });
|
|
37
|
+
parser.startSession();
|
|
38
|
+
let signal = null;
|
|
39
|
+
parser.onSignal((s) => {
|
|
40
|
+
if (!signal)
|
|
41
|
+
signal = s;
|
|
42
|
+
});
|
|
43
|
+
const child = spawnCursorAgent(prompt, { model, sessionId });
|
|
44
|
+
if (!child.stdout) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const verbose = process.env.RALPH_VERBOSE === "1";
|
|
48
|
+
const spinChars = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏";
|
|
49
|
+
let spinIdx = 0;
|
|
50
|
+
const spinnerInterval = !verbose &&
|
|
51
|
+
setInterval(() => {
|
|
52
|
+
const c = spinChars[spinIdx++ % spinChars.length];
|
|
53
|
+
process.stderr.write(`\r 🐛 Agent working... ${c} (watch: tail -f ${workspace}/.ralph/activity.log) `);
|
|
54
|
+
}, 100);
|
|
55
|
+
const TOKEN_LOG_INTERVAL_MS = 30_000;
|
|
56
|
+
let lastTokenLog = Date.now();
|
|
57
|
+
const rl = createInterface({ input: child.stdout, crlfDelay: Infinity });
|
|
58
|
+
for await (const line of rl) {
|
|
59
|
+
if (verbose && line.trim())
|
|
60
|
+
process.stderr.write(`[ralph] ${line}\n`);
|
|
61
|
+
parser.processLine(line);
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
if (now - lastTokenLog >= TOKEN_LOG_INTERVAL_MS) {
|
|
64
|
+
parser.logTokenStatus();
|
|
65
|
+
lastTokenLog = now;
|
|
66
|
+
}
|
|
67
|
+
if (signal === "ROTATE" || signal === "DEFER") {
|
|
68
|
+
child.kill("SIGTERM");
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (spinnerInterval) {
|
|
73
|
+
clearInterval(spinnerInterval);
|
|
74
|
+
process.stderr.write("\r\x1b[K");
|
|
75
|
+
}
|
|
76
|
+
parser.logTokenStatus();
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
child.on("exit", () => resolve(signal));
|
|
79
|
+
if (signal === "ROTATE" || signal === "DEFER") {
|
|
80
|
+
resolve(signal);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Run the main Ralph loop until COMPLETE, GUTTER, or max iterations.
|
|
86
|
+
*/
|
|
87
|
+
export async function runRalphLoop(options) {
|
|
88
|
+
const { workspace, model, maxIterations, taskFilePath, useBranch, openPr, warnThreshold = 70_000, rotateThreshold = 80_000, } = options;
|
|
89
|
+
ensureRalphDir(workspace);
|
|
90
|
+
try {
|
|
91
|
+
const status = execSync("git status --porcelain", { cwd: workspace, encoding: "utf-8" });
|
|
92
|
+
if (status.trim()) {
|
|
93
|
+
execSync("git add -A", { cwd: workspace });
|
|
94
|
+
execSync("git commit -m \"ralph: initial commit before loop\"", { cwd: workspace });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// not a git repo or nothing to commit
|
|
99
|
+
}
|
|
100
|
+
if (useBranch) {
|
|
101
|
+
try {
|
|
102
|
+
execSync(`git checkout -b ${useBranch}`, { cwd: workspace });
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
execSync(`git checkout ${useBranch}`, { cwd: workspace });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
let iteration = getIteration(workspace) || 1;
|
|
109
|
+
let sessionId;
|
|
110
|
+
let deferAttempt = 0;
|
|
111
|
+
while (iteration <= maxIterations) {
|
|
112
|
+
const signal = await runIteration({
|
|
113
|
+
workspace,
|
|
114
|
+
iteration,
|
|
115
|
+
model,
|
|
116
|
+
sessionId,
|
|
117
|
+
warnThreshold,
|
|
118
|
+
rotateThreshold,
|
|
119
|
+
taskFilePath,
|
|
120
|
+
});
|
|
121
|
+
const taskStatus = checkTaskComplete(workspace, taskFilePath);
|
|
122
|
+
if (taskStatus === "COMPLETE") {
|
|
123
|
+
logProgress(workspace, `**Session ${iteration} ended** - ✅ TASK COMPLETE`);
|
|
124
|
+
setIteration(workspace, iteration);
|
|
125
|
+
if (openPr && useBranch)
|
|
126
|
+
tryOpenPr(workspace, useBranch);
|
|
127
|
+
return { success: true, iterations: iteration };
|
|
128
|
+
}
|
|
129
|
+
if (signal === "COMPLETE" && taskStatus === "COMPLETE") {
|
|
130
|
+
logProgress(workspace, `**Session ${iteration} ended** - ✅ TASK COMPLETE (agent signaled)`);
|
|
131
|
+
setIteration(workspace, iteration);
|
|
132
|
+
if (openPr && useBranch)
|
|
133
|
+
tryOpenPr(workspace, useBranch);
|
|
134
|
+
return { success: true, iterations: iteration };
|
|
135
|
+
}
|
|
136
|
+
if (signal === "ROTATE") {
|
|
137
|
+
logProgress(workspace, `**Session ${iteration} ended** - 🔄 Context rotation`);
|
|
138
|
+
iteration += 1;
|
|
139
|
+
setIteration(workspace, iteration);
|
|
140
|
+
sessionId = undefined;
|
|
141
|
+
}
|
|
142
|
+
else if (signal === "GUTTER") {
|
|
143
|
+
logProgress(workspace, `**Session ${iteration} ended** - 🚨 GUTTER`);
|
|
144
|
+
return { success: false, iterations: iteration };
|
|
145
|
+
}
|
|
146
|
+
else if (signal === "DEFER") {
|
|
147
|
+
deferAttempt += 1;
|
|
148
|
+
const delayMs = calculateBackoffDelay(deferAttempt, 15, 120, true);
|
|
149
|
+
logProgress(workspace, `**Session ${iteration} ended** - ⏸️ DEFER (retry in ${delayMs}ms)`);
|
|
150
|
+
await sleepMs(delayMs);
|
|
151
|
+
// retry same iteration, don't increment
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
if (taskStatus.startsWith("INCOMPLETE:")) {
|
|
155
|
+
logProgress(workspace, `**Session ${iteration} ended** - more criteria remaining`);
|
|
156
|
+
iteration += 1;
|
|
157
|
+
setIteration(workspace, iteration);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
await sleepMs(2000);
|
|
161
|
+
}
|
|
162
|
+
logProgress(workspace, `**Loop ended** - ⚠️ Max iterations (${maxIterations}) reached`);
|
|
163
|
+
return { success: false, iterations: iteration - 1 };
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/loop/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/loop/prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAwExE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the Ralph prompt for an iteration.
|
|
3
|
+
*/
|
|
4
|
+
export function buildPrompt(workspace, iteration) {
|
|
5
|
+
return `# Ralph Iteration ${iteration}
|
|
6
|
+
|
|
7
|
+
You are an autonomous development agent using the Ralph methodology.
|
|
8
|
+
|
|
9
|
+
## FIRST: Read State Files
|
|
10
|
+
|
|
11
|
+
Before doing anything:
|
|
12
|
+
1. Read \`RALPH_TASK.md\` - your task and completion criteria
|
|
13
|
+
2. Read \`.ralph/guardrails.md\` - lessons from past failures (FOLLOW THESE)
|
|
14
|
+
3. Read \`.ralph/progress.md\` - what's been accomplished
|
|
15
|
+
4. Read \`.ralph/errors.log\` - recent failures to avoid
|
|
16
|
+
|
|
17
|
+
## Working Directory (Critical)
|
|
18
|
+
|
|
19
|
+
You are already in a git repository. Work HERE, not in a subdirectory:
|
|
20
|
+
|
|
21
|
+
- Do NOT run \`git init\` - the repo already exists
|
|
22
|
+
- Do NOT run scaffolding commands that create nested directories (\`npx create-*\`, \`pnpm init\`, etc.)
|
|
23
|
+
- If you need to scaffold, use flags like \`--no-git\` or scaffold into the current directory (\`.\`)
|
|
24
|
+
- All code should live at the repo root or in subdirectories you create manually
|
|
25
|
+
|
|
26
|
+
## Git Protocol (Critical)
|
|
27
|
+
|
|
28
|
+
Ralph's strength is state-in-git, not LLM memory. Commit early and often:
|
|
29
|
+
|
|
30
|
+
1. After completing each criterion, commit your changes:
|
|
31
|
+
\`git add -A && git commit -m 'ralph: implement state tracker'\`
|
|
32
|
+
\`git add -A && git commit -m 'ralph: fix async race condition'\`
|
|
33
|
+
\`git add -A && git commit -m 'ralph: add CLI adapter with commander'\`
|
|
34
|
+
Always describe what you actually did - never use placeholders like '<description>'
|
|
35
|
+
2. After any significant code change (even partial): commit with descriptive message
|
|
36
|
+
3. Before any risky refactor: commit current state as checkpoint
|
|
37
|
+
4. Push after every 2-3 commits: \`git push\`
|
|
38
|
+
|
|
39
|
+
If you get rotated, the next agent picks up from your last commit. Your commits ARE your memory.
|
|
40
|
+
|
|
41
|
+
## Task Execution
|
|
42
|
+
|
|
43
|
+
1. Work on the next unchecked criterion in RALPH_TASK.md (look for \`[ ]\`)
|
|
44
|
+
2. Run tests after changes (check RALPH_TASK.md for test_command)
|
|
45
|
+
3. **Mark completed criteria**: Edit RALPH_TASK.md and change \`[ ]\` to \`[x]\`
|
|
46
|
+
- Example: \`- [ ] Implement parser\` becomes \`- [x] Implement parser\`
|
|
47
|
+
- This is how progress is tracked - YOU MUST update the file
|
|
48
|
+
4. Update \`.ralph/progress.md\` with what you accomplished
|
|
49
|
+
5. When ALL criteria show \`[x]\`: output \`<ralph>COMPLETE</ralph>\`
|
|
50
|
+
6. If stuck 3+ times on same issue: output \`<ralph>GUTTER</ralph>\`
|
|
51
|
+
|
|
52
|
+
## Learning from Failures
|
|
53
|
+
|
|
54
|
+
When something fails:
|
|
55
|
+
1. Check \`.ralph/errors.log\` for failure history
|
|
56
|
+
2. Figure out the root cause
|
|
57
|
+
3. Add a Sign to \`.ralph/guardrails.md\` using this format:
|
|
58
|
+
|
|
59
|
+
\`\`\`
|
|
60
|
+
### Sign: [Descriptive Name]
|
|
61
|
+
- **Trigger**: When this situation occurs
|
|
62
|
+
- **Instruction**: What to do instead
|
|
63
|
+
- **Added after**: Iteration ${iteration} - what happened
|
|
64
|
+
\`\`\`
|
|
65
|
+
|
|
66
|
+
## Context Rotation Warning
|
|
67
|
+
|
|
68
|
+
You may receive a warning that context is running low. When you see it:
|
|
69
|
+
1. Finish your current file edit
|
|
70
|
+
2. Commit and push your changes
|
|
71
|
+
3. Update .ralph/progress.md with what you accomplished and what's next
|
|
72
|
+
4. You will be rotated to a fresh agent that continues your work
|
|
73
|
+
|
|
74
|
+
Begin by reading the state files.
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/loop/prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,SAAiB;IAC9D,OAAO,qBAAqB,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BA0DR,SAAS;;;;;;;;;;;;CAYvC,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exponential backoff delay for DEFER (rate limit / transient errors).
|
|
3
|
+
* Formula: baseDelayMs * 2^(attempt-1), capped at maxDelayMs.
|
|
4
|
+
* Jitter: 0–25% random addition when useJitter is true.
|
|
5
|
+
*/
|
|
6
|
+
export declare function calculateBackoffDelay(attempt: number, baseDelaySeconds: number, maxDelaySeconds: number, useJitter?: boolean): number;
|
|
7
|
+
export declare function sleepMs(ms: number): Promise<void>;
|
|
8
|
+
export interface WithRetryOptions {
|
|
9
|
+
maxRetries?: number;
|
|
10
|
+
baseDelaySeconds?: number;
|
|
11
|
+
maxDelaySeconds?: number;
|
|
12
|
+
useJitter?: boolean;
|
|
13
|
+
/** Return true if error is retryable (default: isRetryableError from stream-parser). */
|
|
14
|
+
isRetryable?: (err: unknown) => boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Execute an async function with retry and exponential backoff.
|
|
18
|
+
* On rejection, if isRetryable(error) and attempt < maxRetries, waits then retries.
|
|
19
|
+
* Retry with exponential backoff and jitter.
|
|
20
|
+
*/
|
|
21
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options?: WithRetryOptions): Promise<T>;
|
|
22
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/loop/retry.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,EACvB,SAAS,GAAE,OAAc,GACxB,MAAM,CAUR;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wFAAwF;IACxF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;CACzC;AAMD;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,CAAC,CAAC,CA8CZ"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { isRetryableError } from "../stream-parser/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Exponential backoff delay for DEFER (rate limit / transient errors).
|
|
4
|
+
* Formula: baseDelayMs * 2^(attempt-1), capped at maxDelayMs.
|
|
5
|
+
* Jitter: 0–25% random addition when useJitter is true.
|
|
6
|
+
*/
|
|
7
|
+
export function calculateBackoffDelay(attempt, baseDelaySeconds, maxDelaySeconds, useJitter = true) {
|
|
8
|
+
const baseMs = baseDelaySeconds * 1000;
|
|
9
|
+
const maxMs = maxDelaySeconds * 1000;
|
|
10
|
+
let delayMs = baseMs * Math.pow(2, attempt - 1);
|
|
11
|
+
if (delayMs > maxMs)
|
|
12
|
+
delayMs = maxMs;
|
|
13
|
+
if (useJitter) {
|
|
14
|
+
const jitter = Math.floor(delayMs * 0.25 * Math.random());
|
|
15
|
+
delayMs += jitter;
|
|
16
|
+
}
|
|
17
|
+
return Math.floor(delayMs);
|
|
18
|
+
}
|
|
19
|
+
export function sleepMs(ms) {
|
|
20
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
21
|
+
}
|
|
22
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
23
|
+
const DEFAULT_BASE_DELAY = 1;
|
|
24
|
+
const DEFAULT_MAX_DELAY = 60;
|
|
25
|
+
/**
|
|
26
|
+
* Execute an async function with retry and exponential backoff.
|
|
27
|
+
* On rejection, if isRetryable(error) and attempt < maxRetries, waits then retries.
|
|
28
|
+
* Retry with exponential backoff and jitter.
|
|
29
|
+
*/
|
|
30
|
+
export async function withRetry(fn, options = {}) {
|
|
31
|
+
const { maxRetries = DEFAULT_MAX_RETRIES, baseDelaySeconds = DEFAULT_BASE_DELAY, maxDelaySeconds = DEFAULT_MAX_DELAY, useJitter = true, isRetryable = (err) => isRetryableError(err instanceof Error ? err.message : String(err)), } = options;
|
|
32
|
+
let lastError;
|
|
33
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
34
|
+
try {
|
|
35
|
+
return await fn();
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
lastError = err;
|
|
39
|
+
if (attempt === maxRetries) {
|
|
40
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
41
|
+
process.stderr.write(`Error: Command failed after ${maxRetries} attempts\n`);
|
|
42
|
+
process.stderr.write(`Last error: ${msg}\n`);
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
if (!isRetryable(err)) {
|
|
46
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
47
|
+
process.stderr.write("Error: Non-retryable error detected, aborting retries\n");
|
|
48
|
+
process.stderr.write(`Error: ${msg}\n`);
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
const delayMs = calculateBackoffDelay(attempt, baseDelaySeconds, maxDelaySeconds, useJitter);
|
|
52
|
+
const delaySeconds = Math.round(delayMs / 1000);
|
|
53
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
54
|
+
const exitCode = err && typeof err.code === "number" ? err.code : "?";
|
|
55
|
+
process.stderr.write(`⚠️ Attempt ${attempt}/${maxRetries} failed (exit code: ${exitCode})\n`);
|
|
56
|
+
process.stderr.write(` Retrying in ${delaySeconds}s (with exponential backoff)...\n`);
|
|
57
|
+
if (msg.length > 0) {
|
|
58
|
+
process.stderr.write(` Error: ${msg.slice(0, 200)}${msg.length > 200 ? "..." : ""}\n`);
|
|
59
|
+
}
|
|
60
|
+
await sleepMs(delayMs);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
throw lastError;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/loop/retry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function getLockDir(workspace: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Acquire the parallel run lock. Returns a release function on success.
|
|
4
|
+
* Uses atomic mkdir; supports stale recovery (dead PID + age >= LOCK_STALE_MINUTES).
|
|
5
|
+
*/
|
|
6
|
+
export declare function acquireParallelLock(workspace: string): (() => void) | null;
|
|
7
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/parallel/lock.ts"],"names":[],"mappings":"AAMA,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAmDD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAsD1E"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { mkdirSync, rmSync, readFileSync, writeFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
const PARALLEL_LOCK_DIR = ".ralph/locks/parallel.lock";
|
|
4
|
+
const LOCK_STALE_MINUTES = 45;
|
|
5
|
+
export function getLockDir(workspace) {
|
|
6
|
+
return join(workspace, PARALLEL_LOCK_DIR);
|
|
7
|
+
}
|
|
8
|
+
function getLockPidPath(workspace) {
|
|
9
|
+
return join(getLockDir(workspace), "pid");
|
|
10
|
+
}
|
|
11
|
+
function getLockCreatedPath(workspace) {
|
|
12
|
+
return join(getLockDir(workspace), "created_at");
|
|
13
|
+
}
|
|
14
|
+
function isPidAlive(pid) {
|
|
15
|
+
try {
|
|
16
|
+
process.kill(pid, 0);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function parseLockAgeMinutes(workspace) {
|
|
24
|
+
const path = getLockCreatedPath(workspace);
|
|
25
|
+
if (!existsSync(path))
|
|
26
|
+
return null;
|
|
27
|
+
try {
|
|
28
|
+
const iso = readFileSync(path, "utf-8").trim();
|
|
29
|
+
const lockEpoch = new Date(iso).getTime();
|
|
30
|
+
if (Number.isNaN(lockEpoch))
|
|
31
|
+
return null;
|
|
32
|
+
const ageMs = Date.now() - lockEpoch;
|
|
33
|
+
return Math.floor(ageMs / 60_000);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function tryRemoveStaleLock(workspace) {
|
|
40
|
+
const lockDir = getLockDir(workspace);
|
|
41
|
+
const pidPath = getLockPidPath(workspace);
|
|
42
|
+
if (!existsSync(pidPath)) {
|
|
43
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
const pid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
|
|
47
|
+
if (Number.isNaN(pid) || isPidAlive(pid))
|
|
48
|
+
return false;
|
|
49
|
+
const age = parseLockAgeMinutes(workspace);
|
|
50
|
+
if (age === null || age < LOCK_STALE_MINUTES)
|
|
51
|
+
return false;
|
|
52
|
+
console.error(`🔓 Stale lock detected (PID ${pid} dead, age ${age}m >= ${LOCK_STALE_MINUTES}m). Recovering...`);
|
|
53
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Acquire the parallel run lock. Returns a release function on success.
|
|
58
|
+
* Uses atomic mkdir; supports stale recovery (dead PID + age >= LOCK_STALE_MINUTES).
|
|
59
|
+
*/
|
|
60
|
+
export function acquireParallelLock(workspace) {
|
|
61
|
+
const lockDir = getLockDir(workspace);
|
|
62
|
+
const parent = join(lockDir, "..");
|
|
63
|
+
mkdirSync(parent, { recursive: true });
|
|
64
|
+
if (!existsSync(lockDir)) {
|
|
65
|
+
try {
|
|
66
|
+
mkdirSync(lockDir, { recursive: false });
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Race: someone else created it
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!existsSync(lockDir)) {
|
|
73
|
+
console.error("❌ Failed to create lock dir:", lockDir);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const pidPath = getLockPidPath(workspace);
|
|
77
|
+
if (existsSync(pidPath)) {
|
|
78
|
+
if (tryRemoveStaleLock(workspace)) {
|
|
79
|
+
return acquireParallelLock(workspace);
|
|
80
|
+
}
|
|
81
|
+
const pid = readFileSync(pidPath, "utf-8").trim();
|
|
82
|
+
const created = readFileSync(getLockCreatedPath(workspace), "utf-8").trim();
|
|
83
|
+
console.error("❌ Parallel lock already held:", lockDir);
|
|
84
|
+
console.error(" pid:", pid);
|
|
85
|
+
console.error(" created_at:", created);
|
|
86
|
+
console.error(" If you're sure no run is active, delete: rm -rf", JSON.stringify(lockDir));
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
writeFileSync(pidPath, String(process.pid));
|
|
90
|
+
writeFileSync(getLockCreatedPath(workspace), new Date().toISOString());
|
|
91
|
+
const release = () => {
|
|
92
|
+
try {
|
|
93
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// ignore
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const onExit = () => {
|
|
100
|
+
release();
|
|
101
|
+
process.off("exit", onExit);
|
|
102
|
+
process.off("SIGINT", onExit);
|
|
103
|
+
process.off("SIGTERM", onExit);
|
|
104
|
+
};
|
|
105
|
+
process.on("exit", onExit);
|
|
106
|
+
process.on("SIGINT", onExit);
|
|
107
|
+
process.on("SIGTERM", onExit);
|
|
108
|
+
return release;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/parallel/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function getConflictedFiles(workspace: string): string[];
|
|
2
|
+
export type MergeResult = "success" | "conflict" | "error";
|
|
3
|
+
export declare function mergeAgentBranch(workspace: string, branch: string, targetBranch: string): MergeResult;
|
|
4
|
+
export declare function abortMerge(workspace: string): void;
|
|
5
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/parallel/merge.ts"],"names":[],"mappings":"AAGA,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAU9D;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;AAE3D,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,WAAW,CAkBb;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAMlD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
export function getConflictedFiles(workspace) {
|
|
3
|
+
try {
|
|
4
|
+
const out = execSync("git diff --name-only --diff-filter=U", {
|
|
5
|
+
cwd: workspace,
|
|
6
|
+
encoding: "utf-8",
|
|
7
|
+
});
|
|
8
|
+
return out.trim() ? out.trim().split("\n") : [];
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function mergeAgentBranch(workspace, branch, targetBranch) {
|
|
15
|
+
try {
|
|
16
|
+
execSync(`git checkout "${targetBranch}"`, { cwd: workspace, stdio: "pipe" });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return "error";
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const msg = `Merge ${branch} into ${targetBranch}`.replace(/"/g, '\\"');
|
|
23
|
+
execSync(`git -c user.name=ralph-parallel -c user.email=ralph-parallel@localhost merge --no-ff -m "${msg}" "${branch}"`, { cwd: workspace, stdio: "pipe" });
|
|
24
|
+
return "success";
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
const conflicts = getConflictedFiles(workspace);
|
|
28
|
+
return conflicts.length > 0 ? "conflict" : "error";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function abortMerge(workspace) {
|
|
32
|
+
try {
|
|
33
|
+
execSync("git merge --abort", { cwd: workspace, stdio: "pipe" });
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// ignore
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/parallel/merge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,QAAQ,CAAC,sCAAsC,EAAE;YAC3D,GAAG,EAAE,SAAS;YACd,QAAQ,EAAE,OAAO;SAClB,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAID,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,MAAc,EACd,YAAoB;IAEpB,IAAI,CAAC;QACH,QAAQ,CAAC,iBAAiB,YAAY,GAAG,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,SAAS,MAAM,SAAS,YAAY,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxE,QAAQ,CACN,4FAA4F,GAAG,MAAM,MAAM,GAAG,EAC9G,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAClC,CAAC;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,SAAS,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;IACrD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,IAAI,CAAC;QACH,QAAQ,CAAC,mBAAmB,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface RunAgentResult {
|
|
2
|
+
status: "done" | "failed";
|
|
3
|
+
outputStatus: "success" | "no_commits" | "error";
|
|
4
|
+
}
|
|
5
|
+
export interface RunParallelOptions {
|
|
6
|
+
maxParallel: number;
|
|
7
|
+
model: string;
|
|
8
|
+
baseBranch?: string;
|
|
9
|
+
integrationBranch?: string;
|
|
10
|
+
skipMerge?: boolean;
|
|
11
|
+
createPr?: boolean;
|
|
12
|
+
taskFilePath?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function runParallelTasks(workspace: string, options: RunParallelOptions): Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
mergedCount: number;
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/parallel/run.ts"],"names":[],"mappings":"AA0EA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,YAAY,EAAE,SAAS,GAAG,YAAY,GAAG,OAAO,CAAC;CAClD;AAsED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CA0XpD"}
|