tanuki-telemetry 1.1.3 → 1.1.5

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/bin/tanuki.mjs CHANGED
@@ -67,6 +67,7 @@ ${w} Usage:${r}
67
67
  ${cy}npx tanuki-telemetry status${r} check if running
68
68
  ${cy}npx tanuki-telemetry setup${r} configure claude code
69
69
  ${cy}npx tanuki-telemetry update${r} rebuild with latest
70
+ ${cy}npx tanuki-telemetry skills${r} install/update skills
70
71
  ${cy}npx tanuki-telemetry version${r} show version
71
72
 
72
73
  ${w} Environment:${r}
@@ -188,6 +189,46 @@ function getMcpEntry() {
188
189
  };
189
190
  }
190
191
 
192
+ function installSkills() {
193
+ const skillsDir = path.resolve(path.dirname(import.meta.url.replace("file://", "")), "../skills");
194
+ const targetDir = path.join(os.homedir(), ".claude", "commands");
195
+
196
+ if (!fs.existsSync(skillsDir)) {
197
+ info("skills directory not found in package");
198
+ return 0;
199
+ }
200
+
201
+ fs.mkdirSync(targetDir, { recursive: true });
202
+
203
+ const skills = fs.readdirSync(skillsDir).filter((f) => f.endsWith(".md"));
204
+ let installed = 0;
205
+
206
+ for (const skill of skills) {
207
+ const src = path.join(skillsDir, skill);
208
+ const dest = path.join(targetDir, skill);
209
+
210
+ if (fs.existsSync(dest)) {
211
+ // Check if ours is newer/different
212
+ const srcContent = fs.readFileSync(src, "utf-8");
213
+ const destContent = fs.readFileSync(dest, "utf-8");
214
+ if (srcContent === destContent) continue;
215
+
216
+ // Back up existing
217
+ fs.copyFileSync(dest, dest + ".bak");
218
+ }
219
+
220
+ fs.copyFileSync(src, dest);
221
+ installed++;
222
+ }
223
+
224
+ if (installed > 0) {
225
+ ok(`${installed} skill${installed > 1 ? "s" : ""} installed to ~/.claude/commands/`);
226
+ } else {
227
+ ok(`${skills.length} skills up to date`);
228
+ }
229
+ return installed;
230
+ }
231
+
191
232
  function autoConfigureClaude() {
192
233
  const claudeConfig = path.join(os.homedir(), ".claude.json");
193
234
 
@@ -235,7 +276,7 @@ if (command === "start") {
235
276
  log(LOGO);
236
277
  log(BANNER);
237
278
 
238
- step(1, 4, "checking requirements");
279
+ step(1, 5, "checking requirements");
239
280
  checkRequirements();
240
281
 
241
282
  fs.mkdirSync(DATA_DIR, { recursive: true });
@@ -254,14 +295,14 @@ if (command === "start") {
254
295
  catch { return false; }
255
296
  })();
256
297
 
257
- step(2, 4, hasImage ? "docker images" : "building docker images");
298
+ step(2, 5, hasImage ? "docker images" : "building docker images");
258
299
  if (!hasImage) {
259
300
  info("first run — this takes ~30s...");
260
301
  buildImages();
261
302
  }
262
303
  ok(hasImage ? "cached" : "images built");
263
304
 
264
- step(3, 4, "starting dashboard");
305
+ step(3, 5, "starting dashboard");
265
306
  startDashboard();
266
307
 
267
308
  let healthy = false;
@@ -274,21 +315,32 @@ if (command === "start") {
274
315
  }
275
316
  ok(healthy ? `running on port ${PORT}` : `starting — check port ${PORT}`);
276
317
 
277
- step(4, 4, "configuring claude code");
318
+ step(4, 5, "configuring claude code");
278
319
  autoConfigureClaude();
279
320
 
321
+ step(5, 5, "installing skills");
322
+ installSkills();
323
+
280
324
  log("");
281
325
  log(` ${d}─────────────────────────────────────────${r}`);
282
326
  log("");
283
- log(` ${w}dashboard${r} ${cy}http://localhost:${PORT}${r}`);
284
- log(` ${w}data${r} ${d}${DATA_DIR}${r}`);
327
+ log(` ${g}${b}ready${r}`);
285
328
  log("");
286
- log(` ${y}if claude code is already open,${r}`);
287
- log(` ${y}restart it to load MCP tools.${r}`);
329
+ log(` ${w}dashboard${r} ${cy}http://localhost:${PORT}${r}`);
330
+ log(` ${w}data${r} ${d}${DATA_DIR}${r}`);
288
331
  log("");
289
- log(` ${d}npx tanuki-telemetry stop${r}`);
290
- log(` ${d}npx tanuki-telemetry status${r}`);
291
- log(` ${d}npx tanuki-telemetry update${r}`);
332
+ log(` ${w}skills installed:${r}`);
333
+ log(` ${g}/start-work${r} ${d}autonomous dev — context to PR${r}`);
334
+ log(` ${g}/coordinate${r} ${d}manage cmux workspaces${r}`);
335
+ log(` ${g}/walkthrough${r} ${d}execute UI test scenarios${r}`);
336
+ log(` ${g}/debug${r} ${d}systematic bug investigation${r}`);
337
+ log(` ${g}/review-code${r} ${d}PR code review${r}`);
338
+ log(` ${g}/sessions${r} ${d}browse past sessions${r}`);
339
+ log(` ${g}/create-path${r} ${d}generate walkthrough scenarios${r}`);
340
+ log(` ${g}/edit-path${r} ${d}update walkthrough scenarios${r}`);
341
+ log(` ${g}/cmux-guide${r} ${d}workspace navigation ref${r}`);
342
+ log("");
343
+ log(` ${y}restart claude code to load MCP tools + skills${r}`);
292
344
  log("");
293
345
  }
