Skip to content

Add workspace.set_color API and color-coded notifications - #1084

Closed
apollow wants to merge 6 commits into
manaflow-ai:mainfrom
apollow:feature/workspace-color-api
Closed

apollow wants to merge 6 commits into
manaflow-ai:mainfrom
apollow:feature/workspace-color-api

Conversation

@apollow

@apollow apollow commented Mar 8, 2026 •

Copy link
Copy Markdown

Summary

  • Add workspace.set_color v2 socket method + cmux set-workspace-color CLI command for programmatic workspace color control
  • Add custom_color field to workspace.list response
  • Notifications auto-inherit workspace color — colored dot + left-edge bar in notification panel
  • Add --color flag on cmux notify for explicit color override
  • Add skill documentation at skills/cmux-workspace-colors/

Details

New CLI commands

# Set workspace tab color
cmux set-workspace-color "#C0392B"
cmux set-workspace-color --workspace workspace:2 "#1565C0"
cmux set-workspace-color --clear

# Notifications inherit workspace color automatically
cmux notify --title "Build done" --body "All tests passed"

# Or override explicitly
cmux notify --title "Error" --body "Build failed" --color "#C0392B"

How it works

  1. set-workspace-color calls the existing tabManager.setTabColor() via a new workspace.set_color v2 method
  2. All notification creation paths (v1 + v2) resolve color = explicit ?? workspace.customColor
  3. NotificationRow renders the color as a dot indicator + left-edge bar on the card
  4. workspace.list now includes custom_color so tools can query assigned colors

Files changed

File Change
Sources/TerminalController.swift New workspace.set_color v2 handler, color in workspace.list, color in all notification methods
Sources/TerminalNotificationStore.swift color field on TerminalNotification struct
Sources/NotificationsPage.swift Color-coded dot + left-edge bar rendering
Sources/GhosttyTerminalView.swift Terminal-triggered notifications inherit workspace color
CLI/cmux.swift set-workspace-color command, --color on notify, migrated notify to v2
skills/cmux-workspace-colors/ Skill docs + command reference

Test plan

  • Build with ./scripts/reload.sh --tag workspace-colors
  • Run cmux set-workspace-color "#C0392B" — verify sidebar tab turns red
  • Run cmux notify --title Test --body Hello — verify notification shows red dot + left-edge bar
  • Run cmux notify --title Test --color "#1565C0" — verify explicit blue overrides workspace red
  • Run cmux set-workspace-color --clear — verify tab returns to default
  • Run cmux list-workspaces --json — verify custom_color field present
  • Verify cmux set-workspace-color --clear "#C0392B" errors (mutually exclusive)
  • CI unit tests pass

Summary by cubic

Add workspace color support and color‑coded notifications. Workspaces can set a custom color, notifications inherit it by default, and the API/CLI now require an explicit color key (use null to clear).

  • New Features

    • Added v2 workspace.set_color API and cmux set-workspace-color CLI; accepts #RRGGBB or null to clear (--clear and a color are mutually exclusive).
    • Notifications inherit the workspace color by default; override with cmux notify --color. UI shows a colored dot and left-edge bar. workspace.list returns custom_color, and notification create responses and notification.list include color. Docs added at skills/cmux-workspace-colors/.
  • Bug Fixes

    • Stricter color validation in all v2 notification methods: validate #RRGGBB and reject non-string/invalid types.
    • CLI: notify --color now requires a value; set-workspace-color rejects unknown flags; notify usage/help show workspace/surface id|ref|index.
    • workspace.set_color requires an explicit color param (missing key returns invalid_params); cmux set-workspace-color --clear sends explicit null; cache window ID in v2 notification responses.

Written for commit 3bf469e. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Add CLI command to set/clear workspace colors and a --color option for notify; notifications can inherit or override workspace color.
    • Notification UI and desktop notifications now show workspace colors (indicator dot and leading accent) and workspace listings include color fields.
  • Documentation

    • New guides and command reference documenting workspace colors, examples, and CLI usage.

Add programmatic workspace color control via CLI and socket API,
and propagate workspace colors to notifications for visual identification.

New features:
- workspace.set_color v2 method + set-workspace-color CLI command
- custom_color field in workspace.list response
- Notifications auto-inherit workspace color (dot + left-edge bar)
- --color flag on cmux notify for explicit override
- Skill documentation in skills/cmux-workspace-colors/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Mar 8, 2026

Copy link
Copy Markdown

@apollow is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Mar 8, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds workspace color management end-to-end: CLI subcommand to set/clear workspace colors, a v2 workspace.set_color API, propagation of workspace color into notification creation/listings and desktop/UI rendering, plus docs for commands and palette.

Changes

