plank โ€บ What's new

What's new

This site went up in late July 2026, around v2.5. Plank has not held still since. Below are the changes worth knowing about if you have been away, newest first. The full changelog has every last fix; this page has the ones you will actually notice.

Just landed

v4.1.0 is out, and the beta channel is on 4.1.2. The patch number is still the channel: .0 is stable, anything above it is beta.

4.1.2 (beta) stops the sandbox asking about reads. Any bash command that so much as mentioned ~/.plank, including cat ~/.plank/settings.json, used to raise "Allow it to write there?", because the sandbox profile is built before the command runs and the trigger was a text match. A command made only of known read-only utilities with no redirect now runs without the question. Anything that could write still asks, and the sandbox itself is unchanged.

A turn can no longer spend an hour repeating one thought. This release started from a measurement rather than a hunch. A small feature request that Claude Code finishes in a few minutes took plank two hours, and the saved transcript showed why: not a hundred tool calls, but one thinking block of 149K characters in which three paragraphs repeated 263 times until the token cap. The repetition guard that already protected /insights now watches the reasoning of every generation pass, and when the tail starts cycling the pass is stopped and the model is told to act on the plan it already has.

The same request was then rerun against the same commit after each fix, and the numbers are in FINDINGS.md: from no result in two hours, to a finished and tested change in 23 minutes, to 16 minutes. Three things did most of that work. Reasoning now defaults to low effort, which turned a stuttering list of "maybe" thoughts into numbered plans and cut a third off the run; --think gives you the old medium level back. The edit tool understands CRLF files, so a Windows-style source file no longer fails every multi-line edit and quietly converts single lines to LF. And search skips build output, so a search over . no longer returns the target/package/ copy of every file ahead of the real matches.

The system prompt has a working style. A plank-owned section after the tool schemas, outside the byte-for-byte C parity base, tells the model to batch independent tool calls in one stanza, explore briefly and then act, pick the smaller of two plausible designs rather than deliberate, edit straight from search context, stay inside the requested scope, and review diffs one file at a time. Each line was earned by a specific turn wasted in those reruns.

/memory edits every memory file at once. It builds one buffer from your user and project MEMORY.md files, each between markers naming its scope and path, and opens it in the built-in editor. On save the buffer is split back along the markers and only the files whose text changed are written. A note typed under the last section joins the section above it, and deleting a section's markers leaves that file alone.

AGENTS.md is the only instructions file plank reads. CLAUDE.md is no longer a silent fallback. Instead, an interactive start in a project that has a CLAUDE.md and no AGENTS.md links the one to the other and says so, and a project with neither asks whether you want /init to write one. Headless runs do none of this; they also save their transcript now, so a slow run can be analysed afterwards, with --no-session to opt out.

From 4.0.0, the day before: the prompt's idle/busy cursor is drawn into the frame instead of sent as an OSC 12 escape, so it finally shows up in Warp and the other terminals that ignored the recolouring; --ctx takes 128k and 1m; and model upgrades are driven by a versioned manifest that asks before downloading, verifies as it streams, and installs at the next launch rather than under a running session.

v3.6: vision, self-compacting turns, a codebase review

v3.6.0 shipped in early September, with the beta channel on 3.6.3.

Long turns compact themselves now. Plank asked whether the context needed compacting before a turn started, and then never asked again while that turn ran โ€” but a turn appends a tool result on every round, so a session that reads, edits and tests its way through a dozen rounds could sail past the soft limit halfway through and keep generating against a window that was already full. The check now runs at the top of every round, which is what the C reference plank was ported from has always done.

Plank can see. Paste a screenshot and ask what broke. Vision has been in the box for a while, but it never actually worked: four separate faults in the image path sat on top of each other, and between them they either crashed the process outright with malloc: pointer being freed was not allocated or, worse, answered confidently about an image the model had never been shown. A screenshot of a browser window came back described as a close-up photograph of a person's face, which is the sort of failure that looks like a bad model rather than a bad pointer.

