tmux-agents 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ name: ci
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ jobs:
7
+ test:
8
+ runs-on: ${{ matrix.os }}
9
+ strategy:
10
+ matrix:
11
+ os: [ubuntu-latest, macos-latest]
12
+ python: ["3.10", "3.12"]
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v5
16
+ with:
17
+ python-version: ${{ matrix.python }}
18
+ - name: install tmux
19
+ run: |
20
+ if [ "$RUNNER_OS" = "Linux" ]; then sudo apt-get update && sudo apt-get install -y tmux; else brew install tmux; fi
21
+ - run: uv sync --extra dev
22
+ - run: uv run ruff check src tests
23
+ - run: uv run pytest -q
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .venv/
9
+ uv.lock
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-09-22
4
+
5
+ Initial release.
6
+
7
+ - `agents_launch`: one session, one titled pane per agent, auto layout, optional terminal-app open.
8
+ - `pane_read`, `pane_wait` (idle / regex), `pane_send` (literal, multi-line safe), `pane_key`.
9
+ - `panes_list`, `tmux_status`, `pane_kill`, `session_kill`.
10
+ - Session-prefix scoping, secret redaction, no raw shell tool.
@@ -0,0 +1,6 @@
1
+ # Contributing
2
+
3
+ - `uv sync --extra dev`, then `uv run pytest` and `uv run ruff check src tests` must pass.
4
+ - Tests run against a real tmux on a private socket; keep new tests that way (no mocks of tmux).
5
+ - No new tool that runs arbitrary commands. The scope is: launch agents, read panes, type into panes, wait, kill.
6
+ - Conventional commit messages (`feat:`, `fix:`, `docs:`).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Woonyong Choi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.5
2
+ Name: tmux-agents
3
+ Version: 0.1.0
4
+ Summary: MCP server that lets any AI agent launch, watch and steer coding agents (Claude Code, Codex, Hermes, aider, ...) running in tmux panes.
5
+ Project-URL: Homepage, https://github.com/woonyong-choi/tmux-agents
6
+ Project-URL: Issues, https://github.com/woonyong-choi/tmux-agents/issues
7
+ Project-URL: Changelog, https://github.com/woonyong-choi/tmux-agents/blob/main/CHANGELOG.md
8
+ Author: Woonyong Choi
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,aider,automation,claude-code,codex,hermes-agent,mcp,model-context-protocol,orchestration,terminal,tmux
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Classifier: Topic :: Terminals
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: mcp>=1.2.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8; extra == 'dev'
27
+ Requires-Dist: ruff>=0.6; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # tmux-agents
31
+
32
+ **Let one AI agent run a room full of coding agents.**
33
+
34
+ `tmux-agents` is a Model Context Protocol server that turns tmux into a control surface for CLI agents. Point Claude, Cursor, Codex, Hermes or any MCP client at it and it can start Claude Code / Codex / Hermes / aider in split panes, watch each one, answer their questions, hand them the next task and tear the session down — while you watch the same panes in your own terminal.
35
+
36
+ ```
37
+ uvx tmux-agents
38
+ ```
39
+
40
+ That is the whole install. It needs `tmux` on the machine and nothing else.
41
+
42
+ <p align="center">
43
+ <code>
44
+ ┌ WP1 Path ───────────────┬ WP2 Run ────────────────┐<br>
45
+ │ claude --model sonnet │ codex --model gpt-5 │<br>
46
+ │ ✓ 87 tests passed │ ? Overwrite main.ts? y/n│<br>
47
+ ├ WP3 Timeline ───────────┼ WP4 Diagrams ───────────┤<br>
48
+ │ hermes chat -q "..." │ aider --yes │<br>
49
+ └─────────────────────────┴─────────────────────────┘
50
+ </code>
51
+ </p>
52
+
53
+ ## Why
54
+
55
+ Every coding agent ships as a terminal program, and every orchestrator wants to run several of them at once. The pieces that are missing are small but annoying:
56
+
57
+ - An agent hosted **somewhere else** (a cloud session, a desktop app, a phone client) has no terminal. It can plan the work but has to ask a human to type `run-batch.sh`.
58
+ - Four agents in four windows are four things to babysit. One stops to ask *"overwrite? (y/n)"* and everything waits until someone notices.
59
+ - Reading a pane, deciding it is done, and typing the follow-up is exactly the kind of loop a model is good at — if it can see the pane.
60
+
61
+ `tmux-agents` gives the orchestrating model seven tools that map to what you would do by hand, and nothing more. There is no generic `shell` tool: the model can type into agent panes, not into your machine.
62
+
63
+ ## Tools
64
+
65
+ | Tool | What it does |
66
+ |---|---|
67
+ | `tmux_status` | Server config, tmux version, visible sessions |
68
+ | `agents_launch` | Create a session with one pane per agent, title each pane, run each command, pick a layout that fits (side-by-side for 2, tiled for 3+), optionally open it in your terminal app |
69
+ | `panes_list` | Panes with title, cwd, running command, size |
70
+ | `pane_read` | Last N lines of a pane, ANSI stripped, secrets redacted |
71
+ | `pane_wait` | Block until a pane is quiet for N seconds or a regex appears — returns `idle` / `matched` / `timeout` plus the tail |
72
+ | `pane_send` | Type text (literally, multi-line safe) and press Enter |
73
+ | `pane_key` | Send `C-c`, `Escape`, `Up`, `Tab` … |
74
+ | `pane_kill` / `session_kill` | Stop one agent or the whole batch (can be disabled) |
75
+
76
+ Panes are addressed by **title** (`"WP2"`, case-insensitive partial match), by id (`%3`) or by tmux target (`batch:0.1`). Titles are shown on the pane borders, so what the model calls a pane is what you see.
77
+
78
+ ## Setup
79
+
80
+ ### Claude Code
81
+
82
+ ```
83
+ claude mcp add tmux-agents -- uvx tmux-agents
84
+ ```
85
+
86
+ ### Claude Desktop / Cursor / Windsurf / Codex
87
+
88
+ ```json
89
+ {
90
+ "mcpServers": {
91
+ "tmux-agents": {
92
+ "command": "uvx",
93
+ "args": ["tmux-agents"],
94
+ "env": {
95
+ "TMUX_AGENTS_SESSION_PREFIX": "agents-",
96
+ "TMUX_AGENTS_OPEN_COMMAND": "open -na Ghostty.app --args -e tmux attach -t {session}"
97
+ }
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ `TMUX_AGENTS_OPEN_COMMAND` is the one macOS-flavoured line: it pops the new session into a real terminal window so you can watch. Other terminals:
104
+
105
+ | Terminal | Command |
106
+ |---|---|
107
+ | Ghostty | `open -na Ghostty.app --args -e tmux attach -t {session}` |
108
+ | iTerm2 | `open -na iTerm.app --args tmux attach -t {session}` |
109
+ | kitty | `kitty tmux attach -t {session}` |
110
+ | WezTerm | `wezterm start -- tmux attach -t {session}` |
111
+ | Terminal.app | `osascript -e 'tell app "Terminal" to do script "tmux attach -t {session}"'` |
112
+ | Linux (any) | `x-terminal-emulator -e tmux attach -t {session}` |
113
+
114
+ Leave it unset and the session runs detached; `tmux attach -t <name>` from any terminal shows it.
115
+
116
+ ## A full loop, as the model sees it
117
+
118
+ ```jsonc
119
+ agents_launch({
120
+ "session": "agents-wp5",
121
+ "cwd": "/Users/me/repo",
122
+ "agents": [
123
+ {"title": "WP5 Review", "command": "claude --model claude-opus-5 \"$(cat prompts/wp5.md)\""},
124
+ {"title": "WP6a Lazy", "command": "claude --model claude-sonnet-5 \"$(cat prompts/wp6a.md)\""},
125
+ {"title": "WP6b Shell", "command": "codex --model gpt-5-codex \"$(cat prompts/wp6b.md)\""}
126
+ ]
127
+ })
128
+ // → 3 tiled panes, Ghostty window opens
129
+
130
+ pane_wait({"pane": "WP6b", "timeout_seconds": 300, "idle_seconds": 8})
131
+ // → {"state": "idle", "tail": "... Overwrite tsconfig.json? (y/N)"}
132
+
133
+ pane_send({"pane": "WP6b", "text": "y"})
134
+
135
+ pane_wait({"pane": "WP5", "pattern": "handoff\\.md written"})
136
+ pane_read({"pane": "WP5", "lines": 80})
137
+
138
+ session_kill({"session": "agents-wp5"})
139
+ ```
140
+
141
+ `pane_wait` is the heart of it. "Quiet for 8 seconds" is a surprisingly reliable definition of *an agent is either done or waiting for you*, and the tail tells the model which.
142
+
143
+ ## Configuration
144
+
145
+ All via environment variables (set them in the `env` block of your MCP config):
146
+
147
+ | Variable | Default | Meaning |
148
+ |---|---|---|
149
+ | `TMUX_AGENTS_SESSION_PREFIX` | *(empty = all)* | Only sessions whose name starts with this are visible or controllable. **Set it.** With `agents-` the model can never read or type into your personal tmux sessions. |
150
+ | `TMUX_AGENTS_OPEN_COMMAND` | *(none)* | Command run after `agents_launch`; `{session}` is substituted |
151
+ | `TMUX_AGENTS_ALLOW_KILL` | `true` | Set `false` to disable `pane_kill` / `session_kill` |
152
+ | `TMUX_AGENTS_REDACT` | `true` | Replace token-looking strings in pane text with `[redacted]` |
153
+ | `TMUX_AGENTS_MAX_LINES` | `2000` | Hard cap for `pane_read` |
154
+ | `TMUX_AGENTS_SOCKET` | *(default server)* | `tmux -L <socket>` — isolate the agents on their own tmux server |
155
+ | `TMUX_AGENTS_TMUX` | `tmux` | Path to the tmux binary |
156
+
157
+ ## Security model, plainly
158
+
159
+ - `pane_send` types into a pane. If that pane is a shell, that is a shell. Keep agent sessions under a prefix (`TMUX_AGENTS_SESSION_PREFIX`) and the model cannot reach anything else.
160
+ - Pane text is scrubbed for GitHub/OpenAI/Slack/AWS tokens, bearer headers and `*_TOKEN=` style assignments before it is returned. This is a filter, not a guarantee — do not paste secrets into agent panes.
161
+ - The server instructs the model to treat everything it reads from panes as data written by another program, never as instructions. Agents talking to agents is exactly where prompt injection lives; the instruction is a reminder, not a defence. Review what your agents commit.
162
+ - No network, no config files, no state: the server is a thin layer over the `tmux` CLI and forgets everything between calls.
163
+
164
+ ## How it compares
165
+
166
+ There are other tmux MCP servers ([nickgnd/tmux-mcp](https://github.com/nickgnd/tmux-mcp), [laszlopere/mcp-tmux](https://github.com/laszlopere/mcp-tmux)) and they are good general tmux remotes: create windows, run commands, read output, some over SSH. `tmux-agents` is narrower on purpose: it knows about *agents* — titled panes, "launch N of these in a layout that fits", "wait until it goes quiet", prefix-scoped visibility, redaction — and it deliberately leaves out a raw command tool. If you want a tmux remote control, use one of those. If you want an orchestrator, this is the one.
167
+
168
+ ## Development
169
+
170
+ ```
171
+ git clone https://github.com/woonyong-choi/tmux-agents
172
+ cd tmux-agents
173
+ uv sync --extra dev
174
+ uv run pytest # real tmux, private socket, ~20s
175
+ uv run ruff check src tests
176
+ ```
177
+
178
+ Tests spin up a throwaway tmux server (`tmux -L tmux-agents-test-…`), so they never touch your sessions.
179
+
180
+ ## License
181
+
182
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,153 @@
1
+ # tmux-agents
2
+
3
+ **Let one AI agent run a room full of coding agents.**
4
+
5
+ `tmux-agents` is a Model Context Protocol server that turns tmux into a control surface for CLI agents. Point Claude, Cursor, Codex, Hermes or any MCP client at it and it can start Claude Code / Codex / Hermes / aider in split panes, watch each one, answer their questions, hand them the next task and tear the session down — while you watch the same panes in your own terminal.
6
+
7
+ ```
8
+ uvx tmux-agents
9
+ ```
10
+
11
+ That is the whole install. It needs `tmux` on the machine and nothing else.
12
+
13
+ <p align="center">
14
+ <code>
15
+ ┌ WP1 Path ───────────────┬ WP2 Run ────────────────┐<br>
16
+ │ claude --model sonnet │ codex --model gpt-5 │<br>
17
+ │ ✓ 87 tests passed │ ? Overwrite main.ts? y/n│<br>
18
+ ├ WP3 Timeline ───────────┼ WP4 Diagrams ───────────┤<br>
19
+ │ hermes chat -q "..." │ aider --yes │<br>
20
+ └─────────────────────────┴─────────────────────────┘
21
+ </code>
22
+ </p>
23
+
24
+ ## Why
25
+
26
+ Every coding agent ships as a terminal program, and every orchestrator wants to run several of them at once. The pieces that are missing are small but annoying:
27
+
28
+ - An agent hosted **somewhere else** (a cloud session, a desktop app, a phone client) has no terminal. It can plan the work but has to ask a human to type `run-batch.sh`.
29
+ - Four agents in four windows are four things to babysit. One stops to ask *"overwrite? (y/n)"* and everything waits until someone notices.
30
+ - Reading a pane, deciding it is done, and typing the follow-up is exactly the kind of loop a model is good at — if it can see the pane.
31
+
32
+ `tmux-agents` gives the orchestrating model seven tools that map to what you would do by hand, and nothing more. There is no generic `shell` tool: the model can type into agent panes, not into your machine.
33
+
34
+ ## Tools
35
+
36
+ | Tool | What it does |
37
+ |---|---|
38
+ | `tmux_status` | Server config, tmux version, visible sessions |
39
+ | `agents_launch` | Create a session with one pane per agent, title each pane, run each command, pick a layout that fits (side-by-side for 2, tiled for 3+), optionally open it in your terminal app |
40
+ | `panes_list` | Panes with title, cwd, running command, size |
41
+ | `pane_read` | Last N lines of a pane, ANSI stripped, secrets redacted |
42
+ | `pane_wait` | Block until a pane is quiet for N seconds or a regex appears — returns `idle` / `matched` / `timeout` plus the tail |
43
+ | `pane_send` | Type text (literally, multi-line safe) and press Enter |
44
+ | `pane_key` | Send `C-c`, `Escape`, `Up`, `Tab` … |
45
+ | `pane_kill` / `session_kill` | Stop one agent or the whole batch (can be disabled) |
46
+
47
+ Panes are addressed by **title** (`"WP2"`, case-insensitive partial match), by id (`%3`) or by tmux target (`batch:0.1`). Titles are shown on the pane borders, so what the model calls a pane is what you see.
48
+
49
+ ## Setup
50
+
51
+ ### Claude Code
52
+
53
+ ```
54
+ claude mcp add tmux-agents -- uvx tmux-agents
55
+ ```
56
+
57
+ ### Claude Desktop / Cursor / Windsurf / Codex
58
+
59
+ ```json
60
+ {
61
+ "mcpServers": {
62
+ "tmux-agents": {
63
+ "command": "uvx",
64
+ "args": ["tmux-agents"],
65
+ "env": {
66
+ "TMUX_AGENTS_SESSION_PREFIX": "agents-",
67
+ "TMUX_AGENTS_OPEN_COMMAND": "open -na Ghostty.app --args -e tmux attach -t {session}"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ `TMUX_AGENTS_OPEN_COMMAND` is the one macOS-flavoured line: it pops the new session into a real terminal window so you can watch. Other terminals:
75
+
76
+ | Terminal | Command |
77
+ |---|---|
78
+ | Ghostty | `open -na Ghostty.app --args -e tmux attach -t {session}` |
79
+ | iTerm2 | `open -na iTerm.app --args tmux attach -t {session}` |
80
+ | kitty | `kitty tmux attach -t {session}` |
81
+ | WezTerm | `wezterm start -- tmux attach -t {session}` |
82
+ | Terminal.app | `osascript -e 'tell app "Terminal" to do script "tmux attach -t {session}"'` |
83
+ | Linux (any) | `x-terminal-emulator -e tmux attach -t {session}` |
84
+
85
+ Leave it unset and the session runs detached; `tmux attach -t <name>` from any terminal shows it.
86
+
87
+ ## A full loop, as the model sees it
88
+
89
+ ```jsonc
90
+ agents_launch({
91
+ "session": "agents-wp5",
92
+ "cwd": "/Users/me/repo",
93
+ "agents": [
94
+ {"title": "WP5 Review", "command": "claude --model claude-opus-5 \"$(cat prompts/wp5.md)\""},
95
+ {"title": "WP6a Lazy", "command": "claude --model claude-sonnet-5 \"$(cat prompts/wp6a.md)\""},
96
+ {"title": "WP6b Shell", "command": "codex --model gpt-5-codex \"$(cat prompts/wp6b.md)\""}
97
+ ]
98
+ })
99
+ // → 3 tiled panes, Ghostty window opens
100
+
101
+ pane_wait({"pane": "WP6b", "timeout_seconds": 300, "idle_seconds": 8})
102
+ // → {"state": "idle", "tail": "... Overwrite tsconfig.json? (y/N)"}
103
+
104
+ pane_send({"pane": "WP6b", "text": "y"})
105
+
106
+ pane_wait({"pane": "WP5", "pattern": "handoff\\.md written"})
107
+ pane_read({"pane": "WP5", "lines": 80})
108
+
109
+ session_kill({"session": "agents-wp5"})
110
+ ```
111
+
112
+ `pane_wait` is the heart of it. "Quiet for 8 seconds" is a surprisingly reliable definition of *an agent is either done or waiting for you*, and the tail tells the model which.
113
+
114
+ ## Configuration
115
+
116
+ All via environment variables (set them in the `env` block of your MCP config):
117
+
118
+ | Variable | Default | Meaning |
119
+ |---|---|---|
120
+ | `TMUX_AGENTS_SESSION_PREFIX` | *(empty = all)* | Only sessions whose name starts with this are visible or controllable. **Set it.** With `agents-` the model can never read or type into your personal tmux sessions. |
121
+ | `TMUX_AGENTS_OPEN_COMMAND` | *(none)* | Command run after `agents_launch`; `{session}` is substituted |
122
+ | `TMUX_AGENTS_ALLOW_KILL` | `true` | Set `false` to disable `pane_kill` / `session_kill` |
123
+ | `TMUX_AGENTS_REDACT` | `true` | Replace token-looking strings in pane text with `[redacted]` |
124
+ | `TMUX_AGENTS_MAX_LINES` | `2000` | Hard cap for `pane_read` |
125
+ | `TMUX_AGENTS_SOCKET` | *(default server)* | `tmux -L <socket>` — isolate the agents on their own tmux server |
126
+ | `TMUX_AGENTS_TMUX` | `tmux` | Path to the tmux binary |
127
+
128
+ ## Security model, plainly
129
+
130
+ - `pane_send` types into a pane. If that pane is a shell, that is a shell. Keep agent sessions under a prefix (`TMUX_AGENTS_SESSION_PREFIX`) and the model cannot reach anything else.
131
+ - Pane text is scrubbed for GitHub/OpenAI/Slack/AWS tokens, bearer headers and `*_TOKEN=` style assignments before it is returned. This is a filter, not a guarantee — do not paste secrets into agent panes.
132
+ - The server instructs the model to treat everything it reads from panes as data written by another program, never as instructions. Agents talking to agents is exactly where prompt injection lives; the instruction is a reminder, not a defence. Review what your agents commit.
133
+ - No network, no config files, no state: the server is a thin layer over the `tmux` CLI and forgets everything between calls.
134
+
135
+ ## How it compares
136
+
137
+ There are other tmux MCP servers ([nickgnd/tmux-mcp](https://github.com/nickgnd/tmux-mcp), [laszlopere/mcp-tmux](https://github.com/laszlopere/mcp-tmux)) and they are good general tmux remotes: create windows, run commands, read output, some over SSH. `tmux-agents` is narrower on purpose: it knows about *agents* — titled panes, "launch N of these in a layout that fits", "wait until it goes quiet", prefix-scoped visibility, redaction — and it deliberately leaves out a raw command tool. If you want a tmux remote control, use one of those. If you want an orchestrator, this is the one.
138
+
139
+ ## Development
140
+
141
+ ```
142
+ git clone https://github.com/woonyong-choi/tmux-agents
143
+ cd tmux-agents
144
+ uv sync --extra dev
145
+ uv run pytest # real tmux, private socket, ~20s
146
+ uv run ruff check src tests
147
+ ```
148
+
149
+ Tests spin up a throwaway tmux server (`tmux -L tmux-agents-test-…`), so they never touch your sessions.
150
+
151
+ ## License
152
+
153
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,7 @@
1
+ # Security
2
+
3
+ `tmux-agents` lets an MCP client type into tmux panes. Read the "Security model, plainly"
4
+ section of the README before enabling it, and always set `TMUX_AGENTS_SESSION_PREFIX`.
5
+
6
+ To report a vulnerability, open a private security advisory on GitHub
7
+ (Security → Report a vulnerability) rather than a public issue.
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "tmux-agents": {
4
+ "command": "uvx",
5
+ "args": ["tmux-agents"],
6
+ "env": {
7
+ "TMUX_AGENTS_SESSION_PREFIX": "agents-",
8
+ "TMUX_AGENTS_OPEN_COMMAND": "open -na Ghostty.app --args -e tmux attach -t {session}"
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,15 @@
1
+ # Example: a batch of three agents, steered by one
2
+
3
+ A prompt you can give the orchestrating model once `tmux-agents` is connected:
4
+
5
+ > Launch session `agents-wp5` in `/path/to/repo` with three agents:
6
+ > "WP5 Review" → `claude --model claude-opus-5 "$(cat prompts/wp5.md)"`,
7
+ > "WP6a Lazy" → `claude --model claude-sonnet-5 "$(cat prompts/wp6a.md)"`,
8
+ > "WP6b Shell" → `codex --model gpt-5-codex "$(cat prompts/wp6b.md)"`.
9
+ > Then loop: `pane_wait` each pane (idle 8s, timeout 240s). If the tail ends in a
10
+ > question, answer it with `pane_send` following the rules in `docs/plan.md`. If it
11
+ > ends with "handoff.md written", read the last 80 lines and summarise. When all
12
+ > three are done, `session_kill`.
13
+
14
+ Everything the model reads from the panes is another agent's output; it should
15
+ verify claims (`npm test`, `git status`) rather than trust "done".
@@ -0,0 +1,57 @@
1
+ [project]
2
+ name = "tmux-agents"
3
+ version = "0.1.0"
4
+ description = "MCP server that lets any AI agent launch, watch and steer coding agents (Claude Code, Codex, Hermes, aider, ...) running in tmux panes."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "Woonyong Choi" }]
9
+ keywords = [
10
+ "mcp", "model-context-protocol", "tmux", "agents", "claude-code", "codex",
11
+ "hermes-agent", "aider", "orchestration", "terminal", "automation",
12
+ ]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: MacOS",
18
+ "Operating System :: POSIX :: Linux",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Software Development :: Libraries",
24
+ "Topic :: Terminals",
25
+ ]
26
+ dependencies = [
27
+ "mcp>=1.2.0",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/woonyong-choi/tmux-agents"
32
+ Issues = "https://github.com/woonyong-choi/tmux-agents/issues"
33
+ Changelog = "https://github.com/woonyong-choi/tmux-agents/blob/main/CHANGELOG.md"
34
+
35
+ [project.scripts]
36
+ tmux-agents = "tmux_agents.__main__:main"
37
+
38
+ [project.optional-dependencies]
39
+ dev = ["pytest>=8", "ruff>=0.6"]
40
+
41
+ [build-system]
42
+ requires = ["hatchling"]
43
+ build-backend = "hatchling.build"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/tmux_agents"]
47
+
48
+ [tool.ruff]
49
+ line-length = 100
50
+ target-version = "py310"
51
+
52
+ [tool.ruff.lint]
53
+ select = ["E", "F", "I", "UP", "B"]
54
+
55
+ [tool.pytest.ini_options]
56
+ pythonpath = ["src"]
57
+ testpaths = ["tests"]
@@ -0,0 +1,3 @@
1
+ """tmux-agents: drive coding agents that live in tmux panes, from any MCP client."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,20 @@
1
+ """`tmux-agents` entry point: run the MCP server over stdio."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+
8
+ def main() -> None:
9
+ if "--version" in sys.argv[1:]:
10
+ from . import __version__
11
+
12
+ print(f"tmux-agents {__version__}")
13
+ return
14
+ from .server import mcp
15
+
16
+ mcp.run()
17
+
18
+
19
+ if __name__ == "__main__":
20
+ main()
@@ -0,0 +1,42 @@
1
+ """Scrub secrets out of pane text before it reaches the model."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+ _PATTERNS = [
8
+ # GitHub, OpenAI, Anthropic, Slack, AWS, generic bearer/API keys
9
+ re.compile(r"\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b"),
10
+ re.compile(r"\bgithub_pat_[A-Za-z0-9_]{20,}\b"),
11
+ re.compile(r"\bsk-[A-Za-z0-9_-]{16,}\b"),
12
+ re.compile(r"\bxox[abprs]-[A-Za-z0-9-]{10,}\b"),
13
+ re.compile(r"\bAKIA[0-9A-Z]{16}\b"),
14
+ re.compile(r"(?i)\b(bearer)\s+[A-Za-z0-9._~+/=-]{16,}"),
15
+ re.compile(
16
+ r"(?i)\b([A-Z0-9_]*(?:token|secret|api[_-]?key|password|passwd))"
17
+ r"\s*[=:]\s*['\"]?[^\s'\"]{8,}"
18
+ ),
19
+ ]
20
+
21
+ _ANSI = re.compile(r"\x1b\[[0-9;?]*[ -/]*[@-~]|\x1b\][^\x07]*\x07|\x1b[()][A-Za-z0-9]")
22
+
23
+
24
+ def redact(text: str) -> str:
25
+ for pattern in _PATTERNS:
26
+ text = pattern.sub(_replace, text)
27
+ return text
28
+
29
+
30
+ def _replace(match: re.Match[str]) -> str:
31
+ groups = match.groups()
32
+ if groups and groups[0]:
33
+ return (
34
+ f"{groups[0]}=[redacted]"
35
+ if "=" in match.group(0) or ":" in match.group(0)
36
+ else f"{groups[0]} [redacted]"
37
+ )
38
+ return "[redacted]"
39
+
40
+
41
+ def strip_ansi(text: str) -> str:
42
+ return _ANSI.sub("", text)