@arhen/pi-add-mode
pi extension: named modes bundling instructions, tool set, model, subagent model and colour — create/enable/disable with /mode, switch with ctrl+tab.
Package details
Install @arhen/pi-add-mode from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@arhen/pi-add-mode- Package
@arhen/pi-add-mode- Version
1.0.10- Published
- Oct 8, 2026
- Downloads
- 1,318/mo · 1,318/wk
- Author
- arhen
- License
- MIT
- Types
- extension
- Size
- 53.3 KB
- Dependencies
- 0 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@arhen/pi-add-mode
Named modes for pi: each mode bundles an extra instruction block, a tool set,
a model, a subagent model and a colour. Create modes once, then switch the whole setup with /mode or alt+m
instead of changing model/tools/instructions by hand at every session start.
Install
pi install npm:@arhen/pi-add-mode
Use
| Command | Effect |
|---|---|
/mode |
Open the mode panel: list, enable/disable, create, edit, delete, activate |
/mode <name> |
Activate a mode directly |
/mode off |
Back to the built-in default mode |
/mode new |
Create a mode |
/mode edit |
Edit a mode (picks from a list) |
/mode list |
Print modes with enabled/active state |
alt+m |
Cycle forward over enabled modes (default first) |
ctrl+tab / ctrl+shift+tab |
Aliases, only where the terminal does not consume them |
Enable ≠ activate: space in the panel toggles whether a mode is in the alt+m rotation; enter (or
/mode <name>) activates it. Disabled modes can still be activated by name.
pi --start-mode review starts a session with a mode already active.
Status line
While a mode is active, pi's working line shows <mode> is working..., and while idle the editor border shows
<mode> standby. The whole editor border box (top and bottom lines, working indicator) is tinted with the mode
colour. The built-in default mode changes nothing at all: no instructions, no tool changes, no model change, no
border or working-line change.
With @arhen/pi-senja, the live timer keeps the mode label and colour: <mode> is working... 8s.
The extension publishes its styled working label (or undefined for default) on pi.events channel
pi-mode:working-message whenever mode visuals change.
While a coloured mode is active, the thinking-level border colour is replaced by the mode colour. Bash mode still wins:
prefix the input with ! and pi's own border colour comes back, with the mode label hidden. The tint needs this
extension's editor; if another editor extension owns the editor, only the <mode> standby widget line is shown.
Terminal notes
alt+m is the primary shortcut. Ghostty leaves plain alt chords free, Herdr reserves only its ctrl+b prefix, and pi
uses alt+b/f/d/enter/up/left/right/backspace but not alt+m. It also works without the Kitty keyboard protocol, so it
survives plain tmux and old terminals.
ctrl+tab / ctrl+shift+tab are registered as aliases, but most terminals consume them before pi sees them — Ghostty
binds both to tab switching — so they usually do nothing.
Mode fields
| Field | Values |
|---|---|
enabled |
in the alt+m rotation |
color |
theme token (accent, warning, success, error) or hex (#ff9f43) |
description |
free text, shown only in the panel |
instructions |
extra system-prompt section while the mode is active |
tools |
"default", "plan" (read-only), "build" (write set + extras) or an explicit list |
model |
provider/model-id or unset = session model |
thinking |
effort while the mode is active: off/minimal/low/medium/high/xhigh/max, unset = session level |
subagentModel |
provider/model-id for subagent tasks; unset = leave the call's model |
subagentThinking |
effort for subagent tasks; unset = leave the call's level |
leaderOverride |
false (default): subagent model/effort always applied. true: the leader may pick another per task |
Picking a model in the editor always asks for the effort right after; cancelling the effort picker aborts the model
change. The effort list follows the model: off only for non-reasoning models, minus levels the model marks
unsupported.
leaderOverride decides who wins when the leader names its own subagent model or effort:
false(default) — the mode wins. Everysubagenttask and everyresume_subagentruns onsubagentModel/subagentThinking; anymodelorthinkingthe leader passes is replaced. This also covers calls the leader makes from codemode (tools.subagent(...)). The system prompt tells the leader not to pass them.true— the mode only fills tasks that name none; an explicitmodelorthinkingfrom the leader wins, so it can put one risky task on a stronger model.
The editor asks for it right after the subagent effort, and lists it as Leader override. A model pinned in an
agent file (.pi/agents/*.md frontmatter) still wins either way, because the subagent tool resolves that before
the call's own model.
Storage
// ~/.pi/agent/modes.json (global)
// <cwd>/.pi/modes.json (project, same names override global)
{
"review": {
"enabled": true,
"color": "#ff9f43",
"instructions": "Review only. Do not edit files. Report findings with file:line.",
"tools": "plan",
"model": "openai-codex/gpt-6.1-sol",
"thinking": "xhigh",
"subagentModel": "openai-codex/gpt-6.1-luna",
"subagentThinking": "low",
"leaderOverride": false
}
}
The reserved name default is never stored. Seeded modes are created by the panel or by editing these files; run
/reload after manual edits.
Prompt-cache behaviour
Mode instructions are injected as a structured system-prompt section in before_agent_start
(systemPromptOptions.sections.mode). This is the cache-friendliest option pi offers:
- A system-prompt section stays byte-identical for every turn while the mode is active, so the cached prefix stays warm after the first request; pi can also communicate section changes to the provider as a transcript delta instead of rewriting the leading system prompt.
- Injecting the same text as a per-turn user message would keep the prefix warm but re-send (and re-bill) the tokens every turn and pollute the conversation with synthetic user turns.
- Replacing the whole system prompt (
forceSystemPrompt) or rewriting the system message withcontext_with_systeminvalidates the cached prefix on every change.
Switching modes mid-session is the only cache cost: the changed section invalidates the prefix once, then stabilises.
Development
bun test # unit tests
bun run check # typecheck + lint + tests
Load the source directly without installing:
pi --extension packages/add/pi-add-mode/src/index.ts