plank โ€บ User guide โ€บ Configuration

8. Configuration

Precedence

Each layer overrides the one before it:

built-in defaults โ†’ ~/.plank/settings.json โ†’ ./.plank/settings.json โ†’ environment โ†’ command-line flags

The rule of thumb: settings.json holds preferences, flags hold per-run choices.

settings.json

Hierarchical like the MCP config: ~/.plank/settings.json applies everywhere, ./.plank/settings.json in the working directory overrides it key by key. Everything is optional โ€” the file need not exist, and any subset of keys works.

{
  "engine": { "model": "~/models/ds4.gguf", "threads": 8,
              "backend": "metal", "power": 80, "ctx": 262144,
              "thinkingToolCalls": true },
  "ui":     { "respectGitignore": true, "popupRows": 15, "indexRefreshSecs": 5,
              "historySize": 512, "showToolCalls": false, "showToolResults": false,
              "showThinking": true, "notifications": "always", "notifyAfterSecs": 10 },
  "safety": { "sandbox": true, "btwSuspend": true },
  "mcp":    { "timeoutSecs": 30 },
  "ask":    { "maxOptions": 7 },
  "update": { "check": true },
  "agents": { "autoRoute": true, "maxParallel": 32 },
  "git":    { "signCommits": true },
  "worktree": { "sparsePaths": ["src", "docs"],
                "symlinkDirectories": ["target"], "isolateAgents": false }
}

Edit it in-session with /config (an interactive form) or one key at a time:

/config ui.showThinking false

Changes write ./.plank/settings.json and apply immediately. The one-key form behaves the same in the TUI and in the plain REPL; only a bare /config opens the interactive form.

engine

Key Default What
model the catalog default, ds4vision an engine name from engines.json, or a model file path (~ expanded). Same as -m.
threads engine default worker threads. Same as -t.
backend platform default metal, cuda, or cpu. Same as --backend.
power unset GPU power cap percent. Same as --power.
ctx 1048576 context window in tokens. Same as -c.
thinkingToolCalls true dispatch tool calls emitted inside the thinking block. false for strict ds4 parity.

ui

Key Default What
respectGitignore true whether @ completion honours .gitignore for untracked files
popupRows 15 rows the @ completion popup offers
indexRefreshSecs 5 how long the file index is trusted before a rebuild
historySize 512 prompt history entries retained
showToolCalls false show the model's ๐Ÿ› ๏ธ tool-call banners
showToolResults false echo tool result text into the scrollback
showThinking true render thinking (dimmed) in the scrollback
notifications always always, unfocused, or never
notifyAfterSecs 10 minimum turn duration before a completion notification
crtOff true CRT power-off animation on clean TUI exit
reducedMotion false collapse every animation to a static fallback
screensaver 1m idle delay before the screensaver: 1m, 2m, 5m, never
screensaverFace matrix which screen it shows: matrix, starfield, minions, or random for a coin flip each time
easterEggs true whether the arcade commands exist at all
builtinEditor true Ctrl-G uses the built-in editor; false shells out to $EDITOR

None of showToolCalls, showToolResults, or showThinking change what the model receives โ€” only what you see.

safety

Key Default What
sandbox on (macOS) default for the bash write sandbox. Same as --sandbox / --no-sandbox.
btwSuspend true default for /btw mid-generation suspend

tools

Key Default What
tools.editUpto by model whether the edit tool takes [upto] anchored old text and the prompt teaches it. Unset, it is off for DeepSeek and Qwen (as in ds4, whose V4 Flash checkpoint struggles with anchored edits) and on for Gemma. Changes the system prompt, so it takes effect in a new session.
tools.bashNotify false wake the model when a bash job it left running finishes, instead of having it poll with bash_status. Adds one sentence to the system prompt, so it takes effect in a new session. See Background jobs.
tools.loopGuards true arm the loop guards (/loopguard flips it for the session)
tools.noProgressGuard false also stop a turn that generates 32 KB of output without changing a file. Requires tools.loopGuards; see The no-progress budget.
tools.recall, tools.fanout, tools.runCode true offer the recall, fanout and run_code tools to the model
tools.remember true offer the remember and forget tools, which write memory to disk for the next session. See Memory.

context

