ralph-mcp 1.1.0 → 1.1.2
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 +96 -1
- package/dist/index.js +49 -0
- package/dist/tools/reset-stagnation.d.ts +24 -0
- package/dist/tools/reset-stagnation.js +40 -0
- package/dist/tools/retry.d.ts +26 -0
- package/dist/tools/retry.js +65 -0
- package/dist/tools/status.d.ts +1 -0
- package/dist/tools/status.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,13 +39,42 @@ Claude: ralph_start → Task Agent handles everything automatically
|
|
|
39
39
|
- **2-Step Workflow** - Just create PRD and run `ralph_start`, everything else is automatic
|
|
40
40
|
- **Parallel Execution** - Run 5+ PRDs simultaneously with Claude Code Task tool
|
|
41
41
|
- **Git Worktree Isolation** - Each PRD runs in its own worktree, zero conflicts
|
|
42
|
+
- **Dependency Management** - PRDs can depend on other PRDs, auto-triggered when dependencies complete
|
|
43
|
+
- **Stagnation Detection** - Auto-detects stuck agents (no progress, repeated errors) and marks as failed
|
|
42
44
|
- **Agent Memory** - Persistent "Progress Log" learns from mistakes across User Stories
|
|
43
45
|
- **Context Injection** - Inject project rules (CLAUDE.md) into agent context
|
|
44
46
|
- **Auto Quality Gates** - Type check, lint, build before every commit
|
|
45
47
|
- **Auto Merge** - Merges to main when all User Stories pass
|
|
46
|
-
- **
|
|
48
|
+
- **Merge Queue** - Serial merge queue to avoid conflicts
|
|
47
49
|
- **Notifications** - Windows Toast when PRD completes
|
|
48
50
|
|
|
51
|
+
## Progress Log (Agent Memory)
|
|
52
|
+
|
|
53
|
+
Ralph maintains a `ralph-progress.md` file in each worktree that persists learnings across User Stories. This gives agents "memory" of what worked and what didn't.
|
|
54
|
+
|
|
55
|
+
### How it works
|
|
56
|
+
|
|
57
|
+
1. When an agent completes a User Story, it records learnings in the `notes` field of `ralph_update`
|
|
58
|
+
2. Ralph appends these notes to `ralph-progress.md` in the worktree
|
|
59
|
+
3. When the next User Story starts, the agent receives this log in its prompt
|
|
60
|
+
4. The file is automatically git-ignored (via `.git/info/exclude`)
|
|
61
|
+
|
|
62
|
+
### Example progress log
|
|
63
|
+
|
|
64
|
+
```markdown
|
|
65
|
+
## [2024-01-15 14:30] US-001: Setup Database Schema
|
|
66
|
+
- Used Prisma with PostgreSQL
|
|
67
|
+
- Added index on `userId` for faster queries
|
|
68
|
+
- Note: Must run `pnpm db:migrate:dev` after schema changes
|
|
69
|
+
|
|
70
|
+
## [2024-01-15 15:45] US-002: User Registration API
|
|
71
|
+
- Reused validation patterns from existing auth module
|
|
72
|
+
- BCrypt rounds set to 12 for password hashing
|
|
73
|
+
- Integration test requires test database to be running
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This allows later stories to benefit from earlier discoveries without re-learning.
|
|
77
|
+
|
|
49
78
|
## Installation
|
|
50
79
|
|
|
51
80
|
### From npm
|
|
@@ -128,6 +157,7 @@ This enables Claude to automatically use Ralph when you mention PRD execution.
|
|
|
128
157
|
| Tool | Description |
|
|
129
158
|
|------|-------------|
|
|
130
159
|
| `ralph_start` | Start PRD execution (parse PRD, create worktree, return agent prompt) |
|
|
160
|
+
| `ralph_batch_start` | Start multiple PRDs with dependency resolution |
|
|
131
161
|
| `ralph_status` | View all PRD execution status |
|
|
132
162
|
| `ralph_get` | Get single PRD details |
|
|
133
163
|
| `ralph_update` | Update User Story status (called by agent) |
|
|
@@ -135,6 +165,8 @@ This enables Claude to automatically use Ralph when you mention PRD execution.
|
|
|
135
165
|
| `ralph_merge` | Merge to main + cleanup worktree |
|
|
136
166
|
| `ralph_merge_queue` | Manage serial merge queue |
|
|
137
167
|
| `ralph_set_agent_id` | Record Task agent ID |
|
|
168
|
+
| `ralph_retry` | Retry a failed PRD execution |
|
|
169
|
+
| `ralph_reset_stagnation` | Reset stagnation counters after manual intervention |
|
|
138
170
|
|
|
139
171
|
## Usage
|
|
140
172
|
|
|
@@ -193,6 +225,9 @@ ralph_merge({ branch: "ralph/prd-feature" })
|
|
|
193
225
|
// Start PRD execution (returns agent prompt)
|
|
194
226
|
ralph_start({ prdPath: "tasks/prd-feature.md" })
|
|
195
227
|
|
|
228
|
+
// Start multiple PRDs in parallel
|
|
229
|
+
ralph_batch_start({ prdPaths: ["tasks/prd-a.md", "tasks/prd-b.md"] })
|
|
230
|
+
|
|
196
231
|
// View all PRD status
|
|
197
232
|
ralph_status()
|
|
198
233
|
|
|
@@ -210,6 +245,12 @@ ralph_merge({ branch: "ralph/prd-feature" })
|
|
|
210
245
|
|
|
211
246
|
// Record Task agent ID (for tracking)
|
|
212
247
|
ralph_set_agent_id({ branch: "ralph/prd-feature", agentTaskId: "abc123" })
|
|
248
|
+
|
|
249
|
+
// Retry a failed execution
|
|
250
|
+
ralph_retry({ branch: "ralph/prd-feature" })
|
|
251
|
+
|
|
252
|
+
// Reset stagnation counters (after manual fix)
|
|
253
|
+
ralph_reset_stagnation({ branch: "ralph/prd-feature" })
|
|
213
254
|
```
|
|
214
255
|
|
|
215
256
|
## PRD Format
|
|
@@ -291,6 +332,60 @@ ralph_start({
|
|
|
291
332
|
})
|
|
292
333
|
```
|
|
293
334
|
|
|
335
|
+
### ralph_batch_start options
|
|
336
|
+
|
|
337
|
+
Start multiple PRDs with dependency resolution and serial `pnpm install`.
|
|
338
|
+
|
|
339
|
+
| Option | Default | Description |
|
|
340
|
+
|--------|---------|-------------|
|
|
341
|
+
| `prdPaths` | required | Array of paths to PRD markdown files |
|
|
342
|
+
| `projectRoot` | cwd | Project root directory |
|
|
343
|
+
| `worktree` | `true` | Create worktrees for isolation |
|
|
344
|
+
| `autoMerge` | `true` | Auto add to merge queue when all stories pass |
|
|
345
|
+
| `notifyOnComplete` | `true` | Show Windows notification on completion |
|
|
346
|
+
| `onConflict` | `"agent"` | Conflict resolution strategy |
|
|
347
|
+
| `contextInjectionPath` | `undefined` | Path to file (e.g. CLAUDE.md) to inject into prompt |
|
|
348
|
+
| `preheat` | `true` | Run pnpm install serially before starting agents |
|
|
349
|
+
|
|
350
|
+
```javascript
|
|
351
|
+
ralph_batch_start({
|
|
352
|
+
prdPaths: [
|
|
353
|
+
"tasks/prd-auth.md",
|
|
354
|
+
"tasks/prd-dashboard.md",
|
|
355
|
+
"tasks/prd-settings.md"
|
|
356
|
+
],
|
|
357
|
+
contextInjectionPath: "CLAUDE.md",
|
|
358
|
+
autoMerge: true
|
|
359
|
+
})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### ralph_retry
|
|
363
|
+
|
|
364
|
+
Retry a failed PRD execution. Resets stagnation counters and generates a new agent prompt to continue from where it left off.
|
|
365
|
+
|
|
366
|
+
```javascript
|
|
367
|
+
// Retry a failed execution
|
|
368
|
+
ralph_retry({ branch: "ralph/prd-feature" })
|
|
369
|
+
// Returns: { success, branch, message, previousStatus, agentPrompt, progress }
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### ralph_reset_stagnation
|
|
373
|
+
|
|
374
|
+
Reset stagnation counters after manual intervention. Use when you've fixed an issue and want the agent to continue.
|
|
375
|
+
|
|
376
|
+
| Option | Default | Description |
|
|
377
|
+
|--------|---------|-------------|
|
|
378
|
+
| `branch` | required | Branch name |
|
|
379
|
+
| `resumeExecution` | `true` | Also set status back to 'running' if currently 'failed' |
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
// Reset counters and resume
|
|
383
|
+
ralph_reset_stagnation({ branch: "ralph/prd-feature" })
|
|
384
|
+
|
|
385
|
+
// Reset counters only (keep failed status)
|
|
386
|
+
ralph_reset_stagnation({ branch: "ralph/prd-feature", resumeExecution: false })
|
|
387
|
+
```
|
|
388
|
+
|
|
294
389
|
## Credits
|
|
295
390
|
|
|
296
391
|
- [Geoffrey Huntley](https://ghuntley.com/) - Original Ralph pattern
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,8 @@ import { update, updateInputSchema } from "./tools/update.js";
|
|
|
10
10
|
import { stop, stopInputSchema } from "./tools/stop.js";
|
|
11
11
|
import { merge, mergeInputSchema, mergeQueueAction, mergeQueueInputSchema } from "./tools/merge.js";
|
|
12
12
|
import { setAgentId, setAgentIdInputSchema } from "./tools/set-agent-id.js";
|
|
13
|
+
import { resetStagnationTool, resetStagnationInputSchema } from "./tools/reset-stagnation.js";
|
|
14
|
+
import { retry, retryInputSchema } from "./tools/retry.js";
|
|
13
15
|
const server = new Server({
|
|
14
16
|
name: "ralph",
|
|
15
17
|
version: "1.0.0",
|
|
@@ -120,6 +122,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
120
122
|
type: "string",
|
|
121
123
|
description: "Implementation notes",
|
|
122
124
|
},
|
|
125
|
+
filesChanged: {
|
|
126
|
+
type: "number",
|
|
127
|
+
description: "Number of files changed (for stagnation detection)",
|
|
128
|
+
},
|
|
129
|
+
error: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: "Error message if stuck (for stagnation detection)",
|
|
132
|
+
},
|
|
123
133
|
},
|
|
124
134
|
required: ["branch", "storyId", "passes"],
|
|
125
135
|
},
|
|
@@ -258,6 +268,39 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
258
268
|
required: ["prdPaths"],
|
|
259
269
|
},
|
|
260
270
|
},
|
|
271
|
+
{
|
|
272
|
+
name: "ralph_reset_stagnation",
|
|
273
|
+
description: "Reset stagnation counters for an execution. Use after manual intervention to allow the agent to continue.",
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: "object",
|
|
276
|
+
properties: {
|
|
277
|
+
branch: {
|
|
278
|
+
type: "string",
|
|
279
|
+
description: "Branch name (e.g., ralph/task1-agent)",
|
|
280
|
+
},
|
|
281
|
+
resumeExecution: {
|
|
282
|
+
type: "boolean",
|
|
283
|
+
description: "Also set status back to 'running' if currently 'failed' (default: true)",
|
|
284
|
+
default: true,
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
required: ["branch"],
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: "ralph_retry",
|
|
292
|
+
description: "Retry a failed PRD execution. Resets stagnation counters and generates a new agent prompt to continue from where it left off.",
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: "object",
|
|
295
|
+
properties: {
|
|
296
|
+
branch: {
|
|
297
|
+
type: "string",
|
|
298
|
+
description: "Branch name (e.g., ralph/task1-agent)",
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
required: ["branch"],
|
|
302
|
+
},
|
|
303
|
+
},
|
|
261
304
|
],
|
|
262
305
|
};
|
|
263
306
|
});
|
|
@@ -294,6 +337,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
294
337
|
case "ralph_batch_start":
|
|
295
338
|
result = await batchStart(batchStartInputSchema.parse(args));
|
|
296
339
|
break;
|
|
340
|
+
case "ralph_reset_stagnation":
|
|
341
|
+
result = await resetStagnationTool(resetStagnationInputSchema.parse(args));
|
|
342
|
+
break;
|
|
343
|
+
case "ralph_retry":
|
|
344
|
+
result = await retry(retryInputSchema.parse(args));
|
|
345
|
+
break;
|
|
297
346
|
default:
|
|
298
347
|
throw new Error(`Unknown tool: ${name}`);
|
|
299
348
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const resetStagnationInputSchema: z.ZodObject<{
|
|
3
|
+
branch: z.ZodString;
|
|
4
|
+
resumeExecution: z.ZodDefault<z.ZodBoolean>;
|
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
|
6
|
+
branch: string;
|
|
7
|
+
resumeExecution: boolean;
|
|
8
|
+
}, {
|
|
9
|
+
branch: string;
|
|
10
|
+
resumeExecution?: boolean | undefined;
|
|
11
|
+
}>;
|
|
12
|
+
export type ResetStagnationInput = z.infer<typeof resetStagnationInputSchema>;
|
|
13
|
+
export interface ResetStagnationResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
branch: string;
|
|
16
|
+
message: string;
|
|
17
|
+
previousStatus: string;
|
|
18
|
+
newStatus: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reset stagnation counters for an execution.
|
|
22
|
+
* Use this after manual intervention to allow the agent to continue.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resetStagnationTool(input: ResetStagnationInput): Promise<ResetStagnationResult>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { findExecutionByBranch, resetStagnation, updateExecution, } from "../store/state.js";
|
|
3
|
+
export const resetStagnationInputSchema = z.object({
|
|
4
|
+
branch: z.string().describe("Branch name (e.g., ralph/task1-agent)"),
|
|
5
|
+
resumeExecution: z
|
|
6
|
+
.boolean()
|
|
7
|
+
.default(true)
|
|
8
|
+
.describe("Also set status back to 'running' if currently 'failed'"),
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Reset stagnation counters for an execution.
|
|
12
|
+
* Use this after manual intervention to allow the agent to continue.
|
|
13
|
+
*/
|
|
14
|
+
export async function resetStagnationTool(input) {
|
|
15
|
+
const exec = await findExecutionByBranch(input.branch);
|
|
16
|
+
if (!exec) {
|
|
17
|
+
throw new Error(`No execution found for branch: ${input.branch}`);
|
|
18
|
+
}
|
|
19
|
+
const previousStatus = exec.status;
|
|
20
|
+
// Reset stagnation counters
|
|
21
|
+
await resetStagnation(exec.id);
|
|
22
|
+
// Optionally resume execution
|
|
23
|
+
let newStatus = previousStatus;
|
|
24
|
+
if (input.resumeExecution && previousStatus === "failed") {
|
|
25
|
+
await updateExecution(exec.id, {
|
|
26
|
+
status: "running",
|
|
27
|
+
updatedAt: new Date(),
|
|
28
|
+
});
|
|
29
|
+
newStatus = "running";
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
success: true,
|
|
33
|
+
branch: input.branch,
|
|
34
|
+
message: previousStatus === "failed" && input.resumeExecution
|
|
35
|
+
? "Stagnation counters reset and execution resumed"
|
|
36
|
+
: "Stagnation counters reset",
|
|
37
|
+
previousStatus,
|
|
38
|
+
newStatus,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const retryInputSchema: z.ZodObject<{
|
|
3
|
+
branch: z.ZodString;
|
|
4
|
+
}, "strip", z.ZodTypeAny, {
|
|
5
|
+
branch: string;
|
|
6
|
+
}, {
|
|
7
|
+
branch: string;
|
|
8
|
+
}>;
|
|
9
|
+
export type RetryInput = z.infer<typeof retryInputSchema>;
|
|
10
|
+
export interface RetryResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
branch: string;
|
|
13
|
+
message: string;
|
|
14
|
+
previousStatus: string;
|
|
15
|
+
agentPrompt: string | null;
|
|
16
|
+
progress: {
|
|
17
|
+
completed: number;
|
|
18
|
+
total: number;
|
|
19
|
+
percentage: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Retry a failed PRD execution.
|
|
24
|
+
* Resets stagnation counters and generates a new agent prompt.
|
|
25
|
+
*/
|
|
26
|
+
export declare function retry(input: RetryInput): Promise<RetryResult>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { findExecutionByBranch, listUserStoriesByExecutionId, resetStagnation, updateExecution, } from "../store/state.js";
|
|
3
|
+
import { generateAgentPrompt } from "../utils/agent.js";
|
|
4
|
+
export const retryInputSchema = z.object({
|
|
5
|
+
branch: z.string().describe("Branch name (e.g., ralph/task1-agent)"),
|
|
6
|
+
});
|
|
7
|
+
/**
|
|
8
|
+
* Retry a failed PRD execution.
|
|
9
|
+
* Resets stagnation counters and generates a new agent prompt.
|
|
10
|
+
*/
|
|
11
|
+
export async function retry(input) {
|
|
12
|
+
const exec = await findExecutionByBranch(input.branch);
|
|
13
|
+
if (!exec) {
|
|
14
|
+
throw new Error(`No execution found for branch: ${input.branch}`);
|
|
15
|
+
}
|
|
16
|
+
const previousStatus = exec.status;
|
|
17
|
+
// Only allow retry for failed or stopped executions
|
|
18
|
+
if (previousStatus !== "failed" && previousStatus !== "stopped") {
|
|
19
|
+
return {
|
|
20
|
+
success: false,
|
|
21
|
+
branch: input.branch,
|
|
22
|
+
message: `Cannot retry execution with status '${previousStatus}'. Only 'failed' or 'stopped' executions can be retried.`,
|
|
23
|
+
previousStatus,
|
|
24
|
+
agentPrompt: null,
|
|
25
|
+
progress: { completed: 0, total: 0, percentage: 0 },
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// Reset stagnation counters
|
|
29
|
+
await resetStagnation(exec.id);
|
|
30
|
+
// Set status back to running
|
|
31
|
+
await updateExecution(exec.id, {
|
|
32
|
+
status: "running",
|
|
33
|
+
updatedAt: new Date(),
|
|
34
|
+
});
|
|
35
|
+
// Get stories and generate new agent prompt
|
|
36
|
+
const stories = await listUserStoriesByExecutionId(exec.id);
|
|
37
|
+
const completed = stories.filter((s) => s.passes).length;
|
|
38
|
+
const total = stories.length;
|
|
39
|
+
const agentPrompt = generateAgentPrompt(exec.branch, exec.description, exec.worktreePath || exec.projectRoot, stories.map((s) => ({
|
|
40
|
+
storyId: s.storyId,
|
|
41
|
+
title: s.title,
|
|
42
|
+
description: s.description,
|
|
43
|
+
acceptanceCriteria: s.acceptanceCriteria,
|
|
44
|
+
priority: s.priority,
|
|
45
|
+
passes: s.passes,
|
|
46
|
+
})), undefined, // contextPath - would need to re-read from PRD
|
|
47
|
+
{
|
|
48
|
+
loopCount: 0, // Reset loop context for fresh start
|
|
49
|
+
consecutiveNoProgress: 0,
|
|
50
|
+
consecutiveErrors: 0,
|
|
51
|
+
lastError: null,
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
branch: input.branch,
|
|
56
|
+
message: `Execution retried. ${total - completed} stories remaining.`,
|
|
57
|
+
previousStatus,
|
|
58
|
+
agentPrompt,
|
|
59
|
+
progress: {
|
|
60
|
+
completed,
|
|
61
|
+
total,
|
|
62
|
+
percentage: total > 0 ? Math.round((completed / total) * 100) : 0,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
package/dist/tools/status.d.ts
CHANGED
package/dist/tools/status.js
CHANGED
|
@@ -50,6 +50,7 @@ export async function status(input) {
|
|
|
50
50
|
completed: executionStatuses.filter((e) => e.status === "completed").length,
|
|
51
51
|
failed: executionStatuses.filter((e) => e.status === "failed").length,
|
|
52
52
|
pending: executionStatuses.filter((e) => e.status === "pending").length,
|
|
53
|
+
atRisk: executionStatuses.filter((e) => e.consecutiveNoProgress >= 2 || e.consecutiveErrors >= 3).length,
|
|
53
54
|
};
|
|
54
55
|
return {
|
|
55
56
|
executions: executionStatuses,
|
package/package.json
CHANGED