@lovinka/vitrinka
v1.12.0
Published
vitrinka CLI + MCP server — capture and publish artifact sets, drive the annotation-board work queue from any MCP-aware AI.
Maintainers
Readme
@lovinka/vitrinka
The vitrinka client — one package, two bins:
vitrinka— the CLI: capture screenshots into.screenshots/, build a self-contained gallery, publish artifact sets, scaffold interactive artifacts, and arm the annotation-boardwatchmonitor.vitrinka-mcp— the MCP server: lets any MCP-aware AI browse artifact sets and drive the annotation-board work queue (pick up an annotation → fix the app → attach the after-shot → reply) without touching the web UI.
Both share the same token / operator / base-url / actor-encoding conventions,
single-sourced in src/lib/ (previously duplicated between the CLI and the MCP
server — the latin-1 X-Board-Actor encoding bug once had to be fixed in three
places). The MCP server is a thin, stateless stdio process: every tool call
becomes one HTTPS request against the deployed vitrinka Go server.
Install
# both bins on PATH
npm i -g @lovinka/vitrinka
# one-command machine setup — status-first onboarding: prints a ✓/✗ table, silently
# installs the Claude skills bundle (vitrinka + listen, screenshot, artifact,
# brainstorming) + the /vitrinka:listen command stub + PATH shim + shell completion,
# then asks [Y/n] only for the missing config-touchers: MCP registration, write
# token (silent input), operator persona, Claude Code UI wiring.
# (npm install itself has NO side effects; skills land only via this command or
# `vitrinka install-skills`.)
vitrinka install
# later: update the CLI, see what's new, refresh everything a previous install put
# here (npm i -g @lovinka/vitrinka@latest when behind — or git pull in a checkout —
# then re-copies the skills bundle + /vitrinka:listen stub and refreshes the
# Claude UI wiring where it was already consented to; never asks new questions)
vitrinka update
# health check / repair / clean removal
vitrinka doctor # server, token, operator, skills, shim, MCP, UI wiring
vitrinka doctor --fix # repairs the repairable (skills, shim, MCP, UI wiring)
vitrinka uninstall # removes everything install put here (--purge: token too)Registering the MCP by hand (install does this for you when the claude CLI is
present): claude mcp add --scope user vitrinka -- npx -y --package=@lovinka/vitrinka vitrinka-mcp
vitrinka install also offers (interactive [Y/n], or --claude/--no-claude)
to wire the Claude Code UI — re-runnable standalone as vitrinka install-claude:
- Footer badge — a
footerLinksRegexesentry in~/.claude/settings.jsonrenders a clickable 🧷 board badge in Claude Code's footer whenever a/boards/URL appears in the conversation (clickable in every terminal; needs Claude Code ≥ 2.1.176). - Statusline segment —
~/.claude/vitrinka-statusline.shwraps your existing statusline command (recorded on itsVITRINKA_ORIG=line) and appends a🧷 <board-url>segment when the session's.screenshots/.vitrinkadescriptor exists (plain-text URL — Claude Code strips OSC 8 from statusline output, and terminals cmd+click-linkify visible URLs themselves); with no statusline configured it renders a minimaldir │ branch │ 🧷 board │ modelline itself. Needsjq; degrades to pass-through without it, and never double-renders a board segment your own statusline already prints. - Session-start board link —
~/.claude/hooks/vitrinka-board-link.sh, aSessionStarthook that prints the repo's live board URL into session context at start, so the clickable footer badge exists from message one (the statusline URL isn't clickable in every terminal). Silent no-op in repos without a.screenshots/.vitrinkadescriptor; needsjq. - App badge (only when Vitrinka.app is installed) — a second
footerLinksRegexesentry that renders ⧉ app next to the board badge; itsvitrinka://deep link opens the same board in the desktop app (which registers only the custom scheme — https stays in the browser).
Desktop-app flag — ~/.config/vitrinka/desktop-app is the ONE canonical
"is Vitrinka.app installed here" answer (content = the app path). apps/desktop's
make install stamps it; install-claude and doctor re-sync it to reality
(and add/remove the app badge accordingly). Skills and tools should test this
flag file instead of probing /Applications themselves — vitrinka open uses
it to open boards in the app (pass --browser to force the browser).
All steps are idempotent — re-runs detect the wiring and no-op. To remove:
delete ~/.claude/vitrinka-statusline.sh and ~/.claude/hooks/vitrinka-board-link.sh,
restore statusLine.command in ~/.claude/settings.json to the VITRINKA_ORIG
value (or drop the key), and remove the footerLinksRegexes entry whose pattern
contains /boards/ plus the hooks.SessionStart entry running vitrinka-board-link.
For Claude Desktop or any other MCP client, add the equivalent entry by hand:
{
"mcpServers": {
"vitrinka": {
"command": "npx",
"args": ["-y", "--package=@lovinka/vitrinka", "vitrinka-mcp"],
"env": {
"VITRINKA_BASE_URL": "https://vitrinka.in",
"VITRINKA_TOKEN": "…"
}
}
}
}vitrinka is reachable over the WireGuard mesh — bring the VPN up (or point
VITRINKA_BASE_URL at any reachable host) before use.
Configuration (env)
| Var | Default | Purpose |
|---|---|---|
| VITRINKA_URL / VITRINKA_BASE_URL | https://vitrinka.in | vitrinka server base URL. VITRINKA_URL wins, then VITRINKA_BASE_URL; the CLI's --base flag overrides both. |
| VITRINKA_TOKEN | — | Bearer write token. Falls back to ~/.config/vitrinka/token. Required only by the mutating tools; read tools work unauthenticated. |
| VITRINKA_OPERATOR | — | Operator persona name. Falls back to ~/.config/vitrinka/operator (written by vitrinka install onboarding). When set, write calls carry X-Board-Actor: <name> so the board credits the operator's machine — agent-authored content (reply, attach_after) keeps its explicit claude attribution. |
MCP tools
Artifacts library
list_projects— all projects (name, branch/set counts, last update).list_sets— a project's (or one branch's) artifact sets, newest first.get_set— one set's metadata + full file list + share URL.rename_set— set/clear a set's custom URL-addressable name. (write)update_set— edit a set's custom name and/or human title. (write)
Annotation-board work queue
list_boards— the annotation boards (canvases over screenshot sets).ask_board— post a first-class multiple-choice question on a board. (write)scrape_board— read a whole board as one structured testing digest.wait_for_work— block (long-poll) until annotation work exists.list_work— the agent queue (FIFO); filter by status / board / project+branch.get_annotation— one annotation's full brief.get_capsule— one annotation's compact markdown work brief.set_status— move an annotation through its lifecycle. (write)reply— post a reply into an annotation's thread asclaude. (write)attach_after— attach the "after" proof shot to an annotation. (write)highlight— draw an ink rectangle on an annotation's card (live via SSE). (write)
Agent loop: list_work → set_status working → fix the app → push a new set
(vitrinka snap/push) → attach_after → reply → set_status in_review.
Only the user resolves (accepts in the board UI) — never set status
resolved yourself.
Unattended worker
When no listening Claude session is open, the fallback worker long-polls the
queue and spawns one claude -p per annotation:
vitrinka-mcp work --repo ~/Documents/Work/FixIt [--board <slug>] [--grace 8]Local development
cd pkg
npm install
npm run build # tsc → dist/ (rewrites .ts import specifiers to .js on emit)
npm test # node --test tests/ (spawns the server/worker over real stdio + unit-tests src/lib)
npm run typecheck
node dist/cli.js help # CLI
node dist/bin-mcp.js # MCP server over stdio (Ctrl-C to stop)The source (src/*.ts, src/lib/*.ts) is dependency-free, erasable TypeScript
(node builtins + global fetch only) and runs directly under Node 24 via type
stripping — that is how a repo checkout registers the MCP server:
claude mcp add --scope user vitrinka -- node <repo>/mcp/server.ts # shim → pkg/src/mcp.tsThe published dist/ is plain ESM and runs on Node ≥ 20.
Release (Lovinka org)
npm login # as the lovinka npm org
cd pkg
npm publish --access public # prepublishOnly runs the buildfiles is a whitelist (dist + README.md), so npm pack --dry-run should
list only the compiled output — verify before publishing.
Deprecating the old MCP-only package
@lovinka/vitrinka-mcp (the previous MCP-only package) is superseded by this
unified package. After @lovinka/vitrinka is published, deprecate it:
npm deprecate @lovinka/vitrinka-mcp "moved to @lovinka/vitrinka (vitrinka-mcp bin)"Existing npx -y @lovinka/vitrinka-mcp registrations keep resolving (deprecation
only prints a warning); migrate them to
npx -y --package=@lovinka/vitrinka vitrinka-mcp at leisure.
License
MIT
