tmux-team 3.0.0 β†’ 3.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 CHANGED
@@ -1,298 +1,106 @@
1
- # πŸ€– tmux-team
1
+ # tmux-team
2
2
 
3
- **The lightweight coordination layer for terminal-based AI agents.**
3
+ Coordinate AI agents (Claude, Codex, Gemini) running in tmux panes. Send messages, wait for responses, broadcast to all.
4
4
 
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
-
7
- ---
8
-
9
- ## πŸ›‘ The Problem
10
-
11
- As we move from "Chat with an AI" to "Orchestrating a Team," we face major friction points:
12
-
13
- 1. **Isolation** β€” Agents in different panes (Claude, Gemini, local LLMs) have no way to talk to each other
14
- 2. **Synchronization** β€” Humans are stuck in a "manual polling" loopβ€”waiting for an agent to finish before copying its output to the next pane
15
- 3. **Tool Restrictions** β€” AI agents operate under tool whitelists; using `sleep` or arbitrary shell commands is dangerous or blocked
16
- 4. **Token Waste** β€” Repeated polling instructions burn context tokens unnecessarily
17
-
18
- ---
19
-
20
- ## πŸš€ Our Niche: The Universal Transport Layer
21
-
22
- Unlike heavyweight frameworks that require specific SDKs or cloud infrastructure, tmux-team treats the **terminal pane as the universal interface**.
23
-
24
- - **Model Agnostic** β€” Works with Claude Code, Gemini CLI, Codex, Aider, or any CLI tool
25
- - **Zero Infrastructure** β€” No servers, no MCP setup, no complex configuration. If it runs in tmux, tmux-team can talk to it
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 `~/.config/tmux-team/`
28
-
29
- ---
30
-
31
- ## 🧠 Design Philosophy
32
-
33
- > *These principles guide our design decisions.*
34
-
35
- ### 1. Deterministic Transport (`--delay` vs. `sleep`)
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.
38
-
39
- **The Why**: Internal delay keeps the workflow as a single tool call. No shell dependency, no policy friction.
40
-
41
- ### 2. Stateless Handshakes (The "Nonce" Strategy)
42
-
43
- **The Problem**: Terminal panes are streams, not RPC channels. A simple `[DONE]` string could already be in scrollback.
44
-
45
- **The Why**: We use a unique **Nonce** for every request: `{tmux-team-end:8f3a}`.
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
- ### 3. Context Injection (Preambles)
51
-
52
- **The Problem**: AI agents are prone to "instruction drift." Over a long session, they might forget constraints.
53
-
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.
55
-
56
- ---
57
-
58
- ## πŸ“¦ Installation
5
+ ## Install
59
6
 
60
7
  ```bash
61
8
  npm install -g tmux-team
62
9
  ```
63
10
 
64
- **Requirements:** Node.js >= 18, tmux, macOS/Linux
65
-
66
- ### Shell Completion
67
-
68
- ```bash
69
- # Zsh (add to ~/.zshrc)
70
- eval "$(tmux-team completion zsh)"
71
-
72
- # Bash (add to ~/.bashrc)
73
- eval "$(tmux-team completion bash)"
74
- ```
75
-
76
- ### Claude Code Plugin
77
-
78
- ```
79
- /plugin marketplace add wkh237/tmux-team
80
- /plugin install tmux-team@tmux-team
81
- ```
11
+ **Requirements:** Node.js >= 18, tmux
82
12
 
83
- ### Agent Skills (Optional)
13
+ **Alias:** `tmt` (shorthand for `tmux-team`)
84
14
 
85
- Install tmux-team as a native skill for your AI coding agent:
15
+ ## Quick Start
86
16
 
