Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# CLI Agent Rich Input — Shell Command Mode (`!` prefix)
|
||||
|
||||
## Problem
|
||||
|
||||
The CLI agent rich input composer (Ctrl-G) was designed for sending natural language prompts to CLI agents. However, CLI agents like Claude Code support a `!` prefix for running shell commands directly. When users type `!ls` in the composer, they expect the same shell input affordances they get elsewhere in Warp — syntax highlighting, red error underlining for unknown commands, and tab completions. Without this, the composer feels broken for shell commands: no visual feedback, no completions, and the `!` prefix interferes with the parser.
|
||||
|
||||
## Goals
|
||||
|
||||
- Provide full shell input features (syntax highlighting, error underlining, tab completions, autosuggestions) when the user types `!` in the CLI agent rich input.
|
||||
- Reuse the existing agent view `!` shell mode infrastructure with minimal new code.
|
||||
- Correctly send the `!` prefix to the CLI agent on submit so the agent recognises it as a shell command.
|
||||
- Support Claude Code, Codex, and OpenCode (agents that use `!` for shell mode).
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Showing the "backspace to exit shell mode" hint text row in the CLI agent footer. This requires new plumbing since the agent message bar pipeline is coupled to `AgentMessageArgs`. Can be explored later.
|
||||
- Other CLI agents (for now, can expand this later).
|
||||
|
||||
## Figma
|
||||
|
||||
Figma: none provided. The visual treatment (blue `!` indicator, "Run commands" placeholder) is identical to the existing agent view shell mode.
|
||||
|
||||
## User experience
|
||||
|
||||
### Entering shell mode
|
||||
|
||||
1. User opens CLI agent rich input (Ctrl-G or footer button) while a supported CLI agent (Claude Code, Codex, OpenCode) is running.
|
||||
2. Input starts in AI mode (locked). Placeholder reads "Tell the agent what to build...".
|
||||
3. User types `!`.
|
||||
4. The `!` character is stripped from the buffer.
|
||||
5. Input switches to Shell mode (locked).
|
||||
6. A blue `!` indicator appears as a UI prefix in the editor (identical to agent view shell mode).
|
||||
7. Placeholder changes to "Run commands".
|
||||
8. The user types a shell command (e.g. `git status`). Full shell features are active:
|
||||
- Syntax highlighting (command name in green, arguments colored).
|
||||
- Error underlining (unknown commands get red underline, e.g. `gitt`).
|
||||
- Tab completions (pressing Tab after `git co` shows completions).
|
||||
- Autosuggestions (ghosted text from history).
|
||||
|
||||
Note that CMD-I should NOT work for entering/exiting shell mode, and it should be disabled for LRC agent interaction as well (CC or other agents don't use CMD-I).
|
||||
|
||||
### Exiting shell mode
|
||||
|
||||
Shell mode can be exited in three ways:
|
||||
|
||||
1. **Backspace on empty buffer**: When the buffer is empty and the `!` indicator is showing, pressing Backspace removes the indicator and switches back to AI locked mode.
|
||||
2. **Delete all (Cmd+Backspace)**: Same behavior — clears buffer and exits shell mode.
|
||||
3. **Submitting a command**: After pressing Enter to submit a `!`-prefixed command, the input automatically reverts to AI locked mode for the next prompt.
|
||||
|
||||
After exiting shell mode, the placeholder reverts to "Tell the agent what to build..." and no autodetection runs — the input stays locked in AI mode. The user can type `!` again to re-enter shell mode.
|
||||
|
||||
### Submitting
|
||||
|
||||
When the user presses Enter in shell mode:
|
||||
|
||||
1. The `!` prefix is prepended back to the buffer text.
|
||||
2. The full text (e.g. `!git status`) is sent to the CLI agent via the existing `SubmitCLIAgentInput` path.
|
||||
3. The existing submit path already splits the `!` prefix byte with a small delay for agents like Claude Code (`CLI_AGENT_MODE_SWITCH_PREFIXES`).
|
||||
4. The input reverts to AI locked mode.
|
||||
|
||||
### Interaction with other features
|
||||
|
||||
- **Autodetection**: Does not run while in CLI agent shell mode. The `!` prefix is the explicit toggle mechanism; autodetection would fight with the locked state.
|
||||
- **Slash commands**: The slash command menu (`/`) is not affected. It operates independently of shell mode.
|
||||
- **@ context menu**: Works normally in both AI and shell mode.
|
||||
|
||||
### Other Agents
|
||||
|
||||
Starting off with Claude Code, Codex and OpenCode for now. Can check behavior/expand to other CLI agents in a follow-up.
|
||||
|
||||
## Success criteria
|
||||
|
||||
1. Typing `!` in the CLI agent rich input for Claude Code / Codex / OpenCode strips the `!`, shows the blue `!` indicator, switches to Shell locked mode, and changes the placeholder to "Run commands".
|
||||
2. `!git status` shows `git` with syntax highlighting (green) and `status` colored as an argument.
|
||||
3. `!gitt` shows `gitt` with a red error underline.
|
||||
4. Tab after `!git co` opens the completions menu with `commit`, `config`, `checkout`, etc.
|
||||
5. Autosuggestions appear as ghosted text based on command history.
|
||||
6. Backspace on empty buffer with `!` showing removes the indicator and returns to AI locked mode with "Tell the agent what to build..." placeholder.
|
||||
7. Cmd+Backspace (delete all left) exits shell mode the same way.
|
||||
8. Submitting `!pwd` sends `!pwd` to the CLI agent and reverts to AI locked mode.
|
||||
9. After exiting shell mode (by any method), typing normal text does NOT get syntax highlighted — the input is in AI mode.
|
||||
10. Typing `!` for unsupported agents does not trigger shell mode.
|
||||
|
||||
## Validation
|
||||
|
||||
- **Manual testing**: Open each supported CLI agent (Claude Code, Codex, OpenCode), Ctrl-G, type `!`, verify all behaviors above.
|
||||
- **Regression**: Verify agent view `!` shell mode still works identically (the same code paths are shared).
|
||||
- **Edge cases**: Type `!`, backspace, type `!` again — should work. Type `! pwd` (with space) — should work (space is part of the command). Submit empty shell mode (just `!` with empty buffer) — should submit `!` and exit shell mode.
|
||||
|
||||
## Open questions
|
||||
|
||||
- N/A
|
||||
@@ -0,0 +1,184 @@
|
||||
# CLI Agent Rich Input — Shell Command Mode: Tech Spec
|
||||
|
||||
## Problem
|
||||
|
||||
The CLI agent rich input composer enters AI locked mode when opened, suppressing all shell decorations (syntax highlighting, error underlining, completions). CLI agents like Claude Code use `!` as a mode-switch prefix for shell commands. We need to detect this prefix, activate shell input features, and correctly round-trip the `!` on submit — all by reusing the existing agent view `!` shell mode infrastructure rather than building a parallel system.
|
||||
|
||||
## Relevant code
|
||||
|
||||
All changes are in a single file:
|
||||
|
||||
- `app/src/terminal/input.rs` — the `Input` view, which owns the editor, input mode state, decoration dispatch, completions, and the CLI agent submit path.
|
||||
|
||||
Key existing infrastructure reused:
|
||||
|
||||
- `TERMINAL_INPUT_PREFIX` (line ~481) — the `"!"` constant.
|
||||
- `TERMINAL_INPUT_PREFIX` handler (line ~8700) — strips `!`, switches to Shell locked, in the `EditorEvent::Edited` handler. Previously gated to agent view only.
|
||||
- `maybe_render_ai_input_indicators` (line ~13922) — renders the blue `!` indicator as a left-notch decorator element. Previously gated to `is_agent_view_active`.
|
||||
- `maybe_backspace_ai_icon` (line ~9814) — handles `BackspaceOnEmptyBuffer` / `BackspaceAtBeginningOfBuffer` to exit shell mode. Previously gated to `is_fullscreen()`.
|
||||
- `DeleteAllLeft` handler (line ~9206) — same exit-shell-mode behavior for Cmd+Backspace.
|
||||
- `input_enter` / `SubmitCLIAgentInput` (line ~11392) — the CLI agent submit path.
|
||||
- `open_completion_suggestions` (line ~10258) — gated by `is_command_grid_active`.
|
||||
- `set_zero_state_hint_text` (line ~5400) — sets placeholder text.
|
||||
- `run_input_background_jobs` in `decorations.rs` — suppresses decorations when `is_ai_input_enabled()`.
|
||||
- `CLIAgentSessionsModel` — singleton tracking CLI agent sessions, provides `is_input_open()` and `session()`.
|
||||
- `CLIAgent` enum — `Claude`, `Codex`, `OpenCode`, etc.
|
||||
|
||||
## Current state
|
||||
|
||||
Before this change:
|
||||
|
||||
1. CLI agent rich input always enters AI locked mode (PR #23819).
|
||||
2. The `TERMINAL_INPUT_PREFIX` handler only fires when `is_agent_view_active` — it does not fire for CLI agent input.
|
||||
3. Decorations are suppressed in AI mode (`run_input_background_jobs` checks `is_ai_input_enabled()`).
|
||||
4. Completions are blocked during long-running commands (`is_command_grid_active` is false while a CLI agent is executing).
|
||||
5. The `!` indicator only renders for the agent view.
|
||||
6. Backspace / DeleteAllLeft shell-mode exit only handles the agent view case.
|
||||
|
||||
## Proposed changes
|
||||
|
||||
All changes are in `app/src/terminal/input.rs`. No new files, types, or crate-level changes.
|
||||
|
||||
### 1. Extend `TERMINAL_INPUT_PREFIX` handler to CLI agent input
|
||||
|
||||
**Where**: `handle_editor_event` → `EditorEvent::Edited` branch, the `TERMINAL_INPUT_PREFIX` block (~line 8700).
|
||||
|
||||
**What**: The guard condition `(!FeatureFlag::AgentView.is_enabled() || is_agent_view_active)` is extended to `|| is_cli_agent_bash_mode_input_open`, where:
|
||||
|
||||
```rust
|
||||
let is_cli_agent_bash_mode_input_open = CLIAgentSessionsModel::as_ref(ctx)
|
||||
.session(self.terminal_view_id)
|
||||
.is_some_and(|s| {
|
||||
matches!(s.agent, CLIAgent::Claude | CLIAgent::Codex | CLIAgent::OpenCode)
|
||||
&& matches!(s.input_state, CLIAgentInputState::Open { .. })
|
||||
});
|
||||
```
|
||||
|
||||
This is the only place that checks the specific agent type. All downstream code (indicator, backspace, completions) uses the agent-agnostic `is_input_open()` since only supported agents can enter Shell locked mode via this path.
|
||||
|
||||
### 2. Guard autodetection re-enable
|
||||
|
||||
**Where**: Empty-buffer autodetection re-enable block (~line 8675).
|
||||
|
||||
**What**: Add `&& !is_cli_agent_input_open` to the second branch of `should_reenable_autodetection`. Without this, clearing the buffer after `!ls` would unlock Shell mode and let autodetection flip the mode back.
|
||||
|
||||
### 3. Prepend `!` on submit + exit shell mode
|
||||
|
||||
**Where**: `input_enter` → `SubmitCLIAgentInput` path (~line 11392).
|
||||
|
||||
**What**: When `is_locked_in_shell_mode()`, prepend `TERMINAL_INPUT_PREFIX` to the buffer text before emitting the event. Then call `exit_shell_mode_to_ai()` so the next prompt starts in AI mode. The existing `submit_cli_agent_rich_input` in `use_agent_footer/mod.rs` already handles the `!` prefix correctly via `CLI_AGENT_MODE_SWITCH_PREFIXES` (splits it with a delay).
|
||||
|
||||
### 4. `exit_shell_mode_to_ai` helper
|
||||
|
||||
**Where**: New method on `Input` (~line 12497).
|
||||
|
||||
```rust
|
||||
fn exit_shell_mode_to_ai(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
let is_cli_agent_input_open =
|
||||
CLIAgentSessionsModel::as_ref(ctx).is_input_open(self.terminal_view_id);
|
||||
let new_config = if is_cli_agent_input_open {
|
||||
InputConfig { input_type: InputType::AI, is_locked: true }
|
||||
} else {
|
||||
InputConfig { input_type: InputType::AI, is_locked: true }
|
||||
.unlocked_if_autodetection_enabled(true, ctx)
|
||||
};
|
||||
self.ai_input_model.update(ctx, |m, ctx| {
|
||||
m.set_input_config(new_config, true, ctx);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
For CLI agent input, always locks to AI — the `!` prefix is the explicit toggle. For agent view, respects the autodetection setting (existing behavior). Called from three sites: `maybe_backspace_ai_icon`, `DeleteAllLeft`, and `input_enter` submit.
|
||||
|
||||
### 5. Show `!` indicator for CLI agent input
|
||||
|
||||
**Where**: `maybe_render_ai_input_indicators` (~line 13938).
|
||||
|
||||
**What**: Added `terminal_view_id` parameter. The condition is restructured to:
|
||||
|
||||
```rust
|
||||
let is_locked_shell = !is_ai_input_enabled && is_input_type_locked;
|
||||
let is_cli_agent_input_open = CLIAgentSessionsModel::as_ref(app).is_input_open(terminal_view_id);
|
||||
|
||||
if is_locked_shell && (is_agent_view_active || is_cli_agent_input_open) {
|
||||
// render blue ! indicator
|
||||
}
|
||||
```
|
||||
|
||||
This eliminates the previous redundant inner check.
|
||||
|
||||
### 6. Backspace / DeleteAllLeft exit shell mode for CLI agent
|
||||
|
||||
**Where**: `maybe_backspace_ai_icon` (~line 9814) and `DeleteAllLeft` handler (~line 9220).
|
||||
|
||||
**What**: Both now check `|| is_cli_agent_input_open` alongside `is_fullscreen()` and call `exit_shell_mode_to_ai()`. The `maybe_backspace_ai_icon` path also adds an early `return` after the mode switch to prevent fallthrough into the classic AI icon toggle logic.
|
||||
|
||||
### 7. Allow completions during long-running CLI agent command
|
||||
|
||||
**Where**: `open_completion_suggestions` (~line 10266).
|
||||
|
||||
**What**: Added bypass:
|
||||
|
||||
```rust
|
||||
let is_cli_agent_shell_mode = self.is_locked_in_shell_mode(ctx)
|
||||
&& CLIAgentSessionsModel::as_ref(ctx).is_input_open(self.terminal_view_id);
|
||||
|
||||
if (is_command_grid_active || is_cli_agent_shell_mode) && self.can_query_history(ctx) {
|
||||
```
|
||||
|
||||
Note this should NOT be allowed if we're in a Warpified remote host, where we cannot run in-band generators.
|
||||
|
||||
### 8. Placeholder text
|
||||
|
||||
**Where**: `set_zero_state_hint_text` (~line 5400).
|
||||
|
||||
**What**: When CLI agent input is open, check `is_locked_in_shell_mode()` — if true, show `"Run commands"`, otherwise `"Tell the agent what to build..."`.
|
||||
|
||||
## End-to-end flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Editor
|
||||
participant Input (handle_editor_event)
|
||||
participant BlocklistAIInputModel
|
||||
participant Decorations
|
||||
participant CLIAgent (PTY)
|
||||
|
||||
User->>Editor: Types "!"
|
||||
Editor->>Input: EditorEvent::Edited
|
||||
Input->>Input: Detects TERMINAL_INPUT_PREFIX + Claude/Codex/OpenCode
|
||||
Input->>Editor: Strips "!" from buffer
|
||||
Input->>BlocklistAIInputModel: set_input_config(Shell, locked)
|
||||
BlocklistAIInputModel-->>Decorations: InputTypeChanged event
|
||||
Decorations->>Decorations: AI mode suppression OFF → decorations run
|
||||
Note over Editor: Blue ! indicator renders, placeholder = "Run commands"
|
||||
|
||||
User->>Editor: Types "git status"
|
||||
Editor->>Input: EditorEvent::Edited
|
||||
Input->>Decorations: run_input_background_jobs (Shell mode → decorations active)
|
||||
Decorations-->>Editor: Syntax highlighting + error underlining applied
|
||||
|
||||
User->>Editor: Presses Enter
|
||||
Editor->>Input: EditorEvent::Enter → input_enter()
|
||||
Input->>Input: Prepends "!" → text = "!git status"
|
||||
Input->>Input: exit_shell_mode_to_ai() → AI locked
|
||||
Input-->>CLIAgent (PTY): SubmitCLIAgentInput { text: "!git status" }
|
||||
Note over Editor: Reverts to AI mode, placeholder = "Tell the agent what to build..."
|
||||
```
|
||||
|
||||
## Risks and mitigations
|
||||
|
||||
- **Agent view regression**: All changes extend existing conditions with `|| is_cli_agent_*` checks. The agent view paths are unchanged. Mitigation: manual regression testing of agent view `!` shell mode.
|
||||
- **Autodetection fighting locked state**: When exiting shell mode, the helper always locks to AI for CLI agent (not unlocked). This prevents autodetection from re-classifying the next keystroke as Shell. Tested and confirmed.
|
||||
- **`maybe_backspace_ai_icon` fallthrough**: The classic AI icon toggle logic (`with_toggled_type()`) would flip back to Shell after the Shell→AI switch. Fixed with an early `return` after the mode switch.
|
||||
|
||||
## Testing and validation
|
||||
|
||||
- **Manual testing**: Primary validation method — open Claude Code / Codex / OpenCode, Ctrl-G, test all behaviors from the product spec success criteria.
|
||||
- **Regression**: Verify agent view `!` shell mode is unaffected.
|
||||
- **Edge cases**: `!` → backspace → `!` again; `! pwd` (with space); empty submit; typing for unsupported agents.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- "Backspace to exit shell mode" hint text in CLI agent footer (requires message bar plumbing).
|
||||
Reference in New Issue
Block a user