codebyplan 1.13.22 → 1.13.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebyplan",
3
- "version": "1.13.22",
3
+ "version": "1.13.23",
4
4
  "description": "CLI for CodeByPlan — AI-powered development planning and tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -224,63 +224,13 @@ After a `complete_round` MCP call succeeds, reconciles the round's `files_change
224
224
 
225
225
  ---
226
226
 
227
- ### `cbp-cmux-workspace-sync.sh` — SessionStart, matcher `*`
228
-
229
- On every session start, syncs the active [cmux](https://github.com/nicholasgasior/cmux) workspace title to the current git branch, the workspace description to the repo folder basename (the directory that contains `.git/`), and applies the workspace color from `.codebyplan/cmux.json` via `cmux workspace-action --action set-color`. All three actions are delegated to `codebyplan cmux-sync`. If no `workspace_color` is configured, a one-line nudge is printed to stdout prompting the user to run `/cbp-setup-cmux`.
230
-
231
- **Blocks vs warns**: never blocks — exit 0 on every path. A SessionStart hook must never prevent a session from opening.
232
-
233
- **Skips when**: `$CMUX_WORKSPACE_ID` is unset (not running inside a cmux workspace). No-ops silently — cmux is an optional integration and repos without it are completely unaffected.
234
-
235
- **cmux binary resolution order**: `$CMUX_BUNDLED_CLI_PATH` → `$CMUX_CLAUDE_HOOK_CMUX_BIN` → `cmux` on `$PATH`. All three are resolved by the `codebyplan cmux-sync` subcommand; the hook itself does not replicate this logic.
236
-
237
- **Opt out**: settings.json override removing this entry, or plugin disable.
238
-
239
- ---
240
-
241
- ### `cbp-cmux-branch-watch.sh` — PostToolUse, matcher `Bash`
242
-
243
- After any Bash tool call that contains a `git checkout` or `git switch` invocation, syncs the active cmux workspace title to the current branch and the workspace description to the repo folder basename. The match is broad — it also fires on file-restore forms like `git checkout -- <file>` — but `codebyplan cmux-sync` is idempotent, so a redundant sync on a non-branch-change is harmless, and a real branch change is never missed.
244
-
245
- **Blocks vs warns**: never blocks — exit 0 on every path. PostToolUse hooks must never abort Claude.
246
-
247
- **Skips when**: `$CMUX_WORKSPACE_ID` is unset; or the Bash command contains no `git checkout` / `git switch`; or `jq` is not on `$PATH` (safe parse failure → exit 0). Outside cmux or on commands without checkout/switch, the hook exits immediately with no work done.
248
-
249
- **cmux binary resolution order**: same as `cbp-cmux-workspace-sync.sh` — delegated to `codebyplan cmux-sync`.
250
-
251
- **Opt out**: settings.json override removing the PostToolUse Bash entry for this hook, or plugin disable.
252
-
253
- ---
254
-
255
- ### Auto dev server (`codebyplan cmux-serve`)
256
-
257
- At the start of each round, `cbp-round-execute` (Step 2a) calls `codebyplan cmux-serve --files "<round files>"` to auto-start the dev server for any app whose source files are touched. The subcommand probes each allocated port via `node:net`, starts a `cmux new-split` terminal pane + sends the dev command for any non-listening app, then opens a browser pane. If the port is already listening (another worktree) it only opens the browser pane. No hook registration is needed — the skill invokes the subcommand directly. Gated by `auto_dev_server` in `.codebyplan/cmux.json`; no-op outside cmux.
258
-
259
- ---
260
-
261
- ### Status surface (`codebyplan cmux-status`)
262
-
263
- The lifecycle skills push CodeByPlan development state into the cmux workspace sidebar via `codebyplan cmux-status`. No hook registration is needed — the skills invoke the subcommand directly:
264
-
265
- | Skill | What is pushed |
266
- | --- | --- |
267
- | `cbp-task-start` (Step 4.5) | `--checkpoint "CHK-NNN: title" --task "TASK-N: title"` |
268
- | `cbp-task-complete` (Step 7.3) | `--task "TASK-N: title done" --progress completed/total` |
269
- | `cbp-round-execute` (Step 3d) | `--qa "R{n} {status}"` where status ∈ completed / blocked / re-triggering |
270
-
271
- **`auto_status` toggle.** Gated by the `auto_status` field in `.codebyplan/cmux.json` (configured via `/cbp-setup-cmux`). When `auto_status` is `false`, every call is a no-op. Default is `true` (enabled).
272
-
273
- **No-op outside cmux.** `codebyplan cmux-status` checks for `$CMUX_WORKSPACE_ID` before doing anything. Outside a cmux workspace it exits immediately — safe to call unconditionally from skills and hooks.
274
-
275
- ---
276
-
277
227
  ## Supporting (not registered)
278
228
 
279
229
  ### `test-hooks.sh` — invoked by `auto-test-hooks.sh`
280
230
 
281
- Test suite for the plugin's 11 registered hooks. Runs two passes:
231
+ Test suite for the plugin's 9 registered hooks. Runs two passes:
282
232
 
283
- 1. **Header check** — every registered hook (`lint-format-on-edit`, `test-coverage-gate`, `pre-commit-quality-gate`, `maestro-yaml-validate`, `auto-test-hooks`, `mcp-migration-guard`, `validate-git-stash-deny`, `cbp-mcp-round-sync`, `cbp-cmux-workspace-sync`, `cbp-cmux-branch-watch`) carries the required `# Hook:` and `# Purpose:` header comments. `statusline` uses its own `# Claude Code Status Line` marker.
233
+ 1. **Header check** — every registered hook (`lint-format-on-edit`, `test-coverage-gate`, `pre-commit-quality-gate`, `maestro-yaml-validate`, `auto-test-hooks`, `mcp-migration-guard`, `validate-git-stash-deny`, `cbp-mcp-round-sync`) carries the required `# Hook:` and `# Purpose:` header comments. `statusline` uses its own `# Claude Code Status Line` marker.
284
234
  2. **Functional smoke tests** — each hook is invoked with synthetic stdin matching its fast-path / graceful-degrade input; all must exit 0.
285
235
 
286
236
  Not in `hooks.json` — invoked indirectly via `auto-test-hooks.sh` on hook edits, or directly via `bash ${CLAUDE_PLUGIN_ROOT}/hooks/test-hooks.sh`.
@@ -255,77 +255,6 @@ fi
255
255
 
256
256
  echo ""
257
257
 
258
- # ===== HOOK SMOKE TESTS — cbp-cmux-workspace-sync + cbp-cmux-branch-watch =====
259
- echo "## Hook Smoke Tests — cbp-cmux-workspace-sync + cbp-cmux-branch-watch (CHK-162)"
260
-
261
- # --- cbp-cmux-workspace-sync.sh ---
262
-
263
- if [ ! -f "$HOOKS_DIR/cbp-cmux-workspace-sync.sh" ]; then
264
- test_result "cbp-cmux-workspace-sync.sh present" "passed" "missing"
265
- else
266
- test_result "cbp-cmux-workspace-sync.sh present" "passed" "passed"
267
-
268
- FIRST_LINE=$(head -1 "$HOOKS_DIR/cbp-cmux-workspace-sync.sh")
269
- if echo "$FIRST_LINE" | grep -q '^#!/'; then
270
- test_result "cbp-cmux-workspace-sync.sh has shebang" "passed" "passed"
271
- else
272
- test_result "cbp-cmux-workspace-sync.sh has shebang" "passed" "missing"
273
- fi
274
-
275
- if grep -q '@scope: org-shared' "$HOOKS_DIR/cbp-cmux-workspace-sync.sh"; then
276
- test_result "cbp-cmux-workspace-sync.sh has @scope: org-shared" "passed" "passed"
277
- else
278
- test_result "cbp-cmux-workspace-sync.sh has @scope: org-shared" "passed" "missing"
279
- fi
280
-
281
- # Graceful-degrade: CMUX_WORKSPACE_ID unset → fast-path exit 0, no output
282
- ACTUAL_EXIT=$(env -u CMUX_WORKSPACE_ID bash "$HOOKS_DIR/cbp-cmux-workspace-sync.sh" <<< '{}' >/dev/null 2>&1; echo $?)
283
- if [ "$ACTUAL_EXIT" = "0" ]; then
284
- test_result "cbp-cmux-workspace-sync.sh graceful-degrade (no CMUX_WORKSPACE_ID) exits 0" "passed" "passed"
285
- else
286
- test_result "cbp-cmux-workspace-sync.sh graceful-degrade (no CMUX_WORKSPACE_ID) exits 0" "passed" "failed"
287
- fi
288
- fi
289
-
290
- # --- cbp-cmux-branch-watch.sh ---
291
-
292
- if [ ! -f "$HOOKS_DIR/cbp-cmux-branch-watch.sh" ]; then
293
- test_result "cbp-cmux-branch-watch.sh present" "passed" "missing"
294
- else
295
- test_result "cbp-cmux-branch-watch.sh present" "passed" "passed"
296
-
297
- FIRST_LINE=$(head -1 "$HOOKS_DIR/cbp-cmux-branch-watch.sh")
298
- if echo "$FIRST_LINE" | grep -q '^#!/'; then
299
- test_result "cbp-cmux-branch-watch.sh has shebang" "passed" "passed"
300
- else
301
- test_result "cbp-cmux-branch-watch.sh has shebang" "passed" "missing"
302
- fi
303
-
304
- if grep -q '@scope: org-shared' "$HOOKS_DIR/cbp-cmux-branch-watch.sh"; then
305
- test_result "cbp-cmux-branch-watch.sh has @scope: org-shared" "passed" "passed"
306
- else
307
- test_result "cbp-cmux-branch-watch.sh has @scope: org-shared" "passed" "missing"
308
- fi
309
-
310
- # Graceful-degrade: CMUX_WORKSPACE_ID unset → fast-path exit 0
311
- ACTUAL_EXIT=$(env -u CMUX_WORKSPACE_ID bash "$HOOKS_DIR/cbp-cmux-branch-watch.sh" <<< '{"tool_input":{"command":"git checkout main"}}' >/dev/null 2>&1; echo $?)
312
- if [ "$ACTUAL_EXIT" = "0" ]; then
313
- test_result "cbp-cmux-branch-watch.sh graceful-degrade (no CMUX_WORKSPACE_ID) exits 0" "passed" "passed"
314
- else
315
- test_result "cbp-cmux-branch-watch.sh graceful-degrade (no CMUX_WORKSPACE_ID) exits 0" "passed" "failed"
316
- fi
317
-
318
- # Non-checkout command → no-op exit 0 (even with CMUX_WORKSPACE_ID set, non-checkout should skip sync)
319
- ACTUAL_EXIT=$(CMUX_WORKSPACE_ID=test-ws bash "$HOOKS_DIR/cbp-cmux-branch-watch.sh" <<< '{"tool_input":{"command":"git status"}}' >/dev/null 2>&1; echo $?)
320
- if [ "$ACTUAL_EXIT" = "0" ]; then
321
- test_result "cbp-cmux-branch-watch.sh non-checkout command exits 0" "passed" "passed"
322
- else
323
- test_result "cbp-cmux-branch-watch.sh non-checkout command exits 0" "passed" "failed"
324
- fi
325
- fi
326
-
327
- echo ""
328
-
329
258
  # ===== SUMMARY =====
330
259
  echo "=== TEST SUMMARY ==="
331
260
  echo -e "Passed: ${GREEN}$PASSED${NC}"
@@ -1,16 +1,5 @@
1
1
  {
2
2
  "hooks": {
3
- "SessionStart": [
4
- {
5
- "matcher": "*",
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/cbp-cmux-workspace-sync.sh"
10
- }
11
- ]
12
- }
13
- ],
14
3
  "PreToolUse": [
15
4
  {
16
5
  "matcher": "Edit|Write|MultiEdit",
@@ -70,15 +59,6 @@
70
59
  "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/cbp-mcp-round-sync.sh"
71
60
  }
72
61
  ]
73
- },
74
- {
75
- "matcher": "Bash",
76
- "hooks": [
77
- {
78
- "type": "command",
79
- "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/cbp-cmux-branch-watch.sh"
80
- }
81
- ]
82
62
  }
