canvas
Skill1 starsChanged 2 months agoApache-2.0View on GitHub
Run a visual planning/brainstorm/collaboration session on Helix Canvas — the local canvas app where Claude pushes cards (questions, options, decisions, diagrams, artifacts) and the user answers in the browser. Use when the user says "/helix-canvas", "visual brainstorm", "open a canvas", or wants to collaborate on the canvas instead of the terminal. The classic `brainstorm` skill stays untouched for terminal/doc sessions.
45 other projects use a version of this.
Adopted by
Most recently taken up by
- insight68/Skillstheir own version, first seen Nov 8, 2026
- iblai/higher-education-agentstheir own version, first seen Nov 8, 2026
- QVerisAI/QVerisBottheir own version, first seen Nov 1, 2026
- AgriciDaniel/claude-obsidiantheir own version, first seen Oct 25, 2026
- simota/agent-skillstheir own version, first seen Oct 23, 2026
- ArangoGutierrez/claude-toolkittheir own version, first seen Oct 15, 2026
Used with
Projects with it also use taskflow-inbox-triage, update-cli-config and 10 more.
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.
AtomicBot-aiSkilltaskflow-inbox-triage
Also in 5 projects that use this
PandelisZSkillupdate-cli-config
View and modify Cursor CLI configuration settings in ~/.cursor/cli-config.json. Use when the user wants to change CLI settings, configure permissions, switch approval mode, enable vim mode, toggle display options, configure sandbox, or manage any CLI preferences.
Also in 6 projects that use this
jiulingyunSkillBlueBubbles
为Clawdbot构建或更新BlueBubbles外部通道插件(扩展包,REST发送/探测,webhook入站)。
Also in 15 projects that use this
seedprodSkillvoice-call
Start voice calls via the OpenClaw voice-call plugin.
Also in 14 projects that use this
32103210Skillmigrate-to-skills
Convert 'Applied intelligently' Cursor rules (.cursor/rules/*.mdc) and slash commands (.cursor/commands/*.md) to Agent Skills format (.cursor/skills/). Use when you want to migrate rules or commands to skills, convert .mdc rules to SKILL.md format, or consolidate commands into the skills directory.
Also in 6 projects that use this
32103210Skillupdate-cursor-settings
Modify Cursor/VSCode user settings in settings.json. Use when you want to change editor settings, preferences, configuration, themes, font size, tab size, format on save, auto save, keybindings, or any settings.json values.
Also in 6 projects that use this
- +1 more from 32103210
Other versions45 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 45
- AgriciDaniel/claude-obsidian★ 16ksame name
- spinabot/brigade★ 11ksame name
- the-open-agent/openagent★ 5.7ksame name
- casibase/casibase★ 5.7ksame name
- linuxhsj/openclaw-zero-token★ 5.2ksame name
- jiulingyun/openclaw-cn★ 4.7ksame name
- Bitterbot-AI/bitterbot-desktop★ 2.5ksame name
- phodal/routa★ 1.8ksame name
- OpenBMB/EdgeClaw★ 1.2ksame name
- microsoft/vscode-dotnettools★ 331same name
35 more not listed
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.
The file
We split this skill into the rules it gives an agent
11 rules. 0 are common, 4 are distinctive, and 7 are very specific to this project.
0
Common
Most of the 43 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 11
If HELIX_ORCHESTRATOR_RUN_ID or HELIX_SKILL_CREDENTIAL_FILE is set, this skill runs in Helix Console.
- Very specific: line 13
Use the normal helix <surface> up, push, state, and wait commands.
- Very specific: line 14
Do not start local servers or Fleet, open a browser, curl localhost, use --local, log in, or change profiles/projects.
- Distinctive: line 63
Before proposing anything, read the rails that bind this project on this surface:
- Very specific: line 91
Run explicit rounds: push the round's questions/options as cards (grouped by threadId — threads are the stages/columns), then wait.
- Very specific: line 184
Use printf '%s' when piping JSON in shell — zsh echo mangles \n.
- Distinctive: line 186
Call the binary by its full path — a shell variable or function holding it stops the skill's pre-approval from matching, and every call …
- Distinctive: line 220
If a turn ends while you still have canvas work of your own, the same rule applies — push a status card first, do the work, and re-arm.
- Very specific: line 278
Update = push the same JSON with the existing id.
- Distinctive: line 331
Update the project's memory file with: canvas slug, port, cursor reached, open blockers, ratified decisions, artifact statuses, and gate …
- Very specific: line 333
Before stopping, clear spent gates — every user-owned todo/kanban/gantt that is no longer waiting on the user gets updated to done or …
“Common” is measured: kept by at least 6 in 10 of the 43 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
Helix Canvas
Orchestrator cloud context (check before setup)
If HELIX_ORCHESTRATOR_RUN_ID or HELIX_SKILL_CREDENTIAL_FILE is set, this skill runs in Helix Console. This section takes precedence over local setup and browser instructions below.
- First call the plugin's
bin/helix orchestrator-protocol(Windows:bin/helix.cmd orchestrator-protocol). It must print1. Otherwise stop with an actionable request to update the Helix plugin/CLI; never run an older binary's setup commands. - Use the normal
helix <surface> up, push, state, and wait commands. The CLI selects cloud transport and the assigned project automatically. Keep the same slug within this task. - Do not start local servers or Fleet, open a browser, curl localhost, use
--local, log in, or change profiles/projects. Do not read or print the credential file. Do not remove the injected environment variables. upreturns the Console session URL. Use that URL wherever the local instructions request Board/Fleet links. Events go to Console; human responses and gate approvals still come from the person through the normal wait protocol.- On cloud or compatibility errors, report the error. Never fall back to local mode. Outside this context, follow the local behavior below unchanged.
Drive a collaboration session through Helix Canvas (part of Team Helix). You push typed cards over a CLI; the user answers, comments, authors notes, and drags things in a browser. The append-only event log is the record; the canvas is the surface; the terminal is the fallback channel.
Setup
Three verbs, never more (ratified contract):
# start server, open browser; prints {url, port, cursor, viewers}
${CLAUDE_PLUGIN_ROOT}/bin/helix canvas up --canvas <slug> --agent <name> --model <model-id> [--no-open] [--brief "<the job>"]
# card JSON on stdin -> {cardId, cursor}; existing id -> update
${CLAUDE_PLUGIN_ROOT}/bin/helix canvas push --canvas <slug> # card JSON (one object, or an array of them) on stdin
# blocks; exit 0 delta, 3 timeout, 4 no-viewer, 5 server down
# --wake-on user returns only when a human acted (see Draining)
${CLAUDE_PLUGIN_ROOT}/bin/helix canvas wait --canvas <slug> --since <cursor> [--timeout <s>] [--wake-on user]On Windows, ${CLAUDE_PLUGIN_ROOT}\bin\helix.cmd in place of bin/helix; the
rest of the command is the same.
upopens the browser itself, from inside the binary.--no-openis for headless or scripted runs — never a way around a permission prompt. If a prompt is declined, hand the user! open <url>and carry on.upalso brings up Fleet (mission control) when it is down, and returnsfleet: {url, started}. Hand the user both links in one line —Board: <url> · Fleet: <fleet.url>— so they know the overview is there.upnever opens Fleet in the browser; the user follows the link.- Identify yourself on
up(ratified): pass--agentwith your product name, lowercase (claude,codex, …) and--modelwith your exact model id (e.g.claude-fable-5). Fleet shows and filters sessions by them; a session that registers without them shows as anonymous. The session id is picked up from the environment on its own.
Canvas data lives in ~/.helix/canvas/<project>/<slug>/ (SQLite event log,
server.json with port/pid, uploads) — one home for the whole machine, never
inside a repo. The server watches files for file-window/artifact cards and
streams changes to the browser.
Rails first
Before proposing anything, read the rails that bind this project on this surface:
${CLAUDE_PLUGIN_ROOT}/bin/helix rails for --surface canvasWhat comes back is not advice — it is the standard the work is held to. Cite
rail ids when you act on them; full text with helix rails get <id>. Details
in the helix-rails skill.
Session flow
- Confirm the slug with the user (new canvas or resume — list
~/.helix/canvas/<project>/if unsure). Never guess. Canvases are per-project by scope: the cwd names the project; pass--dirto target another one. ${CLAUDE_PLUGIN_ROOT}/bin/helix canvas up --canvas <slug> --brief "<the job, in your words>"— reuses a live server or starts one. Pass--briefwhenever the user has already told you what this is about. An empty board with a brief opens working, that line under the mark; without one it asks What are we working on? — which reads, to someone who just told you, as though you did not hear them. The brief is also your first status pill.- Blank canvas (no topic given): bare
up, no--brief. The app shows the centered prompt;upandwait, and the user's opening prompt arrives as apromptcard in the delta — begin from it. - On resume, fetch state first:
curl -s http://127.0.0.1:<port>/api/state— cards, responses, cursor. Summarize open items to the user before pushing anything. - Run explicit rounds: push the round's questions/options as cards
(grouped by
threadId— threads are the stages/columns), then wait. - Between rounds, drain the delta continuously — react to every user note/comment within one cycle, even mid-build ("built", "on it", or a thread reply). Never let user input sit unacknowledged. Notes that add work fold into the existing claude-owned todo (push the same card id with the new items), or into one "additional requirements" list when they are genuinely a new track — never a new todo card per note; a board with a list for every remark is unreadable.
- Keep the status pill honest:
--briefsets the first one; after that push{"type":"status","payload":{"text":"…"}}when starting a chunk of work, when going idle, and at hand-offs — it renders bottom-left and is how the user knows you're alive mid-build. - Fold ratified decisions into the session's doc/memory as usual.
Ownership & gates (two-way cards)
todo, kanban, gantt, and prototype carry an optional payload.owner:
claude(default) — a progress display; read-only for the user; update it as you work (push with the sameid).user— a gate: rendered ochre and marked yours; the user clicks todo items (pending → active → done), drags kanban cards between columns, toggles their gantt lane. Every change arrives in your delta ascard.updatedwithorigin: "user"— treat these as the signals for when you may proceed. WATCH for updates on any card you gate on.
Pick the right gate. A user-owned checklist is for hand-offs where
every item is genuinely required — the session (and Fleet's attention
queue) treats the card as pending until all items are done. A confirmation
or sign-off is not a checklist: push a decision card (Ratify/Decline — a
Decline is what spawns the "why?" question, per the rejection protocol), or
options when there are real alternatives. Never pad a checklist with
filler items like "one more thing (leave a note)" — notes are always
possible via the card's thread, and an untickable item keeps the session
flagged in Fleet forever. An item that is truly optional gets
optional: true; it stays clickable but never holds attention. When a gate
has served its purpose, update it (all required items done) or delete it;
user-owned gantt cards count as pending for as long as they exist, so hand
them back (owner: "claude") or delete them when the hand-off ends.
Ratified gates. User-owned kanban and prototype cards close on an
explicit Ratify button instead of board/dial state — the user decides
when the arrangement or configuration is final. Ratifying freezes the card
and answers it: the delta carries response.added with kind: "ratify"
holding the final columns (kanban) or values (prototype). Gate on it
with wait --card <id>, then read the response and apply the result. A
claude-owned prototype is an example — playable, never pending.
The dual gantt uses payload.lanes: {claude: [tasks], user: [tasks]} — one
chart, two lanes, the user lane toggleable.
The user's screenshots are yours to read
Attachments the user adds (📎 or clipboard paste) are stored in
~/.helix/canvas/<project>/<slug>/uploads/ and referenced as /api/upload/<file>.
Read the file with the Read tool and look at it — users paste
screenshots of visual bugs; diagnosing from the actual pixels is the
highest-bandwidth feedback loop this system has. Reference the same
uploads in gallery cards to show them back.
The wait loop (cursor discipline — CRITICAL)
Two symmetric failure modes, both real:
- Skipping the past:
--sincefrom a push response silently drops events that arrived between your last drain and that push. Only use the cursor from your last drain or a fresh/api/statefetch. - Waiting for the past: a fresh state cursor sits after everything
already logged — if the user answered while you were building, the wait
never sees it. So when waiting on a specific card, always pass
--card <id>: the server returnsalready-answeredwith the responses instantly if the log already holds them. When waiting generally, first inspect the/api/statesnapshot for answers you haven't processed.
What counts as a human acting — and so what --wake-on user returns on —
is user response.added (kind ≠ reaction), user card.created, user
card.updated (gate ticks, board moves, lane toggles), and client.error.
card.updated is in there deliberately: without it you would sit through
answered gates.
During an active round, drain in the foreground (one Bash call holds ≤9 min):
${CLAUDE_PLUGIN_ROOT}/bin/helix canvas wait --canvas <slug> --since <cursor> --timeout 540 --wake-on user--wake-on user does the holding: viewer churn, your own cards and reactions
advance the cursor inside the call, and it returns only when a human actually
acted. Exit 0 delta on stdout, 3 quiet, 5 server down — restart on the same
port. Bake the slug and cursor in as literal values; the cursor is the one
up printed, or the one your last wait returned.
- Use
printf '%s'when piping JSON in shell — zshechomangles\n. - Heredocs (
<<'EOF') for push payloads, neverecho. - Call the binary by its full path — a shell variable or function holding it stops the skill's pre-approval from matching, and every call prompts.
client.errorevents in the delta are app-side render failures — fix them; don't wait for the user to report.
Never end a turn without a wait running (ratified)
A canvas session has no "probably done". Gates the user hasn't ticked,
threads they might reply in, notes they might drop at 2am — all of it must
resume you. So the last tool call of every turn, for as long as the
session is open, is an endless wait launched with run_in_background —
even when nothing appears to be pending. When it exits you are re-invoked
with the delta on stdout; drain, respond, and end the turn the same way.
# LAST tool call of the turn — Bash with run_in_background: true
${CLAUDE_PLUGIN_ROOT}/bin/helix canvas wait --canvas <slug> --since <cursor> --timeout 3600 --wake-on userExit 0 wakes you with the delta; 5 means the server died — restart it on the same port and re-arm. 3 is a quiet hour, not an ending: re-arm and carry on.
Rules:
- Timeouts and retry counts bound a foreground drain, never the session. Quiet ≠ over. No-viewer ≠ over (the user reopens the tab and expects you live).
- The slug and cursor must be baked in as literal values (background calls share no shell state, and a shell variable stops this skill's pre-approval from matching); the cursor is the one from your last drain.
- The session ends only explicitly: the user says they're done (terminal or canvas), or ratifies a closing decision. Only then do the End-of- session steps and stop re-arming the wait.
- If a turn ends while you still have canvas work of your own, the same rule applies — push a status card first, do the work, and re-arm.
The rejection protocol (ratified — never violate)
- The "why" question exists only once a Decline has landed. It goes out
in the same push as your reaction to that verdict, edged
declined — why?from the decision — never minutes later. Pushing it alongside the decision card, before any verdict, is a violation: it asks the user why they declined something they have not decided yet. - Answered decision cards are spent. Re-deciding = push a retake
decision, edge
superseded byfrom the old one. Never edit a verdict. - While building, drain and acknowledge between edits.
- Everything waiting on the user must be reachable from your turn; anything long-running on your side gets an in-progress card.
Card reference (push JSON on stdin)
Common: {"type", "threadId", "title"?, "payload"}. Unknown types render as
a raw-payload fallback, so new types may be pushed before renderers exist.
| type | payload |
question | {prompt} — free text, accepts multiple replies |
options | {prompt, options:[{id,label,description?}], mode?:"single"|"multi"} — user may attach a note |
decision | {topic, proposal} — explicit Ratify/Decline |
markdown | {markdown} (GFM: tables, fenced code) |
note | {markdown, attachments?} — user-authored; you may push them too |
prompt | {text} — the user's opening ask from a blank canvas (user-side) |
diagram | {mermaid} — renders serialized; errors report back as client.error |
mindmap | {tree:{label,children?[]}} — interactive: balanced two-side layout, click nodes to fold/unfold (+N badge); {mermaid} also accepted |
gantt | {sections:[{name,tasks:[{label,start,duration?|end?,done?,active?}]}]} or {lanes:{claude,user}} or {mermaid}; owner? |
timeline | {entries:[{period, items:[…]}]} or {mermaid} |
kanban | {columns:[{id,title,cards:[{id,label,body?,detail?}]}], owner?} — user-owned = drag & drop, pending until the user clicks Ratify (answers with kind: "ratify" + final columns). Each card may carry content the user opens in a centered dialog (sized to the type — compact for text, near-full-screen for visuals): body (markdown) or detail: {type, payload} embedding any display card type from this table (markdown, diagram, todo, code, table, image, …) — display only, never an interactive type |
todo | {items:[{id,label,state?:"pending"|"active"|"done",optional?}], owner?} — optional items never hold attention |
chart | {kind, labels?, datasets:[…], height?} — kinds: line, area, bar, hbar, stacked-bar, scatter, bubble, pie, doughnut, polar, radar; palette is built-in and CVD-validated |
metric | {tiles:[{label, value, unit?, delta?, tone?:"good"|"bad"|"neutral"}]} — KPI tile row |
table | {columns:[...], rows:[[...]]} |
code | {code, language?} |
image | {src} (http/data) or {path} (absolute file) |
gallery | {images:[{src|path, caption?}]} — first image is the hero |
video | {src|path, poster?} — local files served with range support |
html | {html, height?} — sandboxed iframe, scripts allowed |
prototype | {source, props?, values?, height?} — live React prototype with a Dials tab. source: JSX module that default-exports one component; only react is importable; dial values arrive as props (theme tokens like var(--card), var(--ink), var(--iris) work in styles). props declares the dials, DialKit grammar: n (inferred slider), [def,min,max,step?], true/false (toggle), "#hex" (color, 8-digit keeps alpha), {type:"select",options}, {type:"text"}, {type:"action"} (click count arrives as a prop under its name), nested object = folder (_collapsed?). The user tunes in the opened card's Dials tab; tuned values come back on the card via card.updated. owner?: user makes it a task — pending until the user clicks Ratify, which answers the card with kind: "ratify" + final values; default (claude) is an example, playable but never pending — read the ratified values and apply to the real code |
file-window | {path} — live view, line comments, md preview/raw |
artifact | {path, status?:"forming"|"stable"|"done"} — the deliverable; write the file with Write/Edit, the canvas streams it; modes preview/raw/meta + download |
weave | {text?} — renders this canvas's event log as the double helix; Claude's reflection card |
diff | {diff, path?} — unified-diff string, GitHub-style render; lines clickable for review comments (kind comment + line index in your delta). Review = diff card + line comments + decision card |
terminal | {output, command?, exitCode?} — console-styled command transcript |
test-report | {suites:[{name,tests:[{name,status:"pass"|"fail"|"skip",message?,duration?}]}]} |
api | {method, url, status?, duration?, requestBody?, responseBody?} — request/response pair |
commit | {message, hash?, author?, stats?:{additions,deletions,files}, files?:[{path,additions?,deletions?}]} |
compare | {left:{title}, right:{title}, rows:[{label,left,right,winner?:"left"|"right"}]} |
comment | {cardId, text, attachments?} — reply in a card's thread (no threadId) |
edge | {from, to, label?} — arrows; always link follow-up cards to what prompted them |
status | {text} — not a card: updates the agent-status pill |
delete | {cardId} — removes a card |
Update = push the same JSON with the existing id.
Artifacts & graduation
Content starts as ordinary cards; when a decision stabilizes it, promote:
push an artifact card bound to the repo path, edges from the source
decisions (written into), keep the file written through with Write/Edit,
and advance status forming → stable → done via update pushes. Graduation =
every artifact card done; announce it and update the session's memory.
Server operations
- A stale server restarts on the same port when you run
${CLAUDE_PLUGIN_ROOT}/bin/helix canvas upagain; open tabs reconnect. Manual equivalent: kill the pid inserver.json, relaunch with--port <same port>. - Kill by pid, never by name.
pkill -f "helix canvas serve"matches every canvas server on the machine — other projects, other people's sessions, runs in the middle of their work. It looks local and is not. Read the pid instead:kill "$(sed -n 's/.*"pid": *\([0-9]*\).*/\1/p' ~/.helix/canvas/<project>/<slug>/server.json)". - The event log survives restarts; state is never in memory only.
What the app gives the user (so you can point them at it)
Amber N waiting on you chip → ● your turn: every card blocking the session, oldest first, each headed by the action it needs (Answer, Choose, Ratify or decline, Tick N items) and answerable in place; ⌖ jumps to the card on the canvas (jumping auto-unfolds whatever hides the target). Empty it reads Nothing is waiting on you — which is the answer when a person asks why you are still waiting. ▦ index → every card, findable: tabs by type group plus artifacts and attachments, search, stage/status filters, pagination; row click opens the card's full preview, ⌖ jumps back to the canvas focused on it. ◷ timeline → the session in order: cards added, answers, ratifications, replies and status changes, live-updating, filterable by actor, searchable; row click previews the card, ⌖ jumps to the canvas. ✓ decisions → the register: every decision card as a numbered entry (D1…) with verdict and timestamp, filterable by open/ratified/declined, searchable; retakes nest their superseded ancestors, declined entries link their "why" question. ☐ work → who is blocking whom: two columns — everything waiting on the user (open questions/options/decisions, gates with required items left) and everything Claude has committed to (open todos with progress, boards in play, artifacts still forming, the current status); the toolbar filters pending / done / all. ▣ stages → per-thread show/hide, collapse (collapsed stages become summary cards that re-anchor crossing edges), and ⤢ solo view. compact folds answered cards; arrange re-stacks columns; theme toggle; ⤢ maximize on every card (full mode + thread sidebar); 💬 threads (resizable panels); ⊘ hide + master reveal; double-click empty canvas → note editor (📎 + clipboard paste); reactions; activity toasts bottom-center; drag anything — user positions always win.
End of session
Update the project's memory file with: canvas slug, port, cursor reached, open blockers, ratified decisions, artifact statuses, and gate states. Before stopping, clear spent gates — every user-owned todo/kanban/gantt that is no longer waiting on the user gets updated to done or deleted, so the session drops out of Fleet's attention queue.
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.

