Runbook
pendingby rudkodm
Execute code blocks directly from your notes using a persistent terminal session.
Obsidian Runbook
Turn Obsidian into an executable runbook for DevOps, SREs, and developers. Run code blocks, execute commands line by line, and debug scripts directly from your notes — with a real, stateful terminal session.
Features
- 🖥️ Real terminal — Full xterm.js terminal with ANSI colors, interactive programs (vim, top, less), and tab management
- 🐍 Multi-language — Shell (bash/zsh/sh), Python, JavaScript, and TypeScript
- 🔄 Persistent state — Native interpreter sessions preserve variables across code blocks
- 📓 Session isolation — Each note gets its own shell and interpreter sessions
- ▶️ Run All — Execute an entire runbook sequentially with one command
- 🏷️ Runme compatible — Portable code block annotations work in both Obsidian Runbook and Runme (VS Code)
- ⚙️ Configurable — Custom interpreter paths, font sizes, and editor behavior
Table of Contents
- Installation
- Quick Start
- Supported Languages
- Code Block Annotations
- Frontmatter Configuration
- Interactive Interpreters
- Session Isolation
- Run All
- Terminal
- Settings
- Command Reference
- Runme Compatibility
- Security Considerations
- Known Limitations
- Tech Stack
Installation
Manual Install
- Download
main.js,manifest.json, andstyles.cssfrom the latest release - Create a folder:
<your-vault>/.obsidian/plugins/runbook/ - Copy the three files into that folder
- Open Obsidian → Settings → Community Plugins → Enable Runbook
Build from Source
git clone https://github.com/rudkodm/obsidian-runbook.git
cd obsidian-runbook
npm install
npm run build
Then copy main.js, manifest.json, and styles.css to your vault's plugin directory, or set OBSIDIAN_PLUGINS_HOME and run:
npm run install-plugin
Quick Start
- Open any note in Obsidian
- Create a fenced code block with a supported language:
```bash
echo "Hello from Runbook!"
ls -la
```
- Place your cursor on a line and press Shift + Cmd + Enter (macOS) or Shift + Ctrl + Enter (Windows/Linux) to execute that line
- Or click the ▶ button on the code block to execute the entire block
- Output appears in the terminal panel at the bottom
Supported Languages
| Language | Tags | Execution |
|---|---|---|
| Shell | bash, sh, zsh, shell | Real PTY session (default) |
| Python | python, py | Persistent REPL or one-shot |
| JavaScript | javascript, js | Persistent Node.js REPL or one-shot |
| TypeScript | typescript, ts | Persistent ts-node REPL or one-shot |
Shell
```bash
echo "Current directory: $(pwd)"
for i in 1 2 3; do echo "Item $i"; done
```
Shell blocks run in a real PTY with full ANSI color support, interactive programs, and persistent state.
Python
```python
import pandas as pd
df = pd.DataFrame({"name": ["Alice", "Bob"], "score": [95, 87]})
print(df)
```
By default, Python blocks run in a persistent REPL — variables are preserved across blocks in the same note.
JavaScript
```javascript
const data = [1, 2, 3, 4, 5];
const sum = data.reduce((a, b) => a + b, 0);
console.log(`Sum: ${sum}`);
```
TypeScript
```typescript
interface User { name: string; age: number; }
const user: User = { name: "Dima", age: 30 };
console.log(user);
```
Code Block Annotations
Runbook supports Runme-compatible JSON annotations after the language tag:
```bash {"name": "setup", "cwd": "/tmp"}
echo "Running setup in /tmp"
```
| Attribute | Type | Default | Description |
|---|---|---|---|
name | string | — | Cell identifier |
cwd | string | — | Working directory for this block |
excludeFromRunAll | boolean | false | Skip this block during Run All |
interactive | boolean | true | Use persistent REPL (false = one-shot execution) |
interpreter | string | — | Override interpreter path for this block |
Examples
Skip a block during Run All:
```bash {"excludeFromRunAll": true}
# This is for manual debugging only
kubectl port-forward svc/my-service 8080:80
```
Use a specific Python version:
```python {"interpreter": "python3.11"}
import sys
print(sys.version)
```
One-shot execution (no state preserved):
```python {"interactive": false}
print("This runs via python3 -c, no REPL state")
```
Frontmatter Configuration
Set document-level defaults using YAML frontmatter:
---
shell: /bin/zsh
cwd: ~/projects/my-app
---
| Key | Description |
|---|---|
shell | Default shell for this document |
cwd | Default working directory for all blocks |
Per-block cwd annotations override the frontmatter value.
Interactive Interpreters
By default, non-shell code blocks run in persistent REPL sessions. This means variables, imports, and state are preserved across code blocks within the same note.
Block 1:
```python
x = 42
data = [1, 2, 3]
```
Block 2 (same note — x and data are still available):
```python
print(f"x = {x}, sum = {sum(data)}")
```
Each language gets its own interpreter session per note:
- Shell blocks → PTY shell session
- Python blocks → Python REPL
- JS blocks → Node.js REPL
- TS blocks → ts-node REPL
To disable persistent state for a specific block, use "interactive": false.
Session Isolation
Every note in your vault gets its own set of sessions. Commands in deploy-prod.md won't affect sessions in debug-api.md.
- Sessions are created lazily on first execution
- Terminal tabs show the note name for identification
- Closing a note cleans up its sessions
Run All
Execute every code block in a note sequentially:
- Open Command Palette (Cmd/Ctrl + P)
- Run Runbook: Run all cells
Behavior:
- Blocks execute in document order
- Each block routes to the correct interpreter (shell, Python, JS, TS)
- Blocks with
excludeFromRunAll: trueare skipped - Per-block
cwdis respected - Execution stops on error
Terminal
The terminal panel uses xterm.js for a full-featured terminal experience:
- ANSI 256-color support —
ls --color, syntax highlighting, etc. - Interactive programs —
vim,less,top,htopall work - Clickable URLs — Links in terminal output are clickable
- Tab management — Multiple terminal tabs, rename, close
- Resize — Terminal reflows on panel resize
- Copy/paste — Standard OS shortcuts
- Command history — Up/down arrows (per session)
- Theme integration — Follows your Obsidian theme
Toggle the terminal: Cmd/Ctrl + P → Runbook: Toggle terminal
Settings
Settings → Community Plugins → Runbook
| Setting | Default | Description |
|---|---|---|
| Shell path | $SHELL or /bin/bash | Default shell for terminal sessions |
| Python path | python3 | Python interpreter path |
| Node.js path | node | Node.js interpreter path |
| TypeScript path | npx ts-node | TypeScript interpreter path |
| Font size | 13 | Terminal font size |
| Auto-advance cursor | true | Move cursor to next line after execution |
Command Reference
| Command | Default Hotkey | Description |
|---|---|---|
| Execute line or selection | Shift + Cmd/Ctrl + Enter | Run current line or selected text |
| Run all cells | — | Execute all code blocks in the active note |
| Toggle terminal | — | Show/hide the terminal panel |
| Start shell session | — | Start a new shell session |
| Get session status | — | Show current session info |
| Restart shell | — | Restart the active shell session |
Runme Compatibility
Obsidian Runbook adopts the Runme code block annotation syntax. Runbooks authored in Obsidian Runbook can be opened and executed in Runme (VS Code) and vice versa.
Supported shared attributes: name, cwd, excludeFromRunAll, interactive, interpreter.
Security Considerations
This plugin executes code and commands in real terminal sessions on your system. This means:
- File access is not limited to your vault. Commands can read, write, or delete any files your user account has access to.
- Commands run with your permissions. The plugin does not sandbox execution — it runs as you.
- Be careful with untrusted runbooks. Only execute code blocks from sources you trust.
This is by design — the plugin is meant for DevOps, SRE, and development workflows where full system access is necessary. If you need sandboxed execution, consider running Obsidian in a container or VM.
Known Limitations
-
Python compound statements —
if/else,try/except,for/elseblocks may not work correctly in the interactive REPL when split across indentation transitions. The REPL inserts blank lines between indent changes, which can prematurely terminate compound statements beforeelse:,elif:,except:, orfinally:. Wrap complex control flow in functions as a workaround. -
JS/TS
constredeclaration — Re-running a code block that declaresconstvariables will fail because the REPL scope already has the binding. Useletorvarfor blocks you plan to re-run. This is a fundamental REPL limitation. -
Desktop only — This plugin uses
child_processand PTY sessions. It does not work on Obsidian Mobile.
Tech Stack
| Component | Technology |
|---|---|
| Language | TypeScript |
| Plugin API | Obsidian API |
| Editor | CodeMirror 6 |
| Terminal | xterm.js |
| PTY | Python 3 pty module |
| Build | esbuild |
| Testing | Vitest |
License
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.