83
63
  ]
84
64
  }
@@ -118,7 +118,6 @@
118
118
  "Skill(cbp-round-input)",
119
119
  "Skill(cbp-round-start)",
120
120
  "Skill(cbp-session-start)",
121
- "Skill(cbp-setup-cmux)",
122
121
  "Skill(cbp-setup-e2e)",
123
122
  "Skill(cbp-setup-eslint)",
124
123
  "Skill(cbp-ship-configure)",
@@ -179,12 +178,6 @@
179
178
  "Bash(npx codebyplan whoami:*)",
180
179
  "Bash(codebyplan resolve-worktree:*)",
181
180
  "Bash(npx codebyplan resolve-worktree:*)",
182
- "Bash(codebyplan cmux-sync:*)",
183
- "Bash(npx codebyplan cmux-sync:*)",
184
- "Bash(codebyplan cmux-status:*)",
185
- "Bash(npx codebyplan cmux-status:*)",
186
- "Bash(codebyplan cmux-serve:*)",
187
- "Bash(npx codebyplan cmux-serve:*)",
188
181
  "Bash(codebyplan version-status:*)",
189
182
  "Bash(npx codebyplan version-status:*)",
190
183
  "Bash(codebyplan statusline:*)",
@@ -57,16 +57,6 @@ Read the plan from round context (`context.planner_output`). If no plan: `No app
57
57
 
58
58
  Read effective testing profile: `round.context.testing_profile_override` if set (user override for this round only), else `task.context.testing_profile` (set by planner Phase 4.8), else default `'web'`. Pass the effective profile to all per-wave `cbp-testing-qa-agent` spawns.
59
59
 
60
- ### Step 2a: Auto-Dev-Server (cmux)
61
-
62
- Fire the dev-server hook at round-execution start. Self-no-ops outside cmux or when `auto_dev_server` is disabled in `.codebyplan/cmux.json`.
63
-
64
- ```bash
65
- npx codebyplan cmux-serve --files "<comma-separated approved_plan.files_to_modify[].path>"
66
- ```
67
-
68
- The subcommand reads `.codebyplan/server.json` `port_allocations[]`, resolves which apps' source dirs intersect the round's files, probes each allocated port, and starts a cmux terminal split + browser pane for any app not already serving. Idempotent — if the port is already listening it only opens the browser pane (mitigating the multi-worktree port collision).
69
-
70
60
  ### Step 3: Route Execution Path
71
61
 
72
62
  Inspect `approved_plan.files_to_modify[]` and `approved_plan.round_type`. Four execution paths exist; pick the one that matches BEFORE Step 3a/3b.
@@ -153,14 +143,6 @@ If the approved plan includes database schema changes, RLS policies, or type gen
153
143
  - `status: 'blocked'` → present blocker to user via AskUserQuestion, resolve, re-spawn executor with remaining work
154
144
  - Deliverables incomplete → re-spawn executor with remaining deliverables (max 3 re-triggers). After 3 re-triggers, save partial output and proceed.
155
145
 
156
- ### Step 3d: Push cmux QA Status
157
-
158
- Push the round's QA outcome to the cmux workspace sidebar. Self-no-ops outside cmux or when `auto_status` is disabled. Status is one of: `completed`, `blocked`, or `re-triggering`.
159
-
160
- ```bash
161
- npx codebyplan cmux-status --qa "R{round_number} {status}"
162
- ```
163
-
164
146
  ### Step 4: Dev-Server Probe (rounds 2+, web/desktop profile)
165
147
 
166
148
  When `round_number >= 2` AND `testing_profile` is `'web'` or `'desktop'` AND `files_changed` contains any UI file, probe the dev server BEFORE cbp-testing-qa-agent spawns (saves a full agent spawn when the server is down).
@@ -138,20 +138,6 @@ Skip the push only when nothing was committed in Step 5 AND `/cbp-merge-main` re
138
138
 
139
139
  Call `complete_task(task_id)`. The server resolves the caller's worktree identity from the JWT/ctx and enforces the mutate-lock (CHK-140 TASK-3 — `caller_worktree_id` input field removed). The server auto-clears `assigned_user_id` + `assigned_worktree_id` on the task; if this was the last sibling task, it also clears the parent checkpoint's assignment. (Per CHK-104 hard-lock.)
140
140
 
141
- ### Step 7.3: Push cmux Status (task done + progress)
142
-
143
- Push completion status and checkpoint progress to the cmux workspace sidebar. Self-no-ops outside cmux or when `auto_status` is disabled.
144
-
145
- Compute progress from the `get_tasks` data already loaded by the routing step: `completed = count of tasks with status 'completed'`, `total = total task count for the checkpoint`. For standalone tasks (no checkpoint), omit `--progress`.
146
-
147
- ```bash
148
- # Checkpoint-bound task:
149
- npx codebyplan cmux-status --task "TASK-{N}: {task-title} done" --progress {completed}/{total}
150
-
151
- # Standalone task:
152
- npx codebyplan cmux-status --task "TASK-{N}: {task-title} done"
153
- ```
154
-
155
141
  ### Step 8: Run Cleanup + Migration (inline)
156
142
 
157
143
  Apply the `cleanup` skill inline to remove orphan references to deleted/modified files. Then apply `migration` to propagate renames/moves to consumers. Both run without sub-agent spawns. Skip cleanup if no deletions/modifications; skip migration if cleanup handled everything.
@@ -228,14 +228,6 @@ Display context summary:
228
228
  - **Previous rounds**: [count] completed
229
229
  ```
