plank โ€บ User guide โ€บ Installation

โ† Index ยท Next: Getting started โ†’

1. Installation

Homebrew

Homebrew is the only distribution channel โ€” plank is not on crates.io.

brew install aovestdipaperino/tap/plank-agent        # stable
brew install aovestdipaperino/tap/plank-agent-beta   # beta

The formula is named plank-agent because a plank formula already exists upstream and the bare name collides. The installed binary is still just plank โ€” only the install name carries the suffix.

Prebuilt bottles exist for Apple Silicon and Intel Macs. On anything else Homebrew builds from source, which needs a Rust toolchain.

brew upgrade plank-agent

Stable and beta channels

The patch number is the channel:

A series opens with its stable .0 and accumulates beta work as patch bumps. Promoting a beta to stable opens the next minor as a matching stable/beta pair. See VERSIONING.md for the full model.

Both formulas install a plank binary, so they conflict. Switch channels by uninstalling first:

brew uninstall plank-agent && brew install plank-agent-beta

plank checks GitHub Releases once a day at startup and mentions a newer version if there is one. It is best-effort and silent on failure; turn it off with update.check: false in settings.json.

Building from source

Requires macOS with the Xcode command line tools. Clone with the submodule โ€” that is where the inference engine lives:

git clone --recurse-submodules https://github.com/aovestdipaperino/plank
cd plank
cargo build --release

Getting the model

plank knows its models as named engines: a main model plus the companions it runs with, a drafter for speculative decoding (mtp) and a vision encoder (vision). They are listed in engines.json, a small catalog that ships inside plank and is refreshed from the repository at most once a day. Five are built in:

Engine What Download
ds4vision DeepSeek V4 Flash with its DSpark drafter and vision encoder. The default. ~87 GB, needs 96 GB of RAM
ds41 DeepSeek V4.1 Flash with its vision encoder. ~366 GB, streams from SSD
qwen Qwen3.8-Flash-Next. ~177 GB
gemma4-e4b Gemma 4 E4B instruct (Q4_K_M), on plank's own Rust engine. ~5 GB
gemma4-12b Gemma 4 12B instruct (QAT Q4_0), on the same engine. ~7 GB

Pick one with --model <name>, for example plank --model gemma4-e4b. Real inference with no choice at all runs the default engine. On first run, with nothing at its path (~/.plank/ds4vision.gguf), plank opens an engine menu on a terminal: every engine is listed, the ones this machine cannot run are dimmed with the reason beside them (not enough RAM, not enough free disk, not in this build), and the one you pick downloads in the background while a wait screen shows its progress. When it finishes, plank installs it and records it as your engine.model. Esc leaves the download running; a later launch tells you once it has finished, and picking it then installs it. With --model on the command line plank offers just that engine, and with no terminal it exits with instructions. --pick-engine opens the menu even when the selected engine is installed, and /engines restarts into it from a running session.

Downloads come from Hugging Face. With HF_API_KEY set, plank authenticates to Hugging Face (and only to Hugging Face) and fetches four ranges of a file in parallel, resuming a partial range rather than starting over and checking the whole file again at the end.

Gemma 4

Gemma is the small, portable choice. It runs on Metal on a Mac and on the CPU elsewhere, on a Rust engine that needs neither the refs/ds4 submodule nor macOS, so a plain cargo build does real inference with it. Tool calls, thinking, sessions, sub-agents, memory and KV reuse all work as they do on DeepSeek; vision, speculative decoding and the System-1 memory gate do not exist for it yet.

With no -c, Gemma sizes its context from the RAM: the largest window whose KV fits a third of memory, never below 8,192 tokens and never past what the model was trained for. On a 128 GB Mac that is the full 131,072 tokens for E4B and about 66,000 for 12B; a 16 GB machine gives 12B the 8,192 floor. plank prints the size it chose and what it costs at startup.

Its sessions are a family of their own (.gemma.kv), so switching between Gemma and DeepSeek keeps both histories. Any Gemma 4 GGUF also works by path, plank -m ~/models/gemma-4-E4B-it-Q4_K_M.gguf, recognised from the file's own architecture field.

DeepSeek V4.1 Flash is a family of its own, with its own tool-call dialect and its own .ds41.kv transcripts. --model ds41 fetches and runs it; pointing --model at a V4.1 GGUF path works too, and the family and dialect follow from the file's own architecture field. It is large enough that plank will usually turn on SSD streaming for you.

Things worth knowing before you start an 87 GB transfer:

The DSpark draft checkpoint (~5.6 GB) follows the same path. Speculative decoding is on by default, so for ds4vision it resolves to ~/.plank/ds4vision.mtp.gguf and is offered for download with the same prompt, resume and progress (--mtp-off skips it). See Configuration.

Staying on the current model

