tmux-team 4.0.0-beta.1 → 4.1.0
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.
- package/README.md +102 -24
- package/package.json +1 -1
- package/src/cli.test.ts +15 -1
- package/src/cli.ts +10 -0
- package/src/commands/add.ts +17 -32
- package/src/commands/basic-commands.test.ts +293 -17
- package/src/commands/completion.ts +6 -8
- package/src/commands/config-command.test.ts +9 -8
- package/src/commands/config.ts +1 -5
- package/src/commands/help.ts +5 -2
- package/src/commands/install.test.ts +15 -1
- package/src/commands/list.ts +7 -1
- package/src/commands/migrate.ts +84 -0
- package/src/commands/preamble.test.ts +15 -2
- package/src/commands/preamble.ts +61 -16
- package/src/commands/remove.ts +10 -6
- package/src/commands/talk.test.ts +111 -22
- package/src/commands/talk.ts +8 -3
- package/src/commands/team.ts +172 -0
- package/src/commands/update.ts +45 -14
- package/src/config.test.ts +24 -0
- package/src/config.ts +37 -3
- package/src/context.test.ts +76 -1
- package/src/context.ts +8 -1
- package/src/identity.test.ts +3 -9
- package/src/identity.ts +7 -9
- package/src/registry.test.ts +61 -0
- package/src/registry.ts +29 -0
- package/src/tmux.test.ts +190 -1
- package/src/tmux.ts +291 -11
- package/src/types.ts +55 -0
- package/src/ui.test.ts +7 -1
package/README.md
CHANGED
|
@@ -18,26 +18,35 @@ npm install -g tmux-team
|
|
|
18
18
|
# 1. Install for your AI agent
|
|
19
19
|
tmt install claude # or: tmt install codex
|
|
20
20
|
|
|
21
|
-
# 2. Go to working folder and
|
|
22
|
-
tmt init
|
|
23
|
-
|
|
24
|
-
# 3. Register agents (run inside each agent's pane)
|
|
21
|
+
# 2. Go to working folder and register agents (run inside each agent's pane)
|
|
25
22
|
tmt this claude # registers current pane as "claude"
|
|
26
23
|
tmt this codex # registers current pane as "codex"
|
|
27
24
|
|
|
28
|
-
#
|
|
25
|
+
# 3. Talk to agents
|
|
29
26
|
tmt talk codex "Review this code" # waits for response by default
|
|
30
27
|
|
|
31
|
-
#
|
|
28
|
+
# 4. Update or remove an agent
|
|
32
29
|
tmt update codex --pane 2.3
|
|
33
30
|
tmt rm codex
|
|
34
31
|
```
|
|
35
32
|
|
|
36
33
|
> **Tip:** Most AI agents support `!` to run bash commands. From inside Claude Code, Codex, or Gemini CLI, you can run `!tmt this myname` to quickly register that pane.
|
|
37
34
|
|
|
35
|
+
### How scopes work
|
|
36
|
+
|
|
37
|
+
Registrations live in tmux pane metadata, not in a JSON file you have to track.
|
|
38
|
+
By default they are scoped to the current **workspace** — the nearest Git root,
|
|
39
|
+
or the current folder when you are not inside a Git repo. So `tmt this`,
|
|
40
|
+
`tmt add`, `tmt rm`, `tmt update`, `tmt preamble`, and `tmt list` all act on
|
|
41
|
+
the workspace you are currently in.
|
|
42
|
+
|
|
43
|
+
Reach for `--team <name>` only when you want an explicit shared team that spans
|
|
44
|
+
folders (see [Shared Teams](#shared-teams)).
|
|
45
|
+
|
|
38
46
|
## Cross-Folder Collaboration
|
|
39
47
|
|
|
40
|
-
Agents don't need to be in the same folder to collaborate.
|
|
48
|
+
Agents don't need to be in the same folder to collaborate. From your current
|
|
49
|
+
workspace you can add an agent whose pane lives in another project:
|
|
41
50
|
|
|
42
51
|
```bash
|
|
43
52
|
# In project-a folder, add an agent that's running in project-b
|
|
@@ -46,6 +55,10 @@ tmt add codex-reviewer 5.1 # Use the pane ID from the other project
|
|
|
46
55
|
|
|
47
56
|
Find pane IDs with: `tmux display-message -p "#{pane_id}"`
|
|
48
57
|
|
|
58
|
+
This still uses the default workspace scope: the registration is visible from
|
|
59
|
+
project-a, not from project-b. For long-running collaboration that should be
|
|
60
|
+
visible on both sides, use a [shared team](#shared-teams).
|
|
61
|
+
|
|
49
62
|
## Commands
|
|
50
63
|
|
|
51
64
|
| Command | Description |
|
|
@@ -55,7 +68,10 @@ Find pane IDs with: `tmux display-message -p "#{pane_id}"`
|
|
|
55
68
|
| `talk <agent> "msg"` | Send message and wait for response |
|
|
56
69
|
| `talk all "msg"` | Broadcast to all agents |
|
|
57
70
|
| `check <agent> [lines]` | Read agent's pane output |
|
|
58
|
-
| `list` | Show
|
|
71
|
+
| `list` | Show agents in the current workspace (or `--team <name>`) |
|
|
72
|
+
| `migrate [--dry-run] [--cleanup]` | Move legacy `tmux-team.json` entries into tmux pane metadata |
|
|
73
|
+
| `team ls [--summary\|--json]` | Inspect tmux panes grouped by scope; `--summary` aggregates shared teams |
|
|
74
|
+
| `team rm <team> --force` | Remove a shared team registration from every pane |
|
|
59
75
|
| `learn` | Show educational guide |
|
|
60
76
|
|
|
61
77
|
**Options for `talk`:**
|
|
@@ -76,39 +92,100 @@ tmt config set pasteEnterDelayMs 500
|
|
|
76
92
|
|
|
77
93
|
## Managing Your Team
|
|
78
94
|
|
|
79
|
-
|
|
95
|
+
Agent registrations live in tmux pane metadata, scoped per workspace by
|
|
96
|
+
default. The same-folder workflow never needs `--team`.
|
|
80
97
|
|
|
81
|
-
**List
|
|
98
|
+
**List agents in this workspace:**
|
|
82
99
|
```bash
|
|
83
100
|
tmt ls
|
|
101
|
+
tmt ls --team myproject # or list a shared team
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Inspect every tmux pane** with `tmt team ls`. Output is grouped by scope —
|
|
105
|
+
shared teams first, then workspaces, then unregistered panes — and each
|
|
106
|
+
section's title lists the agents living there:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
Team: acme-app (codex, gemini)
|
|
110
|
+
PANE TARGET CWD CMD
|
|
111
|
+
%12 main:1.0 ~/acme/frontend node
|
|
112
|
+
%17 main:2.0 ~/acme/backend python
|
|
113
|
+
|
|
114
|
+
Workspace: ~/dev/tmux-team (claude)
|
|
115
|
+
PANE TARGET CWD CMD
|
|
116
|
+
%3 work:0.1 ~/dev/tmux-team node
|
|
117
|
+
|
|
118
|
+
Unregistered panes
|
|
119
|
+
PANE TARGET CWD CMD
|
|
120
|
+
%9 misc:0.0 ~/scratch zsh
|
|
84
121
|
```
|
|
85
122
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
123
|
+
```bash
|
|
124
|
+
tmt team ls # grouped pane inventory (default)
|
|
125
|
+
tmt team ls --summary # collapse to a shared-team aggregate (TEAM / AGENTS)
|
|
126
|
+
tmt team ls --json # { teams, panes } incl. each pane's registrations
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Add an agent from any pane.** Targets can be `%pane_id`, `window.pane`, or
|
|
130
|
+
`session:window.pane`; tmux-team stores the canonical `%pane_id`.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
tmt add codex 1.1 "Code reviewer"
|
|
93
134
|
```
|
|
94
135
|
|
|
95
|
-
**Remove**
|
|
136
|
+
**Remove an agent** from the current scope:
|
|
96
137
|
```bash
|
|
97
138
|
tmt rm codex
|
|
98
139
|
```
|
|
99
140
|
|
|
141
|
+
**Migrate from legacy `tmux-team.json`.** Versions before v4 stored agents in
|
|
142
|
+
a JSON file. `tmt migrate` copies those entries into tmux pane metadata so the
|
|
143
|
+
new commands can see them. Run it once per project that still has the file:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
tmt migrate --dry-run # preview what would move
|
|
147
|
+
tmt migrate # move entries into tmux metadata
|
|
148
|
+
tmt migrate --cleanup # also delete the migrated entries from the JSON file
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`tmux-team.json` is still loaded as a fallback when no tmux metadata exists,
|
|
152
|
+
and it remains the home for local `$config` overrides. If you don't use it,
|
|
153
|
+
you can ignore it.
|
|
154
|
+
|
|
100
155
|
---
|
|
101
156
|
|
|
157
|
+
## Agent Preambles
|
|
158
|
+
|
|
159
|
+
Set a per-agent preamble to steer behavior (stored with the pane registration):
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
tmt preamble set codex "You are the code quality guard. Be strict."
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### What Happens When a Preamble Is Set
|
|
166
|
+
|
|
167
|
+
When you send a message, tmux-team injects the preamble like this:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
[SYSTEM: You are the code quality guard. Be strict.]
|
|
171
|
+
|
|
172
|
+
Review the login flow changes.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Control how often it’s injected with `preambleEvery`:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
tmt config set preambleEvery 3
|
|
179
|
+
```
|
|
180
|
+
|
|
102
181
|
## Shared Teams
|
|
103
182
|
|
|
104
183
|
> *Work on different folders but talk to the same team of agents.*
|
|
105
184
|
|
|
106
|
-
By default,
|
|
185
|
+
By default, registrations are scoped to the current workspace. The `--team` flag
|
|
186
|
+
creates an explicit shared team that works across folders:
|
|
107
187
|
|
|
108
188
|
```bash
|
|
109
|
-
# Initialize a shared team
|
|
110
|
-
tmt init --team myproject
|
|
111
|
-
|
|
112
189
|
# Register agents from ANY folder
|
|
113
190
|
cd ~/code/frontend && tmt this claude --team myproject
|
|
114
191
|
cd ~/code/backend && tmt this codex --team myproject
|
|
@@ -128,16 +205,17 @@ tmt talk all "Starting deploy - heads up" --team myproject
|
|
|
128
205
|
|
|
129
206
|
**Single project** (default) — agents work in the same folder:
|
|
130
207
|
```bash
|
|
131
|
-
tmt init
|
|
132
208
|
tmt this claude
|
|
133
209
|
tmt add codex 1.1
|
|
134
210
|
```
|
|
135
211
|
|
|
136
212
|
**Shared team** — agents work across folders but collaborate:
|
|
137
213
|
```bash
|
|
138
|
-
tmt init --team acme-app
|
|
139
214
|
tmt this frontend-claude --team acme-app # from ~/acme/frontend
|
|
140
215
|
tmt this backend-codex --team acme-app # from ~/acme/backend
|
|
216
|
+
tmt ls --team acme-app # list members
|
|
217
|
+
tmt team ls --summary # all shared teams at a glance
|
|
218
|
+
tmt team rm acme-app --force # remove the team from every pane
|
|
141
219
|
```
|
|
142
220
|
|
|
143
221
|
### Multi-team coordination
|
package/package.json
CHANGED
package/src/cli.test.ts
CHANGED
|
@@ -16,7 +16,14 @@ function makeStubContext(): Context {
|
|
|
16
16
|
config: {
|
|
17
17
|
mode: 'polling',
|
|
18
18
|
preambleMode: 'always',
|
|
19
|
-
defaults: {
|
|
19
|
+
defaults: {
|
|
20
|
+
timeout: 180,
|
|
21
|
+
pollInterval: 1,
|
|
22
|
+
captureLines: 100,
|
|
23
|
+
maxCaptureLines: 2000,
|
|
24
|
+
preambleEvery: 3,
|
|
25
|
+
pasteEnterDelayMs: 500,
|
|
26
|
+
},
|
|
20
27
|
agents: {},
|
|
21
28
|
paneRegistry: {},
|
|
22
29
|
},
|
|
@@ -25,6 +32,13 @@ function makeStubContext(): Context {
|
|
|
25
32
|
capture: vi.fn(),
|
|
26
33
|
listPanes: vi.fn(() => []),
|
|
27
34
|
getCurrentPaneId: vi.fn(() => null),
|
|
35
|
+
resolvePaneTarget: vi.fn((target: string) => target),
|
|
36
|
+
getAgentRegistry: vi.fn(() => ({ paneRegistry: {}, agents: {} })),
|
|
37
|
+
setAgentRegistration: vi.fn(),
|
|
38
|
+
clearAgentRegistration: vi.fn(() => false),
|
|
39
|
+
listTeams: vi.fn(() => ({})),
|
|
40
|
+
listTeamPanes: vi.fn(() => []),
|
|
41
|
+
removeTeam: vi.fn(() => ({ removed: 0, agents: [] })),
|
|
28
42
|
},
|
|
29
43
|
paths: {
|
|
30
44
|
globalDir: '/g',
|
package/src/cli.ts
CHANGED
|
@@ -22,6 +22,8 @@ import { cmdPreamble } from './commands/preamble.js';
|
|
|
22
22
|
import { cmdInstall } from './commands/install.js';
|
|
23
23
|
import { cmdLearn } from './commands/learn.js';
|
|
24
24
|
import { cmdThis } from './commands/this.js';
|
|
25
|
+
import { cmdMigrate } from './commands/migrate.js';
|
|
26
|
+
import { cmdTeam } from './commands/team.js';
|
|
25
27
|
|
|
26
28
|
// ─────────────────────────────────────────────────────────────
|
|
27
29
|
// Argument parsing
|
|
@@ -206,6 +208,14 @@ function main(): void {
|
|
|
206
208
|
cmdRemove(ctx, args[0]);
|
|
207
209
|
break;
|
|
208
210
|
|
|
211
|
+
case 'migrate':
|
|
212
|
+
cmdMigrate(ctx, args);
|
|
213
|
+
break;
|
|
214
|
+
|
|
215
|
+
case 'team':
|
|
216
|
+
cmdTeam(ctx, args);
|
|
217
|
+
break;
|
|
218
|
+
|
|
209
219
|
case 'this':
|
|
210
220
|
if (args.length < 1) {
|
|
211
221
|
ctx.ui.error('Usage: tmux-team this <name> [remark]');
|
package/src/commands/add.ts
CHANGED
|
@@ -2,54 +2,39 @@
|
|
|
2
2
|
// add command - register a new agent
|
|
3
3
|
// ─────────────────────────────────────────────────────────────
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import type { Context, PaneEntry } from '../types.js';
|
|
5
|
+
import type { Context } from '../types.js';
|
|
7
6
|
import { ExitCodes } from '../exits.js';
|
|
8
|
-
import {
|
|
7
|
+
import { getRegistryScope, registrationFromEntry } from '../registry.js';
|
|
9
8
|
|
|
10
9
|
export function cmdAdd(ctx: Context, name: string, pane: string, remark?: string): void {
|
|
11
|
-
const { ui, config,
|
|
12
|
-
|
|
13
|
-
// Ensure teams directory exists if using --team
|
|
14
|
-
if (flags.team) {
|
|
15
|
-
ensureTeamsDir(paths.globalDir);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Create config file if it doesn't exist
|
|
19
|
-
if (!fs.existsSync(paths.localConfig)) {
|
|
20
|
-
fs.writeFileSync(paths.localConfig, '{}\n');
|
|
21
|
-
if (!flags.json) {
|
|
22
|
-
if (flags.team) {
|
|
23
|
-
ui.info(`Created shared team "${flags.team}"`);
|
|
24
|
-
} else {
|
|
25
|
-
ui.info(`Created ${paths.localConfig}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
10
|
+
const { ui, config, tmux, flags, exit } = ctx;
|
|
29
11
|
|
|
30
12
|
if (config.paneRegistry[name]) {
|
|
31
13
|
ui.error(`Agent '${name}' already exists. Use 'tmux-team update' to modify.`);
|
|
32
14
|
exit(ExitCodes.ERROR);
|
|
33
15
|
}
|
|
34
16
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (remark) {
|
|
40
|
-
newEntry.remark = remark;
|
|
17
|
+
const resolvedPane = tmux.resolvePaneTarget(pane);
|
|
18
|
+
if (!resolvedPane) {
|
|
19
|
+
ui.error(`Pane '${pane}' not found. Is tmux running?`);
|
|
20
|
+
exit(ExitCodes.PANE_NOT_FOUND);
|
|
41
21
|
}
|
|
42
|
-
|
|
22
|
+
const paneId = resolvedPane as string;
|
|
43
23
|
|
|
44
|
-
|
|
24
|
+
const scope = getRegistryScope(ctx);
|
|
25
|
+
const registration = registrationFromEntry(name, {
|
|
26
|
+
pane: paneId,
|
|
27
|
+
...(remark !== undefined && { remark }),
|
|
28
|
+
});
|
|
29
|
+
tmux.setAgentRegistration(paneId, scope, registration);
|
|
45
30
|
|
|
46
31
|
if (flags.json) {
|
|
47
|
-
ui.json({ added: name, pane, remark, team: flags.team });
|
|
32
|
+
ui.json({ added: name, pane: paneId, remark, team: flags.team });
|
|
48
33
|
} else {
|
|
49
34
|
if (flags.team) {
|
|
50
|
-
ui.success(`Added agent '${name}' to team "${flags.team}" at pane ${
|
|
35
|
+
ui.success(`Added agent '${name}' to team "${flags.team}" at pane ${paneId}`);
|
|
51
36
|
} else {
|
|
52
|
-
ui.success(`Added agent '${name}' at pane ${
|
|
37
|
+
ui.success(`Added agent '${name}' at pane ${paneId}`);
|
|
53
38
|
}
|
|
54
39
|
}
|
|
55
40
|
}
|