All four are fixed. The engine takes ownership of the buffer it is handed and frees it itself, so it now gets a copy of its own instead of a pointer into memory Rust was also freeing. An image already in the conversation keeps the span the engine built for it rather than having one reconstructed on the next turn, which could never have worked, because the engine rewrites that span as it consumes it. The encoder's metadata is read before the call that zeroes it, so an image no longer arrives with an empty layout and gets discarded along with the message text wrapped around it. And the prefill chunk was raised past the 384 token cap on an image block, which the engine refuses to split, so anything larger than a thumbnail no longer fails the whole prefill.

What that adds up to: paste a screenshot of a stack trace, a failing UI, a diagram or a photo of a whiteboard, and the model looks at the picture. The encoder lives beside the main model at ~/.plank/ds4flash.vision.gguf, is fetched on first launch if it is missing, and runs on the same Metal device, so no pixels leave the Mac. Images are encoded within a budget of 384 visual tokens, so crop to the part that matters when you want small text read, and reach for ocr-mcp when you want a dense page transcribed rather than described.

A whole-codebase review, and the fixes that came out of it, shipped in v3.6.0. The one you will feel first is that the sandbox now means what it says: write and edit are held to the same boundary as the shell, so a path outside the working directory, the temp roots and your writablePaths is refused with Tool error: write path escapes workspace, where before the model could route around the bash sandbox simply by choosing the file tool instead. A project's .plank/sandbox.json can now only tighten the policy, so a cloned repository cannot switch your sandbox off. plank serve got the same treatment: binding off loopback with no token now refuses to start unless you pass --insecure on the server side, bodies are capped at 16 MiB, idle connections time out, and shared-engine sessions are keyed per client so many turns from one laptop reuse one warm session. Underneath, every bash job, ! command, MCP server and hook runs in its own process group and is killed as a tree, so a timed-out sleep 600; echo ok or a cmd | tee pipeline no longer outlives plank, and Ctrl-C or Esc on a running command actually reaches it and reports exit 143. And two slash commands the TUI had quietly left to the plain REPL now work in both: /rate [+|-] [note] is in the completion popup, and /config <key> <value> sets the value directly, opening the form only when you type a bare /config.

Every sub-agent gets a console window of its own. Delegating work to sub-agents made plank quieter and less legible at the same time: the parent told you it had handed off, and everything after that happened somewhere you could not see. The debug console now gives each sub-agent its own window, titled plank:<session>:subagent-<n> and numbered in the order they start.

Four turbo-debug-console windows tiled: the parent session plank:sassy-washington bottom-left summarising both results, and subagent-1 and subagent-2 on the right, each streaming its own Python and its own answer

When a block of agent calls fans out, the slots run concurrently and you can read them side by side โ€” above, two sub-agents working the same question in different formats, each with its own code and its own conclusion, while the parent window collects both into one answer. Each window retires when its sub-agent finishes. A single agent call gets a window too, so the serial case is no darker than the parallel one.

While fixing that, one long-standing annoyance went with it: a malformed tool call used to freeze a console window for the rest of the session, so it sat there showing nothing while plank quietly recovered and carried on. It now prints the error and keeps going. Both need turbo-debug-console 0.2.1 or newer.

The footer counts what you have changed. The TUI's top row has always told you which tree you are in โ€” directory and branch, held still while everything else churns. It now also tells you what you have done to it: ๐Ÿ“„ 3 ยท +128 -41, files touched, then lines added in bright green and lines deleted in bright red. Staged and unstaged work are counted together and untracked files are included, so a file you edited and then added appears once, not twice. A clean tree shows nothing at all.

/insights now recommends the plank features you are not using. The report already knew how you work; a new Features to try section turns that into two or three concrete suggestions โ€” a skill for the routine you retype every week, a PostToolUse hook for the lint you keep running by hand, a subagent for the sweeps you do serially โ€” each with a ready-to-run snippet rather than a description. It reads your installed skills, templates, subagents, hooks and MCP servers first, so it suggests what is missing instead of what you already have. See Slash commands.

The thinking you hid is one glance away, in a second window. With showThinking off the scrollback stays about the answer โ€” but the reasoning is still worth watching while it happens, and a log file only tells you afterwards. plank mirrors its whole raw model stream to turbo-debug-console, a text-mode viewer that renders it in its own window: thinking dimmed above the answer, code highlighted, tool calls as banners.

