Command RunbookA VS Code extension and a terminal CLI, sharing one command library. Save useful terminal commands with a description, and run them again later.
Runbook gives you a searchable, local library of the terminal commands you actually use — with a human-readable description attached, so you can find them by what they do rather than by remembering their exact shell syntax. It ships as two frontends over one library: a VS Code extension and a standalone CLI. Both read and write the same JSON files, so a command saved in the editor is available in a bare SSH session, and vice versa. Why does it exist?You spend twenty minutes working out the right incantation:
It works. Three weeks later you need it again — and it is gone. You search your
shell history, dig through a Runbook is the missing step between "this command works" and "I will need this again":
Features
ScreenshotsScreenshots to be added. InstallationFrom a
|
| Flag | Meaning |
|---|---|
-d, --description <text> |
Skip the description prompt |
-t, --tags <a,b> |
Comma-separated tags |
-g, --global / -p, --project |
Choose or filter by scope |
-v, --var key=value |
Fill a {{template}} variable (repeatable) |
--first |
On an ambiguous query, take the best match |
-y, --yes |
Skip confirmations |
-q, --quiet |
Print command text only — pipeable |
--json |
JSON output |
An ambiguous query is never guessed at: Runbook lists the matches and exits non-zero rather than risk running the wrong command.
Usage
Save a command you just ran
- Run something in the integrated terminal.
- Press
Ctrl+Alt+S(Cmd+Alt+Son macOS), or run Runbook: Save Last Command from the Command Palette, or right-click inside the terminal and choose Save Last Command. - Accept or edit the suggested description.
- Optionally add comma-separated tags.
- Choose Project or Global.
Add a command manually
Command Palette → Runbook: Add Command.
Find and run a command
- Press
Ctrl+Alt+R(Cmd+Alt+R) and start typing. Enter runs the highlighted command; the inline buttons insert, copy or edit it. - Or click the Runbook icon in the Activity Bar and pick a command from the tree. Clicking an item opens the Run / Insert / Copy / Edit / Delete menu, and the inline ▶ / terminal icons run or insert it directly.
Command templates
Save a command containing {{placeholders}}:
ssh -i {{key}} {{username}}@{{host}}
Runbook prompts for each value before running or inserting it.
Commands
| Command | ID | Default keybinding |
|---|---|---|
| Runbook: Save Last Command | runbook.saveLastCommand |
Ctrl+Alt+S / Cmd+Alt+S |
| Runbook: Save Command from Recent History | runbook.saveFromHistory |
— |
| Runbook: Add Command | runbook.addCommand |
— |
| Runbook: Search Commands | runbook.searchCommands |
Ctrl+Alt+R / Cmd+Alt+R |
| Runbook: Filter Sidebar | runbook.filter |
— |
| Runbook: Clear Sidebar Filter | runbook.clearFilter |
— |
| Runbook: Refresh | runbook.refresh |
— |
| Runbook: Change Sort Order | runbook.setSortOrder |
— |
| Runbook: Export Commands | runbook.export |
— |
| Runbook: Import Commands | runbook.import |
— |
Item actions (runbook.run, runbook.insert, runbook.copy, runbook.edit,
runbook.delete) are invoked from the sidebar and search results rather than the
Command Palette.
All keybindings can be changed in File → Preferences → Keyboard Shortcuts.
Settings
| Setting | Default | Description |
|---|---|---|
runbook.confirmBeforeRun |
false |
Confirm before running any command. |
runbook.defaultScope |
"project" |
Scope pre-selected when saving. |
runbook.showGlobalCommands |
true |
Show global commands in the sidebar. |
runbook.sortBy |
"recent" |
recent, mostUsed, recentlyAdded, alphabetical. |
runbook.groupByTag |
true |
Group sidebar commands by their first tag. |
runbook.projectStorage |
"file" |
file writes .runbook/commands.json in the project (shareable, CLI-readable); workspaceState keeps them in VS Code's private storage instead. |
runbook.autoDescription |
true |
Pre-fill the description with a generated summary. |
runbook.warnOnSecrets |
true |
Warn before saving/exporting likely credentials. |
runbook.confirmDangerousCommands |
true |
Confirm before running destructive commands. |
Development setup
Requires Node.js 18+ and VS Code 1.93 or newer (the terminal shell integration API this extension relies on was finalised in 1.93).
cd command-runbook
npm install
npm run compile
Launching with F5
- Open the
command-runbookfolder in VS Code (this exact folder must be the workspace root, so that.vscode/launch.jsonis picked up). - Press F5.
- A second VS Code window — the Extension Development Host — opens with Runbook loaded. Its Activity Bar will show the Runbook icon.
- Open a terminal in that window, run a command, and try Runbook: Save Last Command.
npm run watch recompiles on save; use Developer: Reload Window in the
Extension Development Host to pick up changes.
Running the tests
npm test
This compiles the project and runs the unit suite with Node's built-in test
runner. The tests cover file and in-memory storage, storage paths, search,
sorting, duplicate detection, secret detection, danger detection, description
generation, templates, import/export, CLI argument parsing and CLI command
resolution — all of which live in src/core/ or src/cli/ and have no VS Code
dependency precisely so they can be tested this way.
To exercise the CLI by hand without touching your real library, point it at a scratch directory:
RUNBOOK_HOME=/tmp/runbook-scratch node bin/runbook.js save "echo hi"
RUNBOOK_HOME=/tmp/runbook-scratch node bin/runbook.js list
Linting
npm run lint
Building a VSIX
npm run package
This runs vsce package, which compiles the extension and writes
command-runbook-0.2.0.vsix into the project root.
Then install it with Extensions → ... → Install from VSIX..., or:
code --install-extension command-runbook-0.2.0.vsix
Before publishing to the Marketplace
- Create a publisher at https://marketplace.visualstudio.com/manage and set
"publisher"inpackage.jsonto that publisher ID (currentlyirohitsharma21). - Update
"repository","bugs"and"homepage"to the real repository URL. - Add a 128×128 PNG
iconfield topackage.json— the Marketplace listing icon is separate from the monochrome Activity Bar SVG. - Add screenshots to the README.
npx vsce publishwith a Personal Access Token.
Architecture
runbook/
├── src/
│ ├── extension.ts Activation, command registration, wiring
│ │
│ ├── core/ No VS Code imports — unit tested directly
│ │ ├── commandStore.ts CRUD over two KeyValueStores (global/project)
│ │ ├── keyValueStore.ts Storage contract + in-memory implementation
│ │ ├── jsonFileStore.ts JSON-file KeyValueStore shared by both frontends
│ │ ├── paths.ts Storage locations + project-root discovery
│ │ ├── search.ts Case-insensitive ranked search
│ │ ├── sorting.ts recent / mostUsed / recentlyAdded / alphabetical
│ │ ├── duplicates.ts Whitespace-normalised duplicate detection
│ │ ├── secretDetector.ts Credential heuristics + masking
│ │ ├── dangerDetector.ts Destructive-command heuristics
│ │ ├── describer.ts Offline command → description rule engine
│ │ ├── templates.ts {{variable}} extraction and substitution
│ │ ├── portable.ts Export/import format + defensive parsing
│ │ └── ids.ts UUID generation
│ │
│ ├── cli/ The terminal frontend — no VS Code imports
│ │ ├── index.ts Sub-commands
│ │ ├── args.ts Dependency-free argv parser
│ │ ├── resolve.ts Query/id → exactly one command
│ │ ├── prompt.ts readline prompts (written to stderr)
│ │ ├── output.ts Formatting, TTY-aware colour
│ │ └── shellInit.ts bash/zsh integration
│ │
│ ├── services/ Thin VS Code adapters
│ │ ├── storageService.ts File stores, legacy migration, project paths
│ │ ├── terminalService.ts Shell integration capture, run/insert
│ │ ├── prompts.ts Input boxes, quick picks, confirmations
│ │ └── config.ts Typed, defensive settings access
│ │
│ ├── commands/ One file per user-facing action
│ ├── providers/
│ │ └── commandTreeProvider.ts The sidebar TreeDataProvider
│ ├── models/SavedCommand.ts The single persisted data type
│ └── util/logger.ts Output channel + error guard
│
├── resources/runbook.svg Activity Bar icon
└── test/ node:test unit suites
The organising principle: all business logic lives in src/core/ and imports
nothing from vscode. services/ adapts VS Code onto those interfaces, and
cli/ adapts a terminal onto the same ones. That is why two frontends share one
implementation of search, storage, secret detection and description generation —
and why the whole thing is testable without an Electron host.
CommandStore only ever sees a KeyValueStore, so swapping the backing store
(Memento → JSON file, and later a synced backend) touches one adapter.
Data model
interface SavedCommand {
id: string; // UUID
command: string;
description: string;
scope: 'global' | 'project';
tags: string[];
projectPath?: string;
createdAt: number;
updatedAt: number;
usageCount: number;
lastUsedAt?: number;
}
Storage
Plain JSON files, in the export format, so they can be committed, diffed,
hand-edited or piped into runbook import:
| Scope | Location |
|---|---|
| Global | $RUNBOOK_HOME, else $XDG_CONFIG_HOME/runbook/commands.json, else ~/.config/runbook/commands.json (%APPDATA%\runbook\ on Windows) |
| Project | <project>/.runbook/commands.json |
{
"version": 1,
"updatedAt": 1785528188843,
"commands": [ { "id": "...", "command": "...", "description": "...", "...": "..." } ]
}
Files are written temp-then-rename, so an interrupted write cannot truncate your library, and an unparseable file is backed up rather than overwritten. The extension watches both files, so a command saved from the CLI shows up in the sidebar immediately.
The project file is the team-sharing mechanism. Commit
.runbook/commands.json and everyone who clones the repo gets the same project
commands — no server, no account. Add it to .gitignore if you would rather keep
them private, or set runbook.projectStorage to workspaceState so nothing is
written into the repo at all.
Commands saved by v0.1.0 in VS Code's globalState/workspaceState are migrated
to files automatically on first run. The originals are left in place rather than
deleted.
When no folder is open, the project scope is reported as unavailable and saves fall back to global.
How "Save Last Command" works
Runbook listens to window.onDidStartTerminalShellExecution and
window.onDidEndTerminalShellExecution (stable since VS Code 1.93) and keeps the
last 50 executed command lines in memory for the current window, along with each
one's exit code and VS Code's own confidence rating.
Because of this, the extension activates on onStartupFinished — if it only
activated when its commands were invoked, the shell execution event would
already have passed and there would be nothing to save.
Known VS Code API limitations
- Terminal history requires shell integration. There is no API to read terminal scrollback or your shell's history file. Shell integration is enabled by default for bash, zsh, fish and PowerShell; when it is off or the shell is unsupported, Runbook explains this and offers manual entry instead.
- Nothing is captured before activation. Commands run before VS Code finishes starting up are not seen.
- Low-confidence captures. VS Code reports a confidence level for each captured command line. When it is low, Runbook opens the command text for editing before saving rather than saving something that may not run.
- No terminal toolbar button. Extensions cannot add buttons to the terminal
panel toolbar. Runbook instead contributes to the terminal right-click menu
(
terminal/context) and the terminal tab menu (terminal/title/context). - No native search box inside a TreeView. The sidebar filter is an input box
opened from the view's title bar; the primary search experience is the
QuickPick (
Ctrl+Alt+R). - Sidebar items are single-line. VS Code tree items cannot show a description and a command on two separate lines, so the description is the label and the command is the (dimmed) inline description, with both in the hover tooltip.
Roadmap
Deliberately not in v0.2, to keep the scope honest:
- Smart save suggestions after a complex command succeeds (without ever nagging
about
ls,cdorgit status). - Optional AI-generated descriptions — the offline rule engine stays the default.
- Optional sync across machines (the file format is already the hard part).
- Command collections and multi-tag filtering.
- Command history intelligence ("you have run this 8 times — save it?").
- An interactive
runbookTUI picker that does not requirefzf. - Marketplace and npm publication.
Shipped in v0.2: the CLI, shared file storage, and committable per-project command libraries — which covers team sharing without a server.
Contributing
See CONTRIBUTING.md.
Privacy
Runbook is local-first, and has no network code at all.
- No command data is sent to external servers.
- No account is required.
- No telemetry.
- Commands stay on your machine, in the JSON files listed under Storage — you can read them, move them or delete them yourself.
- Export files are written only where you choose to save them.
One thing to be deliberate about: project commands are stored inside your
repository at .runbook/commands.json. That is what makes them shareable, but
it also means git push publishes them. Runbook warns before saving anything
that looks like a credential, but if a project's commands should stay private,
add .runbook/ to .gitignore or set runbook.projectStorage to
workspaceState.
Because terminal commands frequently contain credentials, Runbook additionally warns before saving or exporting anything that looks like a secret — and never modifies or redacts your command without asking.
License
MIT © Rohit Sharma