feat: initial ralph-workplan skill
Standalone Claude Code skill that ties a ralph loop to a workplan file. Retires automatically when all tasks are done — no external dependencies. - plugin/ralph-workplan.md skill entrypoint - plugin/scripts/check-done.sh pre-start guard (reads workplan status) - plugin/scripts/setup.sh writes ralph state file with workplan-aware prompt - install.sh copies plugin files to ~/.claude/plugins/ - workplan-spec.md workplan file format reference - README.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.claude/
|
||||||
104
README.md
Normal file
104
README.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# ralph-workplan
|
||||||
|
|
||||||
|
A Claude Code skill that starts a [Ralph Loop](https://github.com/anthropics/claude-code)
|
||||||
|
tied to a workplan file. The loop retires automatically when all tasks in the
|
||||||
|
workplan are done — no external services required.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
```
|
||||||
|
/ralph-workplan workplans/WP-0001-my-feature.md
|
||||||
|
/ralph-workplan workplans/WP-0001-my-feature.md --max-iterations 15
|
||||||
|
```
|
||||||
|
|
||||||
|
On each iteration, Claude:
|
||||||
|
1. Re-reads the workplan file and checks task statuses
|
||||||
|
2. If all tasks are `done` and workplan `status: done` → outputs `<promise>HEUREKA</promise>` and the loop stops
|
||||||
|
3. Otherwise → continues implementing, marking tasks done as it goes
|
||||||
|
|
||||||
|
Before starting, the skill guards against running on an already-completed workplan.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- [Claude Code](https://claude.ai/code) with the `ralph-loop` plugin installed
|
||||||
|
- Bash (macOS or Linux)
|
||||||
|
|
||||||
|
No other dependencies. No external services.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <this-repo> ~/ralph-workplan
|
||||||
|
cd ~/ralph-workplan
|
||||||
|
./install.sh
|
||||||
|
# restart Claude Code
|
||||||
|
```
|
||||||
|
|
||||||
|
To uninstall:
|
||||||
|
```bash
|
||||||
|
./install.sh --uninstall
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workplan format
|
||||||
|
|
||||||
|
A workplan is a Markdown file with YAML frontmatter and task blocks:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
id: WP-0001
|
||||||
|
title: "Build a REST API"
|
||||||
|
status: active
|
||||||
|
---
|
||||||
|
|
||||||
|
Build a simple REST API with CRUD endpoints for a todo list.
|
||||||
|
|
||||||
|
## Task: Set up project structure
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: T-01
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
## Task: Implement endpoints
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: T-02
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
## Task: Write tests
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: T-03
|
||||||
|
status: todo
|
||||||
|
priority: medium
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
See [workplan-spec.md](workplan-spec.md) for the full format reference.
|
||||||
|
|
||||||
|
## How completion works
|
||||||
|
|
||||||
|
The loop is entirely file-driven. As Claude completes tasks it edits the
|
||||||
|
workplan file:
|
||||||
|
|
||||||
|
```
|
||||||
|
status: todo → status: in_progress → status: done
|
||||||
|
```
|
||||||
|
|
||||||
|
When every task is `done`, Claude also updates the workplan frontmatter to
|
||||||
|
`status: done`. The ralph loop detects this on the next iteration and stops.
|
||||||
|
|
||||||
|
No state hub, no HTTP calls, no external coordination needed.
|
||||||
|
|
||||||
|
## Why not just use `/ralph-loop` directly?
|
||||||
|
|
||||||
|
`/ralph-loop` with a static prompt has no awareness of completion state — it
|
||||||
|
loops forever (or until `--max-iterations`) even if the work is already done.
|
||||||
|
`/ralph-workplan` ties the loop lifecycle to the workplan file, so it:
|
||||||
|
|
||||||
|
- Refuses to start if the workplan is already done
|
||||||
|
- Self-retires the moment all tasks are complete
|
||||||
|
- Always sets `--completion-promise HEUREKA` and a bounded iteration count
|
||||||
39
install.sh
Executable file
39
install.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# install.sh — install the ralph-workplan skill into ~/.claude/plugins/
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./install.sh # install
|
||||||
|
# ./install.sh --uninstall # remove
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PLUGIN_NAME="ralph-workplan"
|
||||||
|
PLUGIN_SRC="$(cd "$(dirname "$0")/plugin" && pwd)"
|
||||||
|
PLUGIN_DEST="${HOME}/.claude/plugins/${PLUGIN_NAME}"
|
||||||
|
|
||||||
|
if [[ "${1:-}" == "--uninstall" ]]; then
|
||||||
|
if [[ -d "$PLUGIN_DEST" ]]; then
|
||||||
|
rm -rf "$PLUGIN_DEST"
|
||||||
|
echo "✅ Uninstalled: $PLUGIN_DEST"
|
||||||
|
else
|
||||||
|
echo "ℹ️ Not installed (nothing to remove)"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install
|
||||||
|
mkdir -p "${PLUGIN_DEST}/scripts"
|
||||||
|
|
||||||
|
cp "${PLUGIN_SRC}/ralph-workplan.md" "${PLUGIN_DEST}/ralph-workplan.md"
|
||||||
|
cp "${PLUGIN_SRC}/scripts/check-done.sh" "${PLUGIN_DEST}/scripts/check-done.sh"
|
||||||
|
cp "${PLUGIN_SRC}/scripts/setup.sh" "${PLUGIN_DEST}/scripts/setup.sh"
|
||||||
|
|
||||||
|
chmod +x "${PLUGIN_DEST}/scripts/check-done.sh"
|
||||||
|
chmod +x "${PLUGIN_DEST}/scripts/setup.sh"
|
||||||
|
|
||||||
|
echo "✅ Installed: $PLUGIN_DEST"
|
||||||
|
echo ""
|
||||||
|
echo " Skill: /ralph-workplan <workplan-file> [--max-iterations N]"
|
||||||
|
echo " Spec: $(cd "$(dirname "$0")" && pwd)/workplan-spec.md"
|
||||||
|
echo ""
|
||||||
|
echo " Restart Claude Code for the skill to appear."
|
||||||
76
plugin/ralph-workplan.md
Normal file
76
plugin/ralph-workplan.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
description: "Start a Ralph loop tied to a workplan file — retires automatically when all tasks are done"
|
||||||
|
argument-hint: "<workplan-file> [--max-iterations N]"
|
||||||
|
allowed-tools: ["Read", "Bash(${CLAUDE_PLUGIN_ROOT}/scripts/check-done.sh:*)", "Bash(${CLAUDE_PLUGIN_ROOT}/scripts/setup.sh:*)"]
|
||||||
|
hide-from-slash-command-tool: "true"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Ralph Workplan
|
||||||
|
|
||||||
|
Start a ralph loop driven by a workplan file. The loop retires automatically
|
||||||
|
when all tasks in the workplan are marked done.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
/ralph-workplan workplans/WP-0001-my-task.md
|
||||||
|
/ralph-workplan workplans/WP-0001-my-task.md --max-iterations 15
|
||||||
|
```
|
||||||
|
|
||||||
|
The workplan file must follow the format described in `workplan-spec.md`
|
||||||
|
(YAML frontmatter with `id`, `title`, `status` fields; tasks as fenced
|
||||||
|
`task` code blocks with `id` and `status`).
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
Parse `$ARGUMENTS` to extract:
|
||||||
|
- `WORKPLAN_FILE` — the positional argument (required)
|
||||||
|
- `MAX_ITERATIONS` — value after `--max-iterations` (default: 20)
|
||||||
|
|
||||||
|
**Step 1 — Validate the workplan file exists**
|
||||||
|
|
||||||
|
If the file does not exist:
|
||||||
|
> "❌ Workplan file not found: $WORKPLAN_FILE"
|
||||||
|
|
||||||
|
Stop.
|
||||||
|
|
||||||
|
**Step 2 — Read the workplan frontmatter**
|
||||||
|
|
||||||
|
Read `$WORKPLAN_FILE`. Extract from the YAML frontmatter:
|
||||||
|
- `id` — workplan identifier
|
||||||
|
- `title` — workplan title
|
||||||
|
- `status` — current status
|
||||||
|
|
||||||
|
**Step 3 — Guard: check if already done**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
"${CLAUDE_PLUGIN_ROOT}/scripts/check-done.sh" "$WORKPLAN_FILE"
|
||||||
|
```
|
||||||
|
|
||||||
|
Exit code 0 means the workplan is already done. If so:
|
||||||
|
> "✅ Workplan $ID ('$TITLE') is already done — all tasks complete.
|
||||||
|
> Nothing to do. No ralph loop started."
|
||||||
|
|
||||||
|
Stop.
|
||||||
|
|
||||||
|
**Step 4 — Show current task summary**
|
||||||
|
|
||||||
|
Count the task blocks in the file (fenced blocks with language tag `task`).
|
||||||
|
Report:
|
||||||
|
> "📋 $ID — $TITLE
|
||||||
|
> Tasks: X done, Y in progress, Z todo (N total)
|
||||||
|
> Starting ralph loop (max $MAX_ITERATIONS iterations)..."
|
||||||
|
|
||||||
|
**Step 5 — Start the loop**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
"${CLAUDE_PLUGIN_ROOT}/scripts/setup.sh" \
|
||||||
|
"$WORKPLAN_FILE" "$ID" "$TITLE" "$MAX_ITERATIONS"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then report:
|
||||||
|
> "🔄 Ralph loop active. The loop will feed the workplan prompt back on every
|
||||||
|
> iteration. It retires when all tasks are done and the workplan status is
|
||||||
|
> updated to 'done' in the file."
|
||||||
33
plugin/scripts/check-done.sh
Executable file
33
plugin/scripts/check-done.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# check-done.sh
|
||||||
|
# Exit 0 if the workplan is done, 1 if not.
|
||||||
|
# Used as the pre-start guard in the ralph-workplan skill.
|
||||||
|
#
|
||||||
|
# Usage: check-done.sh <workplan_file>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
WORKPLAN_FILE="${1:?workplan_file required}"
|
||||||
|
|
||||||
|
if [[ ! -f "$WORKPLAN_FILE" ]]; then
|
||||||
|
echo "❌ Workplan file not found: $WORKPLAN_FILE" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract status from YAML frontmatter (first occurrence between --- markers)
|
||||||
|
STATUS=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$WORKPLAN_FILE" \
|
||||||
|
| grep '^status:' \
|
||||||
|
| head -1 \
|
||||||
|
| sed 's/status:[[:space:]]*//' \
|
||||||
|
| tr -d '"'"'" )
|
||||||
|
|
||||||
|
if [[ -z "$STATUS" ]]; then
|
||||||
|
echo "⚠️ No status field found in frontmatter of: $WORKPLAN_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$STATUS" == "done" ]]; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
102
plugin/scripts/setup.sh
Executable file
102
plugin/scripts/setup.sh
Executable file
@@ -0,0 +1,102 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# setup.sh
|
||||||
|
# Writes the .claude/ralph-loop.local.md state file with a workplan-aware prompt.
|
||||||
|
# Called by the /ralph-workplan skill after the pre-start guard passes.
|
||||||
|
#
|
||||||
|
# Usage: setup.sh <workplan_file> <workplan_id> <title> <max_iterations>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
WORKPLAN_FILE="${1:?workplan_file required}"
|
||||||
|
WORKPLAN_ID="${2:?workplan_id required}"
|
||||||
|
TITLE="${3:?title required}"
|
||||||
|
MAX_ITERATIONS="${4:-20}"
|
||||||
|
SESSION_ID="${CLAUDE_CODE_SESSION_ID:-}"
|
||||||
|
|
||||||
|
STATE_FILE=".claude/ralph-loop.local.md"
|
||||||
|
|
||||||
|
mkdir -p .claude
|
||||||
|
|
||||||
|
# Extract workplan body — everything after the second ---
|
||||||
|
WORKPLAN_BODY=$(awk '/^---$/{i++; next} i>=2' "$WORKPLAN_FILE")
|
||||||
|
|
||||||
|
cat > "$STATE_FILE" <<EOF
|
||||||
|
---
|
||||||
|
active: true
|
||||||
|
iteration: 1
|
||||||
|
session_id: ${SESSION_ID}
|
||||||
|
max_iterations: ${MAX_ITERATIONS}
|
||||||
|
completion_promise: "HEUREKA"
|
||||||
|
workplan_id: ${WORKPLAN_ID}
|
||||||
|
workplan_file: ${WORKPLAN_FILE}
|
||||||
|
started_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workplan Status Check — Do This First, Every Iteration
|
||||||
|
|
||||||
|
Read the workplan file at: \`${WORKPLAN_FILE}\`
|
||||||
|
|
||||||
|
Count the task blocks (fenced code blocks with language tag \`task\`):
|
||||||
|
- How many tasks exist in total?
|
||||||
|
- How many have \`status: done\`?
|
||||||
|
|
||||||
|
If **every task** has \`status: done\` AND the frontmatter \`status\` is \`done\`:
|
||||||
|
The workplan is complete. Output exactly: <promise>HEUREKA</promise>
|
||||||
|
Do nothing else. Stop here.
|
||||||
|
|
||||||
|
Otherwise: continue with the implementation below.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workplan: ${WORKPLAN_ID} — ${TITLE}
|
||||||
|
**File:** \`${WORKPLAN_FILE}\`
|
||||||
|
|
||||||
|
${WORKPLAN_BODY}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Work
|
||||||
|
|
||||||
|
- Stay strictly within the scope of the workplan above
|
||||||
|
- Work through tasks in priority order (high → medium → low)
|
||||||
|
- Use TDD where applicable: write a failing test, make it pass, then refactor
|
||||||
|
- Use whatever test runner, linter, and build tools this repository already uses
|
||||||
|
- Consult existing documentation (README, docs/, wiki/, specs/) for context
|
||||||
|
- Document significant architecture decisions as ADRs if the project uses them
|
||||||
|
|
||||||
|
## Updating Task Status
|
||||||
|
|
||||||
|
As you complete each task, edit the workplan file to update its status:
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
status: todo → status: in_progress (when you start it)
|
||||||
|
status: in_progress → status: done (when it is verified complete)
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
When **every task** is \`done\`, also update the workplan frontmatter:
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
status: active → status: done
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
Before marking the workplan done and outputting \`<promise>HEUREKA</promise>\`,
|
||||||
|
verify all of the following are true:
|
||||||
|
|
||||||
|
1. Every task block in \`${WORKPLAN_FILE}\` has \`status: done\`
|
||||||
|
2. The workplan frontmatter \`status\` is \`done\`
|
||||||
|
3. The full test suite passes with no failures
|
||||||
|
4. The codebase passes the project's standard code-quality checks
|
||||||
|
(linting, type checking, formatting — whatever applies to this project)
|
||||||
|
5. Documentation reflects the implemented behaviour
|
||||||
|
|
||||||
|
Output \`<promise>HEUREKA</promise>\` only when all five are genuinely true.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ Ralph state file written: $STATE_FILE"
|
||||||
|
echo " Workplan : $WORKPLAN_ID — $TITLE"
|
||||||
|
echo " File : $WORKPLAN_FILE"
|
||||||
|
echo " Max iter : $MAX_ITERATIONS"
|
||||||
|
echo " Promise : HEUREKA (output only when workplan is genuinely done)"
|
||||||
71
workplan-spec.md
Normal file
71
workplan-spec.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Workplan File Specification
|
||||||
|
|
||||||
|
A workplan is a Markdown file with YAML frontmatter that describes a unit of
|
||||||
|
work. It is the only input the `/ralph-workplan` skill needs.
|
||||||
|
|
||||||
|
## File Format
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
id: WP-0001
|
||||||
|
title: "Build a thing"
|
||||||
|
status: active
|
||||||
|
---
|
||||||
|
|
||||||
|
Optional free-text description of the workplan scope and goals.
|
||||||
|
|
||||||
|
## Task: Do the first thing
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: T-01
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
## Task: Do the second thing
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: T-02
|
||||||
|
status: todo
|
||||||
|
priority: medium
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frontmatter Fields
|
||||||
|
|
||||||
|
| Field | Required | Values | Description |
|
||||||
|
|-------|----------|--------|-------------|
|
||||||
|
| `id` | yes | string | Unique workplan identifier |
|
||||||
|
| `title` | yes | string | Human-readable name |
|
||||||
|
| `status` | yes | `active` \| `done` \| `paused` | Workplan lifecycle state |
|
||||||
|
|
||||||
|
## Task Blocks
|
||||||
|
|
||||||
|
Each task is a fenced code block with language tag `task`, embedded anywhere in
|
||||||
|
the document body. Fields:
|
||||||
|
|
||||||
|
| Field | Required | Values | Description |
|
||||||
|
|-------|----------|--------|-------------|
|
||||||
|
| `id` | yes | string | Unique task identifier within the workplan |
|
||||||
|
| `status` | yes | `todo` \| `in_progress` \| `done` | Task state |
|
||||||
|
| `priority` | no | `high` \| `medium` \| `low` | Execution priority |
|
||||||
|
|
||||||
|
## Completion Rule
|
||||||
|
|
||||||
|
A workplan is **done** when:
|
||||||
|
1. Every task block has `status: done`
|
||||||
|
2. The frontmatter `status` field is `done`
|
||||||
|
|
||||||
|
The skill updates both as work progresses — tasks individually, then the
|
||||||
|
workplan when all tasks are complete.
|
||||||
|
|
||||||
|
## Naming Convention
|
||||||
|
|
||||||
|
The skill works with any filename. A common convention used in custodian-family
|
||||||
|
projects is:
|
||||||
|
|
||||||
|
```
|
||||||
|
workplans/<PREFIX>-WP-NNNN-<slug>.md
|
||||||
|
```
|
||||||
|
|
||||||
|
but this is not required.
|
||||||
Reference in New Issue
Block a user