Skip to content

Add set-color/clear-color workspace actions for tab color via CLI - #1873

Merged
lawrencecchen merged 2 commits into
manaflow-ai:mainfrom
ariel-wiz:feature/tab-color-cli
Mar 25, 2026
Merged

lawrencecchen merged 2 commits into
manaflow-ai:mainfrom
ariel-wiz:feature/tab-color-cli

Conversation

@ariel-wiz

@ariel-wiz ariel-wiz commented Mar 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Expose the existing tab color functionality through the workspace-action CLI command
  • Supports both named colors (Red, Blue, Amber, etc.) and hex values (#RRGGBB)
  • Named colors resolve against WorkspaceTabColorSettings.defaultPaletteWithOverrides() (respects user hex overrides, excludes ad-hoc custom entries)

Usage

cmux workspace-action --action set-color --color blue
cmux workspace-action --action set-color --color "#C0392B"
cmux workspace-action set-color Amber
cmux workspace-action clear-color

Implementation

No new infrastructure needed — this wires up the existing tabManager.setTabColor(tabId:color:) method (used by the right-click context menu) to the socket-based CLI.

Sources/TerminalController.swift: Added set_color and clear_color to v2WorkspaceAction. The set_color handler resolves named colors from defaultPaletteWithOverrides(), falls back to hex validation via normalizedHex(), and returns an error with the list of valid color names if neither matches.

CLI/cmux.swift: Added --color flag parsing to runWorkspaceAction(), trims whitespace before validation, restricts positional inference to only rename (title) and set-color (color) actions, and updated help text with named color list and examples.

Test plan

  • cmux workspace-action --action set-color --color blue sets tab color to blue
  • cmux workspace-action set-color Amber sets tab color to amber (positional syntax)
  • cmux workspace-action --action set-color --color "#C0392B" sets tab color via hex
  • cmux workspace-action clear-color removes tab color
  • Invalid color name/hex returns error with list of valid named colors
  • cmux workspace-action --help shows new actions, flags, and examples
  • --color " " is rejected (whitespace trimming)
  • workspace-action clear-color extra does not silently send title=extra

Summary by cubic

Adds CLI actions to set or clear a workspace tab color via workspace-action. Supports named colors and #RRGGBB, trims whitespace, improves help, and returns explicit null on clear.

  • New Features

    • Added set-color and clear-color to workspace-action.
    • Named colors (case-insensitive) resolve via WorkspaceTabColorSettings.defaultPaletteWithOverrides(); hex validated with normalizedHex().
    • Positional color supported for set-color; only rename infers positional title; trims whitespace.
    • Invalid input returns an error with named_colors; help and CLI summary updated with color names, examples, and color in output.
  • Bug Fixes

    • clear-color JSON response now includes "color": null to signal a cleared value.

Written for commit f70991d. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added support for named colors (in addition to hex codes) when setting workspace colors.
    • CLI now shows chosen color in non-JSON command summaries.
  • Bug Fixes

    • Positional arguments are only inferred as title/color for the appropriate actions.
    • Improved color validation: trims whitespace and matches named colors case-insensitively; clearer invalid-color responses that list valid names.
  • Documentation

    • Updated help text and examples to show "<name|#hex>" and a list of named colors.

Expose the existing tab color functionality through the workspace-action
CLI command, enabling programmatic tab color setting without the GUI
context menu.

Supports both named colors (Red, Blue, Amber, etc.) and hex values
(#RRGGBB). Named colors resolve against the built-in palette via
case-insensitive matching.

Usage:
  cmux workspace-action --action set-color --color blue
  cmux workspace-action --action set-color --color "#C0392B"
  cmux workspace-action set-color Amber
  cmux workspace-action clear-color
@vercel

vercel Bot commented Mar 20, 2026

Copy link
Copy Markdown

@arieltobiana 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 20, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d2e49dab-a551-4257-84af-e0c096b8df6c

📥 Commits

Reviewing files that changed from the base of the PR and between ca0fb45 and f70991d.

📒 Files selected for processing (1)
  • Sources/TerminalController.swift
✅ Files skipped from review due to trivial changes (1)
  • Sources/TerminalController.swift

📝 Walkthrough

Walkthrough

Workspace action parsing and handling for rename and set_color were adjusted: trailing positional args are inferred only for rename (as title) or set_color (as color); color input now accepts named colors or #hex, resolves names case-insensitively, and returns/prints resolved hex in success or structured error with available named colors.

Changes

Cohort / File(s) Summary
CLI Parsing & Help
CLI/cmux.swift
Changed workspace-action parsing so trailing positional args become implicit --title only for rename, and implicit --color only for set_color. Updated help text/examples to show <name|#hex>, adjusted --color validation message, and include color=<value> in non-JSON summaries when server returns color.
Server Color Handler
Sources/TerminalController.swift
workspace.action set_color now trims input, performs case-insensitive named-color lookup via WorkspaceTabColorSettings.defaultPaletteWithOverrides(), falls back to hex normalization, returns resolved hex on success, and emits invalid_params with named_colors list on invalid input. clear_color behavior remains unchanged.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as CLI Parser
    participant Server as TerminalController
    participant Palette as WorkspaceTabColorSettings

    User->>CLI: workspace-action set_color blue
    CLI->>CLI: Infer color (positional or --color)
    CLI->>Server: RPC workspace.action params["color"]

    Server->>Server: Trim input
    Server->>Palette: Request defaultPaletteWithOverrides()
    Palette-->>Server: Named palette
    Server->>Palette: Case-insensitive match for "blue"

    alt Named match
        Palette-->>Server: Return hex (`#xxxxxx`)
        Server->>Server: Set tab color to hex
        Server-->>CLI: finish({ "color": "#xxxxxx" })
    else No named match
        Server->>Server: Attempt hex normalization
        alt Valid hex
            Server->>Server: Set tab color to hex
            Server-->>CLI: finish({ "color": "#xxxxxx" })
        else Invalid
            Server-->>CLI: invalid_params (message + named_colors)
        end
    end

    CLI-->>User: Print response (includes color=<value> if present)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Soft paws tap keys at night,

Named hues wink in gentle light,
Hex or "blue", both find their place,
Tabs now glow with tidy grace,
I nibble carrots, commit with pace. 🎨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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 clearly and accurately describes the main change: adding set-color/clear-color workspace actions for tab color management via the CLI, which aligns with the core functionality introduced in this PR.
Description check ✅ Passed The description is largely complete with a thorough Summary section, detailed Implementation notes, and a comprehensive Test plan. However, the Checklist section is not fully addressed (all items unchecked), a Demo Video is absent, and the Review Trigger section is not included.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Tip

You can disable the changed files summary in the walkthrough.

Disable the reviews.changed_files_summary setting to disable the changed files summary in the walkthrough.

@greptile-apps

greptile-apps Bot commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires the existing tab color functionality (tabManager.setTabColor) into the socket-based CLI by adding set_color and clear_color handlers to v2WorkspaceAction in TerminalController.swift, and adding --color flag parsing and positional inference to runWorkspaceAction() in CLI/cmux.swift. No new infrastructure is introduced.

Key changes and observations:

  • Named color resolution upgraded: set_color now uses defaultPaletteWithOverrides() (respecting user hex overrides) instead of the static defaultPalette, and switches to caseInsensitiveCompare — both are improvements.
  • Positional inference scoped correctly: Positional arguments now only map to title for rename and to color for set_color, fixing the pre-existing silent bug where any action's trailing positional args were used as a title.
  • clear_color response drops color: null: Changing finish(["color": NSNull()]) to finish() removes the color key from the JSON response entirely. This is a subtle API breaking change — JSON consumers that check for "color" being present (or null) in the clear_color response will behave differently.
  • Help text is accurate and the named color list matches defaultPalette.

Confidence Score: 3/5

  • Safe to merge with minor concern — the clear_color JSON response change is a subtle breaking API change that should be verified against any tooling consuming the JSON output.
  • The set_color logic is clean and correct. The positional inference fix is a genuine improvement. The main concern is the clear_color response dropping "color": null, which changes the JSON API contract and could silently break JSON consumers without any compile-time or test-time signal. Given the project has socket-based CLI consumers that may parse JSON output, this warrants attention before merge.
  • Sources/TerminalController.swift — specifically the clear_color case at line 4089-4091 where the response payload was changed from finish(["color": NSNull()]) to finish().

Important Files Changed

Filename Overview
Sources/TerminalController.swift Refactors set_color handler to use defaultPaletteWithOverrides() and caseInsensitiveCompare, and trims whitespace defensively. However, clear_color is changed from finish(["color": NSNull()]) to finish(), silently dropping "color" from the JSON response and potentially breaking JSON consumers.
CLI/cmux.swift Adds --color flag handling with positional inference for set_color, restricts positional inference to only rename and set_color actions (fixing the silent title=extra bug), and updates help text with named color list and examples. Logic is correct.

Sequence Diagram

sequenceDiagram
    participant User as User (CLI)
    participant CLI as cmux.swift
    participant Socket as SocketClient
    participant TC as TerminalController
    participant WCS as WorkspaceTabColorSettings
    participant TM as TabManager

    User->>CLI: cmux workspace-action set-color Amber
    CLI->>CLI: Parse action="set_color", infer color from positional="Amber"
    CLI->>CLI: Trim whitespace, validate non-empty
    CLI->>Socket: sendV2("workspace.action", {action:"set_color", color:"Amber"})
    Socket->>TC: v2WorkspaceAction(params)
    TC->>WCS: defaultPaletteWithOverrides()
    WCS-->>TC: [WorkspaceTabColorEntry, ...]
    TC->>TC: caseInsensitiveCompare("Amber") → found → hex="#FFC107"
    TC->>TM: setTabColor(tabId:, color:"#FFC107")
    TC-->>Socket: finish(["color": "#FFC107"])
    Socket-->>CLI: payload {color:"#FFC107", ...}
    CLI-->>User: OK action=set_color color=#FFC107

    User->>CLI: cmux workspace-action clear-color
    CLI->>Socket: sendV2("workspace.action", {action:"clear_color"})
    Socket->>TC: v2WorkspaceAction(params)
    TC->>TM: setTabColor(tabId:, color:nil)
    TC-->>Socket: finish()
    Socket-->>CLI: payload {action:"clear_color", ...} (no color key)
    CLI-->>User: OK action=clear_color
Loading

Last reviewed commit: "Add set-color/clear-..."

Comment thread Sources/TerminalController.swift Outdated
Comment thread CLI/cmux.swift
Restore "color": null in the clear_color response payload so JSON
consumers can distinguish "color was cleared" from "no color field".

@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

🧹 Nitpick comments (1)
CLI/cmux.swift (1)

3299-3315: Reject stray trailing args for non-value actions.

After the inference split, commands like workspace-action clear-color extra no longer reuse extra as a title, but they still succeed while silently dropping it. That makes typos look successful. Consider erroring whenever leftover positional text exists for actions other than rename and set-color.

Possible follow-up
         let action = actionRaw.lowercased().replacingOccurrences(of: "-", with: "_")
+        if action != "rename", action != "set_color",
+           let extra = positional.first(where: {
+               !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
+           }) {
+            throw CLIError(
+                message: "workspace-action \(actionRaw) does not accept trailing argument '\(extra)'"
+            )
+        }
         let workspaceArg = workspaceOpt ?? (windowOverride == nil ? ProcessInfo.processInfo.environment["CMUX_WORKSPACE_ID"] : nil)
         let workspaceId = try normalizeWorkspaceHandle(workspaceArg, client: client, allowCurrent: true)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 3299 - 3315, After computing action (from
actionRaw) and inferredPositional, add a validation that rejects any non-empty
inferredPositional for actions that do not accept a trailing value: i.e., if
inferredPositional is not empty and action is not "rename" and not "set_color"
then throw a CLIError with a clear message about unexpected trailing arguments;
place this check after title/color inference (near where title is derived and
color is trimmed) so it covers both rename and set_color exceptions and
references action, inferredPositional, and CLIError.
🤖 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 4071-4087: The palette-derived hex (entry.hex from
effectivePalette in the lookup block) must be normalized before being used or
reported because user overrides may be malformed; change the code that handles
the found entry to call WorkspaceTabColorSettings.normalizedHex(entry.hex) and
assign that normalized value to hex (and if normalization returns nil treat it
like the invalid branch and return the same error), then call
tabManager.setTabColor(tabId:workspace.id, color:hex) and finish(["color":hex])
so the persisted and reported color match.

---

Nitpick comments:
In `@CLI/cmux.swift`:
- Around line 3299-3315: After computing action (from actionRaw) and
inferredPositional, add a validation that rejects any non-empty
inferredPositional for actions that do not accept a trailing value: i.e., if
inferredPositional is not empty and action is not "rename" and not "set_color"
then throw a CLIError with a clear message about unexpected trailing arguments;
place this check after title/color inference (near where title is derived and
color is trimmed) so it covers both rename and set_color exceptions and
references action, inferredPositional, and CLIError.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b365707a-99e1-4fd3-a86f-5e17e75d20b3

📥 Commits

Reviewing files that changed from the base of the PR and between cc0fc55 and ca0fb45.

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

Comment thread Sources/TerminalController.swift

@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.

No issues found across 2 files


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

@arieltobiana

Copy link
Copy Markdown
Contributor

Re: CodeRabbit nitpick about rejecting stray trailing args for non-value actions (e.g. workspace-action clear-color extra silently dropping extra):

Good suggestion — this is a pre-existing behavior across all workspace actions (e.g. workspace-action pin extra also drops it), so fixing it just for color actions would be inconsistent. I'd rather address this as a separate PR that adds the guard for all non-value actions at once, keeping this PR scoped to the color feature.

@lawrencecchen
lawrencecchen merged commit 7cbd07e into manaflow-ai:main Mar 25, 2026
3 of 4 checks passed
@lawrencecchen

Copy link
Copy Markdown
Contributor

Thank you for the contribution!

jingtaozf pushed a commit to jingtaozf/cmux that referenced this pull request Mar 28, 2026
…naflow-ai#1873)

* Add set-color/clear-color workspace actions for tab color via CLI

Expose the existing tab color functionality through the workspace-action
CLI command, enabling programmatic tab color setting without the GUI
context menu.

Supports both named colors (Red, Blue, Amber, etc.) and hex values
(#RRGGBB). Named colors resolve against the built-in palette via
case-insensitive matching.

Usage:
  cmux workspace-action --action set-color --color blue
  cmux workspace-action --action set-color --color "#C0392B"
  cmux workspace-action set-color Amber
  cmux workspace-action clear-color

* Return explicit null color in clear_color JSON response

Restore "color": null in the clear_color response payload so JSON
consumers can distinguish "color was cleared" from "no color field".

---------

Co-authored-by: Ariel Tobiana <arieltobiana@gmail.com>
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
…naflow-ai#1873)

* Add set-color/clear-color workspace actions for tab color via CLI

Expose the existing tab color functionality through the workspace-action
CLI command, enabling programmatic tab color setting without the GUI
context menu.

Supports both named colors (Red, Blue, Amber, etc.) and hex values
(#RRGGBB). Named colors resolve against the built-in palette via
case-insensitive matching.

Usage:
  cmux workspace-action --action set-color --color blue
  cmux workspace-action --action set-color --color "#C0392B"
  cmux workspace-action set-color Amber
  cmux workspace-action clear-color

* Return explicit null color in clear_color JSON response

Restore "color": null in the clear_color response payload so JSON
consumers can distinguish "color was cleared" from "no color field".

---------

Co-authored-by: Ariel Tobiana <arieltobiana@gmail.com>
adamghaleb added a commit to adamghaleb/cmux that referenced this pull request Aug 4, 2026
Transcribed from upstream manaflow-ai/cmux HEAD b46dcb7 per
ADR-0004 amendment (narrow slices with upstream PR breadcrumbs,
never rebase). Implements fadi-orchestrator#26 and #27.

fadi-orchestrator#26:
- workspace.action set_color/clear_color with upstream's shape:
  16-color named palette or #RRGGBB, named_colors in the error
  payload (upstream: PR manaflow-ai#1873). Drives the existing customColor
  rail end to end (sidebar left rail, bonsplit tab accent,
  ghostty_surface_set_accent_color).
- workspace.action set_description/clear_description wired into new
  per-workspace state Workspace.customDescription, persisted through
  SessionWorkspaceSnapshot (upstream: PR manaflow-ai#2475).
- CLI: workspace-action --color/--description flags + help.
- waitAfterCommand child-exit handling: TerminalChildExitPolicy
  (upstream: PR manaflow-ai#8681) — SHOW_CHILD_EXITED now reads runtime ms;
  normal/established exits (Ctrl+D, tmux detach) close the pane so a
  spawn-command surface is never stranded, abnormal fast exits keep
  the dead surface visible for inspection (handler returns false so
  ghostty renders its error text).
- Spawn-command seam (ADR-0004): surface.create accepts an optional
  "command"; plumbed through newTerminalSurface -> TerminalPanel ->
  TerminalSurface into ghostty_surface_config_s.command with
  wait_after_command=true.

fadi-orchestrator#27:
- Label precedence: upstream's CustomTitleSource (.user/.auto)
  provenance for workspace and panel custom titles (upstream: PR
  manaflow-ai#5547). .auto (daemon semantic label) beats auto-derived titles but
  never overwrites or clears a user rename; ordering documented on
  Workspace.resolvedPanelTitle; provenance persists through session
  snapshots.
- workspace.set_auto_title verb transcribed (probe/panel_id/
  panel_only_if_multiple), minus upstream's opt-in auto-naming
  setting — fadid is the labeler here, so always enabled.
- Hover description: .help() on the sidebar workspace row, applied
  only when a description exists. (The pane tab bar lives in the
  vendor/bonsplit submodule — follow-up via bonsplit remote.)

Also contained build fixes in shared files, required to compile the
fadi-layer snapshot: PanelType.observatory switch arms in
Workspace.swift, dedup of Color(hex:) in ContentView (Fadicode's
internal extension collides with the old private one), and pbxproj
registration for Sources/Fadicode/Helpers/NetworkRetry.swift which
existed on disk but was never in the target.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

3 participants