From quiver
Opens a browser-based companion for showing mockups, wireframes, architecture diagrams, and side-by-side visual comparisons during brainstorming sessions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/quiver:visual-companionWhen to use
user wants visual mockups, UI diagrams, or architecture sketches -- '/visual-companion', 'show me a diagram', 'sketch this UI', 'visualize this', 'open visual companion'
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
A browser-based companion for displaying mockups, wireframes, architecture diagrams, and side-by-side visual comparisons during brainstorm sessions.
A browser-based companion for displaying mockups, wireframes, architecture diagrams, and side-by-side visual comparisons during brainstorm sessions.
Decide per question whether to show content in the browser or keep it in the terminal.
Use the browser for:
Use the terminal for:
A question about a UI topic is not automatically a visual question. "Should we use tabs or accordion?" is a terminal question. "Here are two tab layouts -- which one?" is a browser question.
Create a temporary directory for HTML files:
mktemp -d /tmp/visual-companion-XXXXXX
Start the visual companion server in the background:
python3 <skill-dir>/server.py --dir <temp-dir> &
<skill-dir> is resolved by the invoking agent to the absolute path of skills/visual-companion/.
Do NOT pass --owner-pid -- in the Bash tool context, $$ resolves to the ephemeral shell PID that dies immediately, causing the server to self-terminate within seconds. The server already has idle timeout (30 min) and max lifetime (8h) for cleanup.
Wait for the server to become ready before announcing its URL. The & in step 2 returns immediately; the Python process still needs to import modules, bind the port, and write server-info.json. Announcing the URL before this finishes is the #1 cause of "localhost opened but link unreachable" failures. Probe until both server-info.json exists and the port accepts TCP connections:
for i in $(seq 1 30); do
[ -f <temp-dir>/.vc-meta/server-info.json ] && \
python3 -c "import json,socket; info=json.load(open('<temp-dir>/.vc-meta/server-info.json')); s=socket.socket(); s.settimeout(0.5); s.connect(('127.0.0.1', info['port'])); s.close(); print(info['url'])" 2>/dev/null && break
sleep 0.2
done
<temp-dir>/.vc-meta/ and the background-process output, fix the underlying issue, and retry step 2.Tell the user (only after step 3 prints a URL):
Visual companion running at {url}. Open it in your browser.
Push an initial HTML page immediately. Do NOT enter the question flow while the browser shows the "waiting" page -- the user sees a broken-looking screen and loses confidence. Write a context-setting HTML fragment to <temp-dir> right now, before any AskUserQuestion call. The content depends on the session:
For each visual step in the brainstorm:
<temp-dir> with a semantic filename (e.g., layout-options.html). Write body content only -- the server wraps fragments automatically.layout-options-v2.html. Older files stay on disk so the agent can copy them out at the end of the session./ and auto-shows the newest file. The server routes the root URL to the most recently modified .html file in the temp dir; SSE reload triggers whenever any HTML file changes. The user opens the URL once and never has to navigate manually -- each new file replaces the previous view automatically.A step is either browser-answered or terminal-answered. Do not do both in the same step. Mixing them produces the common failure: agent renders clickable cards, user clicks them, agent is actually blocked on AskUserQuestion -- clicks land in events.jsonl that the agent never reads, and the user sees their clicks "do nothing."
Mode A -- Browser-answered (the cards ARE the question):
Use when the whole point is "pick one of these visual options."
Pre-write checklist (mandatory before every Mode A HTML write):
data-choice="<value>" attribute. The client JS ignores elements without it -- styled cards that look clickable but lack data-choice are a silent bug (clicks vanish, user sees nothing happen).AskUserQuestion in Mode A. The browser click IS the answer.python3 -c "import urllib.request; req=urllib.request.Request('http://localhost:{port}/events', method='DELETE'); urllib.request.urlopen(req)"
[data-choice] elements. The client JS highlights the clicked element and POSTs to /event, which appends a line to .vc-meta/events.jsonl.> Click one of the cards in the browser to select your choice.events.jsonl until a new line arrives (up to ~60s):
for i in $(seq 1 300); do
line=$(tail -n 1 <temp-dir>/.vc-meta/events.jsonl 2>/dev/null)
[ -n "$line" ] && echo "$line" && break
sleep 0.2
done
choice field as the answer.AskUserQuestion and treat the step as Mode B from this point on.Mode B -- Terminal-answered (browser is visual aid only):
Use when the browser shows a diagram, mockup, or reference and the real answer is a conceptual choice or free text.
[data-choice] attributes. Clickable styling with no listener on the agent side is the bug that bit us before -- if you are not going to poll events.jsonl, do not ship clickable targets.AskUserQuestion. The browser is decoration; the button click is the answer.Terminal-only steps (no browser): No HTML update needed. Optionally push a placeholder page:
<div style="display:flex;align-items:center;justify-content:center;height:100vh;font-family:system-ui;color:#666">
<p>Continuing in terminal...</p>
</div>
Write body content only. The server wraps fragments with DOCTYPE, CSS, and client JS automatically.
A file is treated as a fragment if its first 256 bytes (stripped, case-insensitive) do not start with <!DOCTYPE or <html>. Full HTML documents are served as-is, with client JS injected before </body>.
.options + .option for comparing 2-3 approaches visually. Mark the recommended option with .recommended..cards + .card for component galleries or feature inventories..mockup with .mock-nav, .mock-sidebar, .mock-content for wireframes..split for before/after or A/B comparisons..pros-cons for tradeoff visualization..mock-button, .mock-input for wireframe form elements.data-choiceAdd data-choice attributes to elements that users can click to make selections:
<div class="options">
<div class="option" data-choice="grid">
<h3>Grid Layout</h3>
<p>Content arranged in a responsive grid</p>
</div>
<div class="option" data-choice="list">
<h3>List Layout</h3>
<p>Content in a vertical list</p>
</div>
</div>
When a user clicks a [data-choice] element:
.selected class is added (blue border + light blue background).vc-meta/events.jsonl:
{"type":"choice","choice":"grid","text":"Grid Layout Content arranged in a responsive grid","timestamp":"2026-04-09T14:30:00.000Z"}
Each line is a JSON object appended by the server. The agent reads this file to see what the user clicked:
{"type":"choice","choice":"option-a","text":"Option A","timestamp":"..."}
{"type":"choice","choice":"option-b","text":"Option B","timestamp":"..."}
The server self-terminates in two cases:
--max-lifetime <seconds>; set to 0 to disable.Note: --owner-pid exists in server.py but is NOT used in the startup template above. In the Bash tool context, $$ resolves to an ephemeral shell PID that dies immediately, making the owner-PID mechanism unreliable. The idle timeout and max lifetime are sufficient.
To stop manually:
kill $(cat <temp-dir>/.vc-meta/server-info.json | python3 -c "import sys,json; print(json.load(sys.stdin)['pid'])") 2>/dev/null
HTML files remain in the temp directory for reference. Optionally copy key mockups to docs/brainstorms/ alongside the spec:
cp <temp-dir>/final-layout.html docs/brainstorms/YYYY-MM-DD-<name>-mockups/
Trigger: Used by /brainstorm Step 1.5 when the user opts into the visual companion. Not directly slash-invoked.
Setup:
Expected behavior:
/brainstorm reads this skill, starts the companion server (server.py) in a temp directory, and points the user to the local URL.<temp-dir>/.vc-meta/server-info.json.docs/brainstorms/.Verification checklist:
/brainstorm only offers the companion when the topic is visual (UI/UX, layout, architecture diagrams).server.py, prints a local URL, and serves written HTML files.<temp-dir>/.vc-meta/server-info.json and used for clean shutdown.Known gotchas:
server.py); never assume skill dirs are prompt-only.pkill -f server.py is needed./ or /index.html) routes to the most recently modified HTML file in the serve dir; if none exists, it returns a built-in "waiting" landing page that subscribes to SSE. Both responses inject the SSE client, so the browser auto-reloads when the agent writes or updates any HTML. Specific paths (e.g. /foo.html) still 404 when missing -- do not rely on root routing to mask broken links.log_request back to unconditional reset; do not remove --max-lifetime.npx claudepluginhub yagizdo/quiver --plugin quiverProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.