13 KiB
/rename-tab Slash Command Technical Spec
Linear: APP-4005
Product spec: specs/APP-4005/PRODUCT.md
Problem
Users can rename tabs today through tab UI interactions, but there is no keyboard-first slash-command path from the terminal or agent input. Technically, this requires connecting two existing subsystems:
- Static slash-command registration, parsing, availability, and execution from terminal input.
- Workspace-level tab custom-title state. The command should require the new tab name inline. An argumentless flow that opens the existing rename editor is intentionally excluded because the focused editor in the tab strip is too subtle relative to the user's attention in terminal input.
Relevant code
app/src/search/slash_command_menu/static_commands/commands.rs:10— static command declarations such as/agent,/cloud-agent, and/open-file.app/src/search/slash_command_menu/static_commands/commands.rs:445—Registryandall_commands()registration path for static slash commands.app/src/search/slash_command_menu/static_commands/mod.rs:9—Availability,Argument, andStaticCommanddefinitions.app/src/terminal/input/slash_commands/data_source/mod.rs:139— active slash-command recomputation from session context, agent view state, local/repository state, and CLI-agent input state.app/src/terminal/input/slash_commands/data_source/mod.rs:151—CLI_AGENT_INPUT_ALLOWED_COMMANDS, which keeps CLI-agent rich input restricted to passthrough-compatible commands.app/src/terminal/input/slash_command_model.rs:369— parsing static slash commands and arguments from input text.app/src/terminal/input/slash_commands/mod.rs:280—Input::execute_slash_command, the handler for static command execution.app/src/workspace/action.rs:82— existing tab-relatedWorkspaceActionvariants includingRenameTab,ResetTabName, andRenameActiveTab.app/src/workspace/action.rs:615—WorkspaceAction::should_save_app_state_on_action, where tab name mutations are marked as requiring app-state save.app/src/workspace/view.rs:4590—rename_tab, which opens the editor and emitsTabRenameEvent::OpenedEditorfor existing UI flows.app/src/workspace/view.rs:4664—clear_tab_name, which clears a custom title and emitsTabRenameEvent::CustomNameClearedfor existing UI flows.app/src/workspace/view.rs:18264—Workspace::handle_action, which maps workspace actions to rename handlers.app/src/pane_group/mod.rs:4636—PaneGroup::display_title, which resolves custom title before focused-pane title.app/src/pane_group/mod.rs:4656—PaneGroup::set_title, which stores tab-level custom title and treats empty strings as clearing the custom title.app/src/tab.rs:269— tab context-menu rename/reset actions using the sameWorkspaceActionvariants.app/src/workspace/view/vertical_tabs.rs:1410— vertical-tabs rows receive the shared rename editor and tab rename state.app/src/terminal/input/slash_command_model_tests.rs:7— slash-command argument parsing test pattern.app/src/workspace/view_test.rs:642— existing tab rename editor selection/reset tests.app/src/server/telemetry/events.rs:414— existingTabRenameEventvalues.
Current state
Static slash commands are represented as StaticCommand values in commands.rs. Each command declares a name, description, icon, availability flags, AI-mode behavior, and optional/required argument metadata. The registry is rebuilt at startup from all_commands().
Each terminal input owns a SlashCommandDataSource that filters registered commands against the current session context. Availability::ALWAYS makes a command available in both terminal view and agent view, subject to global AI enablement and terminal slash-command settings. CLI-agent rich input is a special case: when it is open, the data source filters static commands to CLI_AGENT_INPUT_ALLOWED_COMMANDS only, currently "/prompts" and "/skills".
SlashCommandModel::parse_slash_command splits the input at the first space. For a command with a required argument:
/commanddoes not parse as a completed slash command./command valueparses withargument = Some("value")./commandparses withargument = Some(""), so execution handlers should still reject empty required arguments when needed.Input::execute_slash_commandmatches on command name and performs side effects. After a handled command, it clears the input editor and emits slash-command accepted telemetry. Tab rename state is owned byWorkspaceand each tab'sPaneGroup. Custom tab names are stored onPaneGroup::custom_title;PaneGroup::display_titleresolves custom title first and falls back to the focused pane's automatic title. The slash command should set that same custom-title state directly.
Proposed changes
Add the static command
Add a new RENAME_TAB static command in app/src/search/slash_command_menu/static_commands/commands.rs.
Recommended shape:
name: "/rename-tab"description: "Rename the current tab"icon_path: reuse an existing bundled icon that reads as edit/rename, such asbundled/svg/pencil-line.svg.availability: Availability::ALWAYSrequires_ai_mode: falseargument: Some(Argument::required().with_hint_text("<tab name>"))Do not callwith_execute_on_selection(). Selecting the command from the slash-command menu should insert/rename-taband wait for the user to provide the required name. AddRENAME_TABtoall_commands()unconditionally. No new feature flag is needed because this is an additive command that uses existing tab title behavior and is easy to remove if needed.
Add a workspace action for direct setting
Add a new workspace action for direct active-tab title updates:
WorkspaceAction::SetActiveTabName(String)This avoids making terminal input know the active tab index. It also keeps all tab state mutation insideWorkspace, matching existing ownership. Add the new action to:WorkspaceAction::should_save_app_state_on_actionastrue.Workspace::handle_action. Implement a workspace helper along these lines:fn set_active_tab_name(&mut self, title: &str, ctx: &mut ViewContext<Self>)Behavior:
- If there is no active tab, log a warning and return.
- If a tab rename editor is already open, cancel or clear the transient editor state before direct-setting. The slash command should be authoritative and should not leave stale rename UI.
- Trim the incoming title.
- If the trimmed title is non-empty:
- Compare it with the active pane group's current
display_title. - If different, set it through
PaneGroup::set_title. - Emit
TabRenameEvent::CustomNameSetonly when state changes.
- Compare it with the active pane group's current
- Notify the UI. The slash-command execution handler should reject empty or whitespace-only names before dispatching this action.
Handle command execution
Add a branch to Input::execute_slash_command in app/src/terminal/input/slash_commands/mod.rs:
- If
command.name == commands::RENAME_TAB.name:- If
argumentisNoneor trims to an empty string, show a concise error toast such asPlease provide a tab name after /rename-taband returntrue. - Otherwise, dispatch
WorkspaceAction::SetActiveTabName(trimmed_name.to_owned()). The existing post-match code should then clear the invoking input and emit static slash-command accepted telemetry for successful execution. This should not fall through to the shell or agent as literal/rename-tabtext when handled as a slash command.
- If
Preserve CLI-agent rich input restrictions
Do not add /rename-tab to CLI_AGENT_INPUT_ALLOWED_COMMANDS in the initial implementation. That input currently intentionally exposes only passthrough-compatible commands (/prompts, /skills) while composing text for a running CLI agent.
If the CLI-agent input model later supports Warp-handled workspace commands, this command can be added there as a follow-up by widening the allowlist and ensuring execution is intercepted by Warp rather than written to the PTY.
Tests
Use existing unit-test patterns rather than adding integration infrastructure. Recommended tests:
- In
app/src/terminal/input/slash_command_model_tests.rs, add focused assertions for/rename-tabparsing:/rename-tabdoes not parse as a completed slash command because the required argument is missing./rename-tab Backendparses withSome("Backend")./rename-tab Backend APIpreserves the rest of the line as one argument./rename-tabparses withSome(""), and execution rejects the empty name.
- In
app/src/search/slash_command_menu/static_commands/commands.rstests, rely on the existing uniqueness test and add a direct assertion thatCOMMAND_REGISTRY.get_command_with_name("/rename-tab")exists with required argument metadata andshould_execute_on_selection = false. - In
app/src/workspace/view_test.rs, add workspace-level tests for direct active-tab setting:- Setting a non-empty name updates the active tab's
display_title. - Internal spaces are preserved after trimming surrounding whitespace.
- Direct-setting one tab does not mutate another tab.
- If rename editor state was active, direct-set leaves no tab in
is_tab_being_renamedstate. Because this change affects visible UI, perform manual validation in both horizontal and vertical tabs after unit tests pass.
- Setting a non-empty name updates the active tab's
End-to-end flow
- User selects
/rename-tabfrom the slash-command menu. - Because the command has a required argument and does not execute on selection, Warp inserts
/rename-tabinto the input. - User types the desired tab name and submits the slash command.
SlashCommandModelparses the command and argument.Input::execute_slash_commandtrims and validates the argument.Input::execute_slash_commanddispatchesWorkspaceAction::SetActiveTabName.Workspacemutates the active tab'sPaneGroupcustom title.- Horizontal and vertical tabs re-render from
display_title. - Input clears the slash command buffer and emits slash-command telemetry.
No code path should dispatch
WorkspaceAction::RenameActiveTabfor/rename-tab.
Risks and mitigations
- Accidentally sending
/rename-tabto the shell or agent: Make the command a handled branch inexecute_slash_commandand returntruefor detected invalid empty-argument execution. Cover parsing and execution-adjacent behavior in tests. - CLI-agent input confusion: Keep
/rename-tabout ofCLI_AGENT_INPUT_ALLOWED_COMMANDSfor now so the command is not offered in passthrough-only input. - Stale rename editor state: If direct-set runs while a rename editor is active, clear
current_workspace_state.tab_being_renamedand the shared editor buffer before mutating the active tab name. - Focus surprises after direct-set:
PaneGroup::set_titlerefocuses the focused pane. Verify this does not steal focus from the intended terminal or agent input in manual testing. - Whitespace semantics: Required-argument parsing can still produce
Some("")for/rename-tab; reject empty trimmed names inexecute_slash_command. - Active tab index assumptions: Do not pass a tab index from
Input. The workspace should use its ownactive_tab_indexat execution time.
Testing and validation
Run targeted tests first:
cargo test -p warp --lib rename_tabcargo test -p warp --lib set_active_tab_namecargo test -p warp --lib terminal::input::slash_command_model::testsThen run formatting/lint checks as appropriate:cargo fmt --checkcargo clippy -p warp --lib --tests -- -D warningsManual validation:
- In horizontal tabs, select
/rename-tabfrom the slash-command menu and verify it inserts/rename-tabrather than executing immediately. - In horizontal tabs, type
/rename-tab API server, press Enter, verify the active tab label changes immediately and the shell does not receive the command. - Type
/rename-taband verify no tab name is cleared. - Enable vertical tabs and repeat the direct-set flow.
- In a split-pane tab, focus a non-primary pane and verify direct-set renames the containing tab only.
- In a Warp Agent tab, run
/rename-tab Agent Workand verify only the tab label changes. - Verify tab context-menu Reset tab name still clears names set by the slash command.
Because this changes UI behavior, after implementation invoke the
verify-ui-change-in-cloudskill in an eligible local non-sandboxed environment.
Follow-ups
- Decide whether CLI-agent rich input should support Warp-handled workspace commands like
/rename-tab. - Consider adding a command-palette entry or keybinding for direct active-tab rename if users want a non-slash-command keyboard path.
- Consider adding a dedicated slash-command telemetry source to
TabRenameEventonly if product analytics need to distinguish slash-command-driven tab renames beyond existing slash-command accepted telemetry.