it2 Plugin Development Guide
This guide covers developing plugins for the it2 command-line interface for iTerm2 automation.
See also: taxonomy.md for the authoritative definitions of plugins, hooks, and tools.
Overview
Plugins are external executables that extend it2 functionality. They are discovered automatically from your PATH and invoked programmatically by it2.
Plugins can:
- Enrich listings - Add metadata to session, tab, and window listings
- Automate responses - Monitor and respond to patterns
- Categorize entities - Apply context-specific logic for better organization
Plugins vs Hooks vs Tools
| Concept | Definition | Invocation |
|---|---|---|
| Plugin | Extends it2, auto-discovered | By it2 (it2 plugin run <name>) |
| Hook | Responds to external tool events | By Claude Code, Gemini CLI, etc. |
| Tool | Standalone CLI program | Directly by user |
Plugins are it2-facing: it2 discovers and invokes them. Hooks are external-tool-facing: Claude Code or other tools invoke them based on their configuration.
Plugin Architecture
it2 (core)
↓ Discovery
Plugin Registry (internal/plugins/)
↓ Execution
External Executables (it2-*)
↓ Integration
Enhanced Output
Plugin Types
it2 recognizes four plugin types based on executable naming:
- Session Enrichers (
it2-session-*) - Add data to session listings - Tab Enrichers (
it2-tab-*) - Add data to tab listings - Window Enrichers (
it2-window-*) - Add data to window listings - Session Process Enrichers (
it2-session-process-*) - Add process data to sessions
Other it2-* executable names are not classified as enrichment plugins.
Runtime Contract
The executable prefix determines the plugin type. Enrichment plugins receive
the same context both as positional arguments and as JSON on standard input.
They return a concise value on standard output. A non-zero exit or empty output
omits that plugin's enrichment.
it2 filters the environment passed to plugins and applies the deadline from
IT2_PLUGIN_DEADLINE, which defaults to five seconds. The experimental
manifest data model is not loaded or enforced by the current runtime; see
Plugin Manifest Data Model.
Quick Start
1. Create a Simple Session Plugin
Create an executable named it2-session-example:
#!/bin/bash
# File: it2-session-example
SESSION_ID="$1"
SESSION_NAME="$2"
# Example: Show if session is running a specific tool
if pgrep -f "vim.*$SESSION_ID" >/dev/null; then
echo "vim-active"
elif pgrep -f "git.*$SESSION_ID" >/dev/null; then
echo "git-active"
else
echo "shell"
fi
Make it executable:
chmod +x it2-session-example
Add to PATH:
export PATH="$PATH:$(pwd)"
2. Test Your Plugin
it2 session list
Your plugin output will appear in the session listing as additional metadata.
Plugin Interface
Session Enrichers
Input Arguments:
$1: Session ID (required)$2: Session Name (optional, empty if not set)
Expected Output:
- Single line string describing the session state
- Empty output is ignored
- Exit code 0 for success (non-zero ignored, not treated as error)
Example:
#!/bin/bash
SESSION_ID="$1"
SESSION_NAME="$2"
# Check if this is a Claude Code session
if [ "$SESSION_NAME" = "Claude Code" ] || echo "$SESSION_NAME" | grep -q "claude"; then
echo "claude-session"
fi
Tab Enrichers
Input Arguments:
$1: Tab ID (required)$2: Window ID (required)$3: Tab Title (optional)
Expected Output:
- Single line string describing tab state
- Empty output is ignored
Window Enrichers
Input Arguments:
$1: Window ID (required)$2: Window Title (optional)
Expected Output:
- Single line string describing window state
- Empty output is ignored
Advanced Example
Configuration-Based Plugin
Create it2-session-classifier that reads from config:
#!/bin/bash
CONFIG_FILE="$HOME/.it2/plugins/classifier.yaml"
SESSION_ID="$1"
SESSION_NAME="$2"
if [ ! -f "$CONFIG_FILE" ]; then
exit 0
fi
# Parse YAML config (requires yq or similar)
if command -v yq >/dev/null; then
PATTERNS=$(yq eval '.patterns[]' "$CONFIG_FILE" 2>/dev/null)
while IFS= read -r pattern; do
if echo "$SESSION_NAME" | grep -qE "$pattern"; then
LABEL=$(yq eval ".patterns | to_entries | .[] | select(.value == \"$pattern\") | .key" "$CONFIG_FILE")
echo "$LABEL"
exit 0
fi
done <<< "$PATTERNS"
fi
With config file ~/.it2/plugins/classifier.yaml:
patterns:
development: "(vim|code|emacs|nvim)"
git: "(git|gh|hub)"
database: "(mysql|postgres|redis|mongo)"
containers: "(docker|kubectl|k8s)"
cloud: "(aws|gcp|azure|terraform)"
Plugin Discovery
it2 discovers plugins by:
- Scanning all directories in
$PATH - Looking for executable files with names starting with
it2- - Determining type based on naming pattern:
it2-session-*→ Session enricherit2-tab-*→ Tab enricherit2-window-*→ Window enricherit2-session-process-*→ Session process enricher
Integration Points
Session Listings
Plugins add data to the PluginData field in session listings:
{
"session_id": "sess123",
"session_name": "vim-work",
"plugin_data": {
"example": "vim-active",
"dev-tools": "vim-session"
}
}
Tab Listings
Similar integration for tab metadata:
{
"tab_id": "tab456",
"sessions": [...],
"plugin_data": {
"dev-tools": "coding-tab"
}
}
Best Practices
Performance
- Plugins have a 5-second timeout - keep them fast
- Cache expensive operations when possible
- Exit early for irrelevant sessions/tabs/windows
Error Handling
- Non-zero exit codes are ignored, not treated as errors
- Empty output is ignored
- Use stderr for failure diagnostics; it2 reports stderr when execution fails
Naming Conventions
- Use descriptive plugin names:
it2-session-git-statusnotit2-git - Use hyphens for multi-word names:
it2-session-docker-status - Be specific about what the plugin does
Output Formatting
- Keep output concise (single line preferred)
- Use consistent terminology
- Consider localization if relevant
Configuration
- Store config in
~/.it2/plugins/ - Use standard formats (YAML, JSON, TOML)
- Provide sensible defaults
- Document configuration options
Debugging
Enable Debug Mode
export ITERM2_DEBUG=1
it2 session list
Test Plugin Directly
./it2-session-example "session-id" "session-name"
echo $? # Should be 0
Common Issues
- Plugin not discovered: Check it's in PATH and executable
- No output: Verify plugin returns non-empty string
- Timeout: Plugin takes longer than 5 seconds
- Wrong arguments: Check argument handling for your plugin type
Distribution
Packaging
- Create a directory with your plugin and README
- Include configuration examples
- Add installation script if needed
Example Structure
my-plugin/
├── it2-session-mystatus
├── it2-tab-mystatus
├── README.md
├── config.yaml.example
└── install.sh
Installation Methods
Method 1: PATH Installation
cp it2-session-mystatus /usr/local/bin/
chmod +x /usr/local/bin/it2-session-mystatus
Method 2: Plugin Directory
mkdir -p ~/.it2/plugins/mystatus
cp it2-session-mystatus ~/.it2/plugins/mystatus/
export PATH="$PATH:$HOME/.it2/plugins/mystatus"
Examples Repository
See internal/embedded/plugins/ for the plugins shipped with it2 and
examples/claude-session-monitor/ for a session-monitoring script.
Using the CLI
List Available Plugins
it2 plugin list
Run a Plugin
# Via it2 (automatic session ID injection)
it2 plugin run <name> [args...]
# Or directly (if in PATH)
it2-session-example <session-id> [args...]
The it2 plugin wrapper provides:
- Automatic session ID injection from
$ITERM_SESSION_ID - Plugin discovery information
- Consistent error handling
Install Claude Code Hooks
# Install to project settings (.claude/settings.json)
it2 plugin claude-code-hook --install
# Install to global settings (~/.claude/settings.json)
it2 plugin claude-code-hook --install --scope global
Contributing
To contribute plugins or improvements:
- Test thoroughly with various session types
- Follow naming conventions
- Include comprehensive documentation
- Add examples to the examples directory
- Submit pull requests with test cases
For complex plugins, consider:
- Unit tests for plugin logic
- Integration tests with it2
- Performance benchmarks
- Cross-platform compatibility
Next steps
- Read the extensibility taxonomy for naming boundaries.
- Consult the Claude automation reference for
shipped examples. - Treat the plugin manifest data model as
experimental rather than a current runtime contract.