Key Default What
context.microcompact true let micro-compaction clear old tool-result bodies in place to reclaim context. /mc off is the session equivalent.
context.shortReminder true make the system-prompt reminder plank re-injects every 50K tokens the short form: tool-call syntax, tool names, and a pointer to the original prompt. false re-injects the whole tools prompt as the C reference does.
context.resumeAcrossCompaction false when a reply runs into the context window, compact and continue it in the same message instead of ending the turn cut short. The summary reads the start of the reply and the model carries on from its last words; the reasoning budget spans the whole reply. Experimental: checked on DeepSeek V4 Flash and Gemma 4 E4B, off by default for now.

memory

Key Default What
memory.autoExtract true queue the extraction pass at the end of turns with no tool calls; it runs at the next idle moment and a typed prompt interrupts it. It costs a generation and a KV snapshot each time, at idle.
memory.extractEveryNTurns 1 run the pass only every N eligible turns; 0 is treated as 1
memory.budgets 4096 / 4096 / 6144 / 2048 bytes of user, feedback, project and reference entries rendered into context. Hand-edit only; not exposed in /config.

mcp, ask, update, agents

Key Default What
mcp.timeoutSecs 30 how long an MCP server has to answer before it is considered dead. Raise it for a slow-starting server โ€” one that misses the deadline is dropped along with all its tools.
ask.maxOptions 7 most options the ask tool may offer in one question (minimum is fixed at 2)
update.check true check GitHub Releases at startup for a newer version
agents.autoRoute true let the model pick a subagent definition on its own
agents.maxParallel 32 how many subagents may run at once (capped at 32)

git

