@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.

Packages

Package details

extension

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. Every subagent task and every resume_subagent runs on subagentModel / subagentThinking; any model or thinking the 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 explicit model or thinking from 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 with context_with_system invalidates 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