Cohort / File(s) Summary
CLI & Commands
CLI/cmux.swift
Adds set-workspace-color subcommand; validates --workspace and mutually-exclusive --clear/color; extends notify with optional --color, workspace/surface normalization, and chooses v2 notification methods.
V2 Controller & API
Sources/TerminalController.swift
Adds v2WorkspaceSetColor (v2 method workspace.set_color), exposes it in v2 capabilities, includes custom_color in workspace list, and threads color through notification create/list responses (parse/derive/return color).
Notification Model & Store
Sources/TerminalNotificationStore.swift
Adds color: String? to TerminalNotification and addNotification(..., color: String? = nil) parameter; persists color on creation.
Terminal UI Notification Emission
Sources/GhosttyTerminalView.swift
Computes per-tab/workspace color and passes color: wsColor into addNotification desktop-notification calls.
In-app Notification Display
Sources/NotificationsPage.swift
NotificationRow reads notification.color and colorScheme; computes dotColor, uses it for dot/stroke, and renders a 3pt leading colored bar when color is present.
Docs / Skill
skills/cmux-workspace-colors/SKILL.md, skills/cmux-workspace-colors/references/commands.md
Adds user docs: set/clear workspace colors, notify --color behavior, JSON examples for custom_color/color, palette and usage examples.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI (cmux)
    participant Controller as TerminalController (v2)
    participant TabMgr as TabManager / Workspace state
    participant NotifStore as TerminalNotificationStore
    participant UI as NotificationsPage

    CLI->>Controller: set-workspace-color(workspace_id, color|null)
    Controller->>TabMgr: update workspace.customColor
    TabMgr-->>Controller: confirmation

    CLI->>Controller: notify(target?, surface?, color?)
    Controller->>TabMgr: resolve workspace/surface handles (normalize)
    Controller->>NotifStore: create notification (workspace?, surface?, color resolved)
    NotifStore->>NotifStore: persist TerminalNotification(color)
    NotifStore-->>UI: publish notification
    UI->>UI: compute dotColor from notification.color + colorScheme
    UI-->>User: render notification with colored dot and leading bar
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nibble keys and paint each space,

I hop and mark a bright new place,
A toast now wears its rainbow coat,
Tabs and toasts sing one small note,
Hooray — my hues parade in place!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add workspace.set_color API and color-coded notifications' clearly summarizes the main changes: introducing a new API method, a CLI command, and color-coded UI features.
Description check ✅ Passed PR description is comprehensive and well-structured, covering summary, detailed changes, files affected, test plan, and usage examples.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Mar 8, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires workspace color support end-to-end: a new workspace.set_color v2 socket method and cmux set-workspace-color CLI command let callers assign a hex color to any workspace tab. All notification creation paths (v1 and v2, including terminal-triggered ones in GhosttyTerminalView) then inherit customColor from the workspace automatically, with an explicit --color override available on cmux notify. NotificationsPage renders the color as a dot indicator and a left-edge bar on each notification card.

Key issues found:

  • Missing color validation in notification methods — v2NotificationCreate, v2NotificationCreateForSurface, and v2NotificationCreateForTarget all accept the color param without calling WorkspaceTabColorSettings.normalizedHex(). An invalid hex string is silently stored and returned by notification.list, while the UI falls back to the accent color without any error. v2WorkspaceSetColor correctly validates and should be the template here.
  • list-notifications --json missing color field — The CLI list-notifications command still uses the v1 list_notifications socket command, so the color field documented in references/commands.md and added to the v2 notification.list response is never surfaced to callers of the CLI.
  • v2ResolveWindowId called twice on the same line — In both v2NotificationCreateForSurface and v2NotificationCreateForTarget, the window ID is resolved twice when constructing the ok result dictionary; should be stored in a local variable first.

Confidence Score: 3/5

  • Safe to merge with minor fixes — no crashes or data loss, but invalid color strings are silently stored and the CLI list-notifications omits the new color field.
  • The feature is well-structured and color inheritance is consistently applied across all notification creation paths. The two logic issues (missing color validation in notification methods and the missing color field in list-notifications --json) cause silent bad data storage and an API contract gap between the documented and actual CLI behavior, both of which should be addressed before shipping.
  • Sources/TerminalController.swift (color validation in notification methods) and CLI/cmux.swift (list-notifications missing color field)

Important Files Changed

Filename Overview
Sources/TerminalController.swift Adds workspace.set_color v2 handler, color inheritance in all 3 notification creation paths, and custom_color in workspace.list. Two issues: color strings passed via --color are stored without hex validation in all notification methods, and v2ResolveWindowId is called twice in the same expression in v2NotificationCreateForSurface and v2NotificationCreateForTarget.
CLI/cmux.swift Adds set-workspace-color command, --color flag on notify, and migrates notify to v2. list-notifications --json still uses the v1 path and omits the new color field from its output, making it inconsistent with the documented notification.list v2 response.
Sources/NotificationsPage.swift Adds color-coded dot indicator and left-edge bar to NotificationRow using WorkspaceTabColorSettings.displayColor. Falls back cleanly to cmuxAccentColor() when no color is set. Implementation is straightforward and correct.
Sources/TerminalNotificationStore.swift Adds optional color: String? field to TerminalNotification and updates addNotification with a defaulted color parameter. Clean, backward-compatible change.
Sources/GhosttyTerminalView.swift Two terminal-triggered notification sites updated to look up customColor from tabManager.tabs and pass it to addNotification. Both call sites are consistent with each other.

Sequence Diagram

sequenceDiagram
    participant CLI as cmux CLI
    participant TC as TerminalController (v2)
    participant TM as TabManager
    participant NS as TerminalNotificationStore
    participant UI as NotificationsPage

    Note over CLI,UI: set-workspace-color flow
    CLI->>TC: workspace.set_color {workspace_id, color}
    TC->>TC: normalizedHex(color) validation
    TC->>TM: setTabColor(tabId, color)
    TC-->>CLI: {workspace_id, color}

    Note over CLI,UI: notify flow (color inheritance)
    CLI->>TC: notification.create / create_for_surface / create_for_target
    TC->>TM: resolve workspace → ws.customColor
    TC->>TC: resolvedColor = explicit color ?? ws.customColor
    TC->>NS: addNotification(..., color: resolvedColor)
    NS-->>TC: notification stored
    TC-->>CLI: {workspace_id, surface_id, ...}

    Note over UI: rendering
    UI->>NS: read notifications
    NS-->>UI: [TerminalNotification(color?)]
    UI->>UI: WorkspaceTabColorSettings.displayColor(hex)
    UI->>UI: render dot + left-edge bar in resolved color
