Second half of #29. The index was hand-maintained and had already drifted once: dream and experiment-spec shipped with no row, their rows lost to a squash-merge after a directory reorg. bin/check-index caught that after the fact; generating removes the class. The curated summary and trigger move into each SKILL.md as `summary:` and `use_when:`, carried over verbatim from the existing table rather than derived from `description:` — description is a paragraph written for model selection, and truncating it would have produced a worse table than the one it replaced. Knowledge now lives once, next to the skill it describes, which is the argument that made a hand-maintained index the wrong shape to begin with. The table is written between markers, so the wiring docs, the relationships tree and the trigger table below it are untouched — those are prose, not data. Rows are alphabetical now; the conceptual grouping the old order carried lives in the relationships tree, which is the better place for it. A fourth column shows each skill's surfaces, which is what makes #29's field visible rather than merely enforced. bin/check-index and its test are deleted: a generated table cannot drift in either direction, so `generate-index --check` subsumes both of its assertions. Verified by mutation — deleting the tdd row turns `task check` red with "SKILLS_INDEX.md is out of date". Closes #29 Bump-Type: minor
4.4 KiB
4.4 KiB
name, surfaces, summary, use_when, description
| name | surfaces | summary | use_when | description | ||
|---|---|---|---|---|---|---|
| web-shot |
|
Screenshot a running web UI via a koala k3s Playwright job | Visual verification of a running UI when no local browser exists | Screenshot any running web UI by spawning a one-shot Playwright Job on the koala k3s cluster. Auto-detects the calling host's Tailscale name so a dev server on flamingo:8099, iguana:3000 or koala:8181 just works. Use when the user asks for a screenshot, visual verification, "what does the UI look like", or any time chromium / firefox / a browser is requested but none is installed on the host. |
web-shot
Screenshot a locally-running web UI without installing a browser on the agent's host.
Mechanism
shot.shbuilds a k8s Job that runsmcr.microsoft.com/playwright:v1.49.0-jammyon koala's k3s cluster.hostNetwork: trueis not used — the pod uses cluster DNS so Tailscale Magic DNS resolvesflamingo,iguana,koaladirectly when the koala node has Tailscale up (it does).- The pod renders the URL with chromium, drops PNGs into an
emptyDir, thensleeps briefly sokubectl cpcan pull the files out. - PNGs land in
./screenshots/(or--out DIR) on the agent's host.
Prerequisites
kubectlon the calling host with a kubeconfig pointing at koala's k3s.- The dev server binds 0.0.0.0 (not 127.0.0.1) so pods on koala can reach it across Tailscale. For Go:
http.ListenAndServe(":8099", ...). For Node:--host 0.0.0.0. For Vite:--host. - The dev server's host (flamingo, iguana, koala, …) is reachable on Tailscale and Magic DNS resolves the short hostname.
Usage
shot.sh ships beside this SKILL.md, so resolve it from wherever the harness
installed the skill rather than from a checkout path — an absolute ~/dev/...
went stale the first time the repo moved and cost a caller three hand-rolled
Job attempts before they found the working script (#26).
# Claude Code installs the plugin; Crush/Pi/OpenCode get ~/.agents/skills.
SHOT=$(ls ~/.claude/plugins/marketplaces/*/skills/web-shot/shot.sh \
~/.agents/skills/web-shot/shot.sh 2>/dev/null | head -1 || true)
[ -n "$SHOT" ] || { echo "web-shot not installed on this host: run install.sh or the plugin install" >&2; exit 1; }
# auto-detect Tailscale hostname, screenshot port 8099 root
"$SHOT" --port 8099
# explicit URL
"$SHOT" --url http://flamingo:5173
# capture a search state too (types into input[name=q])
"$SHOT" --port 8181 --search agentsquad
# different output dir
"$SHOT" --port 3000 --out /tmp/shots
Flags
| Flag | Default | Notes |
|---|---|---|
--port N |
— | Host port. Combined with --host and --path. |
--path P |
/ |
URL path after the host:port. |
--host H |
auto via tailscale status |
Override the hostname pods will dial. |
--url U |
— | Full URL. Overrides --host, --port, --path. |
--search Q |
— | Also capture a search state — types Q into input[name=q]. |
--out D |
./screenshots |
Output dir (created if missing). |
--name N |
web-shot |
Job name prefix. |
-h |
— | Show usage. |
Output
<out>/home.png— initial page.<out>/search.png— only when--searchwas passed.
Failure modes
kubectl: command not found→ install kubectl + tailscale-mode kubeconfig.connection refusedfrom the pod → dev server bound to 127.0.0.1, fix the bind.No matches for selectorwhen--searchwas given → page lacksinput[name=q]. Use a project-specific script or rerun without--search.- Image pull slow on first run → ~1GB playwright image cached on koala after first use.
When NOT to use
- The site needs auth / cookies / login state — extend the script or use a project-local Playwright suite.
- You need PDF, video, or interaction more elaborate than "type a query and screenshot". Promote to a real Playwright test under the project repo.