Create, send commands to, list, and close iTerm tabs by name — from Claude Code or any script. One file, four commands, zero dependencies beyond macOS.
If you run Claude Code, a dev server, a database poller, and a log tail — that's four tabs minimum. You cmd-tab between them by position, hoping the order didn't change. iTerm lets you name sessions manually, but there's no CLI for it. So your automation can't target a specific tab.
This script fixes that. Create a tab named "poller", send it a command later, list what's running, close it when you're done — all from the terminal.
Save this anywhere on your PATH. Four commands: create, send, list, close.
#!/usr/bin/env bash
set -eo pipefail
ACTION="${1:-}"
NAME="${2:-}"
CMD="${3:-}"
usage() {
echo "Usage:"
echo " iterm-tab.sh create <name> [command]"
echo " iterm-tab.sh send <name> <command>"
echo " iterm-tab.sh list"
echo " iterm-tab.sh close <name>"
exit 1
}
[[ -z "$ACTION" ]] && usage
case "$ACTION" in
create)
[[ -z "$NAME" ]] && usage
if [[ -n "$CMD" ]]; then
osascript \
-e 'on run {tabName, tabCmd}' \
-e ' tell application "iTerm2"' \
-e ' tell current window' \
-e ' set newTab to (create tab with default profile)' \
-e ' tell current session of newTab' \
-e ' set name to tabName' \
-e ' write text tabCmd' \
-e ' end tell' \
-e ' end tell' \
-e ' end tell' \
-e 'end run' \
-- "$NAME" "$CMD"
else
osascript \
-e 'on run {tabName}' \
-e ' tell application "iTerm2"' \
-e ' tell current window' \
-e ' set newTab to (create tab with default profile)' \
-e ' tell current session of newTab' \
-e ' set name to tabName' \
-e ' end tell' \
-e ' end tell' \
-e ' end tell' \
-e 'end run' \
-- "$NAME"
fi
echo "Tab '$NAME' created"
;;
send)
[[ -z "$NAME" || -z "$CMD" ]] && usage
osascript \
-e 'on run {tabName, tabCmd}' \
-e ' tell application "iTerm2"' \
-e ' set found to false' \
-e ' repeat with w in windows' \
-e ' repeat with t in tabs of w' \
-e ' repeat with s in sessions of t' \
-e ' if name of s starts with tabName then' \
-e ' tell s to write text tabCmd' \
-e ' set found to true' \
-e ' end if' \
-e ' end repeat' \
-e ' end repeat' \
-e ' end repeat' \
-e ' if not found then error "Tab not found: " & tabName' \
-e ' end tell' \
-e 'end run' \
-- "$NAME" "$CMD"
;;
list)
osascript <<EOF
tell application "iTerm2"
set output to ""
repeat with w in windows
repeat with t in tabs of w
repeat with s in sessions of t
set output to output & (name of s) & linefeed
end repeat
end repeat
end repeat
return output
end tell
EOF
;;
close)
[[ -z "$NAME" ]] && usage
osascript <<EOF
tell application "iTerm2"
repeat with w in windows
repeat with t in tabs of w
repeat with s in sessions of t
if name of s starts with "$NAME" then
close s
end if
end repeat
end repeat
end repeat
end tell
EOF
echo "Tab '$NAME' closed"
;;
*)
usage
;;
esac
iterm-tab.sh create poller "~/poll.sh" iterm-tab.sh create logs
Opens a new tab, names it, optionally runs a command.
iterm-tab.sh send poller "echo restart" iterm-tab.sh send logs "tail -f app.log"
Sends a command to the named tab. Uses starts with matching — iTerm appends the process name, so exact match won't work.
iterm-tab.sh list
Prints every session name across all windows and tabs.
iterm-tab.sh close poller
Closes the session matching that name. Clean exit.
starts with instead of exact match?iTerm appends the running process to the session name. You set "poller", but name of s returns "poller (-zsh)" or "poller (node)". Exact match fails silently. The script uses starts with so your names always resolve.
# Start a dev server in a named tab
iterm-tab.sh create dev-server "npm run dev"
# Start a poller in another
iterm-tab.sh create manhattan "~/manhattan/manhattan-poller.sh"
# Restart the dev server without switching tabs
iterm-tab.sh send dev-server "npm run dev"
# See what's running
iterm-tab.sh list
# Clean up when done
iterm-tab.sh close dev-server
iterm-tab.sh close manhattan
Put it in a Claude Code hook, a startup script, or call it from an agent. Any process that can run bash and osascript can control your tabs.
curl -o ~/.local/bin/iterm-tab.sh https://iclaude.lol/iterm-tab.sh
chmod +x ~/.local/bin/iterm-tab.sh
Requires macOS + iTerm2. No other dependencies.
Copy this prompt and paste it into Claude Code. It'll download the script and set up named tabs for your project.
Download iterm-tab.sh from https://iclaude.lol/iterm-tab.sh and save it to ~/.local/bin/iterm-tab.sh (make it executable). Then create a named tab called "dev" and run my dev server in it.