Auto-popup terminals waiting for user input — like a poker MTT client for your terminal
Project description
MTT (Multi-Terminal Taker)
Auto-popup terminals waiting for your input — like a poker MTT client, but for your terminal.
Running 5 Claude Code sessions at once? MTT watches all of them and automatically brings each terminal to the front the moment it needs your input. When you respond, the next waiting terminal pops up. Zero dependencies. Pure Python.
The Problem
You have multiple terminals open — builds, tests, AI agents, servers. Most run on their own, but occasionally one needs your input. You waste time hunting through windows to find which one.
The Solution
MTT monitors all your terminal windows. When one starts waiting for input, it pops up automatically. You deal with it, and the next one pops up — like a poker multi-table tournament client that shows you whichever table needs your action.
How It Works
Terminal.app / iTerm2 MTT
┌──────────────┐
│ Tab 1: build │ ──running──┐
│ Tab 2: test │ ──running──┤
│ Tab 3: claude│ ──idle────┐│ ┌─────────┐ ┌────────┐
│ Tab 4: claude│ ──busy────┤├──▶│ Queue │──▶│ Pop up │
│ Tab 5: zsh │ ──idle────┘│ │ (FIFO) │ │ window │
│ Tab 6: server│ ──running──┘ └─────────┘ └────────┘
└──────────────┘
- Scan — AppleScript reads all terminal windows/tabs every second
- Detect — Checks if each terminal is waiting for input (3 methods)
- Queue — Waiting terminals enter a FIFO queue
- Pop up — The next terminal gets its window raised; when you respond, the next one pops up
Detection Methods
| Method | Trigger | Speed |
|---|---|---|
| Hook marker | Claude Code idle/busy hooks create/remove marker files | Instant |
| Shell prompt | Foreground process is zsh/bash/fish | Instant |
| Content stasis | Interactive app (claude, node) + content unchanged for 8s + no new child processes | 8s delay |
Content stasis catches mid-turn waiting states like Claude Code's AskUserQuestion prompts,
where the Stop hook hasn't fired yet. The busy marker prevents false positives during API calls
(no child processes to detect), and child process tracking prevents false positives during
long tool calls.
Supported Terminals
- Terminal.app — Full support
- iTerm2 — Full support
- Auto-detects which apps are running; works with both simultaneously
Claude Code Integration
MTT integrates with Claude Code hooks for instant idle/busy detection:
Add to ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "/path/to/mtt/scripts/hook-idle.sh"
}
]
}
],
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "/path/to/mtt/scripts/hook-busy.sh"
}
]
}
]
}
}
Without hooks, MTT still works via shell prompt detection and content stasis — hooks just make it faster.
Requirements
- macOS (uses AppleScript)
- Python 3.12+
- Accessibility permission (System Settings → Privacy & Security → Accessibility)
Install
pip install multi-terminal-taker
Or from source:
git clone https://github.com/wkdgks150/multi-terminal-taker.git
cd multi-terminal-taker
pip install -e .
Zero external dependencies — Python standard library only.
Usage
mtt start # Start daemon (foreground)
mtt status # Check if running
mtt stop # Stop daemon
Output while running:
[12:34:56] IDLE
[12:34:57] QUEUE: 2 | SERVING: /dev/ttys003
[12:34:58] QUEUE: 1 | SERVING: /dev/ttys007
[12:34:59] IDLE
Project Structure
src/mtt/
├── cli.py # CLI (start/stop/status)
├── daemon.py # Main polling loop (1s interval)
├── monitor.py # Terminal scanner (Terminal.app + iTerm2)
├── detector.py # Idle detection (hooks + shell + content stasis)
├── queue.py # FIFO popup queue
└── window_controller.py # Window popup (Terminal.app + iTerm2)
scripts/
├── hook-idle.sh # Claude Code Stop hook
└── hook-busy.sh # Claude Code UserPromptSubmit hook
Tests
pip install pytest
pytest
Roadmap
- Terminal scan + auto-popup + FIFO queue
- Claude Code hook integration
- Content stasis detection (AskUserQuestion, etc.)
- iTerm2 support
- Menu bar status indicator
- Priority rules (pin important terminals)
- Configurable polling interval and stasis threshold
License
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 multi_terminal_taker-0.1.0.tar.gz.
File metadata
- Download URL: multi_terminal_taker-0.1.0.tar.gz
- Upload date:
- Size: 17.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0513786600f5997df90365635ba99b161db74a646f9366965e0968033474cd46
|
|
| MD5 |
e0521907372082bbb419fbba06ce2505
|
|
| BLAKE2b-256 |
7ba4f3b1aea66a0f9bb69af72002e547ce24ac8986c075c292ba4eedfc34d0a4
|
File details
Details for the file multi_terminal_taker-0.1.0-py3-none-any.whl.
File metadata
- Download URL: multi_terminal_taker-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc4dd8d347505c3ac4e8c28837356d5d0e6ae898ac91448885ef51276451ea38
|
|
| MD5 |
cc12f29d94f7442f77c9d82bfeb55f34
|
|
| BLAKE2b-256 |
de70351ad776597ff245bb16d4e069a31db1ab701007200d2f7577196a00c7fd
|