87
17
  ```bash
88
- # Install for Claude Code (user-wide)
89
- tmux-team install-skill claude
90
-
91
- # Install for OpenAI Codex (user-wide)
92
- tmux-team install-skill codex
93
-
94
- # Install to project directory instead
95
- tmux-team install-skill claude --local
96
- tmux-team install-skill codex --local
97
- ```
98
-
99
- See [skills/README.md](./skills/README.md) for detailed instructions.
100
-
101
- ---
102
-
103
- ## ⌨️ Quick Start
104
-
105
- ```bash
106
- # Initialize config
107
- tmux-team init
108
-
109
- # Register your agents (name + tmux pane ID)
110
- tmux-team add claude 10.0 "Frontend specialist"
111
- tmux-team add codex 10.1 "Backend engineer"
112
- tmux-team add gemini 10.2 "Code reviewer"
113
-
114
- # Send messages
115
- tmux-team talk codex "Review the auth module and suggest improvements"
116
- tmux-team talk all "Starting the refactor now"
18
+ # 1. Install for your AI agent
19
+ tmux-team install claude # or: tmux-team install codex
117
20
 
118
- # Read responses
119
- tmux-team check codex
120
- tmux-team check codex 200 # More lines if needed
21
+ # 2. Run the setup wizard (auto-detects panes)
22
+ tmux-team setup
121
23
 
122
- # Manage agents
123
- tmux-team list
124
- tmux-team update codex --remark "Now handling tests"
125
- tmux-team remove gemini
126
- ```
127
-
128
- ### From Claude Code
129
-
130
- Once the plugin is installed, coordinate directly from your Claude Code session:
131
-
132
- ```
133
- /tmux-team:team codex "Can you review my changes?"
134
- /tmux-team:team all "I'm starting the database migration"
24
+ # 3. Talk to agents
25
+ tmux-team talk codex "Review this code" --wait
135
26
  ```
136
27
 
137
- ---
28
+ The `--wait` flag blocks until the agent responds, returning the response directly.
138
29
 
139
- ## πŸ“‹ Commands
30
+ ## Commands
140
31
 
141
32
  | Command | Description |
142
33
  |---------|-------------|
143
- | `talk <agent> "<msg>"` | Send message to agent (or `all` for broadcast) |
144
- | `talk ... --delay 5` | Wait 5 seconds before sending |
145
- | `talk ... --wait` | Wait for agent response (nonce-based) |
146
- | `check <agent> [lines]` | Read agent's terminal output (default: 100 lines) |
147
- | `list` | Show all configured agents |
148
- | `add <name> <pane> [remark]` | Register a new agent |
149
- | `update <name> --pane/--remark` | Update agent configuration |
150
- | `remove <name>` | Unregister an agent |
151
- | `init` | Create `tmux-team.json` in current directory |
152
- | `config [show/set/clear]` | View/modify settings |
153
- | `preamble [show/set/clear]` | Manage agent preambles |
154
- | `install-skill <agent>` | Install skill for Claude/Codex (--local/--user) |
155
- | `completion [zsh\|bash]` | Output shell completion script |
34
+ | `install [claude\|codex]` | Install tmux-team for an AI agent |
35
+ | `setup` | Interactive wizard to configure agents |
36
+ | `talk <agent> "msg" --wait` | Send message and wait for response |
37
+ | `talk all "msg" --wait` | Broadcast to all agents |
38
+ | `check <agent> [lines]` | Read agent's pane output (fallback if --wait times out) |
39
+ | `list` | Show configured agents |
40
+ | `learn` | Show educational guide |
156
41
 
157
- ---
42
+ **Options for `talk --wait`:**
43
+ - `--timeout <seconds>` - Max wait time (default: 180s)
44
+ - `--lines <number>` - Lines to capture from response (default: 100)
158
45
 
159
- ## βš™οΈ Configuration
46
+ Run `tmux-team help` for all commands and options.
160
47
 
161
- ### Local Config (`./tmux-team.json`)
48
+ ## Managing Your Team
162
49
 
163
- Per-project agent registry with optional preambles:
50
+ Configuration lives in `tmux-team.json` in your project root.
164
51
 
165
- ```json
166
- {
167
- "claude": {
168
- "pane": "10.0",
169
- "remark": "Frontend specialist",
170
- "preamble": "Focus on UI components. Ask for review before merging."
171
- },
172
- "codex": {
173
- "pane": "10.1",
174
- "remark": "Code reviewer",
175
- "preamble": "You are the code quality guard. Review changes thoroughly."
176
- }
177
- }
52
+ **Create** - Run the setup wizard to auto-detect agents:
53
+ ```bash
54
+ tmux-team setup
178
55
  ```
179
56
 
180
- | Field | Description |
181
- |-------|-------------|
182
- | `pane` | tmux pane ID (required) |
183
- | `remark` | Description shown in `list` |
184
- | `preamble` | Hidden instructions prepended to every message |
185
-
186
- ### Global Config (`~/.config/tmux-team/config.json`)
187
-
188
- Global settings that apply to all projects:
57
+ **Read** - List configured agents:
58
+ ```bash
59
+ tmux-team list
60
+ ```
189
61
 
62
+ **Update** - Edit `tmux-team.json` directly or re-run setup:
190
63
  ```json
