Skip to content

Commit 39bf150

Browse files
feat(shell-plugin): add fish shell plugin
Port the ZSH shell plugin to fish: :command interception, @[file] tagging, tab completion, conversation/session management, right-prompt integration, and CLI completions. Follows the standard ~/.config/fish layout (conf.d/, functions/, completions/) for easy installation. Co-Authored-By: ForgeCode <noreply@forgecode.dev>
1 parent 6ed5d37 commit 39bf150

57 files changed

Lines changed: 1657 additions & 2 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎shell-plugin/README.md‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1-
# Forge ZSH Plugin
1+
# Forge Shell Plugin
22

3-
A powerful ZSH plugin that provides intelligent command transformation, file tagging, and conversation management for the Forge AI assistant.
3+
A powerful shell plugin that provides intelligent command transformation, file tagging, and conversation management for the Forge AI assistant. Available for **ZSH** (this directory) and **Fish** (`fish/` directory).
4+
5+
## Fish Plugin
6+
7+
The Fish port lives in `fish/` and mirrors the standard `~/.config/fish` layout (`conf.d/`, `functions/`, `completions/`), so it can be installed by copying or symlinking those directories into your fish config:
8+
9+
```bash
10+
cp -r fish/conf.d/forge.fish ~/.config/fish/conf.d/
11+
cp -r fish/functions/_forge*.fish ~/.config/fish/functions/
12+
cp -r fish/completions/forge.fish ~/.config/fish/completions/
13+
```
14+
15+
It provides the same `:command` transformation, `@[file]` tagging, tab completion, session management, and right-prompt integration as the ZSH version. Requires **fish 3.6+** and the same prerequisites (`fd`, `forge`) listed below.
16+
17+
## ZSH Plugin
418