294
346
 
@@ -317,6 +369,13 @@ else if (command === "status") {
317
369
  else if (command === "setup") {
318
370
  log(BANNER);
319
371
  autoConfigureClaude();
372
+ installSkills();
373
+ log("");
374
+ }
375
+
376
+ else if (command === "skills") {
377
+ log(BANNER);
378
+ installSkills();
320
379
  log("");
321
380
  }
322
381
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tanuki-telemetry",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Workflow monitor and telemetry dashboard for Claude Code autonomous agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "files": [
11
11
  "bin/",
12
+ "skills/",
12
13
  "src/",
13
14
  "frontend/src/",
14
15
  "frontend/index.html",
@@ -0,0 +1,197 @@
1
+ ---
2
+ description: |
3
+ Reference guide for cmux workspace navigation. Consult this before ANY cmux operation — sending, reading, screenshotting, or creating workspaces.
4
+ allowed-tools: Bash
5
+ ---
6
+
7
+ # cmux Operations Guide
8
+
9
+ ## ⛔ Golden Rules
10
+
11
+ 1. **NEVER omit `--surface`.** Every read/send/send-key MUST target a specific surface.
12
+ 2. **NEVER assume a surface is Claude.** Always discover first.
13
+ 3. **Use full workspace IDs** for newer workspaces (e.g., `workspace:10` not `10`). Shorthand numbers only work reliably for original workspaces.
14
+ 4. **Always resync** after creating or closing workspaces.
15
+
16
+ ---
17
+
18
+ ## Surface Discovery (DO THIS FIRST)
19
+
20
+ Workspaces have multiple surfaces — Claude terminals, browsers, dev servers, idle shells. You must find the Claude surface before doing anything.
21
+
22
+ ```bash
23
+ # Step 1: List surfaces
24
+ cmux list-pane-surfaces --workspace <id>
25
+
26
+ # Step 2: Identify Claude surface
27
+ # Look for: label contains "Claude Code" or "✳ Claude Code"
28
+ # NOT: URLs (browser), "idle", server logs
29
+
30
+ # Step 3: Verify by reading it
31
+ cmux read-screen --workspace <id> --surface <surface_id> --lines 3
32
+ # Should show: ❯ prompt with "bypass permissions" text
33
+
34
+ # Step 4: Cache the surface ID — don't re-discover every time
35
+ ```
36
+
37
+ ### Signs you're on the WRONG surface:
38
+ - ASCII art (fish, buildings) → idle Claude splash screen, actually correct but idle
39
+ - HTML or URLs → browser surface
40
+ - Server logs / compilation output → dev server surface
41
+ - "Surface is not a terminal" error → browser or non-terminal surface
42
+ - Code output from a different task → stale scrollback, might still be correct surface
43
+ - zsh/bash prompt (e.g., `┏[user][branch]`) → Claude exited, this is a bare shell. **Do NOT send to this.** Spin up a new workspace instead.
44
+
45
+ ### Dead workspaces
46
+ Claude can exit in a workspace, leaving only a shell or browser behind. If `list-pane-surfaces` shows NO "Claude Code" label, the workspace is dead. Create a new workspace instead of trying to reuse it.
47
+
48
+ ---
49
+
50
+ ## Reading Screens
51
+
52
+ ```bash
53
+ # Always use --surface
54
+ cmux read-screen --workspace <id> --surface <surface_id> --lines 10
55
+
56
+ # Keep lines LOW (10-15) to protect context
57
+ # Only go higher (30-40) if actively debugging
58
+ ```
59
+
60
+ ### How to tell if Claude is working vs idle:
61
+ - **Working:** "esc to interrupt" at bottom, tool calls visible, spinner
62
+ - **Idle:** Just `❯` prompt with "bypass permissions", no "esc to interrupt"
63
+ - **Queued message:** "Press up to edit queued messages" — message entered but not submitted
64
+
65
+ ---
66
+
67
+ ## Sending Messages
68
+
69
+ ```bash
70
+ # Step 1: Send the text
71
+ cmux send --workspace <id> --surface <surface_id> "<message>"
72
+
73
+ # Step 2: ALWAYS press Enter after
74
+ cmux send-key --workspace <id> --surface <surface_id> Enter
75
+
76
+ # Step 3: Verify it's running (wait 2-3 sec)
77
+ cmux read-screen --workspace <id> --surface <surface_id> --lines 5
78
+ ```
79
+
80
+ ### Send response format:
81
+ - `OK surface:X workspace:Y` — the workspace:Y in the response is an INTERNAL ID, not necessarily the workspace you targeted. Don't trust it for routing confirmation. Always verify by reading the screen.
82
+
83
+ ### Multi-line messages:
84
+ - cmux send handles multi-line strings in quotes
85
+ - For very long prompts, the pasted text shows as `[Pasted text #1 +N lines]`
86
+
87
+ ---
88
+
89
+ ## Creating Workspaces
90
+
91
+ ```bash
92
+ # Create — use the CORRECT working directory for the task
93
+ cmux new-workspace --cwd <path> --command "claude"
94
+
95
+ # MUST wait for initialization (3-5 seconds)
96
+ sleep 5
97
+
98
+ # MUST resync
99
+ cmux list-workspaces
100
+
101
+ # MUST rename immediately so workspaces are identifiable
102
+ cmux rename-workspace --workspace "workspace:N" "Descriptive Task Name"
103
+
104
+ # MUST use full ID format for new workspaces
105
+ cmux list-pane-surfaces --workspace "workspace:N" # NOT just N
106
+
107
+ # Discover and cache the Claude surface
108
+ cmux list-pane-surfaces --workspace "workspace:N"
109
+ ```
110
+
111
+ ### Choose the right --cwd
112
+ The `--cwd` path determines where Claude starts. **Pick the most relevant directory for the task:**
113
+ - Working on the main app? → `--cwd ~/project` (NOT the repo root)
114
+ - Working on a worktree? → `--cwd ~/worktrees/<worktree-name>`
115
+ - Working on a package? → `--cwd ~/project/packages/<package-name>`
116
+ - General/cross-cutting? → `--cwd ~/project`
117
+
118
+ **Wrong cwd = confused Claude.** If Claude is in the repo root, it may not find the right files or understand the project structure. Match the cwd to where the relevant code lives.
119
+
120
+ ### Always rename new workspaces
121
+ New workspaces default to "Claude Code" which is useless when you have 5+ workspaces. **Immediately rename** after creation:
122
+ ```bash
123
+ cmux rename-workspace --workspace "workspace:N" "Short Task Description"
124
+ ```
125
+ Keep names short but descriptive (e.g., "Benchmark Deck", "PPTX Import", "Auth Migration").
126
+
127
+ ### Gotcha: New workspace IDs
128
+ - `cmux new-workspace` returns `OK workspace:N`
129
+ - For commands like `list-pane-surfaces` and `read-screen`, use the full `"workspace:N"` string
130
+ - `send` and `send-key` may work with just the number after surface is known, but prefer full ID
131
+
132
+ ---
133
+
134
+ ## Monitoring Workspaces
135
+
136
+ ### Quick status check (low context cost):
137
+ ```bash
138
+ cmux read-screen --workspace <id> --surface <surface_id> --lines 5
139
+ ```
140
+
141
+ ### What to look for:
142
+ | Screen Content | Status |
143
+ |----------------|--------|
144
+ | `❯` prompt + "bypass permissions" only | Idle |
145
+ | `esc to interrupt` at bottom | Working |
146
+ | `Worked for Xm Ys` | Just finished |
147
+ | Tool calls / file reads visible | Actively working |
148
+ | Error messages | Failed — needs attention |
149
+ | "Press up to edit queued messages" | Message queued, not submitted |
150
+
151
+ ### Prefer telemetry over screen reads when available:
152
+ ```
153
+ mcp__telemetry__get_session_summary({ session_id: "..." })
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Screenshotting / Comparing
159
+
160
+ For visual comparison of slides or UI:
161
+ ```bash
162
+ # Take screenshot of a browser surface (not Claude surface)
163
+ # First find the browser surface:
164
+ cmux list-pane-surfaces --workspace <id>
165
+ # Look for surface with URL in label
166
+
167
+ # Read the browser surface for visual content
168
+ cmux read-screen --workspace <id> --surface <browser_surface_id> --lines 40
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Troubleshooting
174
+
175
+ | Problem | Cause | Fix |
176
+ |---------|-------|-----|
177
+ | "Surface is not a terminal" | Targeting a browser surface | Use `list-pane-surfaces`, pick the terminal |
178
+ | "Workspace index not found" | Using shorthand ID | Use full `"workspace:N"` format |
179
+ | Message sent to wrong workspace | Surface ID belongs to different workspace | Re-run `list-pane-surfaces` for the correct workspace |
180
+ | Reading shows old/stale output | Scrollback from previous task | Check bottom of screen for idle/working indicators |
181
+ | ASCII art fish/buildings | Claude is idle (splash screen) | This IS the Claude surface, it's just idle |
182
+ | Send returns wrong workspace in OK | cmux internal ID ≠ workspace number | Ignore the response ID, verify by reading screen |
183
+
184
+ ---
185
+
186
+ ## Quick Reference
187
+
188
+ ```bash
189
+ cmux list-workspaces # all workspaces
190
+ cmux list-pane-surfaces --workspace "workspace:N" # all surfaces in workspace
191
+ cmux read-screen --workspace "workspace:N" --surface surface:X --lines 10 # read
192
+ cmux send --workspace "workspace:N" --surface surface:X "msg" # send text
193
+ cmux send-key --workspace "workspace:N" --surface surface:X Enter # press key
194
+ cmux new-workspace --cwd <path> --command "claude" # create
195
+ cmux rename-workspace --workspace "workspace:N" "Task Name" # rename (DO THIS immediately)
196
+ cmux notify "message" # macOS notification
197
+ ```
@@ -0,0 +1,270 @@
1
+ ---
2
+ description: |
3
+ Central coordinator mode. Become the hub that manages all cmux workspaces — dispatch work, monitor progress, and persist state across conversations.
4
+ Use this to initialize or resume a coordinator session.
5
+ allowed-tools: Bash, Read, Glob, Grep, Write, Edit, Agent, AskUserQuestion, mcp__telemetry__*, mcp__linear__*
6
+ ---
7
+
8
+ # Coordinate — Central Workspace Coordinator
9
+
10
+ ## Role
11
+
12
+ You are the **central coordinator**. The user talks to you, and you manage all cmux workspaces. You do NOT write code directly — you dispatch, monitor, and integrate.
13
+
14
+ ## ⛔ CRITICAL RULES
15
+
16
+ 1. **You are the hub.** All communication to other workspaces goes through you via `cmux send` + `cmux send-key Enter`.
17
+ 2. **Be concise.** Don't dump raw screen output. Summarize what's happening in each workspace.
18
+ 3. **Protect context.** This is your biggest constraint. Minimize context usage by:
19
+ - Summarizing `cmux read-screen` output instead of showing it raw
20
+ - Using `mcp__telemetry__get_session_summary` instead of screen reads when possible
21
+ - Periodically compacting state (see Phase 3)
22
+ 4. **Persist state.** Save coordinator state to telemetry so you can resume across conversations.
23
+ 5. **Be proactive.** After dispatching work, monitor it without being asked. Alert the user when workspaces finish or hit errors.
24
+ 6. **Always target the Claude surface.** Run `/cmux-guide` if unsure about any cmux operation. Key rules: never omit `--surface`, use full `"workspace:N"` IDs for new workspaces, always discover surfaces before interacting. See "Surface Discovery" below.
25
+ 7. **⛔ LOG EVERYTHING TO THE LIVE FEED.** Every dispatch, every monitor check, every status change, every decision — call `mcp__telemetry__log_event` immediately. The dashboard live feed is useless without constant logging. If you did something and didn't log it, go back and log it now. Target 5+ events per user interaction. Use these event types:
26
+ - `action` — dispatched work, checked workspace, created workspace
27
+ - `decision` — chose which workspace, prioritized a task, skipped something
28
+ - `info` — status update, workspace completed, workspace idle
29
+ - `error` — workspace failed, send failed, workspace unreachable
30
+ 8. **⛔ SYNC STATE AFTER EVERY CHANGE.** Call `save_coordinator_state` after EVERY workspace status change, dispatch, or completion — not just "every 5-10 interactions". The dashboard reads state directly. Stale state = stale dashboard.
31
+
32
+ ---
33
+
34
+ ## Phase 1: Initialize
35
+
36
+ ### If resuming (default — check for prior state first):
37
+ ```
38
+ state = mcp__telemetry__get_coordinator_state()
39
+ ```
40
+ If state exists:
41
+ - Load workspace statuses, pending tasks, decisions
42
+ - Run `cmux list-workspaces` to verify what's actually running
43
+ - Reconcile (workspaces may have changed since last session)
44
+ - Present a brief status update to the user
45
+
46
+ ### If fresh start:
47
+ ```
48
+ cmux list-workspaces
49
+ ```
50
+ - Map all active workspaces
51
+ - Create coordinator state:
52
+ ```
53
+ mcp__telemetry__save_coordinator_state({
54
+ session_id: "<generated-id>",
55
+ state: {
56
+ workspaces: { ... },
57
+ pending_tasks: [],
58
+ decisions: [],
59
+ notes: ""
60
+ }
61
+ })
62
+ ```
63
+ - Present workspace map to user
64
+
65
+ ---
66
+
67
+ ## Surface Discovery
68
+
69
+ Workspaces often have multiple surfaces (panes) — Claude terminals, web browsers, dev servers, idle shells. **You must always identify and target the Claude surface** for reads and sends.
70
+
71
+ ### How to discover surfaces
72
+ ```bash
73
+ cmux list-pane-surfaces --workspace <id>
74
+ ```
75
+ This returns all surfaces in a workspace. Identify the Claude surface by:
76
+ - Label contains "Claude Code"
77
+ - When read, shows the `❯` prompt with `bypass permissions` text
78
+ - NOT a browser (URL in label), NOT a dev server (logs), NOT "idle"
79
+
80
+ ### Cache surface IDs
81
+ Store the Claude surface ID per workspace in coordinator state:
82
+ ```json
83
+ {
84
+ "workspace:1": { "name": "Telemetry MCP", "status": "idle", "claude_surface": "surface:2" },
85
+ "workspace:5": { "name": "CDD Slides", "status": "idle", "claude_surface": "surface:7" }
86
+ }
87
+ ```
88
+
89
+ ### Always use `--surface`
90
+ **Every** `read-screen`, `send`, and `send-key` command MUST include `--surface <surface_id>`:
91
+ ```bash
92
+ cmux read-screen --workspace <id> --surface <surface_id> --lines 10
93
+ cmux send --workspace <id> --surface <surface_id> "<text>"
94
+ cmux send-key --workspace <id> --surface <surface_id> Enter
95
+ ```
96
+
97
+ ### When to re-discover
98
+ - On initialization (Phase 1) — discover all surfaces for all workspaces
99
+ - After creating a new workspace
100
+ - If a `read-screen` or `send` returns an error or unexpected content (browser HTML, ASCII art, server logs)
101
+ - If a surface ID returns "Surface is not a terminal" error
102
+
103
+ ### During Phase 1 initialization
104
+ After `cmux list-workspaces`, run `cmux list-pane-surfaces --workspace <id>` for **every** workspace. Build the full surface map before presenting status.
105
+
106
+ ---
107
+
108
+ ## Phase 2: Active Coordination
109
+
110
+ ### Dispatching work
111
+ When the user asks you to send work to a workspace:
112
+ 1. Get the Claude surface from state (or discover via `cmux list-pane-surfaces` if not cached)
113
+ 2. Check if workspace is idle: `cmux read-screen --workspace <id> --surface <claude_surface> --lines 5`
114
+ 3. If busy, add to `pending_tasks` in state and tell the user
115
+ 4. If idle, send via: `cmux send --workspace <id> --surface <claude_surface> "<message>"` then `cmux send-key --workspace <id> --surface <claude_surface> Enter`
116
+ 4. Log the dispatch to telemetry:
117
+ ```
118
+ mcp__telemetry__log_event({
119
+ session_id: coordinator_session_id,
120
+ phase: "coordination",
121
+ event_type: "action",
122
+ message: "Dispatched to <workspace-name>: <summary>",
123
+ metadata: { workspace: "<id>", full_message: "<what was sent>" }
124
+ })
125
+ ```
126
+ 5. Update workspace status in state
127
+
128
+ ### Monitoring
129
+ When checking on workspaces:
130
+ - **Prefer telemetry over screen reads** — `get_session_summary` is cheaper on context
131
+ - **Screen reads for non-telemetry workspaces** — always use `--surface <claude_surface>`: `cmux read-screen --workspace <id> --surface <claude_surface> --lines 10` (not 40+)
132
+ - **If read returns unexpected content** (HTML, ASCII art, server logs, "Surface is not a terminal") — you're on the wrong surface. Re-discover with `cmux list-pane-surfaces`
133
+ - **Summarize aggressively** — "Telemetry MCP: building screenshot thumbnails, 3min in" not the full output
134
+ - **⛔ Log EVERY monitor check:**
135
+ ```
136
+ mcp__telemetry__log_event({
137
+ session_id: coordinator_session_id,
138
+ phase: "coordination",
139
+ event_type: "info",
140
+ message: "<workspace-name>: <what you observed>",
141
+ metadata: { workspace: "<id>", status: "<idle|working|done|failed>" }
142
+ })
143
+ ```
144
+ - After monitoring, update state AND call `save_coordinator_state` immediately
145
+
146
+ ### Session Linking (for live feed)
147
+ The telemetry dashboard live feed shows events from all workspace sessions — but ONLY if the coordinator state has their `session_id` linked. **You must actively link sessions.**
148
+
149
+ After dispatching `/start-work` to a workspace:
150
+ 1. Wait ~30 seconds for the session to be created
151
+ 2. Run `mcp__telemetry__list_sessions({ status: "in_progress", limit: 5 })`
152
+ 3. Match the new session to the workspace by worktree name
153
+ 4. Update coordinator state with the `session_id` on that workspace
154
+
155
+ **Periodically re-link:** Every 5-10 interactions, run `list_sessions` and reconcile — new sessions may have been created, old ones may have completed.
156
+
157
+ Without this linking, the coordinator live feed will be empty even though workspaces are logging events.
158
+
159
+ ### Responding to the user
160
+ - Lead with the answer, not the process
161
+ - Use tables for multi-workspace status
162
+ - Don't repeat what the user said
163
+
164
+ ### Intent Routing (auto-detect workspace)
165
+
166
+ The user will often just say what they want without naming a workspace. **You must infer the target workspace** from context. Do NOT ask "which workspace?" unless genuinely ambiguous.
167
+
168
+ **Routing priority:**
169
+ 1. **Explicit name** — user says "telemetry" or "slides" → direct match
170
+ 2. **Topic match** — match keywords to workspace descriptions in state:
171
+ - Screenshots, dashboard, events, sessions, thumbnails → Telemetry MCP
172
+ - Slides, generation, template, PPTX, quality score → Slide Generation
173
+ - Database, JWT, auth, migration → DB Migration
174
+ - (Update these mappings as workspaces change)
175
+ 3. **Recency** — if user just saw output from a workspace and responds, it's about that workspace
176
+ 4. **Active work** — if only one workspace is actively working, comments about "it" or "that" refer to it
177
+ 5. **Ambiguous** — only ask if 2+ workspaces are equally likely. Present as numbered options:
178
+ ```
179
+ That could be about:
180
+ 1. Telemetry MCP (working on thumbnails)
181
+ 2. Slide Generation (idle, last worked on quality)
182
+ Which one?
183
+ ```
184
+
185
+ **Build the keyword map from workspace state.** When saving state, include a `topics` array per workspace:
186
+ ```json
187
+ {
188
+ "workspace:1": { "name": "Telemetry MCP", "topics": ["telemetry", "dashboard", "screenshots", "events", "sessions", "MCP"] },
189
+ "workspace:4": { "name": "Slide Generation", "topics": ["slides", "pptx", "generation", "template", "quality"] }
190
+ }
191
+ ```
192
+
193
+ **The user should feel like they're just talking, and messages magically go to the right place.**
194
+
195
+ ---
196
+
197
+ ## Phase 3: Context Management
198
+
199
+ ### Periodic state saves
200
+ After EVERY workspace status change, save state immediately. At minimum every 2-3 interactions:
201
+ ```
202
+ mcp__telemetry__save_coordinator_state({
203
+ session_id: coordinator_session_id,
204
+ state: { workspaces: {...}, pending_tasks: [...], decisions: [...], notes: "..." }
205
+ })
206
+ ```
207
+
208
+ ### When context is getting heavy
209
+ Signs: conversation is long, lots of screen reads accumulated, you're near context limits.
210
+
211
+ 1. Save everything important:
212
+ ```
213
+ mcp__telemetry__compact_coordinator_context({
214
+ session_id: coordinator_session_id,
215
+ context: {
216
+ summary: "<what happened this conversation>",
217
+ decisions: ["<key decisions made>"],
218
+ workspace_states: { ... },
219
+ pending_work: ["<things still to do>"],
220
+ user_preferences_learned: ["<any new preferences>"]
221
+ }
222
+ })
223
+ ```
224
+ 2. Tell the user: "Context is getting heavy. I've saved state — you can start a fresh `/coordinate` and I'll pick up where we left off."
225
+ 3. The user starts a new conversation and runs `/coordinate` — Phase 1 loads the saved state.
226
+
227
+ ---
228
+
229
+ ## Phase 4: Pending Task Management
230
+
231
+ When a workspace finishes and has pending tasks:
232
+ 1. Check the workspace is actually idle
233
+ 2. Send the next pending task
234
+ 3. Remove from pending list
235
+ 4. Update state
236
+
237
+ When the user queues something for a busy workspace:
238
+ 1. Add to `pending_tasks` with workspace ID and message
239
+ 2. Confirm: "Queued for <workspace-name> — will send when it's free"
240
+ 3. Check periodically if it's free
241
+
242
+ ---
243
+
244
+ ## Workspace Command Reference
245
+
246
+ ```bash
247
+ cmux list-workspaces # see all workspaces
248
+ cmux list-pane-surfaces --workspace <id> # list all surfaces in a workspace
249
+ cmux read-screen --workspace <id> --surface <surface_id> --lines 10 # check output (ALWAYS use --surface)
250
+ cmux send --workspace <id> --surface <surface_id> "<text>" # send message (ALWAYS use --surface)
251
+ cmux send-key --workspace <id> --surface <surface_id> Enter # press Enter (ALWAYS use --surface)
252
+ cmux new-workspace --cwd <path> --command "claude" # spawn new workspace
253
+ cmux notify "<message>" # macOS notification
254
+ ```
255
+
256
+ **⚠️ Never omit `--surface`.** Bare commands without `--surface` will target whatever surface is "selected" — which may be a browser, dev server, or idle shell, not Claude.
257
+
258
+ ---
259
+
260
+ ## On Conversation Start
261
+
262
+ Always begin with:
263
+ 1. Load prior coordinator state (or initialize fresh)
264
+ 2. `cmux list-workspaces` to get current workspace map
265
+ 3. `cmux list-pane-surfaces --workspace <id>` for **every** workspace — build the surface map
266
+ 4. Identify the Claude surface in each workspace (label contains "Claude Code", or read to verify)
267
+ 5. Cache `claude_surface` per workspace in coordinator state
268
+ 6. Quick status check on active workspaces (using `--surface` for all reads)
269
+ 7. Present status table to user (include surface IDs for reference)
270
+ 8. Ask: "What should we work on?" (or pick up pending tasks)