191
64
  {
192
- "mode": "polling",
193
- "preambleMode": "always",
194
- "defaults": {
195
- "timeout": 180,
196
- "pollInterval": 1,
197
- "captureLines": 100,
198
- "preambleEvery": 3
199
- }
65
+ "codex": { "pane": "%1", "remark": "Code reviewer" },
66
+ "gemini": { "pane": "%2", "remark": "Documentation" }
200
67
  }
201
68
  ```
202
69
 
203
- | Field | Description |
204
- |-------|-------------|
205
- | `mode` | Default mode: `polling` (manual check) or `wait` (auto-wait) |
206
- | `preambleMode` | `always` (inject preambles) or `disabled` |
207
- | `defaults.timeout` | Default --wait timeout in seconds |
208
- | `defaults.pollInterval` | Polling interval in seconds |
209
- | `defaults.captureLines` | Default lines for `check` command |
210
- | `defaults.preambleEvery` | Inject preamble every N messages (default: 3) |
211
-
212
- ---
70
+ **Delete** - Remove an agent entry from `tmux-team.json` or delete the file entirely.
213
71
 
214
- ## ✨ Features
72
+ Find pane IDs: `tmux display-message -p "#{pane_id}"`
215
73
 
216
- ### πŸ“‘ Enhanced `talk` Command
74
+ ## Claude Code Plugin
217
75
 
218
- ```bash
219
- # Delay before sending (safe alternative to sleep)
220
- tmux-team talk codex "message" --delay 5
221
-
222
- # Wait for response with nonce-based completion detection
223
- tmux-team talk codex "message" --wait --timeout 60
224
76
  ```
225
-
226
- ### πŸ“œ Agent Preambles
227
-
228
- Inject hidden instructions into every message via local `tmux-team.json`:
229
-
230
- ```json
231
- {
232
- "gemini": {
233
- "pane": "10.2",
234
- "preamble": "Always explain your reasoning. Do not edit files directly."
235
- }
236
- }
77
+ /plugin marketplace add wkh237/tmux-team
78
+ /plugin install tmux-team
237
79
  ```
238
80
 
239
- Use the CLI to manage preambles:
81
+ Gives you two slash commands:
240
82
 
241
- ```bash
242
- tmux-team preamble show gemini # View current preamble
243
- tmux-team preamble set gemini "Be concise" # Set preamble
244
- tmux-team preamble clear gemini # Remove preamble
83
+ **`/learn`** - Teach Claude how to use tmux-team
245
84
  ```
85
+ /learn
86
+ ```
87
+ Run this once when starting a session. Claude will understand how to coordinate with other agents.
246
88
 
247
- ---
248
-
249
- ## 🚫 Non-Goals
250
-
251
- tmux-team intentionally stays lightweight:
252
-
253
- - **Not an orchestrator** β€” No automatic agent selection or routing
254
- - **Not a session manager** β€” Doesn't create/manage tmux sessions
255
- - **Not an LLM wrapper** β€” Doesn't process or route messages through AI
256
-
257
- It's the plumbing layer that lets humans and AI agents coordinate via tmux, nothing more.
258
-
259
- ---
260
-
261
- ## πŸ“– Command Reference
262
-
263
- ### talk Options
264
-
265
- | Option | Description |
266
- |--------|-------------|
267
- | `--delay <seconds>` | Wait before sending (whitelist-friendly alternative to `sleep`) |
268
- | `--wait` | Block until agent responds (nonce-based completion detection) |
269
- | `--timeout <seconds>` | Max wait time (default: 180s) |
270
- | `--no-preamble` | Skip agent preamble for this message |
271
-
272
- ### config Command
273
-
274
- ```bash
275
- tmux-team config show # Show current config
276
- tmux-team config set mode wait # Enable wait mode
277
- tmux-team config set preambleMode disabled # Disable preambles
278
- tmux-team config set preambleEvery 5 # Inject preamble every 5 messages
279
- tmux-team config clear <key> # Clear a config value
89
+ **`/team`** - Talk to other agents
90
+ ```
91
+ /team talk codex "Review my authentication changes" --wait
92
+ /team talk all "I'm starting the database migration" --wait
93
+ /team list
280
94
  ```
95
+ Use this to delegate tasks, ask for reviews, or broadcast updates.
281
96
 
282
- ### preamble Command
97
+ ## Learn More
283
98
 
284
99
  ```bash
