ralph-mcp 1.0.2 → 1.0.5
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/dist/index.js +2 -2
- package/dist/tools/update.js +30 -0
- package/dist/utils/agent.js +72 -58
- package/dist/utils/worktree.js +12 -1
- package/package.json +7 -8
package/dist/index.js
CHANGED
|
@@ -47,8 +47,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
47
47
|
},
|
|
48
48
|
autoMerge: {
|
|
49
49
|
type: "boolean",
|
|
50
|
-
description: "Auto add to merge queue when all stories pass (default:
|
|
51
|
-
default:
|
|
50
|
+
description: "Auto add to merge queue when all stories pass (default: true)",
|
|
51
|
+
default: true,
|
|
52
52
|
},
|
|
53
53
|
notifyOnComplete: {
|
|
54
54
|
type: "boolean",
|
package/dist/tools/update.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import notifier from "node-notifier";
|
|
3
|
+
import { appendFile, mkdir } from "fs/promises";
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { join, dirname } from "path";
|
|
3
6
|
import { findExecutionByBranch, findMergeQueueItemByExecutionId, findUserStoryById, insertMergeQueueItem, listMergeQueue, listUserStoriesByExecutionId, updateExecution, updateUserStory, } from "../store/state.js";
|
|
4
7
|
import { mergeQueueAction } from "./merge.js";
|
|
5
8
|
export const updateInputSchema = z.object({
|
|
@@ -8,6 +11,15 @@ export const updateInputSchema = z.object({
|
|
|
8
11
|
passes: z.boolean().describe("Whether the story passes"),
|
|
9
12
|
notes: z.string().optional().describe("Implementation notes"),
|
|
10
13
|
});
|
|
14
|
+
function formatDate(date) {
|
|
15
|
+
const pad = (n) => n.toString().padStart(2, '0');
|
|
16
|
+
const yyyy = date.getFullYear();
|
|
17
|
+
const MM = pad(date.getMonth() + 1);
|
|
18
|
+
const dd = pad(date.getDate());
|
|
19
|
+
const HH = pad(date.getHours());
|
|
20
|
+
const mm = pad(date.getMinutes());
|
|
21
|
+
return `${yyyy}-${MM}-${dd} ${HH}:${mm}`;
|
|
22
|
+
}
|
|
11
23
|
export async function update(input) {
|
|
12
24
|
// Find execution by branch
|
|
13
25
|
const exec = await findExecutionByBranch(input.branch);
|
|
@@ -25,6 +37,24 @@ export async function update(input) {
|
|
|
25
37
|
passes: input.passes,
|
|
26
38
|
notes: input.notes || story.notes,
|
|
27
39
|
});
|
|
40
|
+
// Append to ralph-progress.md if passed
|
|
41
|
+
if (input.passes && exec.worktreePath) {
|
|
42
|
+
try {
|
|
43
|
+
const progressPath = join(exec.worktreePath, "ralph-progress.md");
|
|
44
|
+
const dir = dirname(progressPath);
|
|
45
|
+
if (!existsSync(dir)) {
|
|
46
|
+
await mkdir(dir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
const timestamp = formatDate(new Date());
|
|
49
|
+
const notesContent = input.notes || story.notes || "No notes provided.";
|
|
50
|
+
const entry = `## [${timestamp}] ${story.storyId}: ${story.title}\n${notesContent}\n\n`;
|
|
51
|
+
await appendFile(progressPath, entry, "utf-8");
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
console.error("Failed to write to ralph-progress.md:", e);
|
|
55
|
+
// Continue execution even if logging fails
|
|
56
|
+
}
|
|
57
|
+
}
|
|
28
58
|
// Update execution timestamp and status
|
|
29
59
|
const allStories = await listUserStoriesByExecutionId(exec.id);
|
|
30
60
|
// Check if this update completes all stories
|
package/dist/utils/agent.js
CHANGED
|
@@ -14,41 +14,55 @@ export function generateAgentPrompt(branch, description, worktreePath, stories)
|
|
|
14
14
|
return "All user stories are complete. No action needed.";
|
|
15
15
|
}
|
|
16
16
|
const storiesText = pendingStories
|
|
17
|
-
.map((s) => `
|
|
18
|
-
### ${s.storyId}: ${s.title}
|
|
19
|
-
${s.description}
|
|
20
|
-
|
|
21
|
-
**Acceptance Criteria:**
|
|
22
|
-
${s.acceptanceCriteria.map((ac) => `- ${ac}`).join("\n")}
|
|
17
|
+
.map((s) => `
|
|
18
|
+
### ${s.storyId}: ${s.title}
|
|
19
|
+
${s.description}
|
|
20
|
+
|
|
21
|
+
**Acceptance Criteria:**
|
|
22
|
+
${s.acceptanceCriteria.map((ac) => `- ${ac}`).join("\n")}
|
|
23
23
|
`)
|
|
24
24
|
.join("\n");
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
##
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
25
|
+
// Read progress log if it exists
|
|
26
|
+
let progressLog = "";
|
|
27
|
+
const progressPath = join(worktreePath, "ralph-progress.md");
|
|
28
|
+
if (existsSync(progressPath)) {
|
|
29
|
+
try {
|
|
30
|
+
progressLog = readFileSync(progressPath, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
// Ignore read errors
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return `You are an autonomous coding agent working on the "${branch}" branch.
|
|
37
|
+
|
|
38
|
+
## Working Directory
|
|
39
|
+
${worktreePath}
|
|
40
|
+
|
|
41
|
+
## PRD: ${description}
|
|
42
|
+
|
|
43
|
+
${progressLog ? `## Progress & Learnings\n${progressLog}\n` : ""}
|
|
44
|
+
|
|
45
|
+
## Pending User Stories
|
|
46
|
+
${storiesText}
|
|
47
|
+
|
|
48
|
+
## Instructions
|
|
49
|
+
|
|
50
|
+
1. Work on ONE user story at a time, starting with the highest priority.
|
|
51
|
+
2. ${progressLog ? "Review the 'Progress & Learnings' section above to understand previous decisions and context." : "Check if 'ralph-progress.md' exists and review it for context."}
|
|
52
|
+
3. Implement the feature to satisfy all acceptance criteria.
|
|
53
|
+
4. Run quality checks: \`pnpm check-types\` and \`pnpm --filter api build\` (adjust command for the specific repo structure if needed).
|
|
54
|
+
5. Commit changes with message: \`feat: [${pendingStories[0].storyId}] - ${pendingStories[0].title}\`
|
|
55
|
+
6. After completing a story, call \`ralph_update\` to mark it as passed:
|
|
56
|
+
\`ralph_update({ branch: "${branch}", storyId: "${pendingStories[0].storyId}", passes: true, notes: "Brief summary of implementation, key decisions, and any learnings." })\`
|
|
57
|
+
7. Continue to the next story until all are complete.
|
|
58
|
+
|
|
59
|
+
## Quality Requirements
|
|
60
|
+
- ALL commits must pass typecheck and build
|
|
61
|
+
- Keep changes focused and minimal
|
|
62
|
+
- Follow existing code patterns
|
|
63
|
+
|
|
64
|
+
## Stop Condition
|
|
65
|
+
When all stories are complete, report completion.
|
|
52
66
|
`;
|
|
53
67
|
}
|
|
54
68
|
/**
|
|
@@ -66,31 +80,31 @@ export function generateMergeAgentPrompt(projectRoot, branch, description, confl
|
|
|
66
80
|
if (prdPath && existsSync(prdPath)) {
|
|
67
81
|
prdContent = readFileSync(prdPath, "utf-8");
|
|
68
82
|
}
|
|
69
|
-
return `You are a Git merge expert. Please resolve the following merge conflicts.
|
|
70
|
-
|
|
71
|
-
## Project Architecture
|
|
72
|
-
${architectureContext || "No CLAUDE.md found. Use your best judgment based on the code."}
|
|
73
|
-
|
|
74
|
-
## PRD Context
|
|
75
|
-
${prdContent || `Branch: ${branch}\nDescription: ${description}`}
|
|
76
|
-
|
|
77
|
-
## Conflict Files
|
|
78
|
-
${conflictFiles.map((f) => `- ${f}`).join("\n")}
|
|
79
|
-
|
|
80
|
-
## Tasks
|
|
81
|
-
|
|
82
|
-
1. Read each conflict file to understand both sides of the conflict
|
|
83
|
-
2. Analyze the intent of changes from both branches based on the PRD
|
|
84
|
-
3. Resolve conflicts by keeping valuable changes from both sides
|
|
85
|
-
4. Ensure the PRD requirements are satisfied
|
|
86
|
-
5. Run \`git add <file>\` for each resolved file
|
|
87
|
-
6. Run \`git commit -m "resolve: merge conflicts for ${branch}"\`
|
|
88
|
-
|
|
89
|
-
## Guidelines
|
|
90
|
-
- Prefer keeping both changes when they don't conflict logically
|
|
91
|
-
- If changes conflict logically, prefer the feature branch changes (they implement the PRD)
|
|
92
|
-
- Ensure the code compiles after resolution
|
|
93
|
-
- Run \`pnpm check-types\` to verify
|
|
83
|
+
return `You are a Git merge expert. Please resolve the following merge conflicts.
|
|
84
|
+
|
|
85
|
+
## Project Architecture
|
|
86
|
+
${architectureContext || "No CLAUDE.md found. Use your best judgment based on the code."}
|
|
87
|
+
|
|
88
|
+
## PRD Context
|
|
89
|
+
${prdContent || `Branch: ${branch}\nDescription: ${description}`}
|
|
90
|
+
|
|
91
|
+
## Conflict Files
|
|
92
|
+
${conflictFiles.map((f) => `- ${f}`).join("\n")}
|
|
93
|
+
|
|
94
|
+
## Tasks
|
|
95
|
+
|
|
96
|
+
1. Read each conflict file to understand both sides of the conflict
|
|
97
|
+
2. Analyze the intent of changes from both branches based on the PRD
|
|
98
|
+
3. Resolve conflicts by keeping valuable changes from both sides
|
|
99
|
+
4. Ensure the PRD requirements are satisfied
|
|
100
|
+
5. Run \`git add <file>\` for each resolved file
|
|
101
|
+
6. Run \`git commit -m "resolve: merge conflicts for ${branch}"\`
|
|
102
|
+
|
|
103
|
+
## Guidelines
|
|
104
|
+
- Prefer keeping both changes when they don't conflict logically
|
|
105
|
+
- If changes conflict logically, prefer the feature branch changes (they implement the PRD)
|
|
106
|
+
- Ensure the code compiles after resolution
|
|
107
|
+
- Run \`pnpm check-types\` to verify
|
|
94
108
|
`;
|
|
95
109
|
}
|
|
96
110
|
/**
|
package/dist/utils/worktree.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync, exec } from "child_process";
|
|
2
|
-
import { existsSync } from "fs";
|
|
2
|
+
import { existsSync, appendFileSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { promisify } from "util";
|
|
5
5
|
const execAsync = promisify(exec);
|
|
@@ -25,6 +25,17 @@ export async function createWorktree(projectRoot, branch) {
|
|
|
25
25
|
// Create new branch from main
|
|
26
26
|
await execAsync(`git worktree add -b "${branch}" "${worktreePath}" main`, { cwd: projectRoot });
|
|
27
27
|
}
|
|
28
|
+
// Prevent ralph-progress.md from being committed
|
|
29
|
+
try {
|
|
30
|
+
const excludePath = join(worktreePath, ".git", "info", "exclude");
|
|
31
|
+
if (existsSync(excludePath)) {
|
|
32
|
+
appendFileSync(excludePath, "\nralph-progress.md\n", "utf-8");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
console.error("Failed to update .git/info/exclude:", e);
|
|
37
|
+
// Non-fatal error
|
|
38
|
+
}
|
|
28
39
|
return worktreePath;
|
|
29
40
|
}
|
|
30
41
|
/**
|
package/package.json
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralph-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "MCP server for autonomous PRD execution with Claude Code. Git worktree isolation, progress tracking, auto-merge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"ralph-mcp": "dist/index.js"
|
|
9
9
|
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"dev": "tsx watch src/index.ts",
|
|
13
|
-
"start": "node dist/index.js",
|
|
14
|
-
"prepublishOnly": "npm run build"
|
|
15
|
-
},
|
|
16
10
|
"keywords": [
|
|
17
11
|
"mcp",
|
|
18
12
|
"claude",
|
|
@@ -54,5 +48,10 @@
|
|
|
54
48
|
"@types/node-notifier": "^8.0.5",
|
|
55
49
|
"tsx": "^4.21.0",
|
|
56
50
|
"typescript": "^5.9.3"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc",
|
|
54
|
+
"dev": "tsx watch src/index.ts",
|
|
55
|
+
"start": "node dist/index.js"
|
|
57
56
|
}
|
|
58
|
-
}
|
|
57
|
+
}
|