tmux-team 2.1.0 → 3.0.0-alpha.1
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 +89 -53
- package/package.json +1 -1
- package/src/cli.ts +0 -5
- package/src/commands/add.ts +9 -5
- package/src/commands/config.ts +62 -9
- package/src/commands/help.ts +2 -2
- package/src/commands/preamble.ts +22 -25
- package/src/commands/remove.ts +5 -3
- package/src/commands/talk.test.ts +479 -44
- package/src/commands/talk.ts +95 -65
- package/src/commands/update.ts +16 -5
- package/src/config.test.ts +182 -9
- package/src/config.ts +28 -16
- package/src/identity.ts +89 -0
- package/src/state.test.ts +20 -10
- package/src/state.ts +28 -1
- package/src/types.ts +6 -2
- package/src/ui.ts +2 -1
- package/src/version.ts +1 -1
- package/src/pm/commands.test.ts +0 -1128
- package/src/pm/commands.ts +0 -723
- package/src/pm/manager.test.ts +0 -377
- package/src/pm/manager.ts +0 -146
- package/src/pm/permissions.test.ts +0 -332
- package/src/pm/permissions.ts +0 -279
- package/src/pm/storage/adapter.ts +0 -55
- package/src/pm/storage/fs.test.ts +0 -384
- package/src/pm/storage/fs.ts +0 -256
- package/src/pm/storage/github.ts +0 -763
- package/src/pm/types.ts +0 -85
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**The lightweight coordination layer for terminal-based AI agents.**
|
|
4
4
|
|
|
5
|
-
tmux-team is a protocol-agnostic
|
|
5
|
+
tmux-team is a protocol-agnostic transport layer that enables multi-agent collaboration directly within your existing tmux workflow. It turns a collection of isolated terminal panes into a coordinated AI team.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -24,7 +24,7 @@ Unlike heavyweight frameworks that require specific SDKs or cloud infrastructure
|
|
|
24
24
|
- **Model Agnostic** — Works with Claude Code, Gemini CLI, Codex, Aider, or any CLI tool
|
|
25
25
|
- **Zero Infrastructure** — No servers, no MCP setup, no complex configuration. If it runs in tmux, tmux-team can talk to it
|
|
26
26
|
- **Whitelist-Friendly** — A single `tmux-team talk:*` prefix covers all operations, keeping AI tool permissions simple and safe
|
|
27
|
-
- **Local-First** — Per-project `tmux-team.json` lives with your repo; global config in `~/.tmux-team/`
|
|
27
|
+
- **Local-First** — Per-project `tmux-team.json` lives with your repo; global config in `~/.config/tmux-team/`
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
@@ -34,32 +34,24 @@ Unlike heavyweight frameworks that require specific SDKs or cloud infrastructure
|
|
|
34
34
|
|
|
35
35
|
### 1. Deterministic Transport (`--delay` vs. `sleep`)
|
|
36
36
|
|
|
37
|
-
**The Problem**: Tool allowlists typically approve one safe command (`tmux-team talk ...`) but not arbitrary shell commands. Using `sleep` is often blocked by security policies
|
|
37
|
+
**The Problem**: Tool allowlists typically approve one safe command (`tmux-team talk ...`) but not arbitrary shell commands. Using `sleep` is often blocked by security policies.
|
|
38
38
|
|
|
39
|
-
**The Why**: Internal delay keeps the workflow as a single tool call.
|
|
39
|
+
**The Why**: Internal delay keeps the workflow as a single tool call. No shell dependency, no policy friction.
|
|
40
40
|
|
|
41
41
|
### 2. Stateless Handshakes (The "Nonce" Strategy)
|
|
42
42
|
|
|
43
|
-
**The Problem**: Terminal panes are streams, not RPC channels. A simple `[DONE]` string could already be in scrollback
|
|
43
|
+
**The Problem**: Terminal panes are streams, not RPC channels. A simple `[DONE]` string could already be in scrollback.
|
|
44
44
|
|
|
45
|
-
**The Why**: We use a unique **Nonce**
|
|
45
|
+
**The Why**: We use a unique **Nonce** for every request: `{tmux-team-end:8f3a}`.
|
|
46
46
|
- **Collision Avoidance** — Prevents matching markers from previous turns
|
|
47
|
-
- **Completion Safety** — Ensures the agent has truly finished
|
|
48
|
-
- **Zero-API RPC** — Creates request/response semantics over a standard TTY
|
|
49
|
-
|
|
50
|
-
Combined with one-at-a-time locking, nonce markers ensure state stays consistent and debuggable.
|
|
47
|
+
- **Completion Safety** — Ensures the agent has truly finished
|
|
48
|
+
- **Zero-API RPC** — Creates request/response semantics over a standard TTY
|
|
51
49
|
|
|
52
50
|
### 3. Context Injection (Preambles)
|
|
53
51
|
|
|
54
|
-
**The Problem**: AI agents are prone to "instruction drift." Over a long session, they might
|
|
55
|
-
|
|
56
|
-
**The Why**: Preambles act as a forced system prompt for CLI environments. By injecting these "hidden instructions" at the transport level, we ensure the agent remains in character (e.g., "You are the code reviewer, do not edit files") without cluttering the human's command history. It's about reducing **Cognitive Load**—the human focuses on intent, the CLI enforces protocol.
|
|
57
|
-
|
|
58
|
-
### 4. Token-Efficient Polling
|
|
59
|
-
|
|
60
|
-
**The Problem**: The `--wait` feature is powerful but higher-risk: long-running commands, more state to manage, potential for hung processes.
|
|
52
|
+
**The Problem**: AI agents are prone to "instruction drift." Over a long session, they might forget constraints.
|
|
61
53
|
|
|
62
|
-
**The Why**:
|
|
54
|
+
**The Why**: Preambles act as a forced system prompt for CLI environments. By injecting these "hidden instructions" at the transport level, we ensure the agent remains in character.
|
|
63
55
|
|
|
64
56
|
---
|
|
65
57
|
|
|
@@ -69,7 +61,7 @@ Combined with one-at-a-time locking, nonce markers ensure state stays consistent
|
|
|
69
61
|
npm install -g tmux-team
|
|
70
62
|
```
|
|
71
63
|
|
|
72
|
-
**Requirements:** Node.js >=
|
|
64
|
+
**Requirements:** Node.js >= 18, tmux, macOS/Linux
|
|
73
65
|
|
|
74
66
|
### Shell Completion
|
|
75
67
|
|
|
@@ -139,10 +131,8 @@ Once the plugin is installed, coordinate directly from your Claude Code session:
|
|
|
139
131
|
| `update <name> --pane/--remark` | Update agent configuration |
|
|
140
132
|
| `remove <name>` | Unregister an agent |
|
|
141
133
|
| `init` | Create `tmux-team.json` in current directory |
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `pm t add/list/update/done` | Manage tasks |
|
|
145
|
-
| `pm log` | View audit event log |
|
|
134
|
+
| `config [show/set/clear]` | View/modify settings |
|
|
135
|
+
| `preamble [show/set/clear]` | Manage agent preambles |
|
|
146
136
|
| `completion [zsh\|bash]` | Output shell completion script |
|
|
147
137
|
|
|
148
138
|
---
|
|
@@ -151,36 +141,58 @@ Once the plugin is installed, coordinate directly from your Claude Code session:
|
|
|
151
141
|
|
|
152
142
|
### Local Config (`./tmux-team.json`)
|
|
153
143
|
|
|
154
|
-
Per-project agent registry:
|
|
144
|
+
Per-project agent registry with optional preambles:
|
|
155
145
|
|
|
156
146
|
```json
|
|
157
147
|
{
|
|
158
|
-
"claude": {
|
|
159
|
-
|
|
148
|
+
"claude": {
|
|
149
|
+
"pane": "10.0",
|
|
150
|
+
"remark": "Frontend specialist",
|
|
151
|
+
"preamble": "Focus on UI components. Ask for review before merging."
|
|
152
|
+
},
|
|
153
|
+
"codex": {
|
|
154
|
+
"pane": "10.1",
|
|
155
|
+
"remark": "Code reviewer",
|
|
156
|
+
"preamble": "You are the code quality guard. Review changes thoroughly."
|
|
157
|
+
}
|
|
160
158
|
}
|
|
161
159
|
```
|
|
162
160
|
|
|
161
|
+
| Field | Description |
|
|
162
|
+
|-------|-------------|
|
|
163
|
+
| `pane` | tmux pane ID (required) |
|
|
164
|
+
| `remark` | Description shown in `list` |
|
|
165
|
+
| `preamble` | Hidden instructions prepended to every message |
|
|
166
|
+
|
|
163
167
|
### Global Config (`~/.config/tmux-team/config.json`)
|
|
164
168
|
|
|
169
|
+
Global settings that apply to all projects:
|
|
170
|
+
|
|
165
171
|
```json
|
|
166
172
|
{
|
|
167
173
|
"mode": "polling",
|
|
168
174
|
"preambleMode": "always",
|
|
169
175
|
"defaults": {
|
|
170
|
-
"timeout":
|
|
171
|
-
"pollInterval":
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
"gemini": {
|
|
175
|
-
"preamble": "Do not edit files until explicitly asked."
|
|
176
|
-
}
|
|
176
|
+
"timeout": 180,
|
|
177
|
+
"pollInterval": 1,
|
|
178
|
+
"captureLines": 100,
|
|
179
|
+
"preambleEvery": 3
|
|
177
180
|
}
|
|
178
181
|
}
|
|
179
182
|
```
|
|
180
183
|
|
|
184
|
+
| Field | Description |
|
|
185
|
+
|-------|-------------|
|
|
186
|
+
| `mode` | Default mode: `polling` (manual check) or `wait` (auto-wait) |
|
|
187
|
+
| `preambleMode` | `always` (inject preambles) or `disabled` |
|
|
188
|
+
| `defaults.timeout` | Default --wait timeout in seconds |
|
|
189
|
+
| `defaults.pollInterval` | Polling interval in seconds |
|
|
190
|
+
| `defaults.captureLines` | Default lines for `check` command |
|
|
191
|
+
| `defaults.preambleEvery` | Inject preamble every N messages (default: 3) |
|
|
192
|
+
|
|
181
193
|
---
|
|
182
194
|
|
|
183
|
-
## ✨
|
|
195
|
+
## ✨ Features
|
|
184
196
|
|
|
185
197
|
### 📡 Enhanced `talk` Command
|
|
186
198
|
|
|
@@ -194,32 +206,23 @@ tmux-team talk codex "message" --wait --timeout 60
|
|
|
194
206
|
|
|
195
207
|
### 📜 Agent Preambles
|
|
196
208
|
|
|
197
|
-
Inject hidden instructions into every message
|
|
209
|
+
Inject hidden instructions into every message via local `tmux-team.json`:
|
|
198
210
|
|
|
199
211
|
```json
|
|
200
212
|
{
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
|
|
204
|
-
}
|
|
213
|
+
"gemini": {
|
|
214
|
+
"pane": "10.2",
|
|
215
|
+
"preamble": "Always explain your reasoning. Do not edit files directly."
|
|
205
216
|
}
|
|
206
217
|
}
|
|
207
218
|
```
|
|
208
219
|
|
|
209
|
-
|
|
220
|
+
Use the CLI to manage preambles:
|
|
210
221
|
|
|
211
222
|
```bash
|
|
212
|
-
#
|
|
213
|
-
tmux-team
|
|
214
|
-
|
|
215
|
-
# Manage milestones and tasks
|
|
216
|
-
tmux-team pm m add "MVP Release"
|
|
217
|
-
tmux-team pm t add "Implement login" --milestone 1
|
|
218
|
-
tmux-team pm t update 1 --status in_progress
|
|
219
|
-
tmux-team pm t done 1
|
|
220
|
-
|
|
221
|
-
# View audit log
|
|
222
|
-
tmux-team pm log --limit 10
|
|
223
|
+
tmux-team preamble show gemini # View current preamble
|
|
224
|
+
tmux-team preamble set gemini "..." # Set preamble
|
|
225
|
+
tmux-team preamble clear gemini # Remove preamble
|
|
223
226
|
```
|
|
224
227
|
|
|
225
228
|
---
|
|
@@ -228,16 +231,49 @@ tmux-team pm log --limit 10
|
|
|
228
231
|
|
|
229
232
|
tmux-team intentionally stays lightweight:
|
|
230
233
|
|
|
231
|
-
- **Not an orchestrator** — No automatic
|
|
232
|
-
- **Not a session manager** — Doesn't create/manage tmux sessions
|
|
234
|
+
- **Not an orchestrator** — No automatic agent selection or routing
|
|
235
|
+
- **Not a session manager** — Doesn't create/manage tmux sessions
|
|
233
236
|
- **Not an LLM wrapper** — Doesn't process or route messages through AI
|
|
234
237
|
|
|
235
238
|
It's the plumbing layer that lets humans and AI agents coordinate via tmux, nothing more.
|
|
236
239
|
|
|
237
240
|
---
|
|
238
241
|
|
|
242
|
+
## 📖 Command Reference
|
|
243
|
+
|
|
244
|
+
### talk Options
|
|
245
|
+
|
|
246
|
+
| Option | Description |
|
|
247
|
+
|--------|-------------|
|
|
248
|
+
| `--delay <seconds>` | Wait before sending (whitelist-friendly alternative to `sleep`) |
|
|
249
|
+
| `--wait` | Block until agent responds (nonce-based completion detection) |
|
|
250
|
+
| `--timeout <seconds>` | Max wait time (default: 180s) |
|
|
251
|
+
| `--no-preamble` | Skip agent preamble for this message |
|
|
252
|
+
|
|
253
|
+
### config Command
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
tmux-team config show # Show current config
|
|
257
|
+
tmux-team config set mode wait # Enable wait mode
|
|
258
|
+
tmux-team config set preambleMode disabled # Disable preambles
|
|
259
|
+
tmux-team config set preambleEvery 5 # Inject preamble every 5 messages
|
|
260
|
+
tmux-team config clear <key> # Clear a config value
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### preamble Command
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
tmux-team preamble show <agent> # Show agent's preamble
|
|
267
|
+
tmux-team preamble set <agent> "..." # Set agent's preamble
|
|
268
|
+
tmux-team preamble clear <agent> # Clear agent's preamble
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
239
273
|
*Built for developers who live in the terminal and want their AIs to do the same.*
|
|
240
274
|
|
|
275
|
+
---
|
|
276
|
+
|
|
241
277
|
## License
|
|
242
278
|
|
|
243
279
|
MIT
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -17,7 +17,6 @@ import { cmdRemove } from './commands/remove.js';
|
|
|
17
17
|
import { cmdTalk } from './commands/talk.js';
|
|
18
18
|
import { cmdCheck } from './commands/check.js';
|
|
19
19
|
import { cmdCompletion } from './commands/completion.js';
|
|
20
|
-
import { cmdPm } from './pm/commands.js';
|
|
21
20
|
import { cmdConfig } from './commands/config.js';
|
|
22
21
|
import { cmdPreamble } from './commands/preamble.js';
|
|
23
22
|
|
|
@@ -203,10 +202,6 @@ function main(): void {
|
|
|
203
202
|
cmdCheck(ctx, args[0], args[1] ? parseInt(args[1], 10) : undefined);
|
|
204
203
|
break;
|
|
205
204
|
|
|
206
|
-
case 'pm':
|
|
207
|
-
await cmdPm(ctx, args);
|
|
208
|
-
break;
|
|
209
|
-
|
|
210
205
|
case 'config':
|
|
211
206
|
cmdConfig(ctx, args);
|
|
212
207
|
break;
|
package/src/commands/add.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// ─────────────────────────────────────────────────────────────
|
|
4
4
|
|
|
5
5
|
import fs from 'fs';
|
|
6
|
-
import type { Context } from '../types.js';
|
|
6
|
+
import type { Context, PaneEntry } from '../types.js';
|
|
7
7
|
import { ExitCodes } from '../exits.js';
|
|
8
|
-
import {
|
|
8
|
+
import { loadLocalConfigFile, saveLocalConfigFile } from '../config.js';
|
|
9
9
|
|
|
10
10
|
export function cmdAdd(ctx: Context, name: string, pane: string, remark?: string): void {
|
|
11
11
|
const { ui, config, paths, flags, exit } = ctx;
|
|
@@ -23,12 +23,16 @@ export function cmdAdd(ctx: Context, name: string, pane: string, remark?: string
|
|
|
23
23
|
exit(ExitCodes.ERROR);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
config
|
|
26
|
+
// Load existing config to preserve all fields (preamble, deny, etc.)
|
|
27
|
+
const localConfig = loadLocalConfigFile(paths);
|
|
28
|
+
|
|
29
|
+
const newEntry: PaneEntry = { pane };
|
|
27
30
|
if (remark) {
|
|
28
|
-
|
|
31
|
+
newEntry.remark = remark;
|
|
29
32
|
}
|
|
33
|
+
localConfig[name] = newEntry;
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
saveLocalConfigFile(paths, localConfig);
|
|
32
36
|
|
|
33
37
|
if (flags.json) {
|
|
34
38
|
ui.json({ added: name, pane, remark });
|
package/src/commands/config.ts
CHANGED
|
@@ -13,11 +13,15 @@ import {
|
|
|
13
13
|
clearLocalSettings,
|
|
14
14
|
} from '../config.js';
|
|
15
15
|
|
|
16
|
-
type
|
|
16
|
+
type EnumConfigKey = 'mode' | 'preambleMode';
|
|
17
|
+
type NumericConfigKey = 'preambleEvery';
|
|
18
|
+
type ConfigKey = EnumConfigKey | NumericConfigKey;
|
|
17
19
|
|
|
18
|
-
const
|
|
20
|
+
const ENUM_KEYS: EnumConfigKey[] = ['mode', 'preambleMode'];
|
|
21
|
+
const NUMERIC_KEYS: NumericConfigKey[] = ['preambleEvery'];
|
|
22
|
+
const VALID_KEYS: ConfigKey[] = [...ENUM_KEYS, ...NUMERIC_KEYS];
|
|
19
23
|
|
|
20
|
-
const VALID_VALUES: Record<
|
|
24
|
+
const VALID_VALUES: Record<EnumConfigKey, string[]> = {
|
|
21
25
|
mode: ['polling', 'wait'],
|
|
22
26
|
preambleMode: ['always', 'disabled'],
|
|
23
27
|
};
|
|
@@ -26,7 +30,15 @@ function isValidKey(key: string): key is ConfigKey {
|
|
|
26
30
|
return VALID_KEYS.includes(key as ConfigKey);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
function
|
|
33
|
+
function isEnumKey(key: ConfigKey): key is EnumConfigKey {
|
|
34
|
+
return ENUM_KEYS.includes(key as EnumConfigKey);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isNumericKey(key: ConfigKey): key is NumericConfigKey {
|
|
38
|
+
return NUMERIC_KEYS.includes(key as NumericConfigKey);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isValidValue(key: EnumConfigKey, value: string): boolean {
|
|
30
42
|
return VALID_VALUES[key].includes(value);
|
|
31
43
|
}
|
|
32
44
|
|
|
@@ -43,6 +55,7 @@ function showConfig(ctx: Context): void {
|
|
|
43
55
|
resolved: {
|
|
44
56
|
mode: ctx.config.mode,
|
|
45
57
|
preambleMode: ctx.config.preambleMode,
|
|
58
|
+
preambleEvery: ctx.config.defaults.preambleEvery,
|
|
46
59
|
defaults: ctx.config.defaults,
|
|
47
60
|
},
|
|
48
61
|
sources: {
|
|
@@ -52,6 +65,12 @@ function showConfig(ctx: Context): void {
|
|
|
52
65
|
: globalConfig.preambleMode
|
|
53
66
|
? 'global'
|
|
54
67
|
: 'default',
|
|
68
|
+
preambleEvery:
|
|
69
|
+
localSettings?.preambleEvery !== undefined
|
|
70
|
+
? 'local'
|
|
71
|
+
: globalConfig.defaults?.preambleEvery !== undefined
|
|
72
|
+
? 'global'
|
|
73
|
+
: 'default',
|
|
55
74
|
},
|
|
56
75
|
paths: {
|
|
57
76
|
global: ctx.paths.globalConfig,
|
|
@@ -68,6 +87,12 @@ function showConfig(ctx: Context): void {
|
|
|
68
87
|
: globalConfig.preambleMode
|
|
69
88
|
? '(global)'
|
|
70
89
|
: '(default)';
|
|
90
|
+
const preambleEverySource =
|
|
91
|
+
localSettings?.preambleEvery !== undefined
|
|
92
|
+
? '(local)'
|
|
93
|
+
: globalConfig.defaults?.preambleEvery !== undefined
|
|
94
|
+
? '(global)'
|
|
95
|
+
: '(default)';
|
|
71
96
|
|
|
72
97
|
ctx.ui.info('Current configuration:\n');
|
|
73
98
|
ctx.ui.table(
|
|
@@ -75,6 +100,7 @@ function showConfig(ctx: Context): void {
|
|
|
75
100
|
[
|
|
76
101
|
['mode', ctx.config.mode, modeSource],
|
|
77
102
|
['preambleMode', ctx.config.preambleMode, preambleSource],
|
|
103
|
+
['preambleEvery', String(ctx.config.defaults.preambleEvery), preambleEverySource],
|
|
78
104
|
['defaults.timeout', String(ctx.config.defaults.timeout), '(global)'],
|
|
79
105
|
['defaults.pollInterval', String(ctx.config.defaults.pollInterval), '(global)'],
|
|
80
106
|
['defaults.captureLines', String(ctx.config.defaults.captureLines), '(global)'],
|
|
@@ -95,11 +121,25 @@ function setConfig(ctx: Context, key: string, value: string, global: boolean): v
|
|
|
95
121
|
ctx.exit(ExitCodes.ERROR);
|
|
96
122
|
}
|
|
97
123
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
const validKey = key as ConfigKey;
|
|
125
|
+
|
|
126
|
+
// Validate enum keys
|
|
127
|
+
if (isEnumKey(validKey)) {
|
|
128
|
+
if (!isValidValue(validKey, value)) {
|
|
129
|
+
ctx.ui.error(
|
|
130
|
+
`Invalid value for ${key}: ${value}. Valid values: ${VALID_VALUES[validKey].join(', ')}`
|
|
131
|
+
);
|
|
132
|
+
ctx.exit(ExitCodes.ERROR);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Validate numeric keys
|
|
137
|
+
if (isNumericKey(validKey)) {
|
|
138
|
+
const numValue = parseInt(value, 10);
|
|
139
|
+
if (isNaN(numValue) || numValue < 0) {
|
|
140
|
+
ctx.ui.error(`Invalid value for ${key}: ${value}. Must be a non-negative integer.`);
|
|
141
|
+
ctx.exit(ExitCodes.ERROR);
|
|
142
|
+
}
|
|
103
143
|
}
|
|
104
144
|
|
|
105
145
|
if (global) {
|
|
@@ -109,6 +149,17 @@ function setConfig(ctx: Context, key: string, value: string, global: boolean): v
|
|
|
109
149
|
globalConfig.mode = value as 'polling' | 'wait';
|
|
110
150
|
} else if (key === 'preambleMode') {
|
|
111
151
|
globalConfig.preambleMode = value as 'always' | 'disabled';
|
|
152
|
+
} else if (key === 'preambleEvery') {
|
|
153
|
+
if (!globalConfig.defaults) {
|
|
154
|
+
globalConfig.defaults = {
|
|
155
|
+
timeout: 180,
|
|
156
|
+
pollInterval: 1,
|
|
157
|
+
captureLines: 100,
|
|
158
|
+
preambleEvery: parseInt(value, 10),
|
|
159
|
+
};
|
|
160
|
+
} else {
|
|
161
|
+
globalConfig.defaults.preambleEvery = parseInt(value, 10);
|
|
162
|
+
}
|
|
112
163
|
}
|
|
113
164
|
saveGlobalConfig(ctx.paths, globalConfig);
|
|
114
165
|
ctx.ui.success(`Set ${key}=${value} in global config`);
|
|
@@ -118,6 +169,8 @@ function setConfig(ctx: Context, key: string, value: string, global: boolean): v
|
|
|
118
169
|
updateLocalSettings(ctx.paths, { mode: value as 'polling' | 'wait' });
|
|
119
170
|
} else if (key === 'preambleMode') {
|
|
120
171
|
updateLocalSettings(ctx.paths, { preambleMode: value as 'always' | 'disabled' });
|
|
172
|
+
} else if (key === 'preambleEvery') {
|
|
173
|
+
updateLocalSettings(ctx.paths, { preambleEvery: parseInt(value, 10) });
|
|
121
174
|
}
|
|
122
175
|
ctx.ui.success(`Set ${key}=${value} in local config (repo override)`);
|
|
123
176
|
}
|
package/src/commands/help.ts
CHANGED
|
@@ -42,7 +42,6 @@ ${colors.yellow('COMMANDS')}
|
|
|
42
42
|
${colors.green('init')} Create empty tmux-team.json
|
|
43
43
|
${colors.green('config')} [show|set|clear] View/modify settings
|
|
44
44
|
${colors.green('preamble')} [show|set|clear] Manage agent preambles
|
|
45
|
-
${colors.green('pm')} <subcommand> Project management (run 'pm help')
|
|
46
45
|
${colors.green('completion')} Output shell completion script
|
|
47
46
|
${colors.green('help')} Show this help message
|
|
48
47
|
|
|
@@ -78,6 +77,7 @@ ${colors.yellow('CONFIG')}
|
|
|
78
77
|
${colors.yellow('CHANGE MODE')}
|
|
79
78
|
tmux-team config set mode wait ${colors.dim('Enable wait mode (local)')}
|
|
80
79
|
tmux-team config set mode polling ${colors.dim('Enable polling mode (local)')}
|
|
81
|
-
tmux-team config set
|
|
80
|
+
tmux-team config set preambleMode disabled ${colors.dim('Disable preambles (local)')}
|
|
81
|
+
tmux-team config set preambleEvery 5 ${colors.dim('Inject preamble every 5 messages')}
|
|
82
82
|
`);
|
|
83
83
|
}
|
package/src/commands/preamble.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────────────────────
|
|
2
|
-
// Preamble command - manage agent preambles
|
|
2
|
+
// Preamble command - manage agent preambles (local config only)
|
|
3
3
|
// ─────────────────────────────────────────────────────────────
|
|
4
4
|
|
|
5
5
|
import type { Context } from '../types.js';
|
|
6
6
|
import { ExitCodes } from '../context.js';
|
|
7
|
-
import {
|
|
7
|
+
import { loadLocalConfigFile, saveLocalConfigFile } from '../config.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Show preamble(s) for agent(s).
|
|
@@ -57,25 +57,27 @@ function showPreamble(ctx: Context, agentName?: string): void {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* Set preamble for an agent.
|
|
60
|
+
* Set preamble for an agent (in local config).
|
|
61
61
|
*/
|
|
62
62
|
function setPreamble(ctx: Context, agentName: string, preamble: string): void {
|
|
63
|
-
const { ui, paths, flags } = ctx;
|
|
64
|
-
|
|
65
|
-
const globalConfig = loadGlobalConfig(paths);
|
|
63
|
+
const { ui, paths, flags, config } = ctx;
|
|
66
64
|
|
|
67
|
-
//
|
|
68
|
-
if (!
|
|
69
|
-
|
|
65
|
+
// Check if agent exists in pane registry
|
|
66
|
+
if (!config.paneRegistry[agentName]) {
|
|
67
|
+
ui.error(`Agent '${agentName}' not found in local config`);
|
|
68
|
+
ui.error('Add the agent with: tmux-team add <agent> <pane>');
|
|
69
|
+
ctx.exit(ExitCodes.ERROR);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
const localConfig = loadLocalConfigFile(paths);
|
|
73
|
+
|
|
74
|
+
// Update preamble in local config
|
|
75
|
+
const agentEntry = localConfig[agentName] as { pane: string; preamble?: string } | undefined;
|
|
76
|
+
if (agentEntry) {
|
|
77
|
+
agentEntry.preamble = preamble;
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
saveGlobalConfig(paths, globalConfig);
|
|
80
|
+
saveLocalConfigFile(paths, localConfig);
|
|
79
81
|
|
|
80
82
|
if (flags.json) {
|
|
81
83
|
ui.json({ agent: agentName, preamble, status: 'set' });
|
|
@@ -85,22 +87,17 @@ function setPreamble(ctx: Context, agentName: string, preamble: string): void {
|
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
/**
|
|
88
|
-
* Clear preamble for an agent.
|
|
90
|
+
* Clear preamble for an agent (in local config).
|
|
89
91
|
*/
|
|
90
92
|
function clearPreamble(ctx: Context, agentName: string): void {
|
|
91
93
|
const { ui, paths, flags } = ctx;
|
|
92
94
|
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
if (globalConfig.agents?.[agentName]?.preamble) {
|
|
96
|
-
delete globalConfig.agents[agentName].preamble;
|
|
97
|
-
|
|
98
|
-
// Clean up empty agent config
|
|
99
|
-
if (Object.keys(globalConfig.agents[agentName]).length === 0) {
|
|
100
|
-
delete globalConfig.agents[agentName];
|
|
101
|
-
}
|
|
95
|
+
const localConfig = loadLocalConfigFile(paths);
|
|
96
|
+
const agentEntry = localConfig[agentName] as { pane?: string; preamble?: string } | undefined;
|
|
102
97
|
|
|
103
|
-
|
|
98
|
+
if (agentEntry?.preamble) {
|
|
99
|
+
delete agentEntry.preamble;
|
|
100
|
+
saveLocalConfigFile(paths, localConfig);
|
|
104
101
|
|
|
105
102
|
if (flags.json) {
|
|
106
103
|
ui.json({ agent: agentName, status: 'cleared' });
|
package/src/commands/remove.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type { Context } from '../types.js';
|
|
6
6
|
import { ExitCodes } from '../exits.js';
|
|
7
|
-
import {
|
|
7
|
+
import { loadLocalConfigFile, saveLocalConfigFile } from '../config.js';
|
|
8
8
|
|
|
9
9
|
export function cmdRemove(ctx: Context, name: string): void {
|
|
10
10
|
const { ui, config, paths, flags, exit } = ctx;
|
|
@@ -14,8 +14,10 @@ export function cmdRemove(ctx: Context, name: string): void {
|
|
|
14
14
|
exit(ExitCodes.PANE_NOT_FOUND);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// Load existing config to preserve other agents' fields (preamble, deny, etc.)
|
|
18
|
+
const localConfig = loadLocalConfigFile(paths);
|
|
19
|
+
delete localConfig[name];
|
|
20
|
+
saveLocalConfigFile(paths, localConfig);
|
|
19
21
|
|
|
20
22
|
if (flags.json) {
|
|
21
23
|
ui.json({ removed: name });
|