230
230
 
231
- ### Step 4.5: Push cmux Status
232
-
233
- Push the active checkpoint and task context to the cmux workspace sidebar. Self-no-ops outside cmux or when `auto_status` is disabled in `.codebyplan/cmux.json`.
234
-
235
- ```bash
236
- npx codebyplan cmux-status --checkpoint "CHK-{NNN}: {checkpoint-title}" --task "TASK-{N}: {task-title}"
237
- ```
238
-
239
231
  ### Step 5: Set Task Status
240
232
 
241
233
  Use MCP `update_task(task_id, status: "in_progress")`.
@@ -1,39 +0,0 @@
1
- #!/bin/bash
2
- # @scope: org-shared
3
- # Hook: PostToolUse Bash
4
- # Purpose: After a Bash tool call that contains a git checkout or git switch command,
5
- # sync the active cmux workspace title to the current git branch and the
6
- # workspace description to the repo folder basename.
7
- # Delegates entirely to `codebyplan cmux-sync` — no cmux or git logic here.
8
- # Matching is broad: it also fires on file-restore forms such as
9
- # `git checkout -- <file>`. That is intentional — `codebyplan cmux-sync` is
10
- # idempotent, so a redundant sync on a non-branch-change is harmless, and the
11
- # broad match guarantees a real branch change is never missed.
12
- # No-ops silently when CMUX_WORKSPACE_ID is unset or the command contains no
13
- # checkout/switch. Exit 0 on every path — never blocks tool execution.
14
-
15
- # Fast-path: skip npx spawn when clearly not in a cmux workspace.
16
- [ -n "$CMUX_WORKSPACE_ID" ] || exit 0
17
-
18
- # Parse stdin — PostToolUse hooks receive JSON on stdin.
19
- # Guard against jq absence: if jq is not available the whole block is skipped.
20
- if command -v jq >/dev/null 2>&1; then
21
- INPUT=$(cat 2>/dev/null || true)
22
- CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
23
-
24
- # Run the sync when the command contains git checkout/switch. Broad match by design:
25
- # also catches file-restore forms (e.g. `git checkout -- <file>`); cmux-sync is
26
- # idempotent so a redundant sync is harmless, and a real branch change is never missed.
27
- if ! echo "$CMD" | grep -qE 'git[[:space:]]+(checkout|switch)' 2>/dev/null; then
28
- exit 0
29
- fi
30
- else
31
- # jq not available — drain stdin and skip.
32
- cat >/dev/null 2>&1 || true
33
- exit 0
34
- fi
35
-
36
- # Delegate to the CLI subcommand (self-no-ops when cmux binary is absent).
37
- npx codebyplan cmux-sync >/dev/null 2>&1 || true
38
-
39
- exit 0
@@ -1,19 +0,0 @@
1
- #!/bin/bash
2
- # @scope: org-shared
3
- # Hook: SessionStart
4
- # Purpose: On session start, sync the active cmux workspace title to the current git branch
5
- # and the workspace description to the repo folder basename.
6
- # Delegates entirely to `codebyplan cmux-sync` — no cmux or git logic here.
7
- # No-ops silently when CMUX_WORKSPACE_ID is unset (not inside a cmux workspace).
8
- # Exit 0 on every path — never blocks session start.
9
-
10
- # Fast-path: skip npx spawn when clearly not in a cmux workspace.
11
- [ -n "$CMUX_WORKSPACE_ID" ] || exit 0
12
-
13
- # Drain stdin — SessionStart hooks receive JSON on stdin; we don't use it.
14
- cat >/dev/null 2>&1
15
-
16
- # Delegate to the CLI subcommand (self-no-ops when cmux binary is absent).
17
- npx codebyplan cmux-sync >/dev/null 2>&1 || true
18
-
19
- exit 0
@@ -1,170 +0,0 @@
1
- ---
2
- scope: org-shared
3
- name: cbp-setup-cmux
4
- description: Configure .codebyplan/cmux.json — choose workspace color and toggle auto_status / auto_dev_server. Interactive, idempotent.
5
- argument-hint: "[--force]"
6
- model: sonnet
7
- effort: xhigh
8
- allowed-tools: Read, Write, Edit, Bash(cat *), Bash(jq *), Bash(test *), Bash(mv *), Bash(git check-ignore *), Bash(cmux *), AskUserQuestion
9
- ---
10
-
11
- # cmux Setup
12
-
13
- Configure `.codebyplan/cmux.json` so the cmux workspace integration knows which color to
14
- apply on session start and whether auto_status / auto_dev_server are enabled.
15
-
16
- Invoke at any time. Existing values are preserved unless `--force` is passed.
17
- Pass `--force` to re-ask all questions including already-configured fields.
18
-
19
- ## Arguments
20
-
21
- Inspect `$ARGUMENTS` for `--force`. If present, set `force_mode = true`.
22
- Absent: use idempotent mode — preserve existing configured values, skip re-asking
23
- already-set fields.
24
-
25
- ## Step 1 — Parse --force and read existing config
26
-
27
- ```bash
28
- cat .codebyplan/cmux.json 2>/dev/null || echo '{}'
29
- ```
30
-
31
- Capture the result as `EXISTING_JSON`. Extract existing values:
32
-
33
- ```bash
34
- EXISTING_COLOR=$(echo "$EXISTING_JSON" | jq -r '.workspace_color // empty')
35
- EXISTING_AUTO_STATUS=$(echo "$EXISTING_JSON" | jq -r '.auto_status // empty')
36
- EXISTING_AUTO_DEV=$(echo "$EXISTING_JSON" | jq -r '.auto_dev_server // empty')
37
- ```
38
-
39
- ## Step 2 — Ask for workspace color
40
-
41
- **Idempotency gate**: skip this question if `force_mode = false` AND `EXISTING_COLOR`
42
- is a non-empty string. Print the preserved value and continue to Step 3.
43
-
44
- Otherwise, AskUserQuestion:
45
-
46
- ```
47
- Workspace color for this cmux workspace
48
-
49
- Choose a named color or enter a custom hex value (#RRGGBB).
50
-
51
- Named colors:
52
- A) Red B) Crimson C) Orange D) Amber
53
- E) Olive F) Green G) Teal H) Aqua
54
- I) Blue J) Navy K) Indigo L) Purple
55
- M) Magenta N) Rose O) Brown P) Charcoal
56
- Q) Custom hex — enter a value in the format #RRGGBB
57
-
58
- Enter a letter (A-Q) or type a custom hex value directly:
59
- ```
60
-
61
- Validate the input:
62
- - If a letter A-P: map to the corresponding color name (e.g. "A" → "Red").
63
- - If "Q": prompt the user to type a custom hex value, then validate the typed value
64
- against `^#[0-9A-Fa-f]{6}$`. If invalid, re-prompt once with an error. Never pass the
65
- bare letter "Q" to set-color.
66
- - If the raw input itself already looks like a hex value: validate it against
67
- `^#[0-9A-Fa-f]{6}$` and accept on match.
68
- - Empty input with no existing color: use no color (leave `workspace_color` as `null`).
69
-
70
- Set `NEW_COLOR` to the chosen color name / hex, or `null` if skipped.
71
-
72
- ## Step 3 — Ask for auto_status and auto_dev_server toggles
73
-
74
- **Idempotency gate**: skip questions for fields that are already set (non-empty in
75
- existing config) when `force_mode = false`.
76
-
77
- For each unset field (or all fields when `force_mode = true`), AskUserQuestion:
78
-
79
- ```
80
- cmux integration toggles
81
-
82
- auto_status: automatically run `cmux status` in this workspace on session start?
83
- (default: on)
84
- A) On
85
- B) Off
86
-
87
- auto_dev_server: automatically start the dev server via cmux on session start?
88
- (default: on)
89
- A) On
90
- B) Off
91
- ```
92
-
93
- Ask both in a single question when both are unset. Set `NEW_AUTO_STATUS` and
94
- `NEW_AUTO_DEV` to `true` or `false` based on the answers.
95
-
96
- ## Step 4 — Write .codebyplan/cmux.json
97
-
98
- Build the updated payload using jq deep-merge so sibling fields added by future
99
- schema versions are preserved. Assemble variables first.
100
-
101
- Backfill any field that the Step 2/3 idempotency gates skipped, so each `jq
102
- --argjson` always receives valid JSON — an empty shell variable makes `jq` exit 1,
103
- which would leave `NEW_PAYLOAD` empty and silently no-op the write. Fall back to the
104
- existing value, then to the default (`true` for the toggles):
105
-
106
- ```bash
107
- NEW_COLOR="${NEW_COLOR:-$EXISTING_COLOR}"
108
- NEW_AUTO_STATUS="${NEW_AUTO_STATUS:-${EXISTING_AUTO_STATUS:-true}}"
109
- NEW_AUTO_DEV="${NEW_AUTO_DEV:-${EXISTING_AUTO_DEV:-true}}"
110
- ```
111
-
112
- ```bash
113
- NEW_COLOR_JSON=$(echo "$NEW_COLOR" | jq -R 'if . == "null" or . == "" then null else . end')
114
- NEW_PAYLOAD=$(jq -n \
115
- --argjson color "$NEW_COLOR_JSON" \
116
- --argjson auto_status "$NEW_AUTO_STATUS" \
117
- --argjson auto_dev_server "$NEW_AUTO_DEV" \
118
- '{workspace_color: $color, auto_status: $auto_status, auto_dev_server: $auto_dev_server}')
119
- ```
120
-
121
- Atomic write via jq tmp+mv deep-merge (`. * $new` deep-merges, preserving any
122
- sibling keys not in the new payload):
123
-
124
- ```bash
125
- jq --argjson new "$NEW_PAYLOAD" '. * $new' \
126
- .codebyplan/cmux.json > .codebyplan/cmux.json.tmp \
127
- && mv .codebyplan/cmux.json.tmp .codebyplan/cmux.json
128
- ```
129
-
130
- ## Step 5 — Apply color immediately
131
-
132
- If `NEW_COLOR` is a non-empty, non-null string AND cmux is available in the current
133
- environment (`$CMUX_WORKSPACE_ID` is set), apply the color immediately so the change
134
- is visible without needing to restart the session:
135
-
136
- ```bash
137
- if [ -n "$CMUX_WORKSPACE_ID" ] && [ -n "$NEW_COLOR" ] && [ "$NEW_COLOR" != "null" ]; then
138
- CMUX_BIN="${CMUX_BUNDLED_CLI_PATH:-${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}}"
139
- "$CMUX_BIN" workspace-action --action set-color --color "$NEW_COLOR" 2>/dev/null || true
140
- fi
141
- ```
142
-
143
- The guard (`2>/dev/null || true`) ensures a missing or non-zero cmux never blocks
144
- the skill — applying the color is best-effort.
145
-
146
- ## Step 6 — Verify and report
147
-
148
- Re-read `.codebyplan/cmux.json` and emit a summary:
149
-
150
- ```
151
- cmux Setup — Complete
152
-
153
- workspace_color : <value or "(none)">
154
- auto_status : <true|false>
155
- auto_dev_server : <true|false>
156
-
157
- Config written to .codebyplan/cmux.json
158
-
159
- Next: on the next SessionStart, codebyplan cmux-sync will apply the color
160
- automatically. Run `/cbp-setup-cmux --force` at any time to reconfigure.
161
- ```
162
-
163
- ## Key Rules
164
-
165
- - Atomic write (tmp + mv) — never leaves cmux.json in a partial state
166
- - Deep-merge with `. * $new` — sibling keys from future schema versions are preserved
167
- - Color apply is best-effort — a missing cmux binary never causes an error
168
- - `auto_status` and `auto_dev_server` default to `true` when absent (documented in
169
- `packages/codebyplan-package/src/lib/types.ts` CmuxConfig)
170
- - cmux.json is COMMITTED (not gitignored) — workspace color is shared across team members