519
## Features
620

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Fish completions for forge CLI
2+
# Auto-loaded from ~/.config/fish/completions/
3+
4+
# ── Helpers ───────────────────────────────────────────────────────────────────
5+
# __forge_needs_subcommand <parent>
6+
# True when <parent> has been seen but the next positional arg is still needed.
7+
# Works by counting positional (non-option) tokens on the command line.
8+
function __forge_needs_subcommand
9+
set -l parent $argv[1]
10+
set -l cmd (commandline -opc)
11+
set -l found_parent 0
12+
for tok in $cmd[2..] # skip "forge" itself
13+
switch $tok
14+
case '-*'
15+
continue
16+
case '*'
17+
if test $found_parent -eq 0
18+
if test "$tok" = "$parent"
19+
set found_parent 1
20+
end
21+
else
22+
# A positional token after parent => subcommand already present
23+
return 1
24+
end
25+
end
26+
end
27+
# Return true only if we found the parent and there's no sub yet
28+
test $found_parent -eq 1
29+
end
30+
31+
# __forge_at_depth3 <parent> <child>
32+
# True when "forge <parent> <child>" have been seen and a 3rd positional
33+
# argument is still needed (e.g. "forge config set <TAB>").
34+
function __forge_at_depth3
35+
set -l parent $argv[1]
36+
set -l child $argv[2]
37+
set -l cmd (commandline -opc)
38+
set -l depth 0
39+
set -l match_parent 0
40+
set -l match_child 0
41+
for tok in $cmd[2..]
42+
switch $tok
43+
case '-*'
44+
continue
45+
case '*'
46+
set depth (math $depth + 1)
47+
if test $depth -eq 1; and test "$tok" = "$parent"
48+
set match_parent 1
49+
else if test $depth -eq 2; and test $match_parent -eq 1; and test "$tok" = "$child"
50+
set match_child 1
51+
else if test $depth -ge 3
52+
return 1 # already have a 3rd token
53+
end
54+
end
55+
end
56+
test $match_parent -eq 1; and test $match_child -eq 1
57+
end
58+
59+
# Disable file completions by default for forge
60+
complete -c forge -f
61+
62+
# ── Top-level subcommands ─────────────────────────────────────────────────────
63+
complete -c forge -n __fish_use_subcommand -a agent -d "Manage agents"
64+
complete -c forge -n __fish_use_subcommand -a zsh -d "Generate shell extension scripts"
65+
complete -c forge -n __fish_use_subcommand -a list -d "List agents, models, providers, tools, or MCP servers"
66+
complete -c forge -n __fish_use_subcommand -a banner -d "Display the banner with version information"
67+
complete -c forge -n __fish_use_subcommand -a info -d "Show configuration, active model, and environment status"
68+
complete -c forge -n __fish_use_subcommand -a env -d "Display environment information"
69+
complete -c forge -n __fish_use_subcommand -a config -d "Get, set, or list configuration values"
70+
complete -c forge -n __fish_use_subcommand -a conversation -d "Manage conversation history and state"
71+
complete -c forge -n __fish_use_subcommand -a commit -d "Generate and optionally commit changes with AI-generated message"
72+
complete -c forge -n __fish_use_subcommand -a mcp -d "Manage Model Context Protocol servers"
73+
complete -c forge -n __fish_use_subcommand -a suggest -d "Generate shell commands without executing them"
74+
complete -c forge -n __fish_use_subcommand -a provider -d "Manage API provider authentication"
75+
complete -c forge -n __fish_use_subcommand -a cmd -d "Run or list custom commands"
76+
complete -c forge -n __fish_use_subcommand -a workspace -d "Manage workspaces for semantic search"
77+
complete -c forge -n __fish_use_subcommand -a data -d "Process JSONL data through LLM"
78+
complete -c forge -n __fish_use_subcommand -a vscode -d "VS Code integration commands"
79+
complete -c forge -n __fish_use_subcommand -a update -d "Update forge to the latest version"
80+
complete -c forge -n __fish_use_subcommand -a setup -d "Setup zsh integration"
81+
complete -c forge -n __fish_use_subcommand -a doctor -d "Run diagnostics on shell environment"
82+
83+
# Top-level options
84+
complete -c forge -n __fish_use_subcommand -s p -l prompt -d "Direct prompt to process"
85+
complete -c forge -n __fish_use_subcommand -l conversation -d "Path to conversation JSON" -r -F
86+
complete -c forge -n __fish_use_subcommand -l conversation-id -d "Conversation ID to use"
87+
complete -c forge -n __fish_use_subcommand -s C -l directory -d "Working directory" -r -a "(__fish_complete_directories)"
88+
complete -c forge -n __fish_use_subcommand -l sandbox -d "Isolated git worktree name"
89+
complete -c forge -n __fish_use_subcommand -l verbose -d "Enable verbose logging"
90+
complete -c forge -n __fish_use_subcommand -l agent -d "Agent ID to use"
91+
complete -c forge -n __fish_use_subcommand -s e -l event -d "Event to dispatch (JSON)"
92+
complete -c forge -n __fish_use_subcommand -s h -l help -d "Print help"
93+
complete -c forge -n __fish_use_subcommand -s V -l version -d "Print version"
94+
95+
# ── config subcommands ────────────────────────────────────────────────────────
96+
complete -c forge -n "__forge_needs_subcommand config" -a set -d "Set a configuration value"
97+
complete -c forge -n "__forge_needs_subcommand config" -a get -d "Get a configuration value"
98+
complete -c forge -n "__forge_needs_subcommand config" -a list -d "List configuration"
99+
100+
# config set targets
101+
complete -c forge -n "__forge_at_depth3 config set" -a model -d "Set default model"
102+
complete -c forge -n "__forge_at_depth3 config set" -a provider -d "Set default provider"
103+
complete -c forge -n "__forge_at_depth3 config set" -a commit -d "Set commit model"
104+
complete -c forge -n "__forge_at_depth3 config set" -a suggest -d "Set suggest model"
105+
complete -c forge -n "__forge_at_depth3 config set" -a reasoning-effort -d "Set reasoning effort"
106+
107+
# config get targets
108+
complete -c forge -n "__forge_at_depth3 config get" -a model -d "Get default model"
109+
complete -c forge -n "__forge_at_depth3 config get" -a provider -d "Get default provider"
110+
complete -c forge -n "__forge_at_depth3 config get" -a commit -d "Get commit model"
111+
complete -c forge -n "__forge_at_depth3 config get" -a suggest -d "Get suggest model"
112+
complete -c forge -n "__forge_at_depth3 config get" -a reasoning-effort -d "Get reasoning effort"
113+
114+
# ── conversation subcommands ──────────────────────────────────────────────────
115+
complete -c forge -n "__forge_needs_subcommand conversation" -a list -d "List conversations"
116+
complete -c forge -n "__forge_needs_subcommand conversation" -a new -d "Create new conversation"
117+
complete -c forge -n "__forge_needs_subcommand conversation" -a dump -d "Export conversation"
118+
complete -c forge -n "__forge_needs_subcommand conversation" -a compact -d "Compact conversation"
119+
complete -c forge -n "__forge_needs_subcommand conversation" -a retry -d "Retry last turn"
120+
complete -c forge -n "__forge_needs_subcommand conversation" -a resume -d "Resume conversation"
121+
complete -c forge -n "__forge_needs_subcommand conversation" -a show -d "Show conversation"
122+
complete -c forge -n "__forge_needs_subcommand conversation" -a info -d "Show conversation info"
123+
complete -c forge -n "__forge_needs_subcommand conversation" -a stats -d "Show conversation stats"
124+
complete -c forge -n "__forge_needs_subcommand conversation" -a clone -d "Clone conversation"
125+
complete -c forge -n "__forge_needs_subcommand conversation" -a delete -d "Delete conversation"
126+
complete -c forge -n "__forge_needs_subcommand conversation" -a rename -d "Rename conversation"
127+
128+
# ── list subcommands ──────────────────────────────────────────────────────────
129+
complete -c forge -n "__forge_needs_subcommand list" -a agent -d "List agents"
130+
complete -c forge -n "__forge_needs_subcommand list" -a provider -d "List providers"
131+
complete -c forge -n "__forge_needs_subcommand list" -a model -d "List models"
132+
complete -c forge -n "__forge_needs_subcommand list" -a command -d "List commands"
133+
complete -c forge -n "__forge_needs_subcommand list" -a config -d "List config"
134+
complete -c forge -n "__forge_needs_subcommand list" -a tool -d "List tools"
135+
complete -c forge -n "__forge_needs_subcommand list" -a mcp -d "List MCP servers"
136+
complete -c forge -n "__forge_needs_subcommand list" -a conversation -d "List conversations"
137+
complete -c forge -n "__forge_needs_subcommand list" -a cmd -d "List custom commands"
138+
complete -c forge -n "__forge_needs_subcommand list" -a skill -d "List skills"
139+
140+
# ── provider subcommands ──────────────────────────────────────────────────────
141+
complete -c forge -n "__forge_needs_subcommand provider" -a login -d "Log in to provider"
142+
complete -c forge -n "__forge_needs_subcommand provider" -a logout -d "Log out from provider"
143+
complete -c forge -n "__forge_needs_subcommand provider" -a list -d "List providers"
144+
145+
# ── mcp subcommands ───────────────────────────────────────────────────────────
146+
complete -c forge -n "__forge_needs_subcommand mcp" -a import -d "Import MCP server config"
147+
complete -c forge -n "__forge_needs_subcommand mcp" -a list -d "List MCP servers"
148+
complete -c forge -n "__forge_needs_subcommand mcp" -a remove -d "Remove MCP server"
149+
complete -c forge -n "__forge_needs_subcommand mcp" -a show -d "Show MCP server details"
150+
complete -c forge -n "__forge_needs_subcommand mcp" -a reload -d "Reload MCP servers"
151+
152+
# ── workspace subcommands ─────────────────────────────────────────────────────
153+
complete -c forge -n "__forge_needs_subcommand workspace" -a sync -d "Sync workspace"
154+
complete -c forge -n "__forge_needs_subcommand workspace" -a init -d "Initialize workspace"
155+
complete -c forge -n "__forge_needs_subcommand workspace" -a status -d "Show workspace status"
156+
complete -c forge -n "__forge_needs_subcommand workspace" -a info -d "Show workspace info"
157+
158+
# ── commit options ────────────────────────────────────────────────────────────
159+
complete -c forge -n "__fish_seen_subcommand_from commit" -l max-diff -d "Maximum git diff size in bytes"
160+
complete -c forge -n "__fish_seen_subcommand_from commit" -l preview -d "Preview commit message without committing"
161+
162+
# ── zsh subcommands ───────────────────────────────────────────────────────────
163+
complete -c forge -n "__forge_needs_subcommand zsh" -a plugin -d "Generate shell plugin script"
164+
complete -c forge -n "__forge_needs_subcommand zsh" -a theme -d "Generate shell theme"
165+
complete -c forge -n "__forge_needs_subcommand zsh" -a doctor -d "Run diagnostics"
166+
complete -c forge -n "__forge_needs_subcommand zsh" -a rprompt -d "Get rprompt information"
167+
complete -c forge -n "__forge_needs_subcommand zsh" -a setup -d "Setup zsh integration"
168+
complete -c forge -n "__forge_needs_subcommand zsh" -a keyboard -d "Show keyboard shortcuts"
169+
170+
# Common flags across subcommands
171+
complete -c forge -n "__fish_seen_subcommand_from list agent provider mcp config conversation" -l porcelain -d "Machine-readable output"
172+
complete -c forge -n "__fish_seen_subcommand_from list agent provider mcp config conversation" -s h -l help -d "Print help"
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Forge Code - Fish Shell Plugin
2+
# https://github.com/antinomyhq/forge
3+
# Provides :command integration, right prompt, and key bindings
4+
5+
# ── Fisher lifecycle events ───────────────────────────────────────────────────
6+
function _forge_install --on-event forge_install
7+
# Runs once after: fisher install
8+
end
9+
10+
function _forge_update --on-event forge_update
11+
# Runs once after: fisher update
12+
end
13+
14+
function _forge_uninstall --on-event forge_uninstall
15+
# Clean up key bindings
16+
bind --erase \r
17+
bind --erase \n
18+
bind --erase \t
19+
20+
# Clean up global variables
21+
set --erase _FORGE_BIN
22+
set --erase _FORGE_MAX_COMMIT_DIFF
23+
set --erase _FORGE_DELIMITER
24+
set --erase _FORGE_PREVIEW_WINDOW
25+
set --erase _FORGE_CONVERSATION_ID
26+
set --erase _FORGE_PREVIOUS_CONVERSATION_ID
27+
set --erase _FORGE_ACTIVE_AGENT
28+
set --erase _FORGE_SESSION_MODEL
29+
set --erase _FORGE_SESSION_PROVIDER
30+
set --erase _FORGE_SESSION_REASONING_EFFORT
31+
set --erase _FORGE_COMMANDS
32+
set --erase _FORGE_FD_CMD
33+
set --erase _FORGE_CAT_CMD
34+
set --erase _FORGE_THEME_LOADED
35+
set --erase _FORGE_PLUGIN_LOADED
36+
37+
# Clean up :command abbreviation
38+
abbr --erase _forge_cmd 2>/dev/null
39+
40+
# Restore original right prompt if we wrapped it
41+
if functions -q _forge_original_fish_right_prompt
42+
functions -c _forge_original_fish_right_prompt fish_right_prompt
43+
functions -e _forge_original_fish_right_prompt
44+
end
45+
end
46+
47+
# ── Guard: interactive shells only ────────────────────────────────────────────
48+
if not status is-interactive
49+
return
50+
end
51+
52+
# ── Global variables ──────────────────────────────────────────────────────────
53+
set -g _FORGE_BIN (command -v forge 2>/dev/null; or echo forge)
54+
set -g _FORGE_MAX_COMMIT_DIFF (test -n "$FORGE_MAX_COMMIT_DIFF"; and echo $FORGE_MAX_COMMIT_DIFF; or echo 100000)
55+
set -g _FORGE_DELIMITER '\\s\\s+'
56+
set -g _FORGE_PREVIEW_WINDOW "--preview-window=bottom:75%:wrap:border-sharp"
57+
set -g _FORGE_CONVERSATION_ID ""
58+
set -g _FORGE_PREVIOUS_CONVERSATION_ID ""
59+
set -g _FORGE_ACTIVE_AGENT ""
60+
set -g _FORGE_SESSION_MODEL ""
61+
set -g _FORGE_SESSION_PROVIDER ""
62+
set -g _FORGE_SESSION_REASONING_EFFORT ""
63+
set -g _FORGE_COMMANDS ""
64+
65+
# Detect fd
66+
if command -q fdfind
67+
set -g _FORGE_FD_CMD fdfind
68+
else if command -q fd
69+
set -g _FORGE_FD_CMD fd
70+
else
71+
set -g _FORGE_FD_CMD fd
72+
end
73+
74+
# Detect bat
75+
if command -q bat
76+
set -g _FORGE_CAT_CMD "bat --color=always --style=numbers,changes --line-range=:500"
77+
else
78+
set -g _FORGE_CAT_CMD cat
79+
end
80+
81+
# ── Key bindings ──────────────────────────────────────────────────────────────
82+
# Override Enter to intercept :commands
83+
bind \r _forge_accept_line
84+
bind \n _forge_accept_line
85+
86+
# Tab completion for @file and :command
87+
bind \t _forge_tab_completion
88+
89+
# ── Syntax highlighting for :commands ─────────────────────────────────────────
90+
# Fish's built-in highlighter colors unknown commands red. Since :commands like
91+
# :kb, :model, :suggest are intercepted by our Enter key handler (not real
92+
# commands), they'd always appear red. We register a regex abbreviation that
93+
# matches any `:word` token in command position. The highlighter checks
94+
# abbreviations when deciding if a command is valid, so :commands get the
95+
# normal command color. The abbreviation function returns the input unchanged
96+
# so it never visually expands -- our Enter keybinding fires first anyway.
97+
abbr --erase _forge_cmd 2>/dev/null
98+
abbr --add _forge_cmd --position command --regex ":[a-zA-Z][-a-zA-Z0-9_]*" --function _forge_highlight_noop
99+
100+
# ── Right prompt integration ──────────────────────────────────────────────────
101+
# Wrap the existing fish_right_prompt (e.g. starship) to append forge info.
102+
# We defer this so other conf.d scripts (starship) can set up first.
103+
if not set -q _FORGE_THEME_LOADED
104+
function _forge_install_rprompt --on-event fish_prompt
105+
# Only run once
106+
functions -e _forge_install_rprompt
107+
108+
if functions -q fish_right_prompt
109+
# Save the original right prompt
110+
functions -c fish_right_prompt _forge_original_fish_right_prompt
111+
112+
# Redefine with forge info appended
113+
function fish_right_prompt
114+
set -l forge_info (_forge_rprompt_info)
115+
set -l original (_forge_original_fish_right_prompt)
116+
if test -n "$forge_info"
117+
echo -n "$forge_info "
118+
end
119+
echo -n "$original"
120+
end
121+
else
122+
function fish_right_prompt
123+
_forge_rprompt_info
124+
end
125+
end
126+
end
127+
set -g _FORGE_THEME_LOADED (date +%s)
128+
end
129+
130+
set -g _FORGE_PLUGIN_LOADED (date +%s)

0 commit comments

Comments
 (0)