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 +70 -11
- package/package.json +2 -1
- package/skills/cmux-guide.md +197 -0
- package/skills/coordinate.md +270 -0
- package/skills/create-path.md +179 -0
- package/skills/debug.md +232 -0
- package/skills/edit-path.md +125 -0
- package/skills/review-code.md +211 -0
- package/skills/sessions.md +82 -0
- package/skills/start-work.md +1697 -0
- package/skills/walkthrough.md +175 -0
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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(` ${
|
|
284
|
-
log(` ${w}data${r} ${d}${DATA_DIR}${r}`);
|
|
327
|
+
log(` ${g}${b}ready${r}`);
|
|
285
328
|
log("");
|
|
286
|
-
log(` ${
|
|
287
|
-
log(` ${
|
|
329
|
+
log(` ${w}dashboard${r} ${cy}http://localhost:${PORT}${r}`);
|
|
330
|
+
log(` ${w}data${r} ${d}${DATA_DIR}${r}`);
|
|
288
331
|
log("");
|
|
289
|
-
log(` ${
|
|
290
|
-
log(`
|
|
291
|
-
log(`
|
|
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
|
+
"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)
|