focus
Skill0 starsChanged 2 months agoMITView on GitHub
Make the agent's browser attention visible while it works through a site with browser-harness — an animated cursor glides to the element about to be used, a spotlight dims everything else, a scan line sweeps text being read, a chip names the action (LOOKING / READING / CLICKING / TYPING) and a narration bar explains why. Use whenever a person is watching the browser session live or a recording of it: demos, walkthroughs, QA tours, "show me how you did that", "let me watch".
10 other projects use a version of this.
Adopted by
Most recently taken up by
- whawkinsiv/solo-founder-skillstheir own version, first seen Oct 25, 2026
- wheslleyrimar/BuildCLI-Agentstheir own version, first seen Oct 22, 2026
- whawkinsiv/claude-code-skillstheir own version, first seen Oct 17, 2026
- chrdrk/claude-focustheir own version, first seen Oct 13, 2026
- yanfeid/claude-ambertheir own version, first seen Oct 13, 2026
- devinat1/skillstheir own version, first seen Oct 6, 2026
Used with
Projects with it also use build.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
Microsoft AzureSkillbuild
Build the TypeSpec Rust emitter from source. Run this after cloning the repo, after pulling changes, or when TypeScript source files have been modified.
Also in 3 projects that use this
Other versions10 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 10
- whawkinsiv/solo-founder-skills★ 260same name
- whawkinsiv/claude-code-skills★ 258same name
- kennethleungty/claude-music★ 23same name
- wheslleyrimar/BuildCLI-Agents★ 3same name
- yanfeid/claude-amber★ 2same name
- devinat1/skills★ 1same name
- vishk23/screenblock-mcp★ 1same name
- breethomas/bette-os★ 0same name
- chrdrk/claude-focus★ 0same name
- markbaindesign/bain-studio★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository4 other files it gives its agents.
- deep-researchSkill
- design-documentSkill
- geminiSkill
- walkthroughSkill
The file
We split this skill into the rules it gives an agent
12 rules. 0 are common, 7 are distinctive, and 5 are very specific to this project.
0
Common
Most of the 10 edited versions we read keep these.
7
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 8
When a human watches an agent drive a browser, the frustrating part is not the speed, it is the opacity: things change and nobody can tell …
- Distinctive: line 44
Every wrapper returns the same thing the raw helper would, plus the target rect where useful.
- Distinctive: line 61
Dialogs are never buried: When a modal opens and the target is outside it, the spotlight releases.
- Distinctive: line 68
Gone targets release: If the element under the ring is removed by a re-render, the ring fades instead of framing empty space.
- Distinctive: line 73
When you are weighing candidates (which button is checkout, which link is the pricing page), do not just click one.
- Very specific: line 130
If step k fails there, only the k steps before it are performed visibly and the result has stopped_at, why and ahead, the map of what the …
- Very specific: line 144
Never guess a wait. focus_settled() returns when the page DOM has been quiet for 0.3 s.
- Distinctive: line 195
No sub-agent is needed: the interrupt lands in your main loop, which is exactly where the next decision is made.
- Very specific: line 200
Announce, locate, act: Never act on something the viewer has not seen highlighted.
- Very specific: line 206
Read visibly: When you extract text for a decision, use focus_read on the region so the viewer sees which part of the page the decision …
- Distinctive: line 213
Respect the dock: Never bypass the gate with raw helpers when a viewer is present.
- Very specific: line 215
Pace for humans: Default pacing is about one action per second plus reading time.
“Common” is measured: kept by at least 6 in 10 of the 10 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
focus — show where the agent is looking
When a human watches an agent drive a browser, the frustrating part is not the speed, it is the opacity: things change and nobody can tell what the agent saw or intended. This skill adds a thin visual layer on top of browser-harness so every observation and action is announced, located and animated before it happens, and then happens for real at exactly that spot.
Load it inside any browser-harness driver script:
import os
exec(open(os.path.expanduser("~/.claude/skills/focus/focus.py")).read())focus.py re-injects focus.js into the page on every call, so navigations
and SPA re-renders do not lose the overlay.
The wrappers
| Instead of | Use | What the viewer sees |
new_tab(url) / goto(url) | focus_new_tab(url, label) / focus_goto(url, label) | narration bar stays alive across the navigation |
js("...innerText") | focus_read(selector, label) | spotlight on the region, scan line sweeping it top to bottom, returns the text |
| looking at something | focus_look(selector, label) | spotlight + cursor glide + "LOOKING" chip |
click(x, y) | focus_click(selector, label) / focus_click_at(x, y, label) | cursor glides, presses, ripple, then the real click lands on the same pixel |
type_text(text) | focus_type(selector, text, label) | click into the field, then the characters appear one by one with a caret chip |
press_key("Enter") | focus_press("Enter", label) | narration beat, then the real key |
scroll(...) | focus_scroll(dy, label) | scrolls in visible steps |
| narration | focus_say(text, mood) | bottom bar; mood info, warn (amber, for bugs), ok (green, for verified state) |
wait(4) after a click | focus_settled() / focus_wait_for(selector) | nothing; the script just continues the moment the page is ready |
| clicking a link to see where it goes | focus_peek(selector_or_url) | a dashed PEEKING ring on the link; the page is read ahead without leaving |
| screenshot + probing for selectors | focus_map() / print(focus_brief(m)) | a MAPPING chip; returns every visible interactive element with a verified-unique selector |
| one action per script | focus_run([...steps], label) | a REHEARSING chip while the plan replays in a hidden tab, then each step with its normal choreography |
screenshot(path) | focus_shot(path) | same, but the overlay is in the frame, so it doubles as evidence |
| end of session | focus_clear() | overlay fades out |
Targets are CSS selectors or (x, y) viewport coordinates for canvas UIs.
Every wrapper returns the same thing the raw helper would, plus the target
rect where useful.
Always in motion, and honest about it
The overlay never freezes, even while the driver is between scripts: the cursor drifts gently around its target and the ring breathes. After about two seconds without a call the agent visibly lets go: ring and spotlight fade, the cursor drifts to a rest spot beside the last target, and the chip reads "THINKING · deciding the next step · 12 s" with a running clock, so the viewer never mistakes an old highlight for a current intention. From then on it looks around: every two seconds or so the cursor glides to a nearby clickable thing and rests a faint dashed ring on it, the way eyes scan a page while deciding, until the next real action snaps the ring back to solid. During a rehearsal the chip counts the steps as they replay ("REHEARSING · 3/6 · click Pricing"). Three more things keep the scene truthful:
- Dialogs are never buried. When a modal opens and the target is outside it, the spotlight releases; when the modal reaches into the narration bar's zone, the bar moves to the top of the page.
- Navigations keep the scene. Cursor position, chip, bar text and the chat transcript are snapshotted in the tab and restored the moment the overlay is re-injected, so the first glide on a new page starts from where the cursor was, not from a corner.
- Gone targets release. If the element under the ring is removed by a re-render, the ring fades instead of framing empty space.
Survey — show the options before choosing
When you are weighing candidates (which button is checkout, which link is the pricing page), do not just click one. Call
pick = focus_survey([("#a", "Buy now"), ("nav a[href*=pricing]", "Pricing link"), ("#cta", "Start trial")],
"Which of these leads to the price?")The cursor visits each candidate with a "CANDIDATE 2/3" chip and a soft
spotlight, so the viewer sees the assessment, not only the verdict. In auto
mode it returns None and you decide. In "Ask me" mode the chat offers the
candidates as buttons and it returns the viewer's index, or None for "you
decide".
Peek ahead — read the next page before going there
On an unknown journey most of the wall-clock goes into one-action-per-script
round trips: click, wait, read, decide, next script. focus_peek collapses
that. Give it the links you are considering and it fetches them from the
tab's own origin (cookies included) in parallel, without navigating:
pages = focus_peek(["nav a[href*=pricing]", "nav a[href*=docs]", "https://example.com/faq"],
"Which of these has the price list?")
# each: {url, status, title, text (first 1500 chars), headings, links, forms, via}The viewer sees a dashed PEEKING ring visit each link and a "peeked
ahead: …" line in the chat, so reading ahead is as visible as everything
else. For JS-rendered pages the fetched HTML is thin; mode="auto" then
reads the page in a hidden background tab for about a second and closes it
(mode="tab" forces that). With the peeks in hand, plan the next two or
three actions and run them in one script, each followed by
focus_settled(), so the viewer sees a continuous run instead of stop-and-go.
Map and run — several steps per script
focus_map() is the page in one call: {url, title, headings, text, els}
where each element is {i, tag, text, sel, rect, href?, type?, name?,
value?, options?} and sel has been checked unique in the document (shadow
DOM elements carry shadow: true and a rect for focus_click_at). Read it
with print(focus_brief(m)), then write the next few steps:
r = focus_run([
("type", "#q", "colmar", "Searching for the destination"),
{"kind": "press", "key": "Enter", "expect": "url:results"},
{"kind": "click", "target": "a[href*=details]", "label": "Opening the first result", "expect": "text:Price"},
("read", ".price", "The price we came for"),
], "Search, open the first result, read its price")
print(r["ok"], r["done"], focus_brief(r["map"], 30))Steps: click, type, press, goto, wait, read, scroll, say, as
tuples or dicts; any step may carry expect (url:…, text:…, js:… or a
selector) and timeout. The plan is first replayed in a hidden tab that
shares the session (DOM-level clicks and typing, so it never needs focus).
If step k fails there, only the k steps before it are performed visibly and
the result has stopped_at, why and ahead, the map of what the failing
step actually produced. rehearse=False skips the hidden run: use it for
anything with side effects, since the hidden tab performs the steps for real.
The controlled tab is brought to the front automatically when it is hidden
(a hidden tab neither animates nor accepts input).
Faster
Pacing is meant for humans, but the gaps should be yours, not the tool's:
- The choreography is tight by default (glide 0.45 s, click 0.24 s, read
≤ 3 s);
focus_speed(2)halves it again, and the viewer can do the same from the bubble. - Never guess a wait.
focus_settled()returns when the page DOM has been quiet for 0.3 s;focus_wait_for("#result")returns the instant the selector is visible. Both keep polling for Esc, messages and boxes. - Batch. A script is cheap; a decision is not. Map or peek first, then put
every step you are already sure about into one
focus_run.
The chat bubble — the viewer talks back
Bottom-right is a small chat bubble (unread badge, green/amber/red status dot).
It opens a compact panel: the transcript of everything you said with
focus_say, the viewer's messages, and the controls. Every wrapper passes
through a gate that honours them, and every pause polls, so interrupts land
within about a third of a second:
| Control | Effect on the driver |
| Pause / Resume | the next action blocks until Resume. While paused the page is the viewer's: they can log in, fix a form, scroll around. |
| Step | lets exactly one action through, then pauses again. |
| Stop | raises FocusStopped at the next action; the overlay stays, red dot. |
| Esc anywhere on the page | exit: the overlay disappears at once and the script ends with FocusStopped at its next action. The page is the viewer's. Only if they ask you to carry on, focus_resume() brings it back. In P mode Esc cancels the draft box instead, a second Esc leaves the mode. |
| 1× / 2× / 0.5× | changes pacing live (same as focus_speed). |
| Ask me | manual mode: every click, keystroke, typing and navigation posts a "NEXT · CLICK …" card and waits for Approve or Skip. Skip makes the wrapper return False without acting. Also focus_mode("manual") or FOCUS_MODE=manual. |
| Message box | raises FocusMessage with .messages. |
P — the viewer points at things
Pressing P anywhere on the page (outside a text field) pauses the agent and enters annotation mode: an amber hint appears, the cursor becomes a crosshair, and the viewer drags boxes over anything they want you to look at, typing a short note per box (Enter commits, Esc cancels). Pressing P again sends the boxes and resumes.
On the driver side the boxes arrive as FocusAnnotations, raised at the next
gate or pause, with:
.boxes: one dict per box withn,note,rect(viewport),page(document coordinates),element(tag, id, classes, a CSS selector, its text) for the element under the box centre, andtext, the visible text inside the box;.screenshot: a PNG taken the moment they arrived, boxes and notes drawn.
The boxes stay on the page, dashed, until you answer with
focus_ack("…"), which posts a green reply and removes them.
Protocol for interrupts. A driver script is one browser-harness run, so
FocusMessage, FocusAnnotations and FocusStopped end that run with a
readable "FOCUS: …" line in the output. Read it (open the screenshot for
boxes), answer with focus_say or focus_ack, and continue with a new
script. Chat state, mode and speed live in the tab and survive navigations,
so nothing is lost between scripts. After a pause or takeover, re-assert the
page (URL plus a selector) before continuing: the viewer may have moved.
No sub-agent is needed: the interrupt lands in your main loop, which is
exactly where the next decision is made.
Rules
- Announce, locate, act. Never act on something the viewer has not seen
highlighted.
focus_clickandfocus_typedo this for you. If you fall back to a raw helper, callfocus_lookfirst. - Say why, not just what.
focus_say("Checking the price before adding to cart — the listing said €12")beatsfocus_say("Clicking add to cart"). The chip already names the mechanical action. - Read visibly. When you extract text for a decision, use
focus_readon the region so the viewer sees which part of the page the decision came from. - Never fake. The overlay only decorates. Clicks are real CDP mouse events
at the cursor tip; typing is real
Input.insertText. Do not animate a click without performing it, and do not perform one you did not animate. - Bugs go amber. Anything unexpected gets
focus_say(..., "warn")before you work around it, so the viewer never sees a silent retry. - Respect the dock. Never bypass the gate with raw helpers when a viewer is present. If a message arrives, answer it before doing anything else.
- Pace for humans. Default pacing is about one action per second plus
reading time.
focus_speed(2)for a viewer who knows the flow,focus_speed(0.6)for a demo audience.FOCUS_SPEED=0turns pauses off for unattended runs while keeping the overlay for the screenshots. - Clear at the end.
focus_clear()when the session is over, so the user's tab is left clean.
Walkthrough integration
The walkthrough skill (and /wt) should load focus and use these wrappers
in place of its own narration-bar snippet. focus_say is the narration bar;
focus_click / focus_type / focus_read are the step actions. Everything
else in walkthrough (fresh persona, backend verification, bug log, run record)
is unchanged.
Demo
browser-harness < ~/.claude/skills/focus/demo.pyOpens Wikipedia in a new tab, surveys three candidates, peeks at two links
without leaving, reads the intro, types a search, opens a result, reads its first paragraph, takes evidence
screenshots into /tmp/focus-demo-*.png and clears the overlay. Press P
during the run to try annotation mode.
Extending
focus.js is plain DOM and CSS. Colours are two constants (ACCENT for the
agent, HUMAN for the viewer's marks). New
choreography goes in as another window.__focus.<verb> returning a Promise;
mirror it with a focus_<verb> wrapper in focus.py. Keep animations under
700 ms and everything pointer-events: none, so the overlay can never eat a
click meant for the page.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

