From iterm
iTerm2 tab management for opening, listing, and closing terminal tabs from macOS host or Linux container environments.
npx claudepluginhub manifoldlogic/claude-code-plugins --plugin itermThis skill uses the workspace's default tool permissions.
**Last Updated:** 2026-01-15
references/applescript-reference.mdscripts/iterm-close-tab.shscripts/iterm-list-tabs.shscripts/iterm-open-tab.shscripts/iterm-utils.shscripts/test-iterm-close-tab.shscripts/test-iterm-list-tabs.shscripts/test-iterm-open-tab.shscripts/test-iterm-utils.shtests/test-deprecation-notice.shtests/test-spawn-agent-refactor.shGuides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Last Updated: 2026-01-15
Plugin: iterm
Scripts Location: plugins/iterm/skills/tab-management/scripts/
The tab-management skill provides iTerm2 tab management capabilities for Claude Code, enabling automated terminal tab creation, querying, and cleanup from both macOS host environments and Linux containers (via SSH tunneling).
Key Capabilities:
When to Use This Skill vs Manual Operations:
| Use This Skill | Use Manual Operations |
|---|---|
| Automating worktree workflows | One-off terminal needs |
| Spawning Claude agents in tabs | Quick manual tab creation |
| Cleaning up tabs after worktree deletion | Exploratory testing |
| Scripting multi-tab environments | When iTerm2 UI is preferred |
| Checking existing tabs programmatically | Visual tab inspection |
/Applications/iTerm.app
When running from inside a devcontainer:
SSH Access to Host: Container must be able to SSH to host.docker.internal
~/.ssh/id_ed25519HOST_USER Environment Variable: Required for SSH connection
{
"remoteEnv": {
"HOST_USER": "your-macos-username"
}
}
iTerm2 Profiles Configured: Especially the "Devcontainer" profile
When running directly on macOS:
# Check if running in container vs host
uname -s # Darwin = host, Linux = container
# Verify iTerm2 (host only)
ls /Applications/iTerm.app
# Test SSH connectivity (container only)
ssh -o BatchMode=yes -o ConnectTimeout=5 ${HOST_USER}@host.docker.internal "echo OK"
# Check HOST_USER is set (container only)
echo "HOST_USER=${HOST_USER:-NOT SET}"
| Skill | Script | Purpose |
|---|---|---|
iterm:open-tab | iterm-open-tab.sh | Create new terminal tabs with profile, directory, and command options |
iterm:list-tabs | iterm-list-tabs.sh | Query existing windows and tabs in table or JSON format |
iterm:close-tab | iterm-close-tab.sh | Close tabs by pattern matching against tab titles |
What do you need to do?
|
+-- Need to execute command in new environment?
| |
| +-- Opening new worktree for development?
| | --> Use iterm:open-tab with --directory, --profile "Devcontainer",
| | and --wait-for-prompt (ensures shell is ready before cd)
| |
| +-- Spawning Claude agent for parallel work?
| | --> Use iterm:open-tab with --command "claude ..."
| |
| +-- Opening specific directory in container?
| --> Use iterm:open-tab with --directory
|
+-- Need to check existing tabs?
| |
| +-- Quick visual check?
| | --> Use iterm:list-tabs (table format, default)
| |
| +-- Programmatic parsing/scripting?
| --> Use iterm:list-tabs --format json
|
+-- Need to cleanup tabs?
| |
| +-- Preview what would be closed first?
| | --> Use iterm:close-tab --dry-run "pattern"
| |
| +-- Close matching tabs with confirmation?
| | --> Use iterm:close-tab "pattern"
| |
| +-- Automated cleanup (no prompts)?
| --> Use iterm:close-tab --force "pattern"
|
+-- Need to broadcast to multiple tabs?
--> Consider broadcast-to-agents.sh (separate tool)
After creating a worktree, open a tab connected to it:
# Open tab in the new worktree directory using Devcontainer profile
iterm-open-tab.sh --directory "/workspace/repos/my-project/feature-branch" \
--profile "Devcontainer" \
--wait-for-prompt \
--name "worktree: feature-branch"
What happens:
Open a tab and start a Claude Code agent session:
# Open tab and launch Claude with specific agent
iterm-open-tab.sh --directory "/workspace/repos/project" \
--command "claude --agent security-reviewer" \
--name "agent: security-reviewer"
What happens:
Check if a tab already exists before creating a duplicate:
# List all tabs in table format
iterm-list-tabs.sh
# List tabs in JSON format for scripting
iterm-list-tabs.sh --format json
# Filter to specific window
iterm-list-tabs.sh --window 1
# Check for specific pattern programmatically
iterm-list-tabs.sh --format json | jq '.windows[].tabs[] | select(.title | contains("feature-branch"))'
Example Output (table format):
Window Tab Title Session
1 1 Devcontainer zsh
1 2 worktree: feature-branch zsh
1 3 agent: security-reviewer zsh
Example Output (JSON format):
{
"windows": [
{
"index": 1,
"tabs": [
{"index": 1, "title": "Devcontainer", "session": "zsh"},
{"index": 2, "title": "worktree: feature-branch", "session": "zsh"},
{"index": 3, "title": "agent: security-reviewer", "session": "zsh"}
]
}
]
}
After deleting a worktree, close associated tabs:
# First, preview which tabs would be closed (safe - no changes)
iterm-close-tab.sh --dry-run "worktree: old-feature"
# If preview looks correct, close the tabs
iterm-close-tab.sh "worktree: old-feature"
# For automated scripts, skip confirmation prompt
iterm-close-tab.sh --force "worktree: old-feature"
# Limit to specific window
iterm-close-tab.sh --window 1 "worktree: old-feature"
Confirmation Flow (multiple matches):
Matching tabs found:
Window 1, Tab 2: worktree: old-feature
Window 1, Tab 5: worktree: old-feature-test
Found 2 matching tabs. Close them all? [y/N]:
Opens new iTerm2 tabs with full customization.
Usage:
iterm-open-tab.sh [OPTIONS]
Options:
| Flag | Long Form | Description | Default |
|---|---|---|---|
-d | --directory DIR | Working directory for the new tab | /workspace |
-p | --profile PROFILE | iTerm2 profile name | Devcontainer |
-c | --command CMD | Command to execute after tab opens | (none) |
-n | --name NAME | Tab title | (profile default) |
-w | --window | Create new window instead of tab | (false) |
--wait-for-prompt | Wait for shell prompt before sending command | (false) | |
--dry-run | Show AppleScript without executing | (false) | |
-h | --help | Display help information |
Note on --wait-for-prompt: Inserts an AppleScript polling loop that checks is at shell prompt before sending the shell command. Uses a 3-second initial delay (WAIT_INITIAL_DELAY), then polls every 1 second (WAIT_POLL_INTERVAL) for up to 12 iterations (WAIT_MAX_POLL) — 15-second total maximum. If the prompt is not detected within that window, the command is sent anyway and a warning is logged to Apple System Log. Requires iTerm2 Shell Integration to be enabled on the macOS host (iTerm2 > Install Shell Integration). Has no effect when directory and command are both empty. Typical use case: opening a tab with the Devcontainer profile, where the container shell takes several seconds to become ready after docker exec.
Examples:
# Basic: Open tab in default directory with Devcontainer profile
iterm-open-tab.sh
# Open tab in specific directory
iterm-open-tab.sh -d /workspace/repos/my-project
# Open tab with custom profile and title
iterm-open-tab.sh -p "Development" -n "My Custom Tab"
# Open tab and run a command
iterm-open-tab.sh -c "git status && npm test"
# Open in new window instead of tab
iterm-open-tab.sh -w -d /workspace
# Preview AppleScript without executing
iterm-open-tab.sh --dry-run -d /workspace -n "Test Tab"
Lists all iTerm2 windows and tabs.
Usage:
iterm-list-tabs.sh [OPTIONS]
Options:
| Flag | Long Form | Description | Default |
|---|---|---|---|
-f | --format FORMAT | Output format: json or table | table |
-w | --window INDEX | Filter to specific window (1-based) | (all windows) |
--dry-run | Show AppleScript without executing | (false) | |
-h | --help | Display help information |
Examples:
# List all tabs in table format (default)
iterm-list-tabs.sh
# List all tabs in JSON format
iterm-list-tabs.sh --format json
# List tabs from window 2 only
iterm-list-tabs.sh -w 2
# Combine options
iterm-list-tabs.sh --format json --window 1
# Preview AppleScript without executing
iterm-list-tabs.sh --dry-run
Closes tabs by matching pattern against tab titles.
Usage:
iterm-close-tab.sh [OPTIONS] <pattern>
Arguments:
| Argument | Description |
|---|---|
pattern | Substring to match against tab titles (case-sensitive, not regex) |
Options:
| Flag | Long Form | Description | Default |
|---|---|---|---|
-w | --window INDEX | Limit to specific window (1-based) | (all windows) |
--force | Skip confirmation for multiple matches | (false) | |
--dry-run | Show what would be closed without closing | (false) | |
-h | --help | Display help information |
Examples:
# Close tabs containing "worktree:" in title
iterm-close-tab.sh "worktree:"
# Preview which tabs would be closed
iterm-close-tab.sh --dry-run "feature-branch"
# Close tabs in window 1 only
iterm-close-tab.sh -w 1 "test"
# Close without confirmation prompt
iterm-close-tab.sh --force "cleanup"
# Combine options
iterm-close-tab.sh --force -w 2 "worktree: feature"
The scripts automatically detect whether they are running on the macOS host or inside a container and adapt their execution strategy.
Start
|
v
Check: /.dockerenv exists?
|
+-- Yes --> CONTAINER MODE
|
v
Check: /proc/1/cgroup contains "docker"?
|
+-- Yes --> CONTAINER MODE
|
v
Check: uname != "Darwin"?
|
+-- Yes --> CONTAINER MODE (Linux implies container)
|
v
HOST MODE
How it works:
osascriptRequirements:
/Applications/iTerm.appHow it works:
host.docker.internalosascript on hostRequirements:
host.docker.internalHOST_USER environment variable set| Aspect | Host Mode | Container Mode |
|---|---|---|
| AppleScript Execution | Direct osascript | SSH + osascript |
| Latency | ~100ms | ~500ms (SSH overhead) |
| Environment Variable | Not required | HOST_USER required |
| Prerequisites | iTerm2, permissions | SSH config, HOST_USER |
| Debug Output | Direct | Via SSH tunnel |
The plugin uses iTerm2 profiles as the primary connection mechanism.
How it works:
Example Profile Configuration:
# iTerm2 Profile > Command setting
docker exec -it devcontainer /bin/zsh
Advantages:
For users not using the Devcontainer profile:
How it works:
Example:
# Explicit PATH fixes Docker Desktop bug
osascript -e 'tell application "iTerm2" to tell current session of first window to write text "PATH=/usr/local/bin:$PATH docker exec -it devcontainer /bin/zsh"'
When to use:
Note: Profile-based is strongly preferred. Docker exec approach requires explicit PATH prefix to work around Docker Desktop PATH resolution bug.
Error Message:
[ERROR] iTerm2 not found at /Applications/iTerm.app
[ERROR] Install from: https://iterm2.com
Cause: iTerm2 is not installed on the macOS host.
Solution:
Error Message:
[ERROR] SSH connection to host.docker.internal failed
[ERROR] Verify SSH is configured (see post-start.sh setup)
Causes:
Solutions:
ls -la ~/.ssh/id_ed25519
"mounts": [
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,readonly"
]
ssh -o BatchMode=yes -o ConnectTimeout=5 ${HOST_USER}@host.docker.internal "echo OK"
Error Message:
[ERROR] HOST_USER environment variable not set
[ERROR] Set HOST_USER in devcontainer.json remoteEnv
Cause: HOST_USER is required for SSH from container but not configured.
Solution: Add to devcontainer.json:
{
"remoteEnv": {
"HOST_USER": "your-macos-username"
}
}
Then rebuild the container.
Error Message: Tab opens but shows profile not found or uses Default profile.
Cause: iTerm2 "Devcontainer" profile not configured.
Solution:
docker exec -it devcontainer /bin/zsh
Symptoms:
cd command runsCause: iTerm2 Shell Integration is not installed, so is at shell prompt always returns false.
Solution:
iTerm2 > Install Shell IntegrationiTerm2 > Shell Integration > Check InstallationNote: Even without Shell Integration, the command will still execute after the timeout — it just takes longer.
Symptoms:
Causes:
Solutions:
.zshrc or .bashrc for blocking promptsiterm-open-tab.sh -c "echo test"
Error Message:
[ERROR] AppleScript execution failed
Causes:
Solutions:
iterm-open-tab.sh --dry-run -d /workspace
osascript -e 'tell application "iTerm2" to activate'
All scripts use consistent exit codes for error handling:
| Exit Code | Constant | Meaning | Common Causes |
|---|---|---|---|
| 0 | EXIT_SUCCESS | Success | Operation completed successfully |
| 1 | EXIT_CONNECTION_FAIL | SSH/connection failure | SSH key issues, HOST_USER not set, network problems |
| 2 | EXIT_ITERM_UNAVAILABLE | iTerm2 not available | iTerm2 not installed, not at expected path |
| 3 | EXIT_INVALID_ARGS | Invalid arguments | Missing required args, unknown flags, invalid values |
| 4 | EXIT_NO_MATCH | Pattern matches no tabs | (close-tab only) No tabs match the provided pattern |
Example Error Handling:
#!/bin/bash
if iterm-open-tab.sh -d /workspace/repos/project; then
echo "Tab opened successfully"
else
exit_code=$?
case $exit_code in
1) echo "Connection failed - check SSH config" ;;
2) echo "iTerm2 not available" ;;
3) echo "Invalid arguments" ;;
*) echo "Unknown error: $exit_code" ;;
esac
exit $exit_code
fi
The spawn-worktree.sh script can delegate tab opening to this plugin:
# spawn-worktree.sh uses wrapper approach
open_iterm_tab() {
local directory="$1"
local profile="${2:-Devcontainer}"
local name="$3"
local wait_flag=""
# Wait for shell prompt when using Devcontainer profile
if [ "$profile" = "Devcontainer" ]; then
wait_flag="--wait-for-prompt"
fi
local plugin_script="$ITERM_PLUGIN/scripts/iterm-open-tab.sh"
if [[ -x "$plugin_script" ]]; then
"$plugin_script" \
--directory "$directory" \
--profile "$profile" \
${wait_flag:+"$wait_flag"} \
--name "$name"
else
# Fallback to original implementation (inline AppleScript with polling)
open_iterm_tab_original "$@"
fi
}
Workflow:
spawn-worktree.sh feature-branch --repo myprojectSimilarly, spawn-agent.sh uses the plugin for tab creation:
# spawn-agent.sh delegates tab opening
spawn_agent_tab() {
iterm-open-tab.sh \
--directory "$WORKTREE_PATH" \
--command "claude --agent $AGENT_NAME" \
--name "agent: $AGENT_NAME"
}
Workflow:
spawn-agent.sh security-reviewer --worktree feature-branchWhen deleting worktrees, use iterm:close-tab for cleanup:
# Cleanup workflow
cleanup_worktree() {
local worktree_name="$1"
# Preview tabs to close
iterm-close-tab.sh --dry-run "worktree: $worktree_name"
# Close matching tabs (with --force for automation)
iterm-close-tab.sh --force "worktree: $worktree_name"
# Delete the worktree
crewchief worktree clean "$worktree_name"
}
Each operation from container mode incurs ~500ms SSH overhead:
Optimization: For batch operations, consider running from host mode when possible.
When creating multiple tabs rapidly:
# Add small delay between tab creations
for branch in feature-1 feature-2 feature-3; do
iterm-open-tab.sh -d "/workspace/repos/project/$branch" -n "worktree: $branch"
sleep 0.2 # 200ms delay prevents iTerm2 race conditions
done
Querying tabs with many windows (50+ tabs):
--window to reduce query scope# Faster: Query specific window only
iterm-list-tabs.sh -w 1 -f json
# Slower: Query all windows
iterm-list-tabs.sh -f json
BatchMode=yes - no interactive password promptsScripts validate inputs to prevent injection:
Note: While inputs are sanitized, avoid passing untrusted user input directly to these scripts in production environments.
/tmp/ with random names