turbo-debug-console showing a plank session: the model's thinking in dim grey above its answer in white, in a text-mode window titled plank:sneezy-einstein

Each session gets a window titled plank:<session-name>, matching the name above your prompt, and the window and its scrollback survive plank exiting โ€” restart and the new run appends below a -- reconnected -- rule. It is entirely optional: with nothing listening plank connects to nothing, says nothing, and behaves exactly as it always has. brew install aovestdipaperino/tap/turbo-debug-console.

plank tells you where every setting came from. Settings arrive from five layers โ€” built-in defaults, plugins, ~/.plank, the project's ./.plank, and CLI flags โ€” and "I set that and nothing happened" used to mean reading code. plank --dump-config now prints each effective key with the layer that won and the layers it beat, and /config --resolved does the same inside a session, including which plugin won a contested skill or agent name. See Configuration.

Loop guards for the failure local models actually have. A stale edit anchor sends a model into read/edit/read/edit, and small models often do not notice. On the third identical call the result now carries "you have called this tool with these arguments 3 times; the result has not changed". Nothing is blocked โ€” a legitimate third read exists, and blocking on a guess would be worse โ€” and polling an async job is exempt, because a poll looks exactly like a stuck loop. Set tools.callTimeoutSec and the model is also told when a call overran its budget, so a hung test suite stops being invisible.

Large tool output is no longer a dead end. Ask about a 5 MB build log and read used to truncate with nowhere to go. The full payload now goes to ~/.plank/spill/, the model gets a bounded preview plus a continue_offset it can page through with more, and /export still sees everything the tool returned. The same bound applies to oversized MCP results, which previously had no cap at all โ€” a single chatty server could fill your context in one call.

Context reclaims itself between turns. After a dozen large reads those bodies are dead weight. plank now clears the old ones at end of turn โ€” keeping the newest three, anything small, and everything belonging to the task you are on โ€” with no model round-trip and no summarising. It holds off unless it would reclaim at least 4 KiB, because rewriting the transcript invalidates the KV prefix and an eager pass costs more than it saves.

/search across your own sessions. You remember fixing a Metal crash a few weeks ago but not how. /search metal finds the session, shows the matching snippet and offers /resume; --all widens beyond the current project. It is deliberately compaction-proof โ€” long sessions are both the ones worth searching and the ones that get compacted, so the index keeps conversation the transcript has since dropped.

Goals that outlive the session. /goal --max 5 make the failing test pass runs a loop that adjudicates its own progress and stops on a verdict or the cap. The objective is durable state: it survives /save, /resume and /compact, so you can come back tomorrow and still see what it was pursuing and how far it got.

/rate records what worked, somewhere the model cannot see. A rating goes to a sidecar, never the transcript, context or KV โ€” if the model could read your ratings it would start optimising for them and the signal would be worthless. /insights turns a week of them into satisfaction over time plus the notes on the turns that went wrong.

Three new tools, on by default. recall gives the model that same session history, so it can look up a past decision instead of guessing or interrupting you. fanout runs several independent subtasks and joins their reports in a fixed order, with optional throwaway-worktree isolation when subtasks edit files. run_code batches a few pre-decided operations โ€” read, glob, edit, bash โ€” into one turn instead of a round-trip each; every operation is routed through the normal tool dispatch, so the sandbox, the ~/.plank write grant and your PreToolUse hooks apply exactly as they would to a bare call. Switch any of them off with tools.recall, tools.fanout or tools.runCode.