Loading

Comments Outside Diff (1)

  1. CLI/cmux.swift, line 1529-1548 (link)

    list-notifications --json does not expose the new color field

    The list-notifications command still calls the v1 list_notifications socket command and manually maps the result — color is absent from the output dictionary (lines 1534–1541). The PR description and skills/cmux-workspace-colors/references/commands.md explicitly document that notification.list returns color in each entry, so this is a user-visible inconsistency: cmux list-notifications --json silently omits the field that callers would rely on for color-aware tooling.

    The fix is to migrate list-notifications to the v2 notification.list method (as was done for notify), or at minimum add "color" from the parsed notification data to the output dict.

Last reviewed commit: 6bca50b

Comment thread Sources/TerminalController.swift Outdated
let title = (params["title"] as? String) ?? "Notification"
let subtitle = (params["subtitle"] as? String) ?? ""
let body = (params["body"] as? String) ?? ""
let color = v2String(params, "color")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No color validation in notification creation methods

The color param accepted here is stored directly into TerminalNotification.color without any format validation. v2WorkspaceSetColor validates the hex with WorkspaceTabColorSettings.normalizedHex(), but all three notification creation paths (v2NotificationCreate, v2NotificationCreateForSurface, v2NotificationCreateForTarget) skip this check entirely.

If a caller passes "notacolor" or "#GGGGGG" via --color, WorkspaceTabColorSettings.displayColor(hex:colorScheme:) in NotificationRow will return nil and fall back silently to the accent color — but the invalid string still gets persisted in TerminalNotificationStore and is returned through notification.list.

Apply the same guard that v2WorkspaceSetColor uses before storing the value:

let color: String?
if let raw = v2String(params, "color") {
    guard let normalized = WorkspaceTabColorSettings.normalizedHex(raw) else {
        return .err(code: "invalid_params", message: "Invalid color hex. Use format #RRGGBB.", data: nil)
    }
    color = normalized
} else {
    color = nil
}

This same pattern needs to be applied in v2NotificationCreateForSurface and v2NotificationCreateForTarget as well.

Comment thread Sources/TerminalController.swift Outdated
body: body,
color: resolvedColor
)
result = .ok(["workspace_id": ws.id.uuidString, "workspace_ref": v2Ref(kind: .workspace, uuid: ws.id), "surface_id": surfaceId.uuidString, "surface_ref": v2Ref(kind: .surface, uuid: surfaceId), "window_id": v2OrNull(v2ResolveWindowId(tabManager: tabManager)?.uuidString), "window_ref": v2Ref(kind: .window, uuid: v2ResolveWindowId(tabManager: tabManager))])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v2ResolveWindowId called twice in the same expression

v2ResolveWindowId(tabManager: tabManager) is invoked twice on this single line — once for window_id and again for window_ref. Depending on the implementation, this may trigger duplicate work on the main thread. The same issue exists in v2NotificationCreateForTarget at the equivalent result-construction line.

Store the result in a local variable before building the dictionary:

let windowId = v2ResolveWindowId(tabManager: tabManager)
result = .ok([
    "workspace_id": ws.id.uuidString,
    "workspace_ref": v2Ref(kind: .workspace, uuid: ws.id),
    "surface_id": surfaceId.uuidString,
    "surface_ref": v2Ref(kind: .surface, uuid: surfaceId),
    "window_id": v2OrNull(windowId?.uuidString),
    "window_ref": v2Ref(kind: .window, uuid: windowId)
])

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Sources/GhosttyTerminalView.swift (1)

1723-1734: ⚠️ Potential issue | 🟠 Major

Resolve wsColor from the owning tab manager, not the global one.

Line 1727 hard-codes AppDelegate.shared?.tabManager?.tabs, so notifications emitted from terminals in another window/manager will silently lose their workspace color. This breaks the new inheritance behavior for that path.

🛠️ Suggested fix
             performOnMain {
                 let tabTitle = AppDelegate.shared?.tabManager?.titleForTab(tabId) ?? "Terminal"
                 let command = actionTitle.isEmpty ? tabTitle : actionTitle
                 let body = actionBody
-                let wsColor = AppDelegate.shared?.tabManager?.tabs.first(where: { $0.id == tabId })?.customColor
+                let manager = AppDelegate.shared?.tabManagerFor(tabId: tabId) ?? AppDelegate.shared?.tabManager
+                let wsColor = manager?.tabs.first(where: { $0.id == tabId })?.customColor
                 TerminalNotificationStore.shared.addNotification(
                     tabId: tabId,
                     surfaceId: surfaceId,
                     title: command,
                     subtitle: "",
                     body: body,
                     color: wsColor
                 )
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/GhosttyTerminalView.swift` around lines 1723 - 1734, The code
resolves wsColor using the global AppDelegate.shared?.tabManager?.tabs which can
pick the wrong window's tab list; change the lookup to use the owning tab
manager for this terminal (use the same TabManager instance that provided
titleForTab(tabId) or the owner reference available in GhosttyTerminalView) so
wsColor is fetched from that manager's tabs (e.g., replace
AppDelegate.shared?.tabManager?.tabs.first(where: { $0.id == tabId }) with the
owningManager.tabs.first(where: { $0.id == tabId })). Ensure you still pass
tabId, surfaceId, title (command), subtitle and body into
TerminalNotificationStore.shared.addNotification after fetching wsColor from the
correct tab manager.
Sources/TerminalController.swift (1)

5310-5339: ⚠️ Potential issue | 🟠 Major

Validate notification override colors the same way workspace colors are validated.

These paths persist any non-empty string as a notification color. That bypasses the hex normalizer used by workspace.set_color, so bad override values can leak into notification.list and the new colorized notification UI. Normalize here too and fail fast on invalid overrides.

Suggested fix pattern
-        let color = v2String(params, "color")
+        let color: String?
+        if params.keys.contains("color") {
+            if params["color"] is NSNull {
+                color = nil
+            } else if let raw = params["color"] as? String,
+                      let normalized = WorkspaceTabColorSettings.normalizedHex(raw) {
+                color = normalized
+            } else {
+                return .err(code: "invalid_params", message: "Invalid color hex. Use format `#RRGGBB`.", data: nil)
+            }
+        } else {
+            color = nil
+        }