Key Default What
signCommits true ask the model to end each commit message it writes with a blank line and --Co-Authored by Plank (https://plank-agent.dev). Set it to false and commit messages follow your repository's conventions and nothing else.

kvcache

How long plank keeps the KV snapshots under ~/.plank/kvcache/, and how much disk they may occupy in total. Ages are measured from a blob's last use, so a checkpoint you keep hitting never expires.

Key Default What
ttlSessionDays 14 days a session's KV payload survives after its last use. Expiring one costs a re-prefill of that conversation the next time you resume it, nothing more.
ttlTierDays 30 days a system-prompt or project-context checkpoint survives after its last use. These are the expensive ones to rebuild, so they get the longer window.
maxBytes 21474836480 (20 GB) hard ceiling on the whole cache. Once the age-based pass is done, anything still over budget is evicted least-recently-used first. 0 means no ceiling.

Three things always survive both passes: anything you have pinned in /kvcache, the chain the current launch is actually using, and any checkpoint that something newer still builds on. A budget is a target rather than a licence, so if everything left is protected plank stays over budget instead of deleting something it should not.

worktree

Key Default What
sparsePaths [] (everything) cone-mode sparse-checkout paths for a new worktree. Worth setting for a repository large enough that a second full checkout is painful.
symlinkDirectories [] directories linked from the main checkout instead of duplicated, e.g. target or node_modules. A name that could climb out of the worktree is ignored.
isolateAgents false give every subagent its own throwaway worktree. Off because a checkout per agent costs disk and time, and the work then has to be merged back; turn it on per-definition with isolation: worktree instead if only some need it.

Two things the file deliberately will not do

When it goes wrong

A broken settings file never stops plank from starting. Malformed JSON, a wrongly-typed value, an unknown key, an unrecognised backend name โ€” each falls back to that key's default. (The same bad name passed to --backend is an error: a flag is an explicit instruction, a config file is a preference.)

Because a settings file can quietly move you off Metal or shrink the context โ€” both of which show up only as "plank got slow" โ€” plank prints one startup line naming what is in force, listing only settings actually in effect. With no settings file, or one that changes nothing, there is no line at all.

One limitation: settings come from the directory plank launches in, so project settings do not follow --chdir.

Command-line flags

plank --help prints the full list. The ones you are most likely to want:

Model and engine

Flag What
-m, --model NAME|PATH run a named engine (ds4vision, ds41, qwen, gemma4-e4b, gemma4-12b, or one of yours from ~/.plank/engines.local.json) with its companions, or load a GGUF file on its own
--model:NAME run a named engine, and fail rather than read NAME as a path
--pick-engine open the engine menu at launch even when the selected engine is installed
-t, --threads N worker thread count
--backend NAME metal, cuda, or cpu
--metal / --cuda / --cpu the same, as switches
--power N GPU power cap percent (1..100)
-c, --ctx N context window in tokens, or with a k/m suffix: -c 128k, -c 1m. Without it, Gemma sizes its window from the RAM
-n, --tokens N maximum tokens to generate (default 50000)
--quality quality mode
--warm-weights touch all weights at load

Sampling and reasoning

Flag What
--temp F temperature (0..100)
--top-p F nucleus threshold (0..1)
--min-p F minimum-probability threshold (0..1)
--seed N RNG seed
--think-low (default) / --think / --think-max / --nothink reasoning effort

Session and mode

Flag What
-p, --prompt TEXT run one prompt and exit
--ui MODE front end: tui (default), console (headless), chart (headless, prints only the live /toks chart) or quiet (headless, prints only a start and a finish note); chart and quiet need -p
-sys, --system TEXT override the system prompt
--chdir PATH change working directory before starting
--worktree NAME start inside an isolated git worktree of this repository
--worktree-pr N base that worktree on pull request N (implies --worktree)
--trace PATH append a trace log
--prefix-file PATH condition the session on scripted USER: / ASSISTANT: turns placed after the system prompt; see Conversation prefix. Wins over a profile's prefix.
-h, --help [topic] help, optionally on one topic

Safety and extensions

Flag What
--sandbox / --no-sandbox bash write sandbox (on by default on macOS)
--disable-btw-suspend queue an in-pass /btw at the next boundary instead of suspending
--mcp-config FILE local MCP config (default ./.mcp.json)
--hooks on|off whether plugin and settings hooks run (default on); off also injects no SessionStart context
--tools on|off whether the model gets tools (default on); off sends no tools prompt, starts no MCP servers or WASM tools, refuses any tool call and skips the session-start context

--minimal-prompt implies both off; an explicit flag wins whichever order you give them in.

Speculative decoding

Flag What
--mtp speculative decoding (multi-token prediction), on by default
--mtp-off disable speculative decoding (target-only decode)
--mtp-model PATH the loaded model's companion GGUF: the DSpark drafter for DeepSeek V4
--mtp-confidence F pruning threshold, 0..1 (0 forces fixed five-token blocks)
--mtp-strict load the drafter but keep target-only decode, for comparisons

Speculative decoding is on by default under one name, --mtp. DeepSeek V4 Flash uses its auxiliary DSpark draft checkpoint: it proposes up to five tokens ahead and the main model verifies them, committing only the prefix it agrees with, so one verification pass can advance the stream by several tokens. DeepSeek V4.1 Flash has no drafter upstream, so plank never pairs one with it and decodes target-only there. --mtp-off turns speculation off explicitly.

On V4 the support model (~5.6 GB) needs no flag of its own: it is the ds4vision engine's mtp companion, resolves to ~/.plank/ds4vision.mtp.gguf, and is offered for download through the same resumable path as the main model, unless --mtp-model names one. A model loaded by a bare path has no companions, so speculation there needs --mtp-model.

A drafter plank paired for you is no longer able to stop a model from loading. The engine refuses to open a model at all when the draft checkpoint does not match it, so a checkpoint the default drafter does not fit used to fail until you found --mtp-off; plank now retries the open once without the companion it chose and reports the original error only if that fails too. A companion you named with --mtp-model is never dropped. When speculation turns out not to run, the temperature you would have been sampling at is restored rather than left pinned at 0.

Verification is argmax, so proposals are only used at --temp 0; sampled decoding ignores them. Whether it pays depends on the engine build, the quant and the machine: on an M5 Max it was a 0.71ร— slowdown until the Metal verifier was pipelined upstream, after which the same measurement read 1.19ร—. The peak rates in the exit message are the way to check on your own hardware.

Advanced engine tuning

--mtp PATH, --mtp-draft N, --mtp-margin F configure multi-token prediction with a draft model. --ssd-streaming and its companions (--ssd-streaming-cold, --ssd-streaming-cache-experts, --ssd-streaming-preload-experts) stream experts from SSD instead of loading them resident, which is how you run a model that does not fit. You rarely need to reach for --ssd-streaming yourself any more: plank measures the model file at startup and turns streaming on when it exceeds 80% of installed RAM less the engine's context buffers, printing the calculation it used. Passing the flag yourself skips that decision. --simulate-used-memory <N>GB pretends memory is already used, for testing those paths. Directional steering has a section of its own, Steering, below.

Steering

Directional steering nudges the model's activations along a direction, layer by layer, so it leans towards one kind of answer: terser, more cautious, less likely to refuse. plank keeps every direction you have in one file, ~/.plank/models/vectors.json, and you choose one by name:

plank --dir-steering succinct

The file is a JSON array with one entry per model, each listing its named directions. A direction's value is base64 of the raw little-endian f32 matrix the engine loads, one unit vector per layer:

[
  { "model": "ds4vision",
    "vectors": [
      { "name": "succinct", "value": "Pq3vPLnF...", "emoji": "๐Ÿ“–" },
      { "name": "heretic", "value": "u0Kz/D2p..." }
    ] }
]

An entry's model is the engine name, so a direction built for ds4vision follows that engine whatever its file is called. A model loaded by a path that is no engine is looked up by its file name instead, and plank tries the engine name first and the file name second. A direction belongs to the model it was built on: the same name under another model is a different vector.

You rarely write the file by hand. pt vectorize, from plank-replay, builds a direction from two prompt sets (or from the difference between a model and a modified copy of it) and stores it under the name you give with -n, and pt install <repo>:<path> merges a vectors file someone else published into yours, asking before it replaces a direction of the same name. Both write the file in place through a temporary copy, and keep any fields they do not recognise. One such field is yours to add: a direction's optional emoji is how the status bar marks it while it steers, as ๐Ÿ“– ffn 2, with the direction's name standing in when it has none.

Flag What it does
--dir-steering NAME steer along direction NAME, stored for this model; a name the file does not hold stops the launch and lists the ones it does
--dir-steering-ffn F the scale on the feed-forward edit, -100..100, 1 by default; a positive scale pushes towards the prompts the direction was built --to
--dir-steering-attn F the scale on the attention edit, -100..100, 0 by default
--dir-steering-from user|all user, the default, holds the edit off while the system prompt and session context prefill and arms it for your first message; all steers every token

Holding the edit until your first message keeps the cached prompt unsteered, so a steered run reuses the same prompt checkpoint as an unsteered one. An attention edit cannot be held back that way, so a non-zero --dir-steering-attn means all.

Inside a session, /steer lists the model's directions with the loaded one starred, /steer <scale> changes the feed-forward scale on the spot (name the direction as well when the model has more than one), and /steer <name> [scale] switches to another direction. When you launch without --dir-steering, plank loads the model's first direction at scale 0, which changes nothing until a /steer turns it up. Switching to another direction swaps its vector into the running model, so it applies to the tokens generated next without a reload; the conversation so far keeps the activations the old direction produced. On an engine that cannot swap a vector live, plank restarts into the new direction instead and resumes the session where you were.

A profile can carry a direction too: a steering block pairs one with the profile's recommendedModel (see Profiles), and a --dir-steering on the command line overrides it, scales included.

Steering is a feature of the native DeepSeek and Qwen engine. The Gemma engine, --remote hosts and --provider models ignore it. The direction plank loads is decoded once into ~/.plank/cache/steering/, named by a hash of its contents, so two names for the same vector share one file. The old --dir-steering-file PATH, and a steering block on an engine in engines.local.json, are refused with a pointer to their replacements.

Remote, shared-engine, control, and provider flags are covered in Remote and hosted engines.

Environment variables

Variable What
OPENAI_API_KEY key for --provider openai
ANTHROPIC_API_KEY key for --provider anthropic
HF_API_KEY Hugging Face token: model downloads authenticate with it and fetch four ranges in parallel
PLANK_REMOTE_TOKEN bearer token for --remote, --control, and plank remote
EDITOR editor for Ctrl-G when ui.builtinEditor is false

Next: Extending plank โ†’

Conversation prefix

--prefix-file PATH (or a profile's prefix) reads a file of scripted turns and places them between the system prompt and the conversation, as if that exchange had already happened. The model then continues in the manner the turns demonstrate, which a local model follows more reliably than a rule stated in a prompt:

USER: Write terse answers.
This line is still part of the user turn.
ASSISTANT: Understood.
USER: Return code without an introduction.
ASSISTANT: I will return only the code.

A marker starts a turn only at the beginning of a line; the file must start with USER:, alternate, and end with ASSISTANT:, or plank refuses to start and says which line is wrong. The turns are read once at launch, are never saved with a session or compacted, and apply to whatever session the run starts or resumes. The format is ds4's --prefix-file.