feat(repl): bring REPL to feature parity with ZSH shell plugin#2984
Merged
tusharmath merged 27 commits intomainfrom Apr 13, 2026
Merged
feat(repl): bring REPL to feature parity with ZSH shell plugin#2984tusharmath merged 27 commits intomainfrom
tusharmath merged 27 commits intomainfrom
Conversation
…ver marketing language
…ker and add preview support
d61c37c to
ef20df4
Compare
tusharmath
commented
Apr 13, 2026
| }; | ||
| let command = normalised_command.as_str(); | ||
|
|
||
| // TODO: Can leverage Clap to parse commands and provide correct error messages |
Collaborator
Author
There was a problem hiding this comment.
Can leverage Clap to parse commands and provide correct error messages - drop the sentinel char and then send it to Clap for parsing
2c64016 to
b251e36
Compare
2 tasks
Wang-tianhao
added a commit
to Wang-tianhao/forgecode
that referenced
this pull request
Apr 18, 2026
The forge REPL strips both `/` and `:` prefixes before Clap (part of the tailcallhq#2984 parity work), which meant the speed-dial slot hook and the `SpeedDialMenu` subcommand inherited the same `/ == :` equivalence when the feature landed. That gave `:1..:9`, `:sd`, and `:speed-dial` two entry points with identical behaviour — one inside the forge TUI and one at the zsh prompt — and muddied the mental model the zsh plugin already owned. Narrow the Rust parser so speed-dial activation is slash-only inside the REPL. Other colon commands (`:compact`, `:sage`, ...) keep the parity they had, because only the speed-dial tokens are gated. - model.rs: before the pre-Clap slot hook, if `command_prefix == ':'` and `bare` is a single digit `1..9`, `speed-dial`, or `sd`, return `AppCommand::Message(input)` so the line falls through as plain chat. `is_slot_digit` is hoisted so the later `/N` branch can reuse it. - tests: new `test_parse_speed_dial_colon_prefix_falls_through_as_message` covers all nine `:N`, `:2 explain this`, `:speed-dial`, `:sd`, and `:sd 3 --clear`. Existing `/N` tests unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wang-tianhao
added a commit
to Wang-tianhao/forgecode
that referenced
this pull request
Apr 18, 2026
… REPL" This reverts e995a28. Restoring `:1..:9`, `:speed-dial`, and `:sd` as REPL-usable aliases. The earlier commit special-cased speed-dial by making it slash-only inside the forge TUI while leaving every other command's `/ == :` parity intact. On reflection that asymmetry is the surprise, not the parity — the tailcallhq#2984 design explicitly maps zsh muscle-memory onto the REPL, and speed-dial should not be the one feature that breaks it. Having `:3` mean the same thing inside forge as it does at the shell prompt is the simpler mental model and matches user expectation. The zsh plugin continues to own `:` at the shell level; nothing about that ownership required forge to reject the same tokens inside its own input box. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wang-tianhao
added a commit
to Wang-tianhao/forgecode
that referenced
this pull request
Apr 19, 2026
The forge REPL strips both `/` and `:` prefixes before Clap (part of the tailcallhq#2984 parity work), which meant the speed-dial slot hook and the `SpeedDialMenu` subcommand inherited the same `/ == :` equivalence when the feature landed. That gave `:1..:9`, `:sd`, and `:speed-dial` two entry points with identical behaviour — one inside the forge TUI and one at the zsh prompt — and muddied the mental model the zsh plugin already owned. Narrow the Rust parser so speed-dial activation is slash-only inside the REPL. Other colon commands (`:compact`, `:sage`, ...) keep the parity they had, because only the speed-dial tokens are gated. - model.rs: before the pre-Clap slot hook, if `command_prefix == ':'` and `bare` is a single digit `1..9`, `speed-dial`, or `sd`, return `AppCommand::Message(input)` so the line falls through as plain chat. `is_slot_digit` is hoisted so the later `/N` branch can reuse it. - tests: new `test_parse_speed_dial_colon_prefix_falls_through_as_message` covers all nine `:N`, `:2 explain this`, `:speed-dial`, `:sd`, and `:sd 3 --clear`. Existing `/N` tests unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wang-tianhao
added a commit
to Wang-tianhao/forgecode
that referenced
this pull request
Apr 19, 2026
… REPL" This reverts e995a28. Restoring `:1..:9`, `:speed-dial`, and `:sd` as REPL-usable aliases. The earlier commit special-cased speed-dial by making it slash-only inside the forge TUI while leaving every other command's `/ == :` parity intact. On reflection that asymmetry is the surprise, not the parity — the tailcallhq#2984 design explicitly maps zsh muscle-memory onto the REPL, and speed-dial should not be the one feature that breaks it. Having `:3` mean the same thing inside forge as it does at the shell prompt is the simpler mental model and matches user expectation. The zsh plugin continues to own `:` at the shell level; nothing about that ownership required forge to reject the same tokens inside its own input box. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wang-tianhao
added a commit
to Wang-tianhao/forgecode
that referenced
this pull request
Apr 20, 2026
The forge REPL strips both `/` and `:` prefixes before Clap (part of the tailcallhq#2984 parity work), which meant the speed-dial slot hook and the `SpeedDialMenu` subcommand inherited the same `/ == :` equivalence when the feature landed. That gave `:1..:9`, `:sd`, and `:speed-dial` two entry points with identical behaviour — one inside the forge TUI and one at the zsh prompt — and muddied the mental model the zsh plugin already owned. Narrow the Rust parser so speed-dial activation is slash-only inside the REPL. Other colon commands (`:compact`, `:sage`, ...) keep the parity they had, because only the speed-dial tokens are gated. - model.rs: before the pre-Clap slot hook, if `command_prefix == ':'` and `bare` is a single digit `1..9`, `speed-dial`, or `sd`, return `AppCommand::Message(input)` so the line falls through as plain chat. `is_slot_digit` is hoisted so the later `/N` branch can reuse it. - tests: new `test_parse_speed_dial_colon_prefix_falls_through_as_message` covers all nine `:N`, `:2 explain this`, `:speed-dial`, `:sd`, and `:sd 3 --clear`. Existing `/N` tests unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wang-tianhao
added a commit
to Wang-tianhao/forgecode
that referenced
this pull request
Apr 20, 2026
… REPL" This reverts e995a28. Restoring `:1..:9`, `:speed-dial`, and `:sd` as REPL-usable aliases. The earlier commit special-cased speed-dial by making it slash-only inside the forge TUI while leaving every other command's `/ == :` parity intact. On reflection that asymmetry is the surprise, not the parity — the tailcallhq#2984 design explicitly maps zsh muscle-memory onto the REPL, and speed-dial should not be the one feature that breaks it. Having `:3` mean the same thing inside forge as it does at the shell prompt is the simpler mental model and matches user expectation. The zsh plugin continues to own `:` at the shell level; nothing about that ownership required forge to reject the same tokens inside its own input box. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wang-tianhao
added a commit
to Wang-tianhao/forgecode
that referenced
this pull request
Apr 22, 2026
The forge REPL strips both `/` and `:` prefixes before Clap (part of the tailcallhq#2984 parity work), which meant the speed-dial slot hook and the `SpeedDialMenu` subcommand inherited the same `/ == :` equivalence when the feature landed. That gave `:1..:9`, `:sd`, and `:speed-dial` two entry points with identical behaviour — one inside the forge TUI and one at the zsh prompt — and muddied the mental model the zsh plugin already owned. Narrow the Rust parser so speed-dial activation is slash-only inside the REPL. Other colon commands (`:compact`, `:sage`, ...) keep the parity they had, because only the speed-dial tokens are gated. - model.rs: before the pre-Clap slot hook, if `command_prefix == ':'` and `bare` is a single digit `1..9`, `speed-dial`, or `sd`, return `AppCommand::Message(input)` so the line falls through as plain chat. `is_slot_digit` is hoisted so the later `/N` branch can reuse it. - tests: new `test_parse_speed_dial_colon_prefix_falls_through_as_message` covers all nine `:N`, `:2 explain this`, `:speed-dial`, `:sd`, and `:sd 3 --clear`. Existing `/N` tests unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wang-tianhao
added a commit
to Wang-tianhao/forgecode
that referenced
this pull request
Apr 22, 2026
… REPL" This reverts e995a28. Restoring `:1..:9`, `:speed-dial`, and `:sd` as REPL-usable aliases. The earlier commit special-cased speed-dial by making it slash-only inside the forge TUI while leaving every other command's `/ == :` parity intact. On reflection that asymmetry is the surprise, not the parity — the tailcallhq#2984 design explicitly maps zsh muscle-memory onto the REPL, and speed-dial should not be the one feature that breaks it. Having `:3` mean the same thing inside forge as it does at the shell prompt is the simpler mental model and matches user expectation. The zsh plugin continues to own `:` at the shell level; nothing about that ownership required forge to reject the same tokens inside its own input box. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Bring the REPL to feature parity with the ZSH shell plugin by adding fzf-based file and command pickers, a syntax highlighter, a starship-style prompt redesign, and a unified
forge list filecommand that both the REPL and the shell plugin now share.Context
The ZSH shell plugin has long had rich completion (fzf +
fd, bat preview, colon-prefix commands, highlighted input) that the built-in REPL lacked. Users switching between shell mode and REPL mode experienced a jarring inconsistency. This PR closes that gap by porting the ZSH experience into the REPL itself, while also sharing the underlying file-listing logic through a newforge list fileCLI command so the shell plugin no longer needs a separatefddependency.Changes
ForgeWidget(fzf). File completions now open an interactive picker with bat-powered syntax-highlighted previews (falls back tocat), matching the shell plugin's@-mention completion experience./and:commands with an fzf popup that shows each command name and description side-by-side, making command discovery much easier.:commandnow works identically to/commandin both completion and execution, matching shell plugin behaviour.highlighter.rsthat colours slash/colon commands, shell (!cmd) commands, and file mentions ([path]) in real-time as the user types, giving immediate visual feedback.1.5k), cost, and dims all labels when the session is inactive (no tokens consumed yet).forge list fileCLI command — newforge list --file/forge list filessubcommand that walks the workspace with the same rules asfd --type f --type d --hidden --exclude .gitand prints one path per line. The shell plugin'scompletion.zshnow delegates to this command instead of callingfddirectly, removing the external dependency.Walker::get_blockingmigrated toWalkBuilder::build_parallel()for better performance on large workspaces. Newhiddenflag controls dotfile visibility (false= include hidden, matchingfd --hidden)..gitdirectory is always excluded.Key Implementation Details
Walker::max_all()(used by the REPL picker andforge list file) setshidden: false, so dotfiles are included just likefd --hidden --exclude .git.Walker::default()keepshidden: truefor agents that should not see dotfiles. The parallel walker usesArc<Mutex<…>>shared state so all threads contribute to the same collection and global limits. The fzf command picker buildsCommandRowdisplay objects (namepadded to 30 chars + description) and pre-populates the fzf query with whatever the user has already typed after the sentinel.Use Cases
Testing
Links