Apply the same parsing in all three notification creation methods before resolvedColor is computed.

Also applies to: 5352-5376, 5392-5416

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TerminalController.swift` around lines 5310 - 5339, The notification
handlers currently accept any non-empty string for color and set resolvedColor
directly; instead, run the same workspace color normalization/parsing logic used
by workspace.set_color (the hex normalizer) on the incoming override before
computing resolvedColor and calling
TerminalNotificationStore.shared.addNotification (e.g., in the block that
computes resolvedColor and calls addNotification). If the parser fails, return
an error V2CallResult (fail fast) rather than persisting the invalid value;
apply this fix to all three notification creation methods (the blocks around
resolvedColor and TerminalNotificationStore.shared.addNotification).
🧹 Nitpick comments (2)
skills/cmux-workspace-colors/references/commands.md (2)

22-26: Consider showing both set and clear payload examples.

The text mentions omitting color to clear, but showing both examples would make this more immediately clear to developers:

**Socket method:** `workspace.set_color`
```json
// Set color
{ "workspace_id": "<uuid>", "color": "#C0392B" }

// Clear color
{ "workspace_id": "<uuid>" }
```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/cmux-workspace-colors/references/commands.md` around lines 22 - 26,
Update the docs for the socket method workspace.set_color to include both a
"set" and a "clear" JSON payload example so it's explicit how to clear the
color; show one example with { "workspace_id": "<uuid>", "color": "#C0392B" }
and another with only { "workspace_id": "<uuid>" } and add a short comment label
for each (e.g., "Set color" and "Clear color") so developers immediately see
both use cases.

7-9: Clarify mutual exclusivity of --clear and <#hex> in syntax.

The current syntax shows <#hex> as required, but line 19 demonstrates using --clear without providing a color. Standard CLI documentation conventions would make the mutual exclusivity clearer, for example:

# Either set a color:
cmux set-workspace-color [--workspace <id|ref>] [--] <#hex>

# Or clear the color:
cmux set-workspace-color [--workspace <id|ref>] --clear

Or using a single line with alternation:

