craft-cli
A Craft Docs CLI made for AI agents. Local-first search, API-backed full reads and writes.
craft-cli is a Bun-compiled single binary, built first and foremost for AI coding agents. The goal is to make a Craft Docs vault as pleasant to read and edit from Claude Code, Codex, or OpenCode as a local markdown folder is from inside Obsidian - not to replace the Craft UI for humans. Every design choice below follows from that goal. The CLI also exports a TypeScript library that the Raycast Craft extension and other downstream tools use instead of talking to the REST API directly.
Why agents need their own CLI
Craft ships a solid REST API, but agents do not think in HTTP round-trips. A single document read can take up to 4.5 seconds over the wire. A ten-step workflow that searches the vault, reads a few notes, patches a block, and verifies the result will burn 25 seconds of wall-clock time before the model even starts reasoning about the result. For a human in the Craft app that latency is invisible. For an agent planning its next move, it is the difference between "works" and "unusable".
Local-first mode, in one sentence
Read Markdown, search, and list from Craft's local cache when eligible. Use the API for structured features and every write.
That is the whole architecture.
In source auto, unfiltered document listing and simple
search, plus Markdown document reads, use Craft's on-disk SQLite FTS5 and PlainTextSearch stores.
These are the same data structures that make search instant inside
Craft. Structured document trees, tasks, collections,
links, filtered searches, and all writes use the REST API.
Writes go through the REST API. The API is the only authoritative source for block hierarchy and the only path that triggers cross-device sync. Writing through it keeps the vault consistent between devices. Craft Desktop typically refreshes its local stores within about one second while the app is running and synced.
General document reads treat Craft's cache as read-only and fall back to REST when a query is not locally eligible or the cache is unavailable. The skills library can keep its own local snapshot and optionally sync skill folders to a directory or GitHub. Craft remains the source for those skills.
* Local-first mode assumes the Craft desktop app is installed and running, because it is the process that keeps the local stores in sync. On Linux, headless servers, or any host without the app, use API-only mode (next section).
API-only mode: opinionated agentic wrapper
When the local stores are not available, craft-cli runs in API-only mode. But this is not a thin passthrough. It is an opinionated layer on top of the REST API, shaped specifically for what agents need when they are reasoning about a knowledge vault:
- Backlink extraction on retrieval. The Craft REST
API does not expose backlinks. craft-cli reconstructs them on every
document fetch via title-based search plus
block://URI filtering. For an agent traversing a vault, this turns a single document read into a context-rich view of where the doc is referenced - exactly the kind of adjacency signal that lets a model decide what to load next. - Source-aware search. Simple searches use local FTS5 in auto mode. API-routed searches use RE2 by default and work around the API's silent-drop behavior on short underscored tokens.
- Unified write path with an undo net. Every write
passes through a mutation journal at
~/.cache/craft-cli/journal.db, recording block tree state before and after.craft undo,craft diff, andcraft logall work against it, giving agents a git-like safety net on a platform that otherwise has no version history. -
craft patch- find-and-replace at block granularity, intentionally mirroring the editing semantics of Claude Code'sEdittool. Agents already know this shape. - Compact output. Every command supports
--jsonand a dense default format, so the CLI consumes little of the model's context window.
API-only source also works on macOS if you do not want local-store
reads for some reason: craft source api persists it,
CRAFT_SOURCE=api overrides per process,
--source api or --api overrides per command.
Speed
Benchmarks of the local-store primitives on my personal vault of ~1,200 documents and ~46,000 blocks. The CLI currently exposes the local path for simple search and document listing.
| Operation | REST API | craft-cli (local) | Speedup |
|---|---|---|---|
| Search vault for a term | 2,271 ms | 1.3 ms | 1,700x |
| Resolve cached content (library) | 4,561 ms | 0.7 ms | 6,300x |
| Check if doc changed (contentHash) | 3,247 ms | 0.5 ms | 6,600x |
| List all documents | 1,489 ms | 184 ms | 8x |
The local-store primitives average around 3,600x faster than equivalent REST reads. For agents, the exposed search and listing paths become effectively free.
Install (paste this to your AI agent)
To install craft-cli, paste the block below into your AI coding agent (Claude Code, Codex, OpenCode, Cursor, etc.). The agent will handle clone, build, install, authentication, and skill registration.
You are installing craft-cli (https://github.com/pa1ar/craft-cli) for me. Use the latest public stable release and its matching bundled skill. Proceed end-to-end; ask only when a required choice or credential is missing.
1. RUNTIME CHECK
Check whether `bun` is on PATH. If yes, continue.
If not, use my stated package-manager preference, or install Bun from https://bun.sh. This project builds with Bun.
2. RELEASE + BUILD + INSTALL
Resolve the latest public stable release:
CRAFT_RELEASE_TAG="$(bun -e 'const r = await fetch("https://api.github.com/repos/pa1ar/craft-cli/releases/latest"); if (!r.ok) throw new Error("GitHub release lookup failed: " + r.status); const release = await r.json(); if (!release.tag_name || release.draft || release.prerelease) throw new Error("No stable release"); console.log(release.tag_name)')"
Stop if that lookup fails. Do not silently install main or a local development binary.
CRAFT_RELEASE_DIR="$HOME/.local/share/craft-cli/releases/$CRAFT_RELEASE_TAG"
If the release directory already exists, preserve it. Verify its origin, tag commit and tracked source state before reuse; use a fresh directory if it has local edits.
git clone --branch "$CRAFT_RELEASE_TAG" --depth 1 https://github.com/pa1ar/craft-cli.git "$CRAFT_RELEASE_DIR"
cd "$CRAFT_RELEASE_DIR"
Read this release's install.sh, then run ./install.sh.
If install.sh fails, diagnose and report the failure before using its documented manual build/link equivalent. Keep the same release tag.
Verify `~/.local/bin` is on PATH and `command -v craft` resolves to this release's binary. Add the PATH entry to my shell rc only if missing (detect zsh vs bash). Use `craft` from PATH for normal work.
Record the release tag and `git rev-parse HEAD`; version alone does not prove a build came from a release.
3. CONNECTION
Run `craft doctor --json` to check existing configuration. Preserve a working connection and verify the intended space; do not ask for credentials again.
If configuration is missing or invalid, ask for the Craft API URL and API key from Craft → Connections → New API Connection, then run `craft setup --url "<URL>" --key "<KEY>"`.
Do not print stored API keys. A Craft connector may point to a different space than the CLI.
4. READ SOURCE
On macOS with Craft Desktop installed, run `craft source auto`. Eligible reads use the local cache first and fall back to REST. Do not persist API-only mode or add `--api` to ordinary reads.
On Linux, remote/headless hosts, or machines without Craft Desktop, use `craft source api`.
5. VERIFY
command -v craft
craft doctor --json # connection, intended space, source and local availability
craft docs ls # local output is marked "documents (local)" when the cache is available
Use `craft --help` and `craft which <capability>` for this release's command names. Do not substitute Craft MCP command syntax.
6. MATCHING SKILL
Use the complete `skill/` directory from the same release, including references, rather than a development checkout's skill.
Honor my canonical skills folder first. Preserve existing customizations before replacing or updating a skill. For an existing custom skill, compare it with this release and explicitly resolve differences; do not silently mix versions.
Register the canonical skill directory in the harness's supported user-skill location. Prefer symlinks and preserve real directories. If no canonical folder is specified, use the current harness's supported location; ask only if it cannot be determined.
Inspect installer-created skill links: older releases may link only Claude automatically. Ensure the current harness can read the matching `SKILL.md` and its references, then read that skill before normal CLI work.
Keep fixes for the next release in the development repo; keep the installed release and its skill unchanged while testing the public version.
7. REPORT BACK
Tell me the release tag and commit, resolved binary path, skill location and harness registration, effective read source, intended Craft space, local-cache availability, and any installation failure or workaround. Role and distribution
Personal tool, published as-is. I am not affiliated with Craft and do not plan to maintain package-manager distributions. If the Craft team wants to adopt or fork this into an official CLI, they are welcome to. The library half is in active use inside the Raycast Craft extension and inside several of my own agent workflows across the 1ar labs stack.