flow-next-ralph-init
GitHub用于初始化或更新仓库本地的Ralph自动化测试框架及项目钩子,支持模板复制、权限设置及多后端检测。
Trigger Scenarios
Install
npx skills add gmickel/flow-next --skill flow-next-ralph-init -g -y
SKILL.md
Frontmatter
{
"name": "flow-next-ralph-init",
"description": "Scaffold the repo-local Ralph autonomous harness and project hooks. Use when asked to set up Ralph.",
"user-invocable": false
}
Ralph init
Scaffold or update repo-local Ralph harness. Opt-in only.
Preamble
The plugin root resolves once via the cross-platform env-var fallback (Droid uses DROID_PLUGIN_ROOT; Claude Code documents CLAUDE_PLUGIN_ROOT as its compat alias). Subsequent blocks use $PLUGIN_ROOT:
PLUGIN_ROOT="${CODEX_HOME:-$HOME/.codex}"
Rules
- Every write lands under
scripts/ralph/in the current repo. A file written anywhere else has broken this. - If
scripts/ralph/already exists, offer to update (preserves config.env). - Copy templates from
templates/intoscripts/ralph/(includesralphctl.pyfor pause/resume/stop/status). - Copy
flowctl,flowctl.cmd,flowctl.py,flowctl_bootstrap.py,flowctl-help.txt, and theflowctl_tracker/package (from$PLUGIN_ROOT/scripts/) andpick-python.sh(from$PLUGIN_ROOT/scripts/lib/) intoscripts/ralph/— flat, so the resolver lands atscripts/ralph/pick-python.shwhereralph.shand the hook wrapper source it. Apick-python.shthat landed underscripts/ralph/lib/has broken this. - Set executable bit on
scripts/ralph/ralph.sh,scripts/ralph/ralph_once.sh,scripts/ralph/flowctl, andscripts/ralph/ralphctl.py. - Hook registration is agent-driven skill prose only. The plugin ships no hooks by default; you (the host agent) merge the guard entries into the project's host settings via Read+Edit, idempotently, without clobbering unrelated hooks. The boundary is hard: there is no flowctl subcommand for hook install/remove/status and no hook machinery in Python. A settings file whose hooks object now holds only flow-next entries, or a hook-install subcommand added to the CLI, has broken this.
Workflow
-
Resolve repo root:
git rev-parse --show-toplevel -
Check if
scripts/ralph/exists:- If exists: ask "Update existing Ralph setup? (preserves config.env and runs/) [y/n]"
- If no: stop
- If yes: set UPDATE_MODE=1
- If not exists: set UPDATE_MODE=0
- If exists: ask "Update existing Ralph setup? (preserves config.env and runs/) [y/n]"
-
Detect available review backends (skip if UPDATE_MODE=1):
if command -v rpce-cli >/dev/null 2>&1 \ || [ -x "$HOME/RepoPrompt/repoprompt_ce_cli" ] \ || [ -x "$HOME/Library/Application Support/RepoPrompt CE/repoprompt_ce_cli" ] \ || command -v rp-cli >/dev/null 2>&1; then HAVE_RP=1; else HAVE_RP=0; fi HAVE_CODEX=$(which codex >/dev/null 2>&1 && echo 1 || echo 0) HAVE_COPILOT=$(which copilot >/dev/null 2>&1 && echo 1 || echo 0) HAVE_CURSOR=$(which cursor-agent >/dev/null 2>&1 && echo 1 || echo 0) -
Determine review backend (skip if UPDATE_MODE=1):
- If several are available, ask the user.
Show only the options whose CLIs were detected:
Wait for response. Default if empty/ambiguous: preferMultiple review backends available. Which one? a) RepoPrompt (macOS, visual builder) b) Codex CLI (cross-platform, reviewer models via a Codex subscription) c) GitHub Copilot CLI (cross-platform, Claude/GPT via Copilot) d) Cursor CLI (cross-platform, runs cursor-agent; reviewer models via a Cursor subscription) (Reply: "a", "rp", "b", "codex", "c", "copilot", "d", "cursor", or just tell me)rp>codex>copilot>cursor. - If only the RepoPrompt CLI ladder resolves: use
rp - If only codex available: use
codex - If only copilot available: use
copilot - If only cursor-agent available: use
cursor - If none available: use
none
- If several are available, ask the user.
Show only the options whose CLIs were detected:
-
Copy files using bash — the copies run through
cp, never the Write tool. A template reproduced by writing its contents has broken this:If UPDATE_MODE=1 (updating):
# Backup config.env cp scripts/ralph/config.env /tmp/ralph-config-backup.env # Update templates (preserves runs/) cp "${CODEX_HOME:-$HOME/.codex}/templates/flow-next-ralph-init/ralph.sh" scripts/ralph/ cp "${CODEX_HOME:-$HOME/.codex}/templates/flow-next-ralph-init/ralph_once.sh" scripts/ralph/ cp "${CODEX_HOME:-$HOME/.codex}/templates/flow-next-ralph-init/prompt_plan.md" scripts/ralph/ cp "${CODEX_HOME:-$HOME/.codex}/templates/flow-next-ralph-init/prompt_work.md" scripts/ralph/ cp "${CODEX_HOME:-$HOME/.codex}/templates/flow-next-ralph-init/prompt_completion.md" scripts/ralph/ cp "${CODEX_HOME:-$HOME/.codex}/templates/flow-next-ralph-init/watch-filter.py" scripts/ralph/ cp "${CODEX_HOME:-$HOME/.codex}/templates/flow-next-ralph-init/ralphctl.py" scripts/ralph/ cp "$PLUGIN_ROOT/scripts/flowctl" "$PLUGIN_ROOT/scripts/flowctl.cmd" "$PLUGIN_ROOT/scripts/flowctl.py" "$PLUGIN_ROOT/scripts/flowctl_bootstrap.py" "$PLUGIN_ROOT/scripts/flowctl-help.txt" "$PLUGIN_ROOT/scripts/lib/pick-python.sh" scripts/ralph/ rm -rf scripts/ralph/flowctl_tracker && cp -R "$PLUGIN_ROOT/scripts/flowctl_tracker" scripts/ralph/flowctl_tracker # fn-139.5: verify the tracker package post-copy - fail loudly here, never # later as an ImportError mid-run python3 "$PLUGIN_ROOT/scripts/lib/verify_tracker_manifest.py" scripts/ralph mkdir -p scripts/ralph/hooks cp "$PLUGIN_ROOT/scripts/hooks/ralph-guard.py" "$PLUGIN_ROOT/scripts/hooks/ralph-guard" scripts/ralph/hooks/ chmod +x scripts/ralph/ralph.sh scripts/ralph/ralph_once.sh scripts/ralph/flowctl scripts/ralph/ralphctl.py scripts/ralph/hooks/ralph-guard.py scripts/ralph/hooks/ralph-guard # Restore config.env cp /tmp/ralph-config-backup.env scripts/ralph/config.envIf UPDATE_MODE=0 (fresh install):
mkdir -p scripts/ralph/runs scripts/ralph/hooks cp -R "${CODEX_HOME:-$HOME/.codex}/templates/flow-next-ralph-init/." scripts/ralph/ cp "$PLUGIN_ROOT/scripts/flowctl" "$PLUGIN_ROOT/scripts/flowctl.cmd" "$PLUGIN_ROOT/scripts/flowctl.py" "$PLUGIN_ROOT/scripts/flowctl_bootstrap.py" "$PLUGIN_ROOT/scripts/flowctl-help.txt" "$PLUGIN_ROOT/scripts/lib/pick-python.sh" scripts/ralph/ rm -rf scripts/ralph/flowctl_tracker && cp -R "$PLUGIN_ROOT/scripts/flowctl_tracker" scripts/ralph/flowctl_tracker # fn-139.5: verify the tracker package post-copy - fail loudly here, never # later as an ImportError mid-run python3 "$PLUGIN_ROOT/scripts/lib/verify_tracker_manifest.py" scripts/ralph cp "$PLUGIN_ROOT/scripts/hooks/ralph-guard.py" "$PLUGIN_ROOT/scripts/hooks/ralph-guard" scripts/ralph/hooks/ chmod +x scripts/ralph/ralph.sh scripts/ralph/ralph_once.sh scripts/ralph/flowctl scripts/ralph/ralphctl.py scripts/ralph/hooks/ralph-guard.py scripts/ralph/hooks/ralph-guardNote:
cp -R templates/.copies all files including dotfiles (.gitignore). -
Edit
scripts/ralph/config.envto set the chosen review backend (skip if UPDATE_MODE=1):- Replace
PLAN_REVIEW={{PLAN_REVIEW}}withPLAN_REVIEW=<chosen> - Replace
WORK_REVIEW={{WORK_REVIEW}}withWORK_REVIEW=<chosen> - Replace
COMPLETION_REVIEW={{COMPLETION_REVIEW}}withCOMPLETION_REVIEW=<chosen>
- Replace
-
Register project hooks (agent-driven; required for the guard to fire).
Detect host (same signals as
/flow-next:setupStep 0 when available; otherwise probe the settings paths below). Then Read the target file, merge the flow-next Ralph guard entries, Edit/Write the result. The host's existing hooks survive the merge — a settings file whose hooks object now contains only flow-next entries has broken this. Idempotent: if an entry'scommandalready containsscripts/ralph/hooks/ralph-guard, leave that matcher group alone (or refresh the command string to the canonical form below if it drifted).Fingerprint for "this is a flow-next Ralph guard entry": the hook
commandstring containsscripts/ralph/hooks/ralph-guard(wrapper and/or.pyfallback).Canonical guard command (same on every host that can run bash wrappers):
if [ -f scripts/ralph/hooks/ralph-guard ]; then bash scripts/ralph/hooks/ralph-guard; elif [ -f scripts/ralph/hooks/ralph-guard.py ]; then scripts/ralph/hooks/ralph-guard.py; fiTimeout:
5seconds. Type:command.Claude Code → merge into
.claude/settings.jsonTarget: project file
.claude/settings.json(create{"hooks":{}}skeleton if missing; preserve every non-hooks key).Merge these four event groups under
hooks(Claude schema). Matchers use regex OR so Droid interop and Claude share one entry shape:Event Matcher Notes PreToolUseBash|Executeshell (Claude Bash, DroidExecute)PreToolUseEdit|Writefile tools (Claude host names) PostToolUseBash|Executeshell PostToolUseEdit|Writefile tools (receipt-path gate parity) Stop(no matcher) stop gate SubagentStop(no matcher) subagent stop gate Each event's array entry is one matcher group with a single hook object
{type, command, timeout}using the canonical command above.Consent gate: Claude Code's project-hooks trust prompt is the human consent surface. Do not invent a second consent ceremony. After merge, tell the user they may need to accept/trust project hooks in the host UI for them to load this session.
Factory Droid → merge into
.factory/hooks.jsonTarget (verified against Factory hooks-reference): project file
.factory/hooks.json. Prefer that path. Fallback only if the project already stores hooks under thehookskey of.factory/settings.jsonand has no.factory/hooks.json— merge there instead; never invent a third path.Host-appropriate matchers for Droid (Factory's shell tool is
Execute; file tools includeCreate/ApplyPatch). The guard body accepts the full dual-platform sets (Bash/Execute,Edit/Write/Create/ApplyPatch).Event Matcher Notes PreToolUseBash|Executeshell PreToolUseEdit|Write|Create|ApplyPatchDroid file tools PostToolUseBash|Executeshell PostToolUseEdit|Write|Create|ApplyPatchfile tools (receipt-path gate) Stop(no matcher) stop gate SubagentStop(no matcher) subagent stop gate Prefer project-relative command as above (Ralph harness is repo-local). If the host requires absolute paths, rewrite with
"$FACTORY_PROJECT_DIR"/scripts/ralph/hooks/...but keep the same fingerprint substringscripts/ralph/hooks/ralph-guard.Codex → write/merge project
.codex/hooks.jsonCodex has no Claude-schema plugin hooks auto-load from the marketplace plugin. Project scope is
.codex/hooks.json.Codex subset (no
SubagentStop; noEdit/Writematchers — Codex only intercepts shell):Event Matcher PreToolUseBash|ExecutePostToolUseBash|ExecuteStop(no matcher) The top-level JSON is exactly
{"hooks":{...}}— a siblingdescriptionkey has broken this (Codex rejects unknown fields and disables all hooks).If
.codex/config.tomlexists, ensure exactly onehooks = trueunder[features](drop deprecatedcodex_hooks). Same normalization intent as setup's historical Codex hooks step; do it with a careful edit, not a second copy of setup's python block unless you need it.Cursor / Grok
- Cursor: Ralph hooks are unsupported (Cursor hook schema is
afterFileEdit/beforeShellExecution). Scaffoldscripts/ralph/only; print that the guard will not fire on Cursor; do not invent a Cursor-format hook file. - Grok Build: reads Claude-compat plugin/project surfaces; use the Claude Code path (
.claude/settings.json).
Re-run / update
On UPDATE_MODE=1 still re-merge hooks so a project that had scaffold but lost settings entries is repaired. Skip only when every required event already has a fingerprinted entry with the canonical command.
Done when: the host's registered event set matches its platform (Codex without
SubagentStopor file-tool matchers; Droid with its own file-tool matchers; Cursor scaffold-only plus the printed will-not-fire note), every entry carries the canonical command, and unrelated hooks are still present. - Cursor: Ralph hooks are unsupported (Cursor hook schema is
-
Print next steps — the run ends by printing them, never by starting the loop inside the session. A session that executed
ralph.shhas broken this:If UPDATE_MODE=1:
Ralph updated! Your config.env was preserved. Hooks: project settings were re-merged (idempotent). Accept the host's project-hooks trust prompt if it appears. Run from terminal: - ./scripts/ralph/ralph_once.sh (one iteration, observe) - ./scripts/ralph/ralph.sh (full loop, AFK) - ./scripts/ralph/ralphctl.py status|pause|resume|stop (run control; not flowctl)If UPDATE_MODE=0:
Ralph initialized! Next steps (run from terminal, NOT inside the agent session): - Accept project-hooks trust if the host prompts (required once) - Edit scripts/ralph/config.env to customize settings - ./scripts/ralph/ralph_once.sh (one iteration, observe) - ./scripts/ralph/ralph.sh (full loop, AFK) - ./scripts/ralph/ralphctl.py status|pause|resume|stop (run control; not flowctl) Maintenance: - Re-run /flow-next:ralph-init after plugin updates to refresh scripts + re-merge hooks - Uninstall: /flow-next:uninstall removes hook entries; then manually rm -rf scripts/ralph/ if desired
Version History
- 8baa538 Current 2026-08-20 07:59