cmux set-workspace-color [--workspace <id|ref>] (--clear | [--] <#hex>)

This prevents users from being confused about whether both arguments are allowed simultaneously.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/cmux-workspace-colors/references/commands.md` around lines 7 - 9,
Update the usage line for the `cmux set-workspace-color` command to indicate
that `--clear` and `<#hex>` are mutually exclusive; specifically, modify the
signature shown (`cmux set-workspace-color [--workspace <id|ref>] [--clear] [--]
<#hex>`) to either present two separate usages (one for setting a color with
`<#hex>` and one for clearing with `--clear`) or use an alternation group such
as `(--clear | [--] <#hex>)` so readers of the `commands.md` doc clearly
understand they cannot supply both `--clear` and a hex color together when
invoking `cmux set-workspace-color`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 1369-1384: The code currently strips only "--clear" and then
treats remaining items as the color, which lets unknown flags like "--colour"
slip into the color string; before building colorArgs validate rem1 for unknown
flags by checking rem1.filter { $0.hasPrefix("--") && $0 != "--" } and if that
yields any entries throw a CLIError listing the invalid flags; perform this
check after computing isClear/rem1 (and before computing colorArgs/joining into
color) so functions like resolveWorkspaceId, the params dictionary, and the
set-workspace-color flow reject unknown flags instead of folding them into the
color value.
- Around line 1375-1376: The workspace-ID resolution needs to be scoped to the
selected window: change calls to resolveWorkspaceId (here in
set-workspace-color) to pass the window handle (e.g., windowArg/windowHandle)
and update resolveWorkspaceId to accept windowHandle: when a numeric workspace
identifier is given and a window override is present, call the backend
workspace.list with parameters including ["window_id": windowHandle] so the
lookup only searches workspaces in that window; ensure the returned wsId is
derived from that filtered list and keep the existing params dictionary (var
params) intact for later use.

In `@Sources/TerminalController.swift`:
- Around line 3134-3143: v2String(params, "color") returns nil for both missing
and non-string values, so currently inputs like {"color": 123} silently clear
color; fix by explicitly checking whether the "color" key exists in params and
branch on its actual value: if params does not contain "color", treat it as “no
change” (keep existing behavior outside this snippet); if params["color"] is
null/NSNull, set color = nil to explicitly clear; if params["color"] is a
String, validate it with WorkspaceTabColorSettings.normalizedHex (as currently
done) and use the normalized value; otherwise (any other non-string, non-null
value) return .err(code: "invalid_params", message: "Invalid color: expected
string or null.", data: nil). Update the logic around colorRaw / v2String to use
this presence/type check so malformed non-string values no longer silently clear
state.

---

Outside diff comments:
In `@Sources/GhosttyTerminalView.swift`:
- Around line 1723-1734: The code resolves wsColor using the global
AppDelegate.shared?.tabManager?.tabs which can pick the wrong window's tab list;
change the lookup to use the owning tab manager for this terminal (use the same
TabManager instance that provided titleForTab(tabId) or the owner reference
available in GhosttyTerminalView) so wsColor is fetched from that manager's tabs
(e.g., replace AppDelegate.shared?.tabManager?.tabs.first(where: { $0.id ==
tabId }) with the owningManager.tabs.first(where: { $0.id == tabId })). Ensure
you still pass tabId, surfaceId, title (command), subtitle and body into
TerminalNotificationStore.shared.addNotification after fetching wsColor from the
correct tab manager.

In `@Sources/TerminalController.swift`:
- Around line 5310-5339: The notification handlers currently accept any
non-empty string for color and set resolvedColor directly; instead, run the same
workspace color normalization/parsing logic used by workspace.set_color (the hex
normalizer) on the incoming override before computing resolvedColor and calling
TerminalNotificationStore.shared.addNotification (e.g., in the block that
computes resolvedColor and calls addNotification). If the parser fails, return
an error V2CallResult (fail fast) rather than persisting the invalid value;
apply this fix to all three notification creation methods (the blocks around
resolvedColor and TerminalNotificationStore.shared.addNotification).

---

Nitpick comments:
In `@skills/cmux-workspace-colors/references/commands.md`:
- Around line 22-26: Update the docs for the socket method workspace.set_color
to include both a "set" and a "clear" JSON payload example so it's explicit how
to clear the color; show one example with { "workspace_id": "<uuid>", "color":
"#C0392B" } and another with only { "workspace_id": "<uuid>" } and add a short
comment label for each (e.g., "Set color" and "Clear color") so developers
immediately see both use cases.
- Around line 7-9: Update the usage line for the `cmux set-workspace-color`
command to indicate that `--clear` and `<#hex>` are mutually exclusive;
specifically, modify the signature shown (`cmux set-workspace-color [--workspace
<id|ref>] [--clear] [--] <#hex>`) to either present two separate usages (one for
setting a color with `<#hex>` and one for clearing with `--clear`) or use an
alternation group such as `(--clear | [--] <#hex>)` so readers of the
`commands.md` doc clearly understand they cannot supply both `--clear` and a hex
color together when invoking `cmux set-workspace-color`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2e7f718a-f389-41a6-b58d-e19b777f6394

📥 Commits

Reviewing files that changed from the base of the PR and between 0f29735 and 6bca50b.

📒 Files selected for processing (7)
  • CLI/cmux.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/NotificationsPage.swift
  • Sources/TerminalController.swift
  • Sources/TerminalNotificationStore.swift
  • skills/cmux-workspace-colors/SKILL.md
  • skills/cmux-workspace-colors/references/commands.md

Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift
Comment on lines +1375 to +1376
let wsId = try resolveWorkspaceId(workspaceArg, client: client)
var params: [String: Any] = ["workspace_id": wsId]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Scope workspace index resolution to the selected --window.

Line 1375 calls resolveWorkspaceId without any window context. cmux --window <w> set-workspace-color --workspace 1 ... can therefore update the first workspace index 1 returned across all windows, not the workspace inside the selected window.

🧭 Suggested direction
-            let wsId = try resolveWorkspaceId(workspaceArg, client: client)
+            let windowHandle = try normalizeWindowHandle(windowId, client: client)
+            let wsId = try resolveWorkspaceId(workspaceArg, windowHandle: windowHandle, client: client)
private func resolveWorkspaceId(_ raw: String?, windowHandle: String?, client: SocketClient) throws -> String {
    // For numeric handles, call workspace.list with ["window_id": windowHandle]
    // when a window override is present.
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 1375 - 1376, The workspace-ID resolution needs
to be scoped to the selected window: change calls to resolveWorkspaceId (here in
set-workspace-color) to pass the window handle (e.g., windowArg/windowHandle)
and update resolveWorkspaceId to accept windowHandle: when a numeric workspace
identifier is given and a window override is present, call the backend
workspace.list with parameters including ["window_id": windowHandle] so the
lookup only searches workspaces in that window; ensure the returned wsId is
derived from that filtered list and keep the existing params dictionary (var
params) intact for later use.

Comment thread CLI/cmux.swift Outdated
Comment thread Sources/TerminalController.swift Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="CLI/cmux.swift">

<violation number="1" location="CLI/cmux.swift:7006">
P3: The usage text incorrectly implies `<#hex>` is always required, even though `--clear` is a valid no-color path.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread CLI/cmux.swift Outdated
- Validate color hex in all 3 notification creation methods
- Cache v2ResolveWindowId to avoid duplicate calls
- Reject unknown flags in set-workspace-color CLI
- Handle non-string color params in workspace.set_color
- Fix usage text to show --clear and hex as mutually exclusive

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="CLI/cmux.swift">

<violation number="1" location="CLI/cmux.swift:4574">
P3: The usage line now says `<id|ref|index>` but the `--workspace` flag description still says `<id|ref>`. Update the flag description to match.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread CLI/cmux.swift
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
CLI/cmux.swift (1)

1378-1388: ⚠️ Potential issue | 🟠 Major

Pass --window into workspace resolution instead of depending on current selection.

Line 1378 still resolves the workspace without window context. For cmux --window <w> set-workspace-color --workspace 1 ..., this only works because the top-level --window path focuses that window first, so a non-focus command mutates selection just to disambiguate the index.

🧭 Suggested direction
-            let wsId = try resolveWorkspaceId(workspaceArg, client: client)
+            let windowHandle = try normalizeWindowHandle(windowId, client: client)
+            let wsId = try resolveWorkspaceId(workspaceArg, windowHandle: windowHandle, client: client)

Update resolveWorkspaceId outside this hunk to use workspace.list with window_id when a window handle is present.

Based on learnings, "Socket/CLI commands must not steal macOS app focus; only explicit focus-intent commands may mutate in-app focus/selection".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 1378 - 1388, The code calls
resolveWorkspaceId(workspaceArg, client: client) without the optional window
context, causing non-focus commands to mutate app selection; pass the current
window handle into resolveWorkspaceId (e.g., resolveWorkspaceId(workspaceArg,
window: windowHandle, client: client)) when present instead of relying on global
focus, and update the resolveWorkspaceId implementation to call the
"workspace.list" RPC with params including "window_id" when a window handle is
supplied so workspace indices are resolved in the context of that window; keep
existing behavior for the no-window case and do not change
sendV2("workspace.set_color", params: params) usage other than using the
workspace id returned by the updated resolver.
Sources/TerminalController.swift (1)

3134-3148: ⚠️ Potential issue | 🟠 Major

Require an explicit color value for workspace.set_color.

Line 3146 currently treats an omitted color key as nil, so a request that forgets color will succeed and clear the workspace color. That makes an incomplete call destructive. Only explicit null should clear; a missing key should return invalid_params.

💡 Suggested fix
-        let color: String?
-        if params.keys.contains("color") {
-            if params["color"] is NSNull {
-                color = nil
-            } else if let colorRaw = params["color"] as? String {
-                guard let normalized = WorkspaceTabColorSettings.normalizedHex(colorRaw) else {
-                    return .err(code: "invalid_params", message: "Invalid color hex. Use format `#RRGGBB`.", data: nil)
-                }
-                color = normalized
-            } else {
-                return .err(code: "invalid_params", message: "color must be a string or null", data: nil)
-            }
-        } else {
-            color = nil
-        }
+        guard params.keys.contains("color") else {
+            return .err(code: "invalid_params", message: "Missing color. Use null to clear.", data: nil)
+        }
+
+        let color: String?
+        if params["color"] is NSNull {
+            color = nil
+        } else if let colorRaw = params["color"] as? String {
+            guard let normalized = WorkspaceTabColorSettings.normalizedHex(colorRaw) else {
+                return .err(code: "invalid_params", message: "Invalid color hex. Use format `#RRGGBB`.", data: nil)
+            }
+            color = normalized
+        } else {
+            return .err(code: "invalid_params", message: "color must be a string or null", data: nil)
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TerminalController.swift` around lines 3134 - 3148, The handler for
workspace.set_color currently treats a missing "color" key as nil (clearing the
color); change the logic so that if the "color" key is absent you return
.err(code: "invalid_params", message: "color must be provided", data: nil)
instead of setting color = nil, while preserving the existing branches that
accept NSNull (explicit null -> color = nil) and String (validate via
WorkspaceTabColorSettings.normalizedHex and return the same "invalid_params"
error on bad hex); update the error path for missing key accordingly in the same
scope where you reference WorkspaceTabColorSettings.normalizedHex so only
explicit null clears the workspace color.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/TerminalController.swift`:
- Around line 5318-5326: The current parsing uses v2String(params, "color")
which treats non-strings and whitespace-only strings as nil and silently
inherits ws.customColor; change each occurrence so that if params contains the
"color" key but v2String(params, "color") returns nil you return .err(code:
"invalid_params", message: "Invalid color hex. Use format `#RRGGBB`.", data: nil)
immediately; otherwise (key absent) set color = nil, and if a string is present
validate via WorkspaceTabColorSettings.normalizedHex(raw) as before. Update all
three code paths that call v2String(params, "color") (the blocks using variable
color and WorkspaceTabColorSettings.normalizedHex) to perform this presence/type
check.

---

Duplicate comments:
In `@CLI/cmux.swift`:
- Around line 1378-1388: The code calls resolveWorkspaceId(workspaceArg, client:
client) without the optional window context, causing non-focus commands to
mutate app selection; pass the current window handle into resolveWorkspaceId
(e.g., resolveWorkspaceId(workspaceArg, window: windowHandle, client: client))
when present instead of relying on global focus, and update the
resolveWorkspaceId implementation to call the "workspace.list" RPC with params
including "window_id" when a window handle is supplied so workspace indices are
resolved in the context of that window; keep existing behavior for the no-window
case and do not change sendV2("workspace.set_color", params: params) usage other
than using the workspace id returned by the updated resolver.

In `@Sources/TerminalController.swift`:
- Around line 3134-3148: The handler for workspace.set_color currently treats a
missing "color" key as nil (clearing the color); change the logic so that if the
"color" key is absent you return .err(code: "invalid_params", message: "color
must be provided", data: nil) instead of setting color = nil, while preserving
the existing branches that accept NSNull (explicit null -> color = nil) and
String (validate via WorkspaceTabColorSettings.normalizedHex and return the same
"invalid_params" error on bad hex); update the error path for missing key
accordingly in the same scope where you reference
WorkspaceTabColorSettings.normalizedHex so only explicit null clears the
workspace color.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4b937196-a5a5-4813-a300-a31e69932a81

📥 Commits

Reviewing files that changed from the base of the PR and between 6bca50b and 56d1942.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • Sources/TerminalController.swift

Comment thread Sources/TerminalController.swift
- Missing color key now returns invalid_params (only null clears)
- CLI --clear sends explicit NSNull instead of omitting key
- Update skill docs: usage syntax, set/clear examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
CLI/cmux.swift (1)

1378-1391: ⚠️ Potential issue | 🟠 Major

Resolve set-workspace-color against --window without relying on focus side effects.

Line 1378 still calls resolveWorkspaceId(workspaceArg, client:), which ignores the explicit window context. Right now this only works because the command path focuses the target window earlier, so a passive metadata command mutates selection/focus as a routing side effect. Please thread the normalized window_id into workspace resolution instead. Based on learnings: Socket/CLI commands must not steal macOS app focus; only explicit focus-intent commands may mutate in-app focus/selection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 1378 - 1391, The call to
resolveWorkspaceId(workspaceArg, client:) currently ignores an explicit --window
context and relies on side-effect focus changes; update workspace resolution to
accept and use the normalized window id (the parsed --window value) instead of
relying on global focus—e.g., add/use a resolveWorkspaceId overload or parameter
that takes windowId and pass the normalized window id when constructing params
for workspace.set_color; ensure the code path that computes the window id (the
CLI's window normalization) is used and do not perform any focus-changing
operations during resolution.
Sources/TerminalController.swift (1)

5318-5326: ⚠️ Potential issue | 🟠 Major

Reject invalid notification color values instead of inheriting.

These three paths still parse via v2String(params, "color"), so values like 123 or " " collapse to nil and silently fall back to ws.customColor. That breaks the new validation contract for bad color params.

Suggested fix
-        let color: String?
-        if let raw = v2String(params, "color") {
-            guard let normalized = WorkspaceTabColorSettings.normalizedHex(raw) else {
-                return .err(code: "invalid_params", message: "Invalid color hex. Use format `#RRGGBB`.", data: nil)
-            }
-            color = normalized
-        } else {
-            color = nil
-        }
+        let color: String?
+        if let rawValue = params["color"] {
+            if rawValue is NSNull {
+                color = nil
+            } else if let raw = rawValue as? String {
+                guard let normalized = WorkspaceTabColorSettings.normalizedHex(raw) else {
+                    return .err(code: "invalid_params", message: "Invalid color hex. Use format `#RRGGBB`.", data: nil)
+                }
+                color = normalized
+            } else {
+                return .err(code: "invalid_params", message: "color must be a hex string or null", data: nil)
+            }
+        } else {
+            color = nil
+        }

Also applies to: 5368-5376, 5417-5425

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/TerminalController.swift` around lines 5318 - 5326, The current
parsing uses v2String(params, "color") which treats invalid inputs like "123" or
whitespace as nil and silently falls back to ws.customColor; instead detect when
a color key was provided but normalization failed and return an error. Update
the color parsing branch (the code that calls v2String and
WorkspaceTabColorSettings.normalizedHex) to: first check whether the params
contain the "color" key (e.g., params["color"] != nil or a helper that checks
presence); if present, call v2String to get raw, then if
WorkspaceTabColorSettings.normalizedHex(raw) returns nil return .err(code:
"invalid_params", message: "Invalid color hex. Use format `#RRGGBB`.", data: nil);
only treat color as nil when the key was not provided at all so inheritance to
ws.customColor remains unchanged. Ensure the same fix is applied to the other
two occurrences that call v2String/normalizedHex.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 4861-4867: The help text for the new notify command currently
lists "--workspace <id|ref>" and "--surface <id|ref>" but the implementation
accepts indexes via normalizeWorkspaceHandle and normalizeSurfaceHandle; update
the flag usage strings for the notify command (the lines showing --workspace and
--surface) to advertise "<id|ref|index>" (or "|index") to match other v2-backed
commands so users know indexes are accepted; ensure both occurrences (the block
around the notify flags and the duplicate at the other documented location) are
changed to the same wording.
- Around line 1509-1521: The CLI currently uses optionValue(commandArgs, name:
"--color") and accepts the next token even if it's another flag, so invoking
`--color` with no value (e.g. `--color --workspace ...`) produces a server error
later; change the notify parsing to detect a provided --color flag with a
missing/invalid value: after calling optionValue for "--color" (and/or by
checking commandArgs contains "--color"), validate that the returned color is
non-nil and does not start with "-" (indicating another flag); if invalid, throw
or return a user-facing parse error immediately before building params (the code
around the color variable and the params dictionary) so the CLI fails fast with
a clear message.

---

Duplicate comments:
In `@CLI/cmux.swift`:
- Around line 1378-1391: The call to resolveWorkspaceId(workspaceArg, client:)
currently ignores an explicit --window context and relies on side-effect focus
changes; update workspace resolution to accept and use the normalized window id
(the parsed --window value) instead of relying on global focus—e.g., add/use a
resolveWorkspaceId overload or parameter that takes windowId and pass the
normalized window id when constructing params for workspace.set_color; ensure
the code path that computes the window id (the CLI's window normalization) is
used and do not perform any focus-changing operations during resolution.

In `@Sources/TerminalController.swift`:
- Around line 5318-5326: The current parsing uses v2String(params, "color")
which treats invalid inputs like "123" or whitespace as nil and silently falls
back to ws.customColor; instead detect when a color key was provided but
normalization failed and return an error. Update the color parsing branch (the
code that calls v2String and WorkspaceTabColorSettings.normalizedHex) to: first
check whether the params contain the "color" key (e.g., params["color"] != nil
or a helper that checks presence); if present, call v2String to get raw, then if
WorkspaceTabColorSettings.normalizedHex(raw) returns nil return .err(code:
"invalid_params", message: "Invalid color hex. Use format `#RRGGBB`.", data: nil);
only treat color as nil when the key was not provided at all so inheritance to
ws.customColor remains unchanged. Ensure the same fix is applied to the other
two occurrences that call v2String/normalizedHex.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2cda8d8b-6a66-4a42-81ef-92cbf9663c85

📥 Commits

Reviewing files that changed from the base of the PR and between 56d1942 and e6ae5a2.

📒 Files selected for processing (3)
  • CLI/cmux.swift
  • Sources/TerminalController.swift
  • skills/cmux-workspace-colors/references/commands.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • skills/cmux-workspace-colors/references/commands.md

Comment thread CLI/cmux.swift
Comment thread CLI/cmux.swift Outdated
- Notification color: reject non-string/invalid types (not just strings)
- CLI notify: validate --color has a value before sending request
- Update notify help text to show index option for workspace/surface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
CLI/cmux.swift (2)

1378-1379: ⚠️ Potential issue | 🟠 Major

Scope set-workspace-color resolution to the selected window.

Line 1378 still goes through resolveWorkspaceId(...), which scans across windows for short refs and ignores --window for index lookups. cmux --window window:2 set-workspace-color --workspace workspace:1 ... can therefore recolor the first matching workspace in another window instead of the target one. This path should use the same window-aware normalization flow as the newer v2 commands instead of the global resolver.

🛠️ Suggested direction
-            let wsId = try resolveWorkspaceId(workspaceArg, client: client)
+            let windowHandle = try normalizeWindowHandle(windowId, client: client)
+            let wsId = try resolveWorkspaceId(workspaceArg, windowHandle: windowHandle, client: client)
private func resolveWorkspaceId(_ raw: String?, windowHandle: String?, client: SocketClient) throws -> String {
    // Reuse window-scoped workspace.list lookup when resolving short refs/indexes.
}

Based on learnings: Socket/CLI commands must not steal macOS app focus; only explicit focus-intent commands may mutate in-app focus/selection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 1378 - 1379, The set-workspace-color command
currently calls resolveWorkspaceId(workspaceArg, client: client) which uses the
global resolver that ignores the --window option and can match a workspace in
another window; change the resolution to the window-aware normalization used by
v2 commands by adding/using a variant like resolveWorkspaceId(_ raw: String?,
windowHandle: String?, client: SocketClient) (or pass the existing window handle
into the resolver) and perform a window-scoped workspace.list lookup for short
refs/indexes so index/short-ref resolution is limited to the selected window
instead of the global resolver; ensure set-workspace-color uses that
window-aware resolver and does not trigger focus changes.

7021-7021: ⚠️ Potential issue | 🟡 Minor

Advertise index handles in the top-level notify usage.

Line 7021 still documents --workspace <id|ref> / --surface <id|ref>, but Lines 1518-1519 normalize both through index-aware helpers. The summary usage should match the actual accepted handle formats.

📝 Suggested wording
-          notify --title <text> [--subtitle <text>] [--body <text>] [--color <#hex>] [--workspace <id|ref>] [--surface <id|ref>]
+          notify --title <text> [--subtitle <text>] [--body <text>] [--color <#hex>] [--workspace <id|ref|index>] [--surface <id|ref|index>]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` at line 7021, Update the top-level usage string for the
notify command (the line starting with "notify --title <text> ...") to advertise
the index-aware handle formats used by the helpers: replace "--workspace
<id|ref>" and "--surface <id|ref>" with the normalized form that includes index
(e.g. "--workspace <id|ref|index>" and "--surface <id|ref|index>"). Locate the
usage text emitted for the notify command in CLI/cmux.swift (the "notify --title
<text> ..." literal) and make the string match the normalized handle formats
used by the index-aware helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/TerminalController.swift`:
- Around line 5347-5355: The v2 create handlers omit the resolved color from
successful responses; update the response dictionaries in
v2NotificationCreateForSurface, v2NotificationCreateForTarget (and the shown
handler building result with window_id/window_ref) to include the resolvedColor
(or resolvedColor?.uuidString as appropriate) under the "color" key using the
existing v2OrNull/v2Ref helpers so the create response returns "color":
v2OrNull(resolvedColor?.uuidString) (or the equivalent reference) alongside
workspace_id/workspace_ref/surface_id/window_id entries.

---

Duplicate comments:
In `@CLI/cmux.swift`:
- Around line 1378-1379: The set-workspace-color command currently calls
resolveWorkspaceId(workspaceArg, client: client) which uses the global resolver
that ignores the --window option and can match a workspace in another window;
change the resolution to the window-aware normalization used by v2 commands by
adding/using a variant like resolveWorkspaceId(_ raw: String?, windowHandle:
String?, client: SocketClient) (or pass the existing window handle into the
resolver) and perform a window-scoped workspace.list lookup for short
refs/indexes so index/short-ref resolution is limited to the selected window
instead of the global resolver; ensure set-workspace-color uses that
window-aware resolver and does not trigger focus changes.
- Line 7021: Update the top-level usage string for the notify command (the line
starting with "notify --title <text> ...") to advertise the index-aware handle
formats used by the helpers: replace "--workspace <id|ref>" and "--surface
<id|ref>" with the normalized form that includes index (e.g. "--workspace
<id|ref|index>" and "--surface <id|ref|index>"). Locate the usage text emitted
for the notify command in CLI/cmux.swift (the "notify --title <text> ..."
literal) and make the string match the normalized handle formats used by the
index-aware helpers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3fdeacc7-87cd-48d5-bf54-49abd24bc121

📥 Commits

Reviewing files that changed from the base of the PR and between e6ae5a2 and 16ab61d.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • Sources/TerminalController.swift

Comment thread Sources/TerminalController.swift
- All 3 notification create methods now include color in response
- Top-level notify usage text shows id|ref|index for workspace/surface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@apollow

apollow commented Mar 9, 2026 •

Copy link
Copy Markdown
Author

@austinywang undertsood the idea based on async convo -- closing this since they will own it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant