
testing-tui-runtime
How to drive the ralph-tui TUI (run, watch mode, pause/quit paths) end-to-end in a real terminal with a fake agent and a scratch beads tracker, without AI credentials.
- Installs
- 58
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/subsy/ralph-tui --skill testing-tui-runtime- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads testing-tui-runtime automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Runtime/E2E testing of the ralph-tui TUI
Use this when a change must be proven in the real TUI (watch mode, engine status labels, pause/resume, shutdown/quit paths) rather than with unit tests.
Build & run
cd /path/to/ralph-tui && bun run build # produces dist/cli.js
Run the built CLI (not bun run dev) so behaviour matches shipped code:
bun /path/to/ralph-tui/dist/cli.js run [flags]
Scratch tracker (never touch the repo's own .beads)
Create a throwaway project dir with its own beads store and drive scenarios with bd:
export RALPH_TEST_DIR="$(mktemp -d "${TMPDIR:-/tmp}/ralph-tui-run.XXXXXX")" && cd "$RALPH_TEST_DIR"
bd init # or `bd create ...` which bootstraps the store
bd create "Watch mode epic" --type epic -p 1 # note the epic id bd prints
bd create "Alpha task" --type task -p 2 --parent <epic-id>
A fresh dir per run, not a fixed one: a reused dir carries over .beads tasks, the
.ralph-tui session file, locks and guards — exactly the things most scenarios here assert on, so
a Recovered stale session banner or a stale-lock prompt from the previous run reads as a
finding of the build under test. The unique dir also gives the run a unique epic id, which is how
you address its process (see the signal trap below).
Gotchas:
bdcommands must be run with the scratch dir as the working directory, otherwise you get "no beads database found".- With
--tracker beads, the TUI requires--epic <id>; without it the TUI shows "No epics found" and interactive epic selection blocks automation. br(beads-rust) may not be installed even though AGENTS.md references it;bdworks.
Fake agents (no AI credentials needed)
Put plugins in ~/.config/ralph-tui/plugins/agents/:
echo.ts— returnsecho-agent: local fake response\n<promise>COMPLETE</promise>instantly.slowecho.ts— same but sleeps ~25s in interruptible 500 ms chunks. Essential for testing quit/pause mid-iteration; an instant agent gives you no window to interrupt.
Select with --agent echo / --agent slowecho.
Making timing visible on screen
Wrap the CLI so quit latency is provable in the recording:
#!/bin/bash
cd "${RALPH_TEST_DIR:?export RALPH_TEST_DIR to the scratch dir first}"
export PATH="$PATH:$HOME/.local/bin"
echo "START $(date +%H:%M:%S.%3N) args: $*"
bun /path/to/ralph-tui/dist/cli.js run "$@"
echo "=== PROCESS EXITED code=$? at $(date +%H:%M:%S.%3N) ==="
exec bash
Typical invocation:
./runwatch.sh --watch --poll 60 --agent echo --tracker beads --epic <epic-id> --no-setup --force
Use a long --poll (60s) to make a stalled pause/quit obvious, and a short one (10s) when you
want auto-pickup to happen quickly.
TUI keys
s start · p pause/resume · q → quit dialog → y · d dashboard · r refresh.
Header status labels: Waiting (watch idle), Selecting, Executing, Paused, Complete.
Dashboard shows Waiting for new tasks when watch-idle.
Known traps
- Interrupt (Ctrl+C) path: on builds that include the #431 fix (
src/tui/utils/keyboard-shortcuts.tsonInterruptRequestinRunApp.tsx+exitSignals: ['SIGQUIT','SIGABRT']inrun.tsx), keyboard Ctrl+C and externalkill -INT <pid>both render the⚠ Interrupt Ralph?dialog with the display intact;n/Esccancel,yexits code 0 in ~2s and resets the active task toopen, and a second Ctrl+C within ~1s force-quits viaprocess.exit(1)(wrappercode=1, terminal left un-repainted; on builds whoseexithandler callsreleaseLockSyncthe force quit no longer leaves a lock file behind, on older ones it does). On older builds Ctrl+C is a complete no-op andkill -INTblanks the display and hangs, so always confirm which build you are on before blaming a change.
- Ctrl+Shift+C is indistinguishable from Ctrl+C in most terminals: konsole sends byte
0x03for both (verify withstty -isig; cat -v— Ctrl+Shift+C prints^C, Alt+C prints^[c), so the "copy" shortcut opens the interrupt dialog whenever the terminal does not consume it itself (konsole only consumes it when a konsole-level text selection exists). Alt+C is safely distinct. Clipboard contents cannot be verified on a box withoutxclip/xsel/wl-paste(that is whatsrc/utils/clipboard.tsshells out to on Linux) — record the clipboard write as untested. - Early-startup signals need timing, and are quirky: the app's SIGINT/SIGTERM handlers are not
installed for roughly the first ~0.7s. Drive it with
( sleep 0.5; kill -INT "$(pgrep -f "cli\.js run.*--epic $EPIC_ID")" ) & ./runwatch.sh ..., where$EPIC_IDis this run's epic — check the pattern matches exactly one pid (pgrep -cf ...) before signalling, and note the wrapper script is a different process from the CLI it launches, so signal the pid you matched rather than the wrapper. Do not usepkill -f 'dist/cli.js run': it hits every ralph run on the box, including a parallel test or amainworktree build you are comparing against, and mutates their session and task state. Historic behaviour (before the #434 fix insrc/session/lock.ts): SIGINT in that window exited130but left a stale lock, and SIGTERM was delivered but ignored (the TUI mounted and kept running). On builds whereregisterLockCleanupHandlersowns the startup signals (guarded byprocess.listenerCount(signal) > 1), expectcode=143for SIGTERM /code=130for SIGINT at every delay in the window, with.ralph-tui/ralph.lockremoved; a signal landing beforeacquireLockWithPrompt(~0.05s) uses default disposition but cannot leave a lock. Always sweep several delays (0.05/0.2/0.3/0.4/0.5/0.7s), log that the signal was actually sent, and checkls .ralph-tui/ralph.lockplus the next launch's banner each time. Two signals in quick succession inside the window should still yield a single clean 130/143, nevercode=1. Recovered stale sessionis not the same thing asStale lock detected: the lock file can be correctly removed while.ralph-tui/session.jsonkeepsstatus: "running", in which case every next launch prints⚠ Recovered stale session / Session status set to "interrupted" (resumable)— even after a gracefulq/yexit (code=0). Checkjq .status .ralph-tui/session.jsonandsession-meta.jsonbefore attributing that banner to a lock/signal change; it may be an independent session-persistence bug.- In TUI mode the process does not exit by itself when all tasks are done in non-watch mode;
it parks on
Completeuntil you quit. To prove "exits when work runs out", use headless mode:--no-tui(alias--headless) exits with code 0 and prints a run summary. Headless watch mode logs[watch] All tasks complete — polling every Nsand stays alive. - Killed sessions may leave a stale lock (older builds; fixed once the lock layer owns
startup signals and the
exithandler releases synchronously): the next launch prompts "Remove the stale lock and continue? (Y/n)" before the TUI starts. Answeryfirst; keys you send too early (e.g.s) land in that prompt. Prefer gracefulq/yexits to avoid this. - After creating a task externally, allow up to one full poll interval plus a few seconds before concluding auto-pickup failed.
- Lock guard file (
.ralph-tui/ralph.lock.guard) andlockId: on builds that serialize lock mutations,.ralph-tui/ralph.lockcarries alockIdnonce and every create/stale-clean/--force/release goes through a guard file created withO_EXCL. Useful observer helper: print the lock'spid/lockId, whether the guard exists (and its contents), andpgreped ralph pids, and sample it before/after every scenario. The guard should never be observed present while a run merely executes — only during a mutation. Things worth planting by hand: an empty ornot-jsonguard (should be reclaimed as malformed after ~250ms and the run starts normally), a guard naming a dead pid (reclaimed), and a guard naming a live pid (respected: acquire fails after ~2s withTimed out waiting for the session lock (another ralph-tui process may be starting or exiting)). On such builds a guard held past the sync budget at exit time is expected to leave the lock behind (later recovered as stale) rather than unlink it — a lock left behind there is a pass; a hang, a multi-second stall, or a live process's lock disappearing is the bug. Make sure the "live pid" in a planted guard is really alive (kill -0); a pid that has already exited silently converts the test into the dead-pid case. run --resumemay fail where standaloneresumeworks:run.tsxacquires the session lock before callingresumeSession, which acquires again and refuses because a lock exists — observed asResuming previous session... / Failed to resume sessionwith exit 1, whileralph-tui resumeon the same interrupted session starts fine. Reproduced onmaintoo, so treat it as pre-existing unless the build under test claims to fix it; always cross-check against agit worktreebuild ofmainbefore calling it a regression.- Watch for an orphaned run with no lock file: observed once — the TUI painted nothing at all
yet the engine kept executing iterations and
.ralph-tui/ralph.lockwas absent. That is the signature of an uncaught exception handler that releases the lock without exiting. It did not reproduce in follow-up attempts; if you hit a blank TUI, capture stdout/stderr by wrapping the run inscript -q -c "./runwatch.sh ..." /tmp/run.log(this preserves the TTY, unlike a pipe) before killing it. Noterunwatch.shends withexec bash, so ascript-wrapped run leaves you in an inner shell — typeexitto continue a scripted loop.
Devin Secrets Needed
None — the fake agent plugins and the local bd store avoid all external credentials.
FAQ
What does the testing-tui-runtime skill do?
How to drive the ralph-tui TUI (run, watch mode, pause/quit paths) end-to-end in a real terminal with a fake agent and a scratch beads tracker, without AI credentials.
How do I install testing-tui-runtime?
Run npx skills add https://github.com/subsy/ralph-tui --skill testing-tui-runtime in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/testing-tui-runtime/ (personal) or .claude/skills/testing-tui-runtime/ (project).
Which AI agents can use testing-tui-runtime?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is testing-tui-runtime safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains testing-tui-runtime?
It is published by subsy in subsy/ralph-tui. It has 58 recorded installs.
More from subsy/ralph-tui
ralph-tui-prd
subsy/ralph-tui
Generate a Product Requirements Document (PRD) for ralph-tui task orchestration. Creates PRDs with user stories that can be converted to beads issues or prd.json for automated execution. Triggers on: create a prd, write prd for, plan this feature, requirements for, spec out.
ralph-tui-create-json
subsy/ralph-tui
Convert PRDs to prd.json format for ralph-tui execution. Creates JSON task files with user stories, acceptance criteria, and dependencies. Triggers on: create prd.json, convert to json, ralph json, create json tasks.
ralph-tui-create-beads
subsy/ralph-tui
Convert PRDs to beads for ralph-tui execution. Creates an epic with child beads for each user story. Use when you have a PRD and want to use ralph-tui with beads as the task source. Triggers on: create beads, convert prd to beads, beads for ralph, ralph beads.
ralph-tui-create-beads-rust
subsy/ralph-tui
Convert PRDs to beads for ralph-tui execution using beads-rust (br CLI). Creates an epic with child beads for each user story. Use when you have a PRD and want to use ralph-tui with beads-rust as the task source. Triggers on: create beads, convert prd to beads, beads for ralph, ralph beads, br beads.
Popular testing & qa skills
tdd
mattpocock/skills
Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
grilling
mattpocock/skills
Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.
migrate-to-shoehorn
mattpocock/skills
Migrate test files from as type assertions to @total-typescript/shoehorn. Use when user mentions shoehorn, wants to replace as in tests, or needs partial test data.
run-train
lllllllama/rigorpilot-skills
Rigor Train skill for deep learning research repositories. Use when a documented or selected training command should be run conservatively for startup verification, short-run verification, full kickoff, or resume, with command, config, seed, log, checkpoint, status, and metric evidence written to standardized trainoutputs/. Do not use for environment setup, exploratory sweeps, speculative idea implementation, or end-to-end orchestration.
test-driven-development
obra/superpowers
Use when implementing any feature or bugfix, before writing implementation code
finishing-a-development-branch
obra/superpowers
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work