285
- tmux-team preamble show <agent> # Show agent's preamble
286
- tmux-team preamble set <agent> "text" # Set agent's preamble
287
- tmux-team preamble clear <agent> # Clear agent's preamble
100
+ tmux-team learn # Comprehensive guide
101
+ tmux-team help # All commands and options
288
102
  ```
289
103
 
290
- ---
291
-
292
- *Built for developers who live in the terminal and want their AIs to do the same.*
293
-
294
- ---
295
-
296
104
  ## License
297
105
 
298
106
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmux-team",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "CLI tool for AI agent collaboration in tmux - manage cross-pane communication",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,8 +10,9 @@
10
10
  "scripts": {
11
11
  "dev": "tsx src/cli.ts",
12
12
  "tmt": "./bin/tmux-team",
13
- "test": "vitest",
14
- "test:run": "vitest run",
13
+ "test": "npm run test:run",
14
+ "test:watch": "vitest",
15
+ "test:run": "vitest run --coverage && node scripts/check-coverage.mjs --threshold 95",
15
16
  "lint": "oxlint src/",
16
17
  "lint:fix": "oxlint src/ --fix",
17
18
  "format": "prettier --write src/",
@@ -50,6 +51,7 @@
50
51
  },
51
52
  "devDependencies": {
52
53
  "@types/node": "^25.0.3",
54
+ "@vitest/coverage-v8": "^1.6.1",
53
55
  "oxlint": "^1.34.0",
54
56
  "prettier": "^3.7.4",
55
57
  "typescript": "^5.3.0",
package/skills/README.md CHANGED
@@ -16,22 +16,21 @@ The easiest way to add tmux-team to Claude Code is via the plugin system:
16
16
 
17
17
  This gives you `/team` and `/learn` slash commands automatically.
18
18
 
19
- ## Quick Install (Standalone Skills)
19
+ ## Quick Install
20
20
 
21
- If you prefer standalone skills without the full plugin:
21
+ Use the interactive install command:
22
22
 
23
23
  ```bash
24
- # Install for Claude Code (user-wide)
25
- tmux-team install-skill claude
24
+ # Auto-detect environment and install
25
+ tmux-team install
26
26
 