Once a model is installed, plank checks for a newer one at most once a day by fetching engines.json, where each engine carries its own version number and the artifacts that make it up. When a newer version of the engine you run appears, plank asks first: Download it in the background? [y/N], defaulting to no. Say yes and it starts in a detached background process rather than blocking the session: it keeps running even if you quit plank or close the terminal, so closing the laptop lid mid-transfer costs nothing but time. Say no (or just press Enter) and nothing downloads yet; run /model download whenever you are ready to start it. A dropped connection or a stopped helper leaves verified artifacts and partial files in ~/.plank/staging/<engine>/; nothing resumes it automatically, but accepting the next daily offer or running /model download picks up right where it left off, re-downloading only what wasn't finished. Only one such download runs per machine, whichever plank noticed the update first.

While a download is live, a status segment shows its progress, for example โ‡ฉ model 2/3 41% 12MB/s. In the TUI, Alt-M opens a prompt to cancel it: keep the partial files (resume with /model download, or the next daily offer) or delete them outright. From either the TUI or the plain REPL, /model (or /model status) reports what is happening, /model cancel stops it (add --delete to also remove the partial files), and /model download starts one by hand.

New artifacts are verified by SHA-256 as they stream in, but they are not installed the moment the download finishes โ€” a running plank has the current model mapped into memory, so the swap happens at the next launch instead. Expect a fresh model to be in effect the next time you start plank, not mid-session.

To use a model you already have somewhere else, give --model (or -m) a path instead of a name:

plank --model ~/models/my-ds4.gguf

A bare path loads that file and nothing else. plank never downloads or upgrades it, and looks for no drafter or encoder beside it, so a DeepSeek model loaded this way runs without speculative decoding unless you name a drafter with --mtp-model. There are two exceptions: a path to a managed engine's main file under any other name (a symlink or hard link included) selects that engine with all its companions, and a .ggd delta built on a managed engine's main inherits that engine's companions while the patched clone itself is never upgraded. --model:<name> is the strict form: it must name an engine and never falls back to a path, which is what a script wants. Set either permanently with engine.model in settings.json.

To keep a model of your own together with its companions, describe it as a local engine in ~/.plank/engines.local.json. That file is layered over the catalog, is the only place a role may name a path, and can also change the default:

{
  "default": "mine",
  "engines": {
    "mine": {
      "main":   { "path": "~/models/my-ds4.gguf", "url": "https://huggingface.co/owner/repo/blob/main/my-ds4.gguf" },
      "mtp":    { "path": "~/models/my-ds4.dspark.gguf" },
      "vision": { "path": "~/models/my-ds4.vision.gguf" }
    }
  }
}

plank --model mine then loads all three, and with "default": "mine" so does a plain plank. A local entry with the same name as a built-in engine replaces it. A role that names a path may also give a url (an https:// link, where a Hugging Face /blob/ page is turned into its /resolve/ download): when the file is missing, plank offers to download it into exactly that path, but it still never checks it for upgrades.

Upgrading from a release before engines renames the files under ~/.plank once, at the first launch: ds4flash.gguf becomes ds4vision.gguf, its drafter and encoder become ds4vision.mtp.gguf and ds4vision.vision.gguf, and the V4.1 files become ds41.*. Nothing is downloaded again, but it is one way: an older plank sharing the same ~/.plank no longer finds its model and offers to fetch it.

No model, no problem (sort of)

Without a model file, or in a build with no engine that can load it, plank runs against a built-in echo engine. Every command, tool, session feature and UI element works; the "model" just echoes. This is how the test suite runs and how UI work gets done, and it is what you will see if you launch on an unsupported platform.

Where plank keeps its files

Path What
~/.plank/<engine>.gguf an engine's main model, for example ds4vision.gguf (the default), ds41.gguf, qwen.gguf or gemma4-e4b.gguf
~/.plank/<engine>.mtp.gguf an engine's drafter, for speculation (--mtp): ds4vision.mtp.gguf is DeepSeek V4's DSpark model
~/.plank/<engine>.vision.gguf an engine's vision encoder. qwen.vision.gguf is installed with the engine, but plank does not load one for Qwen yet
~/.plank/engines/ one <engine>.installed.json per installed engine, recording which version is on disk
~/.plank/engines.local.json your own engines and default, layered over the catalog
~/.plank/staging/<engine>/ background downloads in progress, installed at the next launch
~/.plank/kvcache/ saved sessions (<name>.<family>.kv, such as .ds4.kv or .gemma.kv) plus the KV snapshots (*.kv_raw) and their metadata (*.json). Browse it with /kvcache.
~/.plank/settings.json global preferences
~/.plank/.mcp.json global MCP server config
~/.plank/hooks.json global hooks
~/.plank/sandbox.json global sandbox policy
~/.plank/skills/, templates/, agents/ global extensions
~/.plank/MEMORY.md user-scope memory
~/.plank/repro/ /repro dumps
~/.plank/usage-data/ /insights reports
~/.plank/doc-cache/ PDFs converted to Markdown
~/.plank/image-cache/ pasted images, deduplicated
~/.plank/mcp-advert/ last-known-good MCP tool advertisements
~/.plank/errors.log full detail behind terse tool errors
~/.plank/tool-call-errors.log malformed tool calls the model emitted
./.plank/ the same set, project-scoped, overriding the global one

The two logs are the first place to look when something failed and the on-screen message was too terse to act on. See Troubleshooting.


Next: Getting started โ†’