mirror of
https://github.com/Hestia-Homes/agentic-toolkit.git
synced 2026-06-08 11:37:26 +00:00
feat: add /ralph-loop skill (subscription-based runner)
Subscription-based counterpart to `agentic-toolkit run`. Instead of sandcastle + Docker + Anthropic API, dispatches each ready ticket to a fresh Claude Code subagent (general-purpose) — same fresh-context property as per-container sandcastle runs, but zero infra. Trade-off: no sandbox isolation. Recommend running on a clean checkout. Mirrors the CLI runner's project schema, phase logic, branch naming, status transitions, and idempotency. v1 fails on first error (no retry state machine yet) — failure-handler.ts parity is future work. README updated with two-path workflow diagram and comparison table. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
66aa00532c
commit
3f27d10fb6
2 changed files with 223 additions and 2 deletions
15
README.md
15
README.md
|
|
@ -50,10 +50,21 @@ Modes:
|
|||
## Workflow
|
||||
|
||||
```
|
||||
/grill-me → /to-prd → /to-issues → /to-project → agentic-toolkit run --project N --mode <variant>
|
||||
┌─→ agentic-toolkit run --project N --mode <variant> (Docker + ANTHROPIC_API_KEY)
|
||||
/grill-me → /to-prd → /to-issues → /to-project ─┤
|
||||
└─→ /ralph-loop project=N mode=<variant> (Claude Code subscription, no Docker)
|
||||
```
|
||||
|
||||
`to-project` lives in `skills/engineering/to-project/SKILL.md` and is installed by `setup.sh`.
|
||||
`to-project` and `ralph-loop` skills live under `skills/engineering/` and are installed by `setup.sh`.
|
||||
|
||||
### Pick a runner
|
||||
|
||||
| Path | Auth | Sandbox | Cost | Parallelism |
|
||||
|------|------|---------|------|-------------|
|
||||
| `agentic-toolkit run` (sandcastle) | `ANTHROPIC_API_KEY` | Docker container | API metered | Per-container |
|
||||
| `/ralph-loop` (skill) | Claude Code subscription | None — host repo | Subscription flat | Serial (one ticket at a time per Claude session) |
|
||||
|
||||
`/ralph-loop` is the zero-infra path: no Docker, no API key. It dispatches each ticket to a fresh Claude Code subagent (clean context per tick) using the same project schema and phase logic as the CLI runner. Trade-off: no sandbox isolation — run on a clean checkout. See `skills/engineering/ralph-loop/SKILL.md`.
|
||||
|
||||
## Architecture (modules in `src/modules/`)
|
||||
|
||||
|
|
|
|||
210
skills/engineering/ralph-loop/SKILL.md
Normal file
210
skills/engineering/ralph-loop/SKILL.md
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
---
|
||||
name: ralph-loop
|
||||
description: Run a GitHub Project (v2) of issues against the current repo using Claude Code subagents — one fresh subagent per ticket. Subscription-only alternative to `agentic-toolkit run` (which uses sandcastle + Docker + Anthropic API). Use after `/to-project` once the project is ready.
|
||||
---
|
||||
|
||||
# Ralph Loop
|
||||
|
||||
Implement a GitHub Project's issues by dispatching each `Ready` issue to a fresh Claude Code subagent (`subagent_type=general-purpose`). Each subagent gets a clean context window — the "ralph loop" property that keeps long-running multi-ticket work coherent.
|
||||
|
||||
This skill is the subscription-based counterpart to `agentic-toolkit run`. Pick the path that fits your setup:
|
||||
|
||||
| Path | Auth | Sandbox | Cost | Parallelism |
|
||||
|------|------|---------|------|-------------|
|
||||
| `agentic-toolkit run` (sandcastle) | `ANTHROPIC_API_KEY` | Docker container | API metered | Per-container |
|
||||
| `/ralph-loop` (this skill) | Claude Code subscription | None — host repo | Subscription flat | Serial (one per Claude session) |
|
||||
|
||||
**Trade-offs you accept by using this skill:**
|
||||
|
||||
- No sandbox isolation. The subagent has full tool access on your host repo. **Run on a clean checkout** with no uncommitted personal work.
|
||||
- Serial only — one ticket at a time per Claude Code session. Cannot parallelise across machines.
|
||||
- Visible: every tick happens in the foreground Claude Code UI. You can interrupt anytime.
|
||||
|
||||
## Inputs
|
||||
|
||||
Confirm the following before starting. If anything is missing, ask the user.
|
||||
|
||||
- **Project number** (e.g. `2`).
|
||||
- **Mode** — `per-ticket` or `single-pr`. Same semantics as the runner CLI.
|
||||
- **Owner** — defaults to current `gh` repo owner (`gh repo view --json owner --jq .owner.login`).
|
||||
- **Repo** — defaults to current `gh` repo (`gh repo view --json name --jq .name`).
|
||||
- **Target repo path** — the local checkout the subagent will work in. Defaults to the current working directory.
|
||||
- **Base branch** — defaults to current HEAD of the target repo (`git -C <path> rev-parse --abbrev-ref HEAD`).
|
||||
|
||||
Pre-flight:
|
||||
- `gh auth status` shows scopes `project`, `read:project`, `repo`. If `project` missing, run `gh auth refresh -s project,read:project --hostname github.com`.
|
||||
- The target repo working tree is clean (`git -C <path> status --porcelain` is empty). If not, halt and tell the user.
|
||||
|
||||
## Algorithm
|
||||
|
||||
The loop runs in the current Claude Code conversation. The orchestrator (you) coordinates state via `gh` and dispatches one subagent per ticket via the Agent tool.
|
||||
|
||||
### 1. Read project state
|
||||
|
||||
Run a GraphQL query equivalent to the runner's `PROJECT_QUERY`:
|
||||
|
||||
```graphql
|
||||
query($owner: String!, $repo: String!, $number: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
projectV2(number: $number) {
|
||||
id
|
||||
field(name: "Status") { ... on ProjectV2SingleSelectField { id options { id name } } }
|
||||
items(first: 100) {
|
||||
nodes {
|
||||
id
|
||||
fieldValues(first: 20) {
|
||||
nodes { ... on ProjectV2ItemFieldSingleSelectValue { name field { ... on ProjectV2SingleSelectField { name } } } }
|
||||
}
|
||||
content {
|
||||
... on Issue { id number title body labels(first: 20) { nodes { name } } assignees(first: 5) { nodes { login } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Parse each item to: `{ number, nodeId, itemId, title, body, labels, status, blockedBy, kind }` where:
|
||||
- `kind = "HITL"` if labels include `hitl` or `ready-for-human`, else `"AFK"`.
|
||||
- `blockedBy` = numbers extracted from the `## Blocked by` section of the body.
|
||||
|
||||
If the Status field is missing any of the six required options (Backlog, Ready, In progress, In review, Needs human, Done), halt and tell the user to run `/to-project`.
|
||||
|
||||
### 2. Autopromote
|
||||
|
||||
For each issue with `kind === "AFK"` AND `status === "Backlog"` AND every `blockedBy` is either `Done` in this project OR not present in this project: set its Status to `Ready`.
|
||||
|
||||
### 3. Compute current phase + pick next
|
||||
|
||||
Topologically partition issues by `Blocked by` (issues with all blockers in earlier phases form phase N). The current phase = lowest-indexed phase with any non-Done issue.
|
||||
|
||||
Within the current phase, `pickNextReady` = the lowest-numbered issue with `status === "Ready"` AND `kind === "AFK"`.
|
||||
|
||||
If no such issue:
|
||||
- `single-pr`: jump to step 9 (finalise).
|
||||
- `per-ticket`: exit with "Phase N complete. Merge open PRs, then re-invoke."
|
||||
|
||||
### 4. Claim
|
||||
|
||||
- Set the issue's Status to `In progress`.
|
||||
- Assign to the current viewer: `gh api user --jq .login` -> `gh issue edit <N> --add-assignee <login>` (best-effort; ignore failure).
|
||||
|
||||
### 5. Prepare branch
|
||||
|
||||
Project slug = `<repo>-p<projectNumber>` (matches CLI's `projectSlugFrom`).
|
||||
|
||||
Branch name:
|
||||
- `per-ticket`: `claude/<projectSlug>/<issueNumber>-<title-slug>` (slug = lowercased, non-alphanumeric -> `-`, trimmed, max 50 chars).
|
||||
- `single-pr`: `claude/<projectSlug>` (reused across tickets).
|
||||
|
||||
In the target repo:
|
||||
```sh
|
||||
git fetch origin
|
||||
git checkout <base-branch>
|
||||
git pull
|
||||
if branch exists: git checkout <branch>
|
||||
else: git checkout -b <branch>
|
||||
```
|
||||
|
||||
### 6. Dispatch subagent
|
||||
|
||||
Use the Agent tool. `subagent_type=general-purpose`. The prompt MUST be fully self-contained — the subagent has no memory of this conversation.
|
||||
|
||||
Prompt template:
|
||||
|
||||
````
|
||||
You are implementing GitHub issue #<NUMBER> in <OWNER>/<REPO>.
|
||||
|
||||
# Issue title
|
||||
<TITLE>
|
||||
|
||||
# Issue body
|
||||
<BODY>
|
||||
|
||||
# Working environment
|
||||
- Target repo path: <TARGET-REPO-PATH>
|
||||
- You are on branch: <BRANCH-NAME>
|
||||
- Branch base: <BASE-BRANCH>
|
||||
- Do NOT change branches. Do NOT push. Do NOT open PRs. The orchestrator handles those.
|
||||
|
||||
# Your task
|
||||
1. Implement the issue's acceptance criteria fully.
|
||||
2. If the repo has a test suite, run it and resolve failures before reporting complete.
|
||||
3. Make commits as you go using the repo's commit style. Don't squash; the orchestrator may amend.
|
||||
4. If you encounter ambiguity that blocks progress (e.g. unclear acceptance criteria, missing context, conflicting requirements), commit any progress made and report blocked rather than guessing.
|
||||
|
||||
# Reporting back
|
||||
Your FINAL message must be exactly one of:
|
||||
|
||||
- `TICKET_COMPLETE: <one-line summary of what was implemented>`
|
||||
- `TICKET_BLOCKED: <one-line reason and what's needed to unblock>`
|
||||
|
||||
Do not include any text after that line. The orchestrator parses it.
|
||||
````
|
||||
|
||||
### 7. Validate result
|
||||
|
||||
After the subagent returns:
|
||||
|
||||
- Read the last line of the subagent's final message.
|
||||
- Count commits: `git -C <target> rev-list --count <base-branch>..HEAD`.
|
||||
- Success = subagent reported `TICKET_COMPLETE` AND commit count > 0.
|
||||
- Otherwise: failure (treat `TICKET_BLOCKED`, missing tag, or zero commits all as failure).
|
||||
|
||||
### 8. After-success actions
|
||||
|
||||
`per-ticket`:
|
||||
- `git -C <target> push -u origin <branch>`.
|
||||
- `gh pr create --base <base> --head <branch> --title "<title> (#<N>)" --body "Closes #<N>\n\nImplemented by /ralph-loop."`.
|
||||
- Set Status = `In review`.
|
||||
- Loop back to step 1 (state may have changed; re-read).
|
||||
|
||||
`single-pr`:
|
||||
- Leave branch local — no push, no PR yet.
|
||||
- Loop back to step 1.
|
||||
|
||||
### 9. Finalise (single-pr only)
|
||||
|
||||
When `pickNextReady` returns nothing:
|
||||
- `commits = git -C <target> rev-list --count <base>..<branch>`.
|
||||
- If `commits === 0`: halt with "No commits to PR. Nothing was implemented."
|
||||
- Else: `git push -u origin <branch>`; open ONE PR `Implement project #<projectNumber>` with body listing `Closes #<number>` for every non-Done issue. Set those issues' Status to `In review`.
|
||||
- Print PR URL.
|
||||
|
||||
## Failure handling (v1)
|
||||
|
||||
Halt on first failure. No retry. Specifically, when step 7 detects failure:
|
||||
|
||||
1. Set the failing issue's Status to `Needs human`.
|
||||
2. Post a comment on the issue:
|
||||
```
|
||||
### Automated run failed (/ralph-loop)
|
||||
|
||||
<subagent's TICKET_BLOCKED reason or "Subagent returned without completing">
|
||||
|
||||
<details><summary>Last subagent message</summary>
|
||||
|
||||
<last 6000 chars of subagent's final message>
|
||||
|
||||
</details>
|
||||
```
|
||||
3. Stop the loop. Print: `Halted on issue #<N>: <reason>`. Tell the user to fix the underlying issue and re-invoke.
|
||||
|
||||
Future versions can add retry logic mirroring `failure-handler.ts` in the toolkit's `src/modules/`.
|
||||
|
||||
## Idempotency
|
||||
|
||||
Re-invoking the skill is safe and resumes from current state:
|
||||
- Issues already `Done` are excluded from phase computation.
|
||||
- Issues `In review` are skipped (their PR is awaiting human merge).
|
||||
- The loop picks up at the next `Ready` issue.
|
||||
|
||||
In `single-pr` mode, the shared branch is reused across invocations — do not delete it between runs unless you're abandoning the project.
|
||||
|
||||
## Notes
|
||||
|
||||
- The skill needs `gh` CLI authed with `project + read:project + repo` scopes. The runner's CLI shares the same requirement; if you've already used `agentic-toolkit run` on the same machine, you're set.
|
||||
- This skill must run with `Bash` and `Agent` tools enabled. The subagent uses standard `general-purpose` permissions (file edit, bash, etc.).
|
||||
- For privacy/security, the subagent prompt includes the full issue body — do not put secrets in issue descriptions.
|
||||
- The orchestrator (you, the calling Claude session) keeps full conversation context across the entire loop. Only the subagents reset. If the orchestrator's context fills up, the user can `/clear` and re-invoke; idempotency handles resumption.
|
||||
Loading…
Add table
Reference in a new issue