diff --git a/crates/forge_main/src/zsh/plugin.rs b/crates/forge_main/src/zsh/plugin.rs index 27ec8678eb..8bc64e48ba 100644 --- a/crates/forge_main/src/zsh/plugin.rs +++ b/crates/forge_main/src/zsh/plugin.rs @@ -44,7 +44,9 @@ pub fn generate_zsh_plugin() -> Result { output.push_str(&completions_str); // Set environment variable to indicate plugin is loaded (with timestamp) - output.push_str("\n_FORGE_PLUGIN_LOADED=$(date +%s)\n"); + // Use typeset -g so the variable is global even when eval'd inside a function + // (e.g. lazy-loading plugin managers like zinit, zplug, zsh-defer) + output.push_str("\ntypeset -g _FORGE_PLUGIN_LOADED=$(date +%s)\n"); Ok(output) } @@ -55,7 +57,9 @@ pub fn generate_zsh_theme() -> Result { super::normalize_script(include_str!("../../../../shell-plugin/forge.theme.zsh")); // Set environment variable to indicate theme is loaded (with timestamp) - content.push_str("\n_FORGE_THEME_LOADED=$(date +%s)\n"); + // Use typeset -g so the variable is global even when eval'd inside a function + // (e.g. lazy-loading plugin managers like zinit, zplug, zsh-defer) + content.push_str("\ntypeset -g _FORGE_THEME_LOADED=$(date +%s)\n"); Ok(content) } diff --git a/shell-plugin/forge.theme.zsh b/shell-plugin/forge.theme.zsh index 065f275a0b..816844f153 100644 --- a/shell-plugin/forge.theme.zsh +++ b/shell-plugin/forge.theme.zsh @@ -1,7 +1,8 @@ #!/usr/bin/env zsh # Enable prompt substitution for RPROMPT -setopt PROMPT_SUBST +# Use emulate to ensure setopt applies globally even when sourced from a function +emulate -R zsh -c 'setopt PROMPT_SUBST' # Model and agent info with token count # Fully formatted output directly from Rust @@ -22,6 +23,7 @@ function _forge_prompt_info() { # Right prompt: agent and model with token count (uses single forge prompt command) # Set RPROMPT if empty, otherwise append to existing value +# Use typeset -g to ensure RPROMPT is set globally even when sourced from a function if [[ -z "$_FORGE_THEME_LOADED" ]]; then - RPROMPT='$(_forge_prompt_info)'"${RPROMPT:+ ${RPROMPT}}" + typeset -g RPROMPT='$(_forge_prompt_info)'"${RPROMPT:+ ${RPROMPT}}" fi diff --git a/shell-plugin/lib/config.zsh b/shell-plugin/lib/config.zsh index 0a7510b321..e4392833ef 100644 --- a/shell-plugin/lib/config.zsh +++ b/shell-plugin/lib/config.zsh @@ -1,36 +1,38 @@ #!/usr/bin/env zsh # Configuration variables for forge plugin -# Using typeset to keep variables local to plugin scope and prevent public exposure +# Using typeset -gh (global + hidden) so variables survive lazy-loading +# from within a function scope (e.g. zinit, zplug, zsh-defer) while +# staying hidden from `typeset` listings. -typeset -h _FORGE_BIN="${FORGE_BIN:-forge}" -typeset -h _FORGE_CONVERSATION_PATTERN=":" -typeset -h _FORGE_MAX_COMMIT_DIFF="${FORGE_MAX_COMMIT_DIFF:-100000}" -typeset -h _FORGE_DELIMITER='\s\s+' -typeset -h _FORGE_PREVIEW_WINDOW="--preview-window=bottom:75%:wrap:border-sharp" +typeset -gh _FORGE_BIN="${FORGE_BIN:-forge}" +typeset -gh _FORGE_CONVERSATION_PATTERN=":" +typeset -gh _FORGE_MAX_COMMIT_DIFF="${FORGE_MAX_COMMIT_DIFF:-100000}" +typeset -gh _FORGE_DELIMITER='\s\s+' +typeset -gh _FORGE_PREVIEW_WINDOW="--preview-window=bottom:75%:wrap:border-sharp" # Detect fd command - Ubuntu/Debian use 'fdfind', others use 'fd' -typeset -h _FORGE_FD_CMD="$(command -v fdfind 2>/dev/null || command -v fd 2>/dev/null || echo 'fd')" +typeset -gh _FORGE_FD_CMD="$(command -v fdfind 2>/dev/null || command -v fd 2>/dev/null || echo 'fd')" # Detect bat command - use bat if available, otherwise fall back to cat if command -v bat &>/dev/null; then - typeset -h _FORGE_CAT_CMD="bat --color=always --style=numbers,changes --line-range=:500" + typeset -gh _FORGE_CAT_CMD="bat --color=always --style=numbers,changes --line-range=:500" else - typeset -h _FORGE_CAT_CMD="cat" + typeset -gh _FORGE_CAT_CMD="cat" fi # Commands cache - loaded lazily on first use -typeset -h _FORGE_COMMANDS="" +typeset -gh _FORGE_COMMANDS="" # Hidden variables to be used only via the ForgeCLI -typeset -h _FORGE_CONVERSATION_ID -typeset -h _FORGE_ACTIVE_AGENT +typeset -gh _FORGE_CONVERSATION_ID +typeset -gh _FORGE_ACTIVE_AGENT # Previous conversation ID for :conversation - (like cd -) -typeset -h _FORGE_PREVIOUS_CONVERSATION_ID +typeset -gh _FORGE_PREVIOUS_CONVERSATION_ID # Session-scoped model and provider overrides (set via :model / :m). # When non-empty, these are passed as --model / --provider to every forge # invocation for the lifetime of the current shell session. -typeset -h _FORGE_SESSION_MODEL -typeset -h _FORGE_SESSION_PROVIDER +typeset -gh _FORGE_SESSION_MODEL +typeset -gh _FORGE_SESSION_PROVIDER diff --git a/shell-plugin/lib/highlight.zsh b/shell-plugin/lib/highlight.zsh index cdefe7c4b4..ec1f0c59de 100644 --- a/shell-plugin/lib/highlight.zsh +++ b/shell-plugin/lib/highlight.zsh @@ -3,6 +3,14 @@ # Syntax highlighting configuration for forge commands # Style the conversation pattern with appropriate highlighting # Keywords in yellow, rest in default white +# +# Use global declarations so we update the shared zsh-syntax-highlighting +# collections even when sourced from within a function (lazy-loading plugin +# managers). Patterns must remain an associative array because the pattern +# highlighter stores regex => style entries in ZSH_HIGHLIGHT_PATTERNS. + +typeset -gA ZSH_HIGHLIGHT_PATTERNS +typeset -ga ZSH_HIGHLIGHT_HIGHLIGHTERS # Style tagged files ZSH_HIGHLIGHT_PATTERNS+=('@\[[^]]#\]' 'fg=cyan,bold')