27
- # Install for OpenAI Codex (user-wide)
28
- tmux-team install-skill codex
29
-
30
- # Install to project directory (local scope)
31
- tmux-team install-skill claude --local
32
- tmux-team install-skill codex --local
27
+ # Or specify agent directly
28
+ tmux-team install claude
29
+ tmux-team install codex
33
30
  ```
34
31
 
32
+ After installation, run `tmux-team setup` to configure your agents interactively.
33
+
35
34
  ## Claude Code
36
35
 
37
36
  Claude Code uses slash commands stored in `~/.claude/commands/` (user) or `.claude/commands/` (local).
@@ -0,0 +1,163 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import type { Context } from './types.js';
3
+
4
+ function makeStubContext(): Context {
5
+ return {
6
+ argv: [],
7
+ flags: { json: false, verbose: false },
8
+ ui: {
9
+ info: vi.fn(),
10
+ success: vi.fn(),
11
+ warn: vi.fn(),
12
+ error: vi.fn(),
13
+ table: vi.fn(),
14
+ json: vi.fn(),
15
+ },
16
+ config: {
17
+ mode: 'polling',
18
+ preambleMode: 'always',
19
+ defaults: { timeout: 180, pollInterval: 1, captureLines: 100, preambleEvery: 3 },
20
+ agents: {},
21
+ paneRegistry: {},
22
+ },
23
+ tmux: {
24
+ send: vi.fn(),
25
+ capture: vi.fn(),
26
+ listPanes: vi.fn(() => []),
27
+ getCurrentPaneId: vi.fn(() => null),
28
+ },
29
+ paths: {
30
+ globalDir: '/g',
31
+ globalConfig: '/g/c.json',
32
+ localConfig: '/p/t.json',
33
+ stateFile: '/g/s.json',
34
+ },
35
+ exit: ((code: number) => {
36
+ const err = new Error(`exit(${code})`);
37
+ (err as Error & { exitCode: number }).exitCode = code;
38
+ throw err;
39
+ }) as any,
40
+ };
41
+ }
42
+
43
+ describe('cli', () => {
44
+ const originalArgv = process.argv;
45
+
46
+ beforeEach(() => {
47
+ vi.restoreAllMocks();
48
+ });
49
+
50
+ afterEach(() => {
51
+ process.argv = originalArgv;
52
+ vi.restoreAllMocks();
53
+ });
54
+
55
+ it('prints completion for bash', async () => {
56
+ vi.resetModules();
57
+ process.argv = ['node', 'cli', 'completion', 'bash'];
58
+
59
+ vi.doMock('./context.js', () => ({
60
+ createContext: () => makeStubContext(),
61
+ ExitCodes: { SUCCESS: 0, ERROR: 1 },
62
+ }));
63
+ vi.doMock('./commands/completion.js', () => ({
64
+ cmdCompletion: (shell?: string) => {
65
+ console.log(`completion:${shell}`);
66
+ },
67
+ }));
68
+
69
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
70
+ const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
71
+
72
+ await import('./cli.js');
73
+ expect(logSpy).toHaveBeenCalledWith('completion:bash');
74
+ expect(exitSpy).toHaveBeenCalledWith(0);
75
+ });
76
+
77
+ it('errors on invalid time format', async () => {
78
+ vi.resetModules();
79
+ process.argv = ['node', 'cli', 'talk', 'codex', 'hi', '--delay', 'abc'];
80
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation(((code?: number) => {
81
+ throw new Error(`exit(${code})`);
82
+ }) as any);
83
+ const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
84
+
85
+ await expect(import('./cli.js')).rejects.toThrow('exit(1)');
86
+ expect(errSpy).toHaveBeenCalled();
87
+ expect(exitSpy).toHaveBeenCalledWith(1);
88
+ });
89
+
90
+ it('routes unknown command to ctx.ui.error and exits', async () => {
91
+ vi.resetModules();
92
+ process.argv = ['node', 'cli', 'nope'];
93
+
94
+ const ctx = makeStubContext();
95
+ vi.doMock('./context.js', () => ({
96
+ createContext: () => ctx,
97
+ ExitCodes: { SUCCESS: 0, ERROR: 1 },
98
+ }));
99
+
100
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
101
+ await import('./cli.js');
102
+ expect(ctx.ui.error).toHaveBeenCalled();
103
+ expect(exitSpy).toHaveBeenCalledWith(1);
104
+ });
105
+
106
+ it('handles --version by printing VERSION', async () => {
107
+ vi.resetModules();
108
+ process.argv = ['node', 'cli', '--version'];
109
+ const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
110
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
111
+
112
+ await import('./cli.js');
113
+ // allow the dynamic import to resolve
114
+ await new Promise((r) => setTimeout(r, 0));
115
+ expect(logSpy).toHaveBeenCalled();
116
+ expect(exitSpy).not.toHaveBeenCalled();
117
+ });
118
+
119
+ it('routes learn command and does not exit', async () => {
120
+ vi.resetModules();
121
+ process.argv = ['node', 'cli', 'learn'];
122
+
123
+ const ctx = makeStubContext();
124
+ const learnSpy = vi.fn();
125
+ vi.doMock('./context.js', () => ({
126
+ createContext: () => ctx,
127
+ ExitCodes: { SUCCESS: 0, ERROR: 1 },
128
+ }));
129
+ vi.doMock('./commands/learn.js', () => ({ cmdLearn: learnSpy }));
130
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
131
+
132
+ await import('./cli.js');
133
+ expect(learnSpy).toHaveBeenCalled();
134
+ expect(exitSpy).not.toHaveBeenCalled();
135
+ });
136
+
137
+ it('prints JSON error when --json and a command throws', async () => {
138
+ vi.resetModules();
139
+ process.argv = ['node', 'cli', 'remove', 'some-agent', '--json']; // will throw in our mocked cmdRemove
140
+
141
+ const ctx = makeStubContext();
142
+ ctx.flags.json = true;
143
+ vi.doMock('./context.js', () => ({
144
+ createContext: () => ctx,
145
+ ExitCodes: { SUCCESS: 0, ERROR: 1 },
146
+ }));
147
+ vi.doMock('./commands/remove.js', () => ({
148
+ cmdRemove: () => {
149
+ throw new Error('boom');
150
+ },
151
+ }));
152
+
153
+ const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
154
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
155
+
156
+ await import('./cli.js');
157
+ // allow the run().catch handler to run
158
+ await new Promise((r) => setTimeout(r, 0));
159
+
160
+ expect(errSpy).toHaveBeenCalledWith(JSON.stringify({ error: 'boom' }));
161
+ expect(exitSpy).toHaveBeenCalledWith(1);
162
+ });
163
+ });
package/src/cli.ts CHANGED
@@ -19,7 +19,9 @@ import { cmdCheck } from './commands/check.js';
19
19
  import { cmdCompletion } from './commands/completion.js';
20
20
  import { cmdConfig } from './commands/config.js';
21
21
  import { cmdPreamble } from './commands/preamble.js';
22
- import { cmdInstallSkill } from './commands/install-skill.js';
22
+ import { cmdInstall } from './commands/install.js';
23
+ import { cmdSetup } from './commands/setup.js';
24
+ import { cmdLearn } from './commands/learn.js';
23
25
 
24
26
  // ─────────────────────────────────────────────────────────────
25
27
  // Argument parsing
@@ -41,6 +43,8 @@ function parseArgs(argv: string[]): { command: string; args: string[]; flags: Fl
41
43
  flags.json = true;
42
44
  } else if (arg === '--verbose' || arg === '-v') {
43
45
  flags.verbose = true;
46
+ } else if (arg === '--debug') {
47
+ flags.debug = true;
44
48
  } else if (arg === '--force' || arg === '-f') {
45
49
  flags.force = true;
46
50
  } else if (arg === '--config') {
@@ -51,6 +55,8 @@ function parseArgs(argv: string[]): { command: string; args: string[]; flags: Fl
51
55
  flags.wait = true;
52
56
  } else if (arg === '--timeout') {
53
57
  flags.timeout = parseTime(argv[++i]);
58
+ } else if (arg === '--lines') {
59
+ flags.lines = parseInt(argv[++i], 10) || 100;
54
60
  } else if (arg === '--no-preamble') {
55
61
  flags.noPreamble = true;
56
62
  } else if (arg.startsWith('--pane=')) {
@@ -106,19 +112,23 @@ function main(): void {
106
112
 
107
113
  // Help - load config to show current mode/timeout
108
114
  if (!command || command === 'help' || command === '--help' || command === '-h') {
115
+ // Show intro highlight when running just `tmux-team` with no args
116
+ const showIntro = !command || argv.length === 0;
109
117
  try {
110
118
  const paths = resolvePaths();
111
119
  const config = loadConfig(paths);
112
120
  const helpConfig: HelpConfig = {
113
121
  mode: config.mode,
114
122
  timeout: config.defaults.timeout,
123
+ showIntro,
115
124
  };
116
125
  cmdHelp(helpConfig);
117
126
  } catch {
118
127
  // Fallback if config can't be loaded
119
- cmdHelp();
128
+ cmdHelp({ showIntro });
120
129
  }
121
130
  process.exit(ExitCodes.SUCCESS);
131
+ return;
122
132
  }
123
133
 
124
134
  if (command === '--version' || command === '-V') {
@@ -130,11 +140,18 @@ function main(): void {
130
140
  if (command === 'completion') {
131
141
  cmdCompletion(args[0]);
132
142
  process.exit(ExitCodes.SUCCESS);
143
+ return;
133
144
  }
134
145
 
135
146
  // Create context for all other commands
136
147
  const ctx = createContext({ argv, flags });
137
148
 
149
+ // Warn if not in tmux for commands that require it
150
+ const TMUX_REQUIRED_COMMANDS = ['talk', 'send', 'check', 'read', 'setup'];
151
+ if (!process.env.TMUX && TMUX_REQUIRED_COMMANDS.includes(command)) {
152
+ ctx.ui.warn('Not running inside tmux. Some features may not work.');
153
+ }
154
+
138
155
  const run = async (): Promise<void> => {
139
156
  switch (command) {
140
157
  case 'init':
@@ -211,22 +228,16 @@ function main(): void {
211
228
  cmdPreamble(ctx, args);
212
229
  break;
213
230
 
214
- case 'install-skill':
215
- {
216
- // Parse --local or --user flag
217
- let scope = 'user';
218
- const filteredArgs: string[] = [];
219
- for (const arg of args) {
220
- if (arg === '--local') {
221
- scope = 'local';
222
- } else if (arg === '--user') {
223
- scope = 'user';
224
- } else {
225
- filteredArgs.push(arg);
226
- }
227
- }
228
- cmdInstallSkill(ctx, filteredArgs[0], scope);
229
- }
231
+ case 'install':
232
+ await cmdInstall(ctx, args[0]);
233
+ break;
234
+
235
+ case 'setup':
236
+ await cmdSetup(ctx);
237
+ break;
238
+
239
+ case 'learn':
240
+ cmdLearn();
230
241
  break;
231
242
 
232
243
  default: