@nanstey/pi-command-shortcuts

Bind keyboard shortcuts to any extension slash command in pi via a JSON config

Packages

Package details

extension

Install @nanstey/pi-command-shortcuts from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@nanstey/pi-command-shortcuts
Package
@nanstey/pi-command-shortcuts
Version
0.1.0
Published
Jul 8, 2026
Downloads
112/mo · 13/wk
Author
nanstey
License
MIT
Types
extension
Size
13.2 KB
Dependencies
0 dependencies · 0 peers
Pi manifest JSON
{
  "extensions": [
    "./extensions"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

@nanstey/pi-command-shortcuts

Bind keyboard shortcuts to any extension slash command in pi.

pi's built-in keybindings.json only remaps built-in actions — it can't bind a key to an extension command like /voice. This extension does, driven by a small JSON config.

Install

pi install npm:@nanstey/pi-command-shortcuts

Usage

Create ~/.pi/agent/command-shortcuts.json mapping keys to commands:

{
  "ctrl+r": "voice",
  "ctrl+g": { "command": "websearch", "args": "" }
}

Reload with /reload (or restart pi). Press the key to run the command.

Each value is either:

  • a string — the command name (no leading slash). The owning extension is auto-discovered by scanning your installed packages.
  • an object{ "command": "...", "from": "...", "args": "..." }
    • command — command name to run (required).
    • from — package name (@scope/pkg) or module path that registers the command. Skips the scan; faster and unambiguous. Optional.
    • args — default argument string passed to the command. Optional.

Keys use pi's key syntax, e.g. ctrl+r, ctrl+shift+v, alt+enter.

How it works

Extension command handlers are private to pi, and shortcut handlers have no "run command by name" API. So this extension re-runs the owning extension's factory with a Proxy of the real pi that:

  • captures registerCommand() handlers,
  • no-ops the other register* / on() calls so nothing is double-registered,
  • forwards every other method (sendMessage, sendUserMessage, …) to the real pi, so a captured handler behaves exactly like the real command.

The resolved handler is cached per binding and invoked when you press the key. Modules are loaded through pi's own loader, so TypeScript extensions work without a build step.

Notes

  • Context limitation. Shortcut handlers receive an ExtensionContext, not the richer ExtensionCommandContext. Commands that rely on command-only methods (newSession, waitForIdle, fork, …) may not work from a key. This is a pi limitation tracked in pi#4422. Commands that only use ctx.ui and messaging (like /voice) work fine.
  • Conflicts. If a key already drives a built-in action (e.g. ctrl+r is the session-picker rename), pi logs a harmless conflict diagnostic and this binding wins in the editor. Pick a free combo (ctrl+shift+…) to avoid it.
  • Discovery. Prefer from for speed and to avoid importing unrelated extensions during the auto-scan.

License

MIT