Automate opening of new tabs and windows in terminal programs
Project description
automate-terminal
Automate opening of new tabs and windows in terminal programs. Currently supports iTerm2, Terminal.app, Ghostty, Guake, tmux, WezTerm, Kitty, Cursor, and Visual Studio Code.
automate-terminal is a best-effort project. Some terminals do not support automation at all! It's also intended to be used as a component in other tools, so it errs on the side of strictness over fallbacks. See the command reference for specifics.
automate-terminal was originally part of autowt, the git worktree manager.
Installation
pip install automate-terminal
mise install pip:automate-terminal
Supported Terminals
| Terminal | Platform | New Tabs/Windows | Switch by ID | Switch by Working Dir | List Sessions | Paste Commands | Run in Active Session |
|---|---|---|---|---|---|---|---|
| iTerm2 | macOS | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Terminal.app | macOS | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| Ghostty | macOS | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| Guake | Linux | ⚠️ (tabs only) | ✅ | ✅ | ✅ | ✅ | ✅ |
| tmux | Cross-platform | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| WezTerm | Cross-platform | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Kitty | Cross-platform | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* |
| VSCode | Cross-platform | ⚠️ (no tabs) | ❌ | ✅ | ❌ | ❌ | ❌ |
| Cursor | Cross-platform | ⚠️ (no tabs) | ❌ | ✅ | ❌ | ❌ | ❌ |
* Kitty requires allow_remote_control yes in kitty.conf to enable automation features.
Guake requires dbus-python (system package, usually pre-installed with Guake) for DBus communication.
Other terminals are not supported; automate-terminal will exit with an error code in unsupported terminals.
Quick Start
Command Line
# Check if your terminal is supported
automate-terminal check
# Create a new tab
automate-terminal new-tab /path/to/project
# Switch to existing session by directory
automate-terminal switch-to --working-directory=/path/to/project
# Create new window with initialization script
automate-terminal new-window /path/to/project \
--paste-and-run="source .env && npm run dev"
# Run a command in the currently active session
automate-terminal run-in-active-session "git status"
What is possible with terminal automation?
This is the "manage expectations" section of the README.
The scope of automation covered by automate-terminal is "create or navigate to a terminal session in a specific working directory."
What is a terminal emulator?
A terminal emulator, for the purposes of this project, is the thing you type your terminal commands into. For the author, it's iTerm2. For many people, it's the built-in terminal in Visual Studio Code. For hipsters, it's Ghostty. Linux users have more options than I know of, including KConsole. And there's WezTerm, and the built-in terminals for macOS and Windows. Every few years, somebody spins up a new one.
Within terminal emulators, a session is, for the purposes of this project, one specific terminal running a shell. Your sessions might be organized in windows, or tabs, or splits within a window or tab.
How are they controlled?
In many different ways, which is why this problem is complex enough to warrant a library! The ability to open a new tab or window, or switch to a specific existing session based on some criteria, is not standardized at all among terminal emulators. The spread of what is possible for a given terminal emulator is incredibly wide, as shown by the table at the top of this README.
macOS terminals are primarily controlled via AppleScript. iTerm2, for example, has a comprehensive AppleScript API, which is why it has the best support. Ghostty has no AppleScript API at all, so automate-terminal uses the SystemEvents API to "click" its menu items, and we have no way of knowing which Ghostty tab is in a particular working directory. VSCode also has no AppleScript API, but it does have its code <path> command to open or switch to a specific window.
tmux uses its native CLI commands (tmux list-panes, tmux select-window, tmux send-keys, etc.) and works identically across all platforms where tmux is available.
WezTerm uses its native CLI commands (wezterm cli list, wezterm cli activate-pane, wezterm cli send-text, etc.) and works identically across all platforms where WezTerm is available.
Kitty uses its remote control protocol (kitten @ ls, kitten @ focus-window, kitten @ send-text, etc.) and works across all platforms where Kitty is available. Note: Kitty requires allow_remote_control yes in your kitty.conf file to enable automation.
All this is to say, if you are unhappy with the level of automation provided by automate-terminal on macOS, switch to another terminal emulator, or lobby the authors of your favorite one to add automation support.
Commands
check
Detect terminal capabilities.
automate-terminal check
automate-terminal check --output=json
Example output:
Terminal: iTerm2
Terminal Program: iTerm.app
Shell: zsh
Current session ID: w0t5p1:24AF055B-8BD2-4C7F-AB1E-B310FDCBCEA1
Current working directory: /Users/steve/dev/libraries/automate-terminal
Capabilities:
can_create_tabs: True
can_create_windows: True
can_list_sessions: True
can_switch_to_session: True
can_detect_session_id: True
can_detect_working_directory: True
can_paste_commands: True
can_run_in_active_session: True
new-tab
Create new tab in a specific directory, optionally pasting content into the new tab.
The term "paste" here means it will be as if the user pasted text into the new terminal and hit Enter. It doesn't use your system pasteboard.
automate-terminal new-tab /path/to/dir
automate-terminal new-tab /path/to/dir --paste-and-run="echo 'I am in the new directory!'"
There are options to run additional scripts only in specific shells. This is useful if your wrapper tool needs to support multiple shells for workflows that require nontrivial shell commands.
automate-terminal new-tab /path/to/dir --paste-and-run-fish="echo 'I am a fish shell user'"
new-window
Create new window. Takes the same arguments as automate-terminal new-tab.
automate-terminal new-window /path/to/dir
switch-to
Switch to an existing session, returning an error no matching session can be found or your terminal doesn't provide session information. You can either pass a working directory with --working-directory/--wd, or a session ID if the session ID is known.
If you pass session ID, working directory is ignored. --wd is likely what you want, though.
If you are using an unsupported terminal emulator and want to open a new terminal in a given directory as a fallback, use new-tab or new-window. automate-terminal doesn't do this automatically because it would create unpleasant edge cases for tools that use automate-terminal.
# By working directory (or use --wd alias)
automate-terminal switch-to --working-directory=/path/to/dir
# By session ID (or use --id alias)
automate-terminal switch-to --session-id=w0t0p2:ABC123
list-sessions
List all sessions.
automate-terminal list-sessions
automate-terminal list-sessions --output=json
run-in-active-session
Run a command in the currently active terminal session.
This command sends the specified command to the active terminal session without switching windows or tabs. It's useful for programmatically executing commands in the terminal session you're currently working in.
# Run a simple command
automate-terminal run-in-active-session "echo 'Hello World'"
# Run a git command
automate-terminal run-in-active-session "git status"
# Run multiple commands
automate-terminal run-in-active-session "cd /tmp && ls -la"
Note: This feature requires terminal support. VSCode and Cursor do not support running commands in the active session. Check capabilities with automate-terminal check to see if your terminal supports this feature.
Options
Output Format
--output=text- Human-readable (default)--output=json- JSON for programmatic use--output=none- Silent
Paste and Run
Execute commands after creating/switching sessions.
--paste-and-run="echo 'I run unconditionally'"
--paste-and-run-bash="echo 'I only run if the current shell is bash'"
--paste-and-run-zsh="echo 'I only run if the current shell is zsh'"
--paste-and-run-fish="echo 'I only run if the current shell is fish'"
Shell-specific flags override generic --paste-and-run when detected shell matches.
Note: Some terminals (VSCode, Cursor) cannot execute paste scripts programmatically. When using --output=json, check the paste_script_executed field to determine if you need to run the script manually:
true: The paste script was executed by the terminalfalse: The paste script was provided but the terminal cannot execute it (you should run it manually)- Field omitted: No paste script was provided
Debug and Dry Run
--debug # Enable debug logging to stderr
--dry-run # Log actions instead of executing them
Use --dry-run to see what commands would be executed without actually running them (AppleScript for macOS terminals, tmux CLI commands for tmux). Useful for debugging and understanding what the tool will do.
Python API
from automate_terminal import (
check,
new_tab,
new_window,
switch_to_session,
list_sessions,
get_current_session_id,
get_shell_name,
run_in_active_session,
TerminalNotFoundError,
)
check(dry_run=False, debug=False) -> dict[str, str | Capabilities]
new_tab(working_directory, paste_script=None, dry_run=False, debug=False) -> bool
new_window(
working_directory,
paste_script=None,
dry_run=False,
debug=False) -> bool
switch_to_session(
session_id=None,
working_directory=None,
paste_script=None,
subdirectory_ok=False,
dry_run=False,
debug=False) -> bool
list_sessions(dry_run=False, debug=False) -> list[dict[str, str]]
get_current_session_id(dry_run=False, debug=False) -> str | None
get_shell_name(dry_run=False, debug=False) -> str | None
run_in_active_session(command, dry_run=False, debug=False) -> bool
References
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file automate_terminal-0.1.7.tar.gz.
File metadata
- Download URL: automate_terminal-0.1.7.tar.gz
- Upload date:
- Size: 53.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
971f34aed7b8f65b6d1dfb1cd8d4ed7d796efd67ae1845765da544ceb6b7788e
|
|
| MD5 |
88d4209845a09467933450b3f39f304c
|
|
| BLAKE2b-256 |
f2e698d3a5c075f0bbb23ba6060dbd4835696249711f1d20d14ec850fdafdc14
|
Provenance
The following attestation bundles were made for automate_terminal-0.1.7.tar.gz:
Publisher:
pypi.yml on irskep/automate-terminal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
automate_terminal-0.1.7.tar.gz -
Subject digest:
971f34aed7b8f65b6d1dfb1cd8d4ed7d796efd67ae1845765da544ceb6b7788e - Sigstore transparency entry: 702175484
- Sigstore integration time:
-
Permalink:
irskep/automate-terminal@3834353ff2bfd27848cf3efab1f4720eeb549aa0 -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/irskep
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@3834353ff2bfd27848cf3efab1f4720eeb549aa0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file automate_terminal-0.1.7-py3-none-any.whl.
File metadata
- Download URL: automate_terminal-0.1.7-py3-none-any.whl
- Upload date:
- Size: 33.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4434f2d18fd7b3edce1492df3e884f97af51d393ea86453b84e3b56da04fcab2
|
|
| MD5 |
e6a135cd582060c31f17f047cfe5cd82
|
|
| BLAKE2b-256 |
240283aa892a6d2eef928899ca700dc961f734de05806a1180e80900c36f97cd
|
Provenance
The following attestation bundles were made for automate_terminal-0.1.7-py3-none-any.whl:
Publisher:
pypi.yml on irskep/automate-terminal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
automate_terminal-0.1.7-py3-none-any.whl -
Subject digest:
4434f2d18fd7b3edce1492df3e884f97af51d393ea86453b84e3b56da04fcab2 - Sigstore transparency entry: 702175487
- Sigstore integration time:
-
Permalink:
irskep/automate-terminal@3834353ff2bfd27848cf3efab1f4720eeb549aa0 -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/irskep
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@3834353ff2bfd27848cf3efab1f4720eeb549aa0 -
Trigger Event:
push
-
Statement type: