feat(codegraff): slash command palette + autocomplete#21
Merged
Conversation
Adds a fuzzy slash command palette to the codegraff TUI. Typing `/` when the composer is empty opens a centered overlay listing all slash commands ported from graff's REPL AppCommand enum, plus codegraff-specific commands (/workflow, /image, /logs, etc.). Implementation notes: - New Overlay::CommandPalette variant with CommandPaletteState (query + selected_index) lives next to the existing overlay types in main.rs to avoid risky refactors. - Static PALETTE_COMMANDS table holds (name, description). The list mirrors crates/forge_main/src/model.rs, excluding REPL-only entries (:exit, :edit, :retry). - Fuzzy ranking uses the workspace `nucleo` dep (already declared in Cargo.toml). Items with score 0 are filtered out; ties preserve catalogue order. - `dispatch_command_palette` writes "/<name>" into self.composer and reuses the existing handle_enter dispatch path, so commands like /workflow keep their parser unchanged. - Keybindings: Up/Down navigate, Tab autocompletes, Enter dispatches, Esc closes, Backspace deletes (closes when query empty), any other char appends to the query. - Render path calls Clear over the overlay rect; full-frame Clear at the top of render() prevents close artifacts. overlay_area is recomputed each frame, so resize re-clamps automatically. Tests cover empty-query catalogue, fuzzy match, zero-score filter, catalogue uniqueness, REPL-only exclusion, and rendered line content. All 118 codegraff bin tests pass; clippy and release build are clean. Closes #17 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…utes Pre-merge review caught that ~30 entries in PALETTE_COMMANDS exposed commands the codegraff TUI dispatcher does not yet handle locally (`/new`, `/info`, `/agent`, `/commit`, `/dump`, `/skill`, `/tools`, `/copy`, `/clone`, `/conversation*`, `/workspace-*`, `/index`, `/update`, `/compact`, `/sage`, `/help`, `/plan`, `/act`, `/rename`, `/fast`, `/config-*`, `/suggest`, etc.). Selecting any of those from the palette would silently send the literal slash text to the LLM as a chat message, which is strictly worse UX than not exposing them. Restrict the palette to the 8 commands `handle_enter` does route: `/connect`, `/image`, `/login`, `/logs`, `/model`, `/models`, `/usage`, `/workflow`. Wiring the rest into the TUI dispatcher is tracked in a follow-up issue under the parity tracker. Also tighten `palette_command_names_are_unique_and_lowercase` to actually assert lowercase (it didn't before — name was misleading) and add a sanity test confirming every locally-handled command is in the palette so the dispatcher stays in sync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a fuzzy
/command palette to the codegraff TUI (closes #17). Typing/on an empty composer pops a centered overlay that fuzzy-matches commands ported from the graff REPLAppCommandenum, with single-line descriptions, keyboard navigation, Tab autocomplete, and Enter dispatch through the existinghandle_enterparser path.Overlay::CommandPalette(CommandPaletteState)variant; declared next to existing overlay types somain.rsstays a single file.PALETTE_COMMANDScatalogue mirrors the AppCommand enum (crates/forge_main/src/model.rs) plus codegraff-only commands (/workflow,/image,/logs,/login,/connect,/models,/usage). REPL-only entries (:exit,:edit,:retry) are explicitly excluded.nucleo = "0.5.0"dep (already declared, just wired intocrates/codegraff-tui/Cargo.toml). Score-0 results are filtered; ties preserve catalogue order.self.composer = "/<name>"and callshandle_enter, so/workflow,/image,/login,/models, etc. keep their existing per-command branches and parsers untouched.Clearover the overlay rect on top of the full-frameClearalready present inrender(), so opening, closing, and resizing the palette do not leave ghost borders.overlay_area()is recomputed every frame, so resize re-clamps.Files changed:
Cargo.lockcrates/codegraff-tui/Cargo.toml(addnucleo.workspace = true)crates/codegraff-tui/src/main.rs(palette table, overlay variant, palette key handler, dispatcher, render arm, 7 unit tests)Keybindings inside the palette
Test plan
cargo check -p codegraffcleancargo build --release -p codegraffcleancargo test -p codegraff --bin codegraff— 118 passed (111 prior + 7 new palette tests)cargo clippy -p codegraff --tests -- -D warningsclean/opens the palette, fuzzy filtering ranks results live, Tab autocompletes, Enter dispatches/usage//models//workflow <goal>, Esc closes without artifacts, Backspace closes from empty query, terminal resize re-centers the overlay. (I could not run the interactive TUI from the sandbox shell; the binary builds and unit-test coverage exercises the pure logic. Reviewer please confirm on a real terminal.)Notes / asks for human review
AppCommandexcept:exit,:edit,:retryis in the palette. If anything else should be hidden (e.g.delete, which iscommand(skip)in the parser and not user-typable from the REPL — that's why I left it out), please flag.#[strum(props(usage = ...))]annotations to fit a single line; they are not auto-derived. If you want them generated, that's a follow-up./only fires when the composer is empty so users can still type/mid-message.🤖 Generated with Claude Code