[Bug]: PATH export injected into ~/.zshrc outside of forge initialize markers, causing perpetual conflict with dotfile managers
Description
During install and forge update, forge prepends the following two lines to ~/.zshrc:
# Added by ForgeCode installer
export PATH="/Users/<user>/.local/bin:$PATH"
The ensure_install_dir_shell_path function in the install script strips any existing instance and unconditionally re-prepends these lines on every invocation — it does not check whether ~/.local/bin is already configured for persistence in any other sourced file (.zprofile, .zshenv, .profile). This function appears to be called both on initial install and by forge update. The lines are also injected outside the >>> forge initialize >>> / <<< forge initialize <<< markers that forge already uses for the rest of its shell setup.
Relevant install script snippet:
ensure_install_dir_shell_path() {
export_line="export PATH=\"$INSTALL_DIR:\$PATH\""
marker="# Added by ForgeCode installer"
for rc_file in "$HOME/.bashrc" "$HOME/.zshrc"; do
if [ -f "$rc_file" ]; then
temp_rc=$(mktemp)
grep -vF "$marker" "$rc_file" | grep -vF "$export_line" > "$temp_rc" || true
{
printf '%s\n' "$marker"
printf '%s\n' "$export_line"
cat "$temp_rc"
} > "$temp_rc.new"
mv "$temp_rc.new" "$rc_file"
rm -f "$temp_rc"
fi
done
}
This strip-then-prepend approach avoids duplicates (good) but means .zshrc is always rewritten on every install/update, regardless of whether the path is already available via other means.
This creates a conflict for users who manage ~/.zshrc with a dotfile manager (chezmoi, GNU Stow, etc.):
- Dotfile manager's source doesn't contain the forge-injected lines
- User runs
chezmoi apply / equivalent → lines are removed from the live file
- User runs
forge update → lines are re-injected (since they're now absent)
- Go to step 2 — perpetual drift cycle
Steps to reproduce
- Manage
~/.zshrc with chezmoi (or any dotfile manager)
- Run
forge update
- Run
chezmoi diff — the two PATH lines appear as live-only changes not in source
- Run
chezmoi apply ~/.zshrc — lines removed
- Run
forge update — lines re-injected
- Repeat indefinitely
Expected
The PATH export should be placed inside the >>> forge initialize >>> block (or a similarly named marker block), so dotfile managers can include the full marked section in their managed source as a stable unit. This is already the pattern forge uses for the rest of its zsh integration.
Alternatively, forge could check whether ~/.local/bin is already present in $PATH via any sourced config file (.zprofile, .zshenv, etc.) before modifying ~/.zshrc at all.
Actual
Lines are injected at the top of ~/.zshrc outside any marker block, making them invisible to the >>> forge initialize >>> idempotency system and untrackable by dotfile managers.
Environment
- OS: macOS 26.4.1 (Apple Silicon)
- Shell: zsh
- Installation method:
curl -fsSL https://forgecode.dev/cli | sh
Additional context
The >>> forge initialize >>> pattern already solves exactly this problem for forge's main shell setup block. Extending it to cover the PATH export too would be a minimal, consistent fix.
[Bug]: PATH export injected into
~/.zshrcoutside offorge initializemarkers, causing perpetual conflict with dotfile managersDescription
During install and
forge update, forge prepends the following two lines to~/.zshrc:The
ensure_install_dir_shell_pathfunction in the install script strips any existing instance and unconditionally re-prepends these lines on every invocation — it does not check whether~/.local/binis already configured for persistence in any other sourced file (.zprofile,.zshenv,.profile). This function appears to be called both on initial install and byforge update. The lines are also injected outside the>>> forge initialize >>>/<<< forge initialize <<<markers that forge already uses for the rest of its shell setup.Relevant install script snippet:
This strip-then-prepend approach avoids duplicates (good) but means
.zshrcis always rewritten on every install/update, regardless of whether the path is already available via other means.This creates a conflict for users who manage
~/.zshrcwith a dotfile manager (chezmoi, GNU Stow, etc.):chezmoi apply/ equivalent → lines are removed from the live fileforge update→ lines are re-injected (since they're now absent)Steps to reproduce
~/.zshrcwith chezmoi (or any dotfile manager)forge updatechezmoi diff— the two PATH lines appear as live-only changes not in sourcechezmoi apply ~/.zshrc— lines removedforge update— lines re-injectedExpected
The PATH export should be placed inside the
>>> forge initialize >>>block (or a similarly named marker block), so dotfile managers can include the full marked section in their managed source as a stable unit. This is already the pattern forge uses for the rest of its zsh integration.Alternatively, forge could check whether
~/.local/binis already present in$PATHvia any sourced config file (.zprofile,.zshenv, etc.) before modifying~/.zshrcat all.Actual
Lines are injected at the top of
~/.zshrcoutside any marker block, making them invisible to the>>> forge initialize >>>idempotency system and untrackable by dotfile managers.Environment
curl -fsSL https://forgecode.dev/cli | shAdditional context
The
>>> forge initialize >>>pattern already solves exactly this problem for forge's main shell setup block. Extending it to cover the PATH export too would be a minimal, consistent fix.