Browser Routines
SkillWeb & browsingRecord, compile, and replay Browser Routines — saved, named browser workflows. (Alias for openbrowser-routines.) Supports subcommands: "list [query]" to list/search routines, "new" to record a new routine, "execute <name>" to replay a saved routine. Use when the user says "list routines", "record a routine", "replay X", "execute X", or "/ob-routines <subcommand>".
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Browser Routines skill
What this skill tells your AI
The instructions your AI receives, as published by softpudding/openbrowser in skill/claude/ob-routines/SKILL.md and read by ahel’s review.
Browser Routines are named, compiled workflows captured from real Chrome sessions. The pipeline has four stages: record → compile → name → replay.
Subcommand dispatch
When invoked with arguments, act immediately — do not ask the user what they want:
| Invocation | Action |
|---|---|
/ob-routines | Show available routines and ask what to do |
/ob-routines list [query] | Run list_routines.py [query] and display results |
/ob-routines new | Ask only for the one-line goal/intention, then start recording immediately (see "Before recording" below) |
/ob-routines execute <name> | Run replay.py <name> immediately |
Your role during compilation
You are a bridge and quality gate, not the compiler. The Compiler Agent does the reasoning; you ensure it did its job correctly before finalizing.
Bridge duties
- Run
compile.pyin a tmux pane (mandatory — see below). - Watch for
[compiler:question]— relay it to the user, send their answer back. - Watch for
[compiler:stalled]— show the agent's message, optionally prompt a follow-up. - At
[compiler:name_prompt]— help the user pick a short slug.
Quality gate (run before every finalize)
After the compiler reports status=review, read the compiled routine markdown
and check both of the following before calling /compile/finalize:
Gate 1 — Intent clarity
Did the compiler understand why the user performed each action, not just what they clicked? Red flags:
- Steps that say "click X" with no explanation of goal or condition
- A position-based selection from a sorted/filtered list without asking whether to replay by position or by identity (e.g. "upvote the top 3 posts" — top 3 today vs. the same 3 posts always?)
- A value (date, search query, ticker, ID) that will obviously change between runs, not parameterized
If any red flag is present and the compiler did NOT ask about it: relay the
ambiguity to the user yourself, get their answer, then send it via
POST /recordings/{id}/compile/answer so the compiler can revise.
Gate 2 — Delivery goal for read-only workflows
A workflow is read-only if it has no form submission, no purchase, no
send/post/create/delete action — the user only navigated, read, filtered, or
inspected. For read-only workflows, ask: does the compiled routine end with a
delivery step (a file_editor write, a terminal command, or an explicit
instruction to report results in chat)?
If the routine is read-only AND has no delivery step, the compiler made an error. Do not finalize. Instead:
- Tell the user: "This routine reads data but doesn't capture results anywhere.
How do you want results delivered on replay?"
- (a) Summary shown in chat (brief / structured table / full details?)
- (b) Written to a local file (path + format: plain text, Markdown, CSV, JSON?)
- (c) Both
- Get their answer.
- Send it to the compiler via
POST /recordings/{id}/compile/answer— the compiler will revise the routine to include the delivery step. - Wait for the next
status=review, then re-run both gates.
Why this matters: A routine that just clicks through pages is useless on replay — OpenBrowser will navigate and stop with no output. The delivery step is what makes the routine meaningful.
Preconditions
First time? Complete the full setup in ~/.claude/skills/open-browser/references/setup.md
before using this skill. That guide covers: loading the Chrome extension, connecting
it to the server, and obtaining a valid OPENBROWSER_CHROME_UUID. Without that,
recording and replay will fail immediately.
For subsequent uses, confirm:
- OpenBrowser server at
http://127.0.0.1:8765 - Chrome extension connected
OPENBROWSER_CHROME_UUIDset (or passed via--chrome-uuid)
Quick check:
python3 ~/.claude/skills/open-browser/scripts/check_status.py --chrome-uuid "$OPENBROWSER_CHROME_UUID"
Start the server if needed:
cd /Users/yangxiao/git/OpenBrowser && uv run local-chrome-server serve
Scripts path: ~/.claude/skills/ob-routines/scripts/.
List & search routines
python3 ~/.claude/skills/ob-routines/scripts/list_routines.py
python3 ~/.claude/skills/ob-routines/scripts/list_routines.py "login"
python3 ~/.claude/skills/ob-routines/scripts/list_routines.py --recordings
Record a routine
Before recording — DO NOT interrogate the user
The whole point of record → compile is that the browser actions are observed, and the Compiler Agent asks clarifying questions after it has seen them.
Ask the user only for a short goal/intention (one line). Do NOT ask:
- which site or URL to start from
- which tool/screener to use
- how to define filter terms ("what's high-value?", "what's significant?")
- which parameters should vary between runs
All of that is the compiler's job during Gate 1. Pre-record interrogation defeats the pipeline and wastes the user's time. If the user's goal is vague ("find good stocks"), that's fine — start recording. The compiler will ask.
Step 1 — start recording
python3 ~/.claude/skills/ob-routines/scripts/start_recording.py \
--chrome-uuid "$OPENBROWSER_CHROME_UUID" \
--name "xiaohongshu-messages" \
--intent "check messages on Xiaohongshu"
Prints [recording:started] <recording_id>. Save this ID.
Tell the user: "Perform your actions in the browser window, then come back and say done." Do NOT proceed until the user confirms.
Step 2 — stop recording
python3 ~/.claude/skills/ob-routines/scripts/stop_recording.py <recording_id>
Compile to a routine — MANDATORY: tmux interactive session
compile.py uses input() for Q&A and the name prompt. It MUST run in an
interactive shell. Never invoke it directly via the Bash tool — it will block
and then be killed, losing the compiler session.
Pick the compiler model before launching
The Compiler Agent benefits from a large model. Before launching compile.py:
curl -s http://127.0.0.1:8765/api/config | jq '.config'
- If
default_compiler_aliasis set → launchcompile.pywith no--model-aliasflag; the server uses that alias. - If
default_compiler_aliasisnull→ pick the best alias fromllm_configs(only entries withhas_api_key: true) and pass it via--model-alias <alias>. Prefer*-plusover*-flash, and prefer the regulardashscope.aliyuncs.com/compatible-mode/v1endpoint overcoding.dashscope.aliyuncs.com(the coding endpoint has tighter quota semantics — see project memory).
Always tell the user which model the compiler is using, in one line, before launching — whether it came from the server default or your pick.
Launch in tmux
Default (compiler alias configured on server, or no override needed):
tmux new-window -n "compile" \
"python3 ~/.claude/skills/ob-routines/scripts/compile.py <recording_id>; echo '[compile-done]'; exec zsh"
With an explicit override (when you picked a model automatically):
tmux new-window -n "compile" \
"python3 ~/.claude/skills/ob-routines/scripts/compile.py <recording_id> --model-alias <alias>; echo '[compile-done]'; exec zsh"
Why
exec zsh: Without it, the tmux window closes the instant compile.py exits — the final[compiler:saved]/[compile-done]markers can land between monitor polls and be lost, leaving you unable to capture confirmation. The trailing shell keeps the window alive so the last lines of the pane are always readable. Close it manually withtmux kill-window -t compileafter confirming the routine saved.
Monitor output
tmux capture-pane -t "compile" -p
For long runs, stream markers via the Monitor tool instead of polling by hand. The script MUST detect pane-gone and emit a terminal event — otherwise, if the window exits between polls, the monitor silently runs forever on a dead pane and you never learn the routine finished. Template:
rm -f /tmp/ob_compile_seen
while true; do
if ! tmux list-windows -F '#{window_name}' | grep -qx compile; then
echo "[tmux:pane-gone] compile window exited"
break
fi
cur=$(tmux capture-pane -t compile -p -S -400 2>/dev/null)
echo "$cur" | grep -E --line-buffered \
"\[compiler:question\]|\[compiler:stalled\]|\[compiler:gate_check\]|\[compiler:name_prompt\]|\[compiler:saved\]|\[compile-done\]|\[compiler:error\]|Traceback" \
| while read -r line; do
grep -qxF "$line" /tmp/ob_compile_seen 2>/dev/null && continue
echo "$line" >> /tmp/ob_compile_seen
echo "$line"
done
sleep 3
done
Send an answer
tmux send-keys -t "compile" "the answer" Enter
Markers to watch for
| Marker | Your action |
|---|---|
[compiler:thought] / [compiler:action] | Relay as progress to user |
[compiler:question] <text> | Relay to user, wait for answer, send via tmux send-keys |
[compiler:stalled] <text> | Show message, ask user for follow-up |
[compiler:complete] goal=… steps=N | Compilation reached review state |
[compiler:routine_draft] | Full routine markdown printed for inspection |
[compiler:gate_check] | Run both quality gates here. Send feedback or press Enter |
[compiler:name_prompt] | Gates passed — help user pick slug |
[compiler:saved] | Run list_routines.py <name> to verify, then report name + id to user |
Quality gate checkpoint
When [compiler:gate_check] appears in the pane, compile.py is explicitly
paused waiting for your review of [compiler:routine_draft]. The gate
check is Claude's judgment, not the compiler's — the compiler's own "Let me
know if I misread your intent" message in [compiler:message:assistant] is
not a substitute. You must:
- Read the
[compiler:routine_draft]markdown in the pane capture. - Write out Gate 1 and Gate 2 reasoning as user-visible text before sending any input — one or two lines per gate, stating what passed or failed and why. This makes the judgment inspectable by the user instead of hidden inside an empty Enter.
- Then either pass or fail:
- Gates pass → send an empty Enter:
tmux send-keys -t compile "" Enter - Gate fails → send corrective feedback:
tmux send-keys -t compile "Please add a delivery step: summarise results in chat as a structured list of tickers with metrics." Enter
compile.py forwards non-empty input back to the compiler, streams the revision,
and loops back to another [compiler:gate_check]. Only an empty Enter advances
to [compiler:name_prompt].
Never send gate feedback at the [compiler:name_prompt] stage — that input
goes directly to the routine name field, not the compiler.
Replay a routine
python3 ~/.claude/skills/ob-routines/scripts/replay.py "routine-name" \
--chrome-uuid "$OPENBROWSER_CHROME_UUID"
# List without replaying
python3 ~/.claude/skills/ob-routines/scripts/replay.py --list
Name matching: exact → ID → prefix → substring.
Full example workflow
1. /ob-routines new → ask user what to record
2. start_recording → [recording:started] abc123
3. (user records in browser, says "done")
4. stop_recording abc123 → [recording:events] 21 events
5. tmux new-window "compile.py abc123"
6. monitor pane → relay questions → send answers
7. [compiler:complete] → run Gate 1 + Gate 2
Gate 2 fails: routine is read-only, no delivery step
→ ask user: chat summary, file, or both?
→ send answer via tmux send-keys
→ wait for next [compiler:complete]
8. Gates pass → [compiler:name_prompt] → user picks slug
9. [compiler:saved] name='…' id=…
10. list_routines.py <name> to verify, then report saved + verified to user
11. /ob-routines execute <name> → streams [action] … [complete]
Failure handling
- Server unreachable:
uv run local-chrome-server serve - Browser UUID invalid: reconnect Chrome extension, get fresh UUID
- 0 events captured: browser disconnected; re-record
- tmux not found:
brew install tmux - tmux window conflict: check
tmux list-windows, use a unique-nname - Compiler session expired (pane exited before finalize): call
POST /recordings/{id}/compileagain to restart — session is fresh - Relay stuck:
[observation:error]lines in SSE stream; relay to user
Signals
- GitHub stars
- 189
- Forks
- 18
- Last commit
- May 2026
ahel review
K1binfo
installs-packagesK6low
bundled executables the agent is told to run
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Catalog kind
- skill
- Gateway key
ob-routines- Source
- github.com/softpudding/openbrowser