plank signs the commits it writes. When the model creates a git commit, the message now ends with a blank line and the single line --Co-Authored by Plank (https://plank-agent.dev), so a git log months from now still says which commits came out of a session rather than out of your fingers. It is a system-prompt instruction, not a hook, which means the model can be told to leave it off for one commit and your repository conventions still come first. If you would rather it never appear, set "git": { "signCommits": false } in settings.json or run /config git.signCommits false, and the instruction is gone from the prompt entirely. See Configuration.

DSpark speculative decoding is on by default. The auxiliary draft checkpoint used to need --dspark; now it is the default and --dspark-off is how you get target-only decode. Speculation only engages at temperature 0, so a bare plank samples argmax: pass --temp if you want sampling back, and --dspark-off leaves the old 0.6 default in force. The support model is still fetched on demand the first time it is needed.

The exit summary says where the session went. It used to end with a peak prefill rate and a peak generation rate โ€” one lucky pass each, which tells you nothing about the run you just had. Now every model that ran gets a line: time spent prefilling and time spent generating, each with the session's average rate, and time spent running tools.

avg deepseek-v4-flash  prefill 12.3s (1420.5 tok/s)  ยท  generation 45.2s (38.1 tok/s)  ยท  tools 8.4s

That last number is the one nobody was measuring, and a turn that felt slow is often not the model at all.

The think segment shows the router working. Two braille cells beside the reasoning level re-roll on every decoded token, standing in for the mixture-of-experts routing. Being straight about it: the real selection never leaves the GPU on the Metal path, so the pattern is derived from the token id. It is honest about sparsity, about routing changing every token, and about the same token lighting the same dots โ€” and it does not know which experts. The reasoning level itself is now colored by how hard the model is thinking, red for max down to grey for off.

The decode rate stopped lying on long prompts. It was timed from the start of the generation call, so it divided tokens by decode time plus prefill plus the wait for the first token, and opened far below the real rate. It is measured from the first token out now.

Greedy chain decode on Metal. At temperature 0 a run of argmax tokens decodes with the next token id kept on-device, dropping the per-token GPU sync and logits readback. Output is bit-identical. Off on M5, where it measures slightly slower than the plain path.

The --dspark footer reads 1.5t/step, not 1.5x. It was always tokens committed per speculative step, and that is not a wall-clock speedup: on Metal it sits above 1.0 on runs that decode slower than plain decode. Calling it a multiplier was the bug.

plank can read your screenshots. Image pasting is on by default now, and paired with the ocr-mcp server the model can act on what you paste: it calls transcribe_image on the cached path and gets the text back. Screenshot a stack trace, paste it, ask what it says. Transcription runs on your own machine against a 0.9B OCR model, so no image leaves the laptop and there is no API key. Install it with brew install llama.cpp && cargo install ocr-mcp, register it in .mcp.json, and see Extending plank for the weights. Pasted images are also cached byte-for-byte now: the old downsampling was inherited from an image-upload limit plank never had, and it only threw away the pixel density an OCR tool needs.

Plugins load. A plugin is one directory bundling skills, agents, templates, hooks, an .mcp.json and a settings.json, contributed to a session as a unit. plank picks them up from ~/.plank/plugins/dev/, from ./.plank/plugins/, or from a repeatable --plugin-dir for the session you are in, and reads both its own spelling and Claude Code's. A plugin contribution is always addressable as <plugin>:<name> and keeps the bare name only when nothing else claims it, so your own skills and agents never lose theirs. Plugin settings sit below your own files, and /plugins shows what loaded, what each one contributes and every warning. There is no installer and no marketplace yet โ€” you place the directory yourself. See Extending plank.

v3.3.0 is out, and the beta channel has opened on 3.3.1. The patch number is still the channel: .0 is stable, anything above it is beta.

Your session has a name from the first frame. The memorable adjective-celebrity name used to be minted when a session was first saved, so until you quit there was nothing to call the conversation you were in. It is minted at the start now, and floats at the right end of the rule above the prompt. /rename <name> changes what later saves use without touching what is already on disk, so the earlier file stays resumable under its old name. Resuming a session no longer replays plank's own scaffolding at you either โ€” the agent instructions, memory, git status and date that open a session are sent to the model, not typed by you, and they stay out of the transcript you read. See Sessions.

/kvcache shows the cache as the tree it really is. Every KV snapshot on disk now carries a small metadata file recording what it is, which snapshot it was built on, the model and reasoning level behind it, its size, how many times it has been reused, and when. /kvcache draws that as a tree you can walk with the arrow keys: p pins an entry so nothing will ever sweep it, d deletes one, g sweeps now.

The cache expires on age and is capped on size. Snapshots used to be kept only for the current system prompt and project, and every sibling was deleted, so switching model or reasoning level and back paid a full system-prompt re-prefill each way. Now they expire on time since last use (14 days for a conversation, 30 for a shared checkpoint) and a 20 GB ceiling evicts the least recently used beyond that. Several system prompts coexist for as long as you are using them. See Sessions and Configuration.

/open [path] edits an existing file in plank's own editor: Ctrl-S saves, Esc discards. Bare /open reopens the last file a tool call touched, which is usually the one you wanted to look at.

One session, several engines

Cross-engine sub-agents. A subagent definition can name the engine it runs on, independently of the main agent: a provider: and model: in its frontmatter, with an optional base URL and the name of the environment variable holding the key, so the file stays committable. provider: local names the local engine specifically, so a hosted main agent can delegate to the model on your own Mac. /agent shows each definition's engine and the variable to set when it is missing.

Git worktrees. A session can move itself into an isolated worktree and back out again, so an agent can work on a copy of the repo without touching yours. Subagents can each get their own with isolation: worktree.

DSpark speculative decoding, behind --dspark. DeepSeek's auxiliary draft checkpoint (~5.6 GB on top of the model) proposes tokens the main model verifies in batches. It downloads and resumes the same way the model does.

A live agent roster under the status bar. Instead of one sub-agent's output in a hidden pane, every run gets a row: what it is doing, how long it has been at it, and what it has spent. โ† on an empty prompt steps into it, Enter opens an agent's output in full, Esc comes back. A fan-out shows every agent at once, each with its own buffer, and what comes back into your transcript is the agent's answer with its thinking stripped out. See The agent roster.

Drive it from somewhere else

/remote-control, or /rc, starts and stops a remote-control server from inside a running session, and /grant approves a client that asks for control. The old --control* flags are gone.

The bundled web client is a real front-end now. It wears plank's own dark theme, streams the turn as it happens, and tells you unmistakably when the connection drops. Attached clients get the end-of-turn notification too, so you can walk away from the laptop and still be told when it is your move.

Reasoning you can dial

/think off | low | medium | max, with --think-max and friends on the command line. low is experimental and cheap; max prepends a reasoning-effort preamble and is the one worth reaching for on a hard problem. The status footer shows which level is in force as a ๐Ÿง  med segment, because it changes both cost and answers and used to be invisible.

The terminal got more useful

! and !!. A bare !command runs a shell command and hands the result to the model, which is what you almost always wanted; !!command keeps the output to yourself.

/btw <question> answers beside the running task instead of freezing it. The aside runs on a fork of the session in a split panel, so the real conversation is never touched and neither side waits for the other.

PDFs are readable. read on a .pdf converts the document to Markdown, with OCR for a scanned one.

/insights builds a personal usage report over every session you have ever saved and writes it to an HTML file: where the time went, which tools you lean on, how the model actually behaves for you.

A prompt editor on Ctrl-G, built in rather than shelling out to $EDITOR, for when the thing you are about to ask has outgrown one line.

/compact [instructions] compacts the conversation now rather than waiting for the automatic pass, and an argument steers what that one summary keeps. Compaction shows its progress in the status bar, and Ctrl-C interrupts it.

A screensaver, and an arcade. After a few idle minutes plank goes to a starfield, matrix rain, or a couple of minions, chosen at random and configurable. There are also games that run over a live turn, which is either a feature or a confession (chapter 11).

Sessions became a tree

/tree, /fork, /clone. A session is a tree of messages rather than a line. /tree draws it and numbers the fork points; /fork <n> rewinds to just before one of your prompts and keeps everything after it as a sibling branch, so you can try a different approach without losing the first; /clone freezes the current branch and continues on a copy. All of it is shaped so the cache is reused rather than rebuilt.

/export [md|html] renders the transcript to a shareable file. The HTML is standalone.

Prompt templates. Markdown files in ~/.plank/templates become commands, with {{variable}} substitution.

MCP over Streamable HTTP. An .mcp.json entry with a "url" speaks to a remote MCP server, alongside the stdio servers that were already supported.

Things that were quietly broken

A resumed session used to re-prefill its whole conversation. /new and /clear used to rebuild the system-prompt cache from scratch. A dropped network could hang a turn forever with Ctrl-C doing nothing. A compaction that produced no usable summary could destroy the transcript. Several shapes of tool call the model emits were parsed wrongly and died. All fixed, and each one has an entry in the changelog explaining what actually went wrong, if you like that sort of thing.


New here instead? Start with the user guide, or install it and get going.