sketchi
v0.35.0
Published
Local Sketchi authoring CLI with offline workflows and four credential-free network commands for generation, Universal Canvas compilation, and encrypted Excalidraw sharing.
Maintainers
Readme
Sketchi stores canonical diagram documents and authoritative Excalidraw artifacts
under ~/.sketchi/diagrams. Its manual and recovery workflows stay local;
generation, Universal Canvas compilation, and encrypted snapshot exchange are explicit, credential-free network
commands.
Install
Use the noninteractive installer to install the npm package and configure completions for a detected Zsh, Bash, or Fish shell:
curl -fsSL https://raw.githubusercontent.com/shpitdev/sketchi/main/install.sh | shOr install directly from npm:
npm install -g sketchi
sketchi --versionSketchi requires Node.js 24.13.0 or newer.
Generate a PNG from a prompt
generate sends one prompt to Sketchi's public generation API, validates and
persists the editable result, then writes <generated-id>.png in the current
directory:
sketchi generate
# Direct and noninteractive:
sketchi generate --prompt "Map release approval with pass and revise branches"With no --prompt, Sketchi opens a short wizard only when both standard input
and standard output are human terminals, output is text, and CI is absent. It
asks for prompt text, flowchart (the default), mind map, sequence diagram, timeline, chart, state machine, Gantt chart, ER diagram, class diagram, org chart, Venn diagram, journey map, Sankey diagram, wireframe, or quadrant chart, and where to save the
PNG. The choices are the current directory, a diagrams/ folder under exactly
the directory where Sketchi was run, or a custom path with the same semantics as
--dest. Sketchi does not inspect Git, and it creates diagrams/ only after
generation succeeds. An explicit --type or file --dest presets and skips
that wizard question. Explicit --format png may proceed; another format or
--dest - requires --prompt because the interactive wizard writes PNG files.
Scripts, pipes, redirected streams, CI, and --output json never prompt or
block. They must pass --prompt; omitting it retains the deterministic usage
error. Passing --prompt is always direct and noninteractive.
--type accepts only flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, wireframe, or quadrant and is authoritative.
Without it, the generation model selects one of those from the request; a
request for any other kind of diagram gets the closest of those.
The PNG is rendered locally from the returned validated artifacts and is not written back into the record. Choose another artifact or destination explicitly:
sketchi generate --prompt "Organize launch readiness" --type mindmap --format excalidraw --dest launch.excalidraw
sketchi generate --prompt "Show Browser calling API and API returning success" --type sequence --dest request-sequence.png
sketchi generate --prompt "Map release approval" --dest - > release.png
sketchi generate --prompt "Map release approval" --output json--format accepts png, excalidraw, or scene; their default names are
<id>.png, <id>.excalidraw, and <id>.scene.json. With --dest -, stdout
contains artifact bytes only and the text or JSON status envelope moves to
stderr. Generation, revision, Universal Canvas compilation, sharing, and pulling are the CLI's five network commands.
A successful generate prints the title and type, where the file went, and the id:
Checkout flow (flowchart)
Saved checkout-flow.png
Copied to clipboard, paste anywhere
Id checkout-flowWhen stdout is not a terminal, as when an agent runs it, a final Hint row says to
display the saved PNG as an inline Markdown image. --output json keeps everything
else: the full record, the generation model and provider, and export details.
Clipboard
After writing a PNG file, generate copies the image to the desktop clipboard so
you can paste it straight into a doc, chat, or issue. It uses wl-copy on Wayland
(install wl-clipboard), xclip on X11, osascript on macOS, and PowerShell on
Windows; Sketchi adds no native dependency. The copy happens whenever a desktop
clipboard is available, including when an agent runs the CLI with piped output.
It is skipped in CI, for --dest -, for --format excalidraw|scene, and with
--no-copy, and nothing is printed when the session has no desktop clipboard.
If the copy fails, one line says why; the file is still saved and the exit code
does not change. JSON output adds "clipboard": {"copied": true} or
{"copied": false, "reason": "..."}.
Set SKETCHI_CLIPBOARD_COMMAND to a command that reads a PNG on standard input to
use a clipboard Sketchi does not detect, or to off to never copy.
Copy any stored diagram again, rendering its PNG on demand:
sketchi copy # the newest diagram
sketchi copy release # by id or unique id prefixmacOS and Windows clipboard support is unit-tested but not yet verified on those systems.
Revise a diagram with a request
revise changes a stored diagram the way you describe it. It sends the diagram's
document and your request to the same generate API, keeps the complete revised
diagram as the record's next revision, and writes its PNG:
sketchi revise checkout --prompt "Add a fraud check before payment"
sketchi revise latest --prompt "Rename Ship to Ship order and drop the manual review"
sketchi revise latest --prompt "Show it as a sequence diagram" --type sequenceColors and shapes a prompt asks for, in generate or revise ("draw the
reject path in red", "make the gate a purple diamond"), are drawn as one more
revision on top, the way sketchi patch draws them; the revision before stays
unstyled and restorable.
The diagram keeps its id and family unless --type names another, and
everything the request doesn't mention stays as it was. sketchi restore ID
--revision N brings back the earlier revision. If the diagram changes while
the request runs, revise keeps that change and exits 6 instead of replacing
it. Patched, pulled and canvas records refuse revise, as they refuse edit. --format, --dest,
--no-copy, the clipboard and --output json work as they do for generate.
Find past diagrams
list shows the newest diagrams first with id, title, type, and how long ago
each was created. Add words to search titles, ids, and the prompts that generated
them:
sketchi list
sketchi list checkout
sketchi list --type sequence --limit 5Text output shows 20 diagrams and says how many older ones it hid. --output json
returns every match as an array in the same newest-first order, adding createdAt,
updatedAt, and prompt when known. Records made before Sketchi stored creation
times sort by when they last changed.
Every command that takes a diagram id also accepts latest for the newest
diagram, or any unique prefix of an id. An unknown id suggests the closest ones:
sketchi show latest
sketchi export check --format png --dest checkout.pngshow prints a summary: title, type, id, when it was created, its prompt,
revisions, and where it is stored. Add --document for the canonical document;
show --output json always includes it.
Build a Universal CanvasSpec
canvas sends an already-authored, typed CanvasSpec to Sketchi's public
create-canvas API, validates the response, preserves the normalized scene and
editable Excalidraw artifact in the local store, and exports PNG by default:
sketchi canvas --file canvas.json --output json
printf '%s' "$CANVAS_SPEC" | sketchi canvas --file - --format excalidraw --dest canvas.excalidrawThe input is the CanvasSpec object itself—not a request wrapper and not raw
Excalidraw JSON. The command never prompts, so files and piped stdin are safe for
agents and scripts. --format and --dest have the same behavior as generate.
The endpoint defaults to
https://playground.sketchi.app/api/v1/canvases/create; use --endpoint URL or
SKETCHI_CANVAS_ENDPOINT only for preview and local testing. The existing
create command remains the separate, strictly offline command for accepted
flowchart, mindmap, sequence, timeline, chart, state-machine, gantt, er, class, org-chart, venn, journey, sankey, wireframe, and quadrant documents.
Create and work with a diagram offline
Create a canonical flowchart from inline JSON:
sketchi create --json '{"type":"flowchart","spec":{"id":"release-flow","title":"Release approval","nodes":[{"id":"start","label":"Change proposed","kind":"start"},{"id":"review","label":"Review evidence","kind":"process"},{"id":"end","label":"Release approved","kind":"end"}],"edges":[{"source":"start","target":"review"},{"source":"review","target":"end"}]}}'Inspect it, replace it with another complete canonical document, and list the local store:
sketchi show release-flow
sketchi edit release-flow --json '{"type":"flowchart","spec":{"id":"release-flow","title":"Release approval revised","nodes":[{"id":"start","label":"Change proposed","kind":"start"},{"id":"review","label":"Review complete evidence","kind":"process"},{"id":"end","label":"Release approved","kind":"end"}],"edges":[{"source":"start","target":"review"},{"source":"review","target":"end"}]}}'
sketchi patch release-flow --json '{"operations":[{"op":"setStyle","selector":{"nodeIds":["review"]},"style":{"fillColor":"#dbeafe","strokeColor":"#2563eb"}}]}'
sketchi listpatch reuses Sketchi's semantic selectors and operations (setStyle,
setDefaultStyle, setShape, translate, replaceText, and
rerouteEdges) without exposing raw Excalidraw mutation. It is fully offline,
preserves the prior full revision, retains document.json unchanged as
provenance, makes scene.json authoritative, and removes any stale stored PNG.
Patched records report authority: "patched" and
documentAuthoritative: false; restore a canonical revision before using
edit again.
Export the stored editable artifact:
sketchi export release-flow --format excalidraw --dest release-flow.excalidrawOr render a PNG directly from the stored scene and Excalidraw artifacts:
sketchi export release-flow --format png --dest release-flow.pngPNG rendering is deterministic and fully local. Sketchi uses Excalidraw's
headless SVG exporter, bundled Excalifont files, and a bundled WASM rasterizer;
it does not start a browser or use the network. The rendered image is
export-only and is not added to the diagram record, while a pre-existing
diagram.png remains the fast path.
Every command also supports --output json for a stable machine-readable
result envelope. Export status always uses stderr. export --dest - writes
only artifact bytes to stdout; a PNG file export also includes one generic
inline-Markdown display hint for calling agents. Its destination is enclosed
in CommonMark angle brackets so local paths containing spaces remain valid.
Share, pull, and restore browser edits
Create an immutable encrypted Excalidraw snapshot link from the record's current authoritative artifact:
sketchi share release-flow
sketchi share release-flow --open --output jsonThe URL is a bearer secret: anyone who receives the complete URL can decrypt the
snapshot. Encryption hides the drawing from the storage service, but the service
can observe connection metadata, timing, and ciphertext size. Retention is
uncontrolled and may be indefinite; Sketchi cannot revoke or delete a link.
--open is opt-in, discloses the URL to the local opener and browser history,
and reports only whether the OS accepted the request—not whether a visible
window opened.
Browser edits do not update an existing link. In Excalidraw choose Save to… → Export to Link, then pull the newly exported link:
sketchi pull release-flow --link 'https://excalidraw.com/#json=ID,KEY'
printf '%s\n' 'https://excalidraw.com/#json=ID,KEY' | sketchi pull release-flow --link -The linked drawing has no trusted Sketchi identity and may be unrelated to
release-flow. A successful pull preserves the complete prior record under
revisions/, removes any stale stored PNG, and makes diagram.excalidraw the
sole authoritative artifact. show and list then report authority:
"detached" and documentAuthoritative: false; retained document.json and
scene.json are provenance only. Detached edit refuses, scene export is
unavailable, and on-demand PNG renders directly from diagram.excalidraw using
its background without a stale canonical title.
Recover any retained full snapshot through the strictly offline CLI:
sketchi restore release-flow --revision 1Restore archives the displaced current state first, does not consume the chosen
snapshot, and advances revision numbering monotonically. New edit/pull/restore
history uses full revisions/000001/ authority snapshots; legacy
revisions/000001.json document revisions remain restorable on canonical
records.
Share/pull v1 supports exactly rectangle, ellipse, diamond, arrow,
line, freedraw, and text. Text must use Excalifont fontFamily id 5.
Images, non-empty files, frames, embeddables, iframes, external resources,
custom fonts, libraries, and unknown element types are rejected. Compatibility
is pinned to Excalidraw e6ae6bf and excalidraw-store 76de642; both commands
use the unofficial json.excalidraw.com API without retries.
The enforced v1 bounds are a 4 KiB bearer link, 2 MiB encrypted body, 16 MiB
inflated contents, 10,000 elements, and JSON depth 64. Before any raster
allocation, Sketchi also limits each element dimension to 16,000 units, each
canvas dimension to 16,000 units, canvas area to 25,165,824 square units, and the
final PNG to 25,165,824 pixels and 16,384 pixels a side. Share enforces its compressed-output budget
while streaming and reserves framing plus encryption overhead before encryption.
Pull requires exactly one literal root
https://excalidraw.com/#json=ID,KEY or
https://www.excalidraw.com/#json=ID,KEY URL (or one such URL through stdin);
userinfo, ports, query strings, alternate spellings, and repeated --link
flags are rejected.
Shell completions
The installer configures completions automatically. For a direct npm install, generate the script for your shell and source the dedicated file:
Zsh initializes its completion system before loading Sketchi:
mkdir -p ~/.sketchi/completions
sketchi --completions zsh > ~/.sketchi/completions/sketchi.zsh
autoload -Uz compinit
(( ${+functions[compdef]} )) || compinit
source ~/.sketchi/completions/sketchi.zshFor Bash:
mkdir -p ~/.sketchi/completions
sketchi --completions bash > ~/.sketchi/completions/sketchi.bash
if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
source ~/.sketchi/completions/sketchi.bash
fiGenerated Bash completions require Bash 4 or newer. The installer silently
skips them on the stock macOS Bash 3.2. Bash login shells may not read
.bashrc, so .bash_profile may need to source .bashrc for completions to be
available.
For Fish:
mkdir -p ~/.sketchi/completions
sketchi --completions fish > ~/.sketchi/completions/sketchi.fish
source ~/.sketchi/completions/sketchi.fishFor AI agents
The deterministic commands—create, patch, show, edit, list,
export, and restore—are fully offline and require no credentials, account,
browser, model, or MCP server. They, and copy, never send stored records
to a provider.
Agents can use
--output json, complete noninteractive input through --json or --file, and
raw artifact output through export --dest - without prompts.
Agents must pass --prompt to generate. Interactive prompting is restricted
to human text TTYs and is never enabled by JSON output, pipes, redirects, or CI.
After writing a PNG file, generate and export give agents a hint to display
that path as an inline Markdown image for the user: a Hint row in generate's
text output when stdout is not a terminal, export.hint in generate's JSON, and
hint: in export's status. When generate reports Copied, the user can also
paste the image themselves.
generate, canvas, share, and pull are deliberately separate, explicit network
commands. They remain credential-free and each makes one HTTPS request. Share
is randomized by its key and IV; pull depends on remote availability and
untrusted input, so neither belongs to determinism claims.
License
Sketchi CLI's own code is MIT-licensed. The published package also bundles
Excalidraw, resvg-wasm, and Excalifont under their respective licenses; see
the included THIRD_PARTY_NOTICES file for copyright, license, and source
availability details.
Help
sketchi --help
sketchi docs
sketchi create --help
sketchi patch --help
sketchi generate --help
sketchi canvas --help
sketchi share --help
sketchi pull --help
sketchi restore --helpThe root screen is deliberately progressive: it leads with generate, one
copyable prompt, and the few commands used to continue working with a diagram.
sketchi docs is the complete command and automation map; targeted
COMMAND --help pages retain every flag and example. Interactive terminals get
the Sketchi pencil lockup and restrained brand color. Pipes, NO_COLOR, and
JSON output remain plain and contain no ANSI control sequences.
Source, issue tracking, and architecture documentation live in the Sketchi repository.
See the changelog for everything that changed, or browse published versions on GitHub Releases.
Native timelines: use buildTimeline (Code Mode) or generate --type timeline (CLI). Supply events with unique IDs, labels, and ISO dates at the source's precision (year, month, day, or instant with Z or offset) or explicit numeric order; optional end (phase), kind: "milestone", lane, today, and concise descriptions are preserved. Dates and order cannot be mixed. The renderer spaces events by elapsed time, humanizes dates, and wraps long chronologies. No dependency scheduling.
Native charts: use buildChart (Code Mode) or generate --type chart (CLI). Supply kind (bar, line, or pie), categories, and series: [{ name, values }] with one value per category; optional unit, categoryAxisTitle, valueAxisTitle, stacked and orientation (bars), and donut (pies). The renderer draws nice-tick axes from zero for bars, value and percentage labels, a legend for several series, and leader lines for thin pie slices. Never supply coordinates.
Native state machines: use buildStateMachine (Code Mode) or generate --type state-machine (CLI). Supply initial, states with unique ids and optional labels (regular-state labels default to ids verbatim; kind: "final" for an end, "choice" for a guarded branch; states naming a parent form a composite with its own initial), and transitions with source, target and optional event, guard and action, drawn as event [guard] / action. Self-loops and transitions into or out of a composite are supported; composites do not nest.
Native Gantt charts: use buildGantt (Code Mode) or generate --type gantt (CLI). Supply tasks with unique ids, labels, a start day (YYYY-MM-DD) and either end (last day, inclusive) or duration (calendar days); milestone: true draws a diamond. Optional dependsOn (finish-to-start predecessor ids), phase on every task (phases sets their order), progress (percent) and today. The renderer draws a to-scale date axis with day, week, month, quarter or year ticks, one row per task under phase headers, a date column, and dependency arrows routed clear of every bar and label. Never supply coordinates.
Native ER diagrams: use buildErDiagram (Code Mode) or generate --type er (CLI). Supply entities with unique ids (no dots) and fields written "name type markers" ("id uuid PK", "customer_id uuid FK", "email text UK", "note text?" for nullable), and relationships whose from and to name entity.field or an entity, each end with a cardinality (one, zero-or-one, many, one-or-many or zero-or-many) and an optional label. The renderer draws each entity as a table and each relationship as an orthogonal line between the rows it joins, with crow's-foot ends; it owns every coordinate.
Native class diagrams: use buildClassDiagram (Code Mode) or generate --type class (CLI). Supply classes with unique ids (no colons or spaces), an optional kind (abstract, interface or enum) and members written as UML does ("- radius: double", "+ area(): double {abstract}"), and relationships with a kind: inheritance or realization (from extends or implements to), composition or aggregation (from is the whole), association (navigable: true adds an arrow) or dependency (from uses to). Associations, compositions and aggregations take multiplicities and roles at each end. The renderer draws three-compartment boxes with superclasses above their subclasses and each relationship's UML head; it owns every coordinate.
Native org charts: use buildOrgChart (Code Mode) or generate --type org-chart (CLI). Supply people with unique ids (no colons), a name, an optional title and department, and manager: the id of the person they report to (only the top person has none; forest: true allows several). assistant: true places an assistant beside their manager, avatar: true draws an initials placeholder, dottedLines: [{ report, manager, label? }] adds dotted-line or matrix reports (a stated scope or reason, such as "for clinical systems", goes in label), and groupByDepartment: true draws each department below the top as a labeled region. The renderer draws a card per person top-down, a bus line from each manager to their reports, long lists of reports stacked in one or two columns, and dotted lines dashed with a head at the second manager and their label beside them; it owns every coordinate.
Native Venn diagrams: use buildVennDiagram (Code Mode) or generate --type venn (CLI). Supply two or three sets: [{ id, label, color? }] and regions: [{ sets, items }], one region per overlap: ["a"] holds what is only in a, ["a", "b"] what is in a and b but not c. A region holds items (short phrases) or a count, and an optional label naming the overlap; omit empty regions. A set may instead list its whole membership as items, and each item then lands in the region of exactly the sets that list it. The renderer draws equal, overlapping translucent circles in the standard symmetric arrangement, each set's label outside its circle, and each region's text centred inside exactly that region; it sizes the circles until every region fits and owns every coordinate.
Native journey maps: use buildJourneyMap (Code Mode) or generate --type journey (CLI). Supply stages in order, each { name, steps: [{ label, score, actor?, touchpoint? }], painPoints?, opportunities? }, with an optional persona (whose journey it is) and actors. score is a whole number from 1 (very negative) to 5 (very positive); 3 is neutral. The renderer draws the stages as headers across the top, each step's card under its stage, an emotion curve through a face per step at its score over a dashed neutral baseline, and each stage's pain points and opportunities in rows beneath it; a journey too wide to read wraps into bands of whole stages with the curve running on between them. It owns every coordinate.
Native Sankey diagrams: use buildSankey (Code Mode) or generate --type sankey (CLI). Supply nodes: [{ id, label }] (ids without colons) and links: [{ source, target, value, label? }] with every stated amount (value > 0), plus an optional unit formatted as charts format it ($k gives $310k; a word unit such as visits reads on node totals). Links run one way: no cycles, self-links or repeated pairs, and no invented balancing node. The renderer places each node one column past the longest run of links into it (a sink fed by several nodes aligns with the last column), draws each node as a bar as tall as the larger of its inflow and outflow with its label and total, and each link as a band as wide as its value in its source's color with its value written; a band under 3 px draws at 3 px and keeps its exact value. It owns every coordinate.
Native wireframes: use buildWireframe (Code Mode) or generate --type wireframe (CLI). Supply screens, each with a name, a device (desktop, tablet or mobile, which sets the frame's size) and children: a tree of components in reading order. Containers hold children: header, footer, sidebar (side: "left" | "right"), main, section, card and modal (with a title), row, column and grid (columns: n); align spreads, centers or ends a row's children. Widgets draw from their own fields: heading, text (or lines of placeholder copy), link, badge and button show text; input, textarea, select and search take a label, a placeholder or the value they show; checkbox, radio and toggle take a label; tabs, nav, breadcrumbs and list take items; and avatar, icon, image, chart, table (columns of headers) and divider draw placeholders. State: variant: "primary" on the main action, checked: true on what is on, and active on the selected tab or nav item; target on a button or link names the screen it opens, drawn as an arrow to that screen's frame. The renderer draws each screen as an Excalidraw frame, its header across the top, footer along the bottom, sidebars down their sides and modals over a dimmed backdrop, with every component a group it places: rows wrap and grids drop columns before anything overflows, and text wraps at whole words; it owns every coordinate.
Native quadrant charts: use buildQuadrantChart (Code Mode) or generate --type quadrant (CLI). Supply xAxis and yAxis as { title, low, high } (the end labels, x left to right and y bottom to top, as the source words them), quadrants as { topLeft, topRight, bottomLeft, bottomRight }, and up to 30 points: [{ label, x, y, group? }] with x and y from 0 (the low end) to 1 (the high end); a score out of 100 is score / 100. group colors points and adds a legend. The renderer draws a square plot of four tinted quadrants with their names in their corners, axis arrows carrying the end labels, and each point as a dot at its exact position with its label beside it, or on a leader line where the plot is crowded; it owns every coordinate.
