fix(install): write claude-cowork alias via temp+mv to survive legacy symlink#140
Merged
Merged
Conversation
… symlink Pre-8ad9bd7 installs left ~/.local/bin/claude-cowork as a symlink to claude-desktop. The cat-redirect that writes the alias would follow that symlink and overwrite the freshly-written claude-desktop launcher with the 56-byte wrapper, which then exec's itself in an infinite loop (99% CPU, no UI, no logs) on the next launch. Confirmed on upgrades to current master. Write the wrapper to a temp file in the same directory and mv it into place. mv replaces the symlink itself rather than its target, atomically, and avoids an unconditional rm on a live path. Co-authored-by: rmorison <220009+rmorison@users.noreply.github.com> https://claude.ai/code/session_01JN4faDKhhBv6qGFvPWRF21
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes an upgrade-time installer regression where a legacy ~/.local/bin/claude-cowork -> claude-desktop symlink could cause the installer to overwrite the claude-desktop launcher when writing the claude-cowork wrapper. The change updates alias creation to use an atomic temp-file write + mv -f to replace the symlink itself safely.
Changes:
- Write the
claude-coworkwrapper to a temp file in~/.local/binand atomically replace the destination viamv -fto avoid following legacy symlinks. - Add explanatory comments documenting the legacy-symlink failure mode and why the temp+mv approach is used.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Pre-8ad9bd7 installs left ~/.local/bin/claude-cowork as a symlink to | ||
| # claude-desktop. A plain `cat > claude-cowork` would FOLLOW that symlink | ||
| # and overwrite the just-written claude-desktop launcher with this wrapper, | ||
| # which then exec's itself in an infinite loop (PR #138). Write to a temp |
| # which then exec's itself in an infinite loop (PR #138). Write to a temp | ||
| # file in the same dir and `mv` it into place: mv replaces the symlink | ||
| # itself rather than its target, atomically, with no unconditional rm. | ||
| local cowork_alias="$HOME/.local/bin/claude-cowork" |
Comment on lines
+813
to
817
| cowork_tmp="$(mktemp "$HOME/.local/bin/.claude-cowork.XXXXXX")" || die "Failed to create temp file for claude-cowork wrapper" | ||
| cat > "$cowork_tmp" << 'WRAP' | ||
| #!/bin/bash | ||
| exec "$HOME/.local/bin/claude-desktop" "$@" | ||
| WRAP |
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
Fixes the upgrade-time regression diagnosed in #138 (credit to @rmorison), using the symlink-safe atomic-replace variant instead of an unconditional
rm.The bug: Pre-
8ad9bd7installs created~/.local/bin/claude-coworkas a symlink toclaude-desktop. The currentcreate_launcher()writes the alias withcat > ~/.local/bin/claude-cowork, which follows that legacy symlink and overwrites the just-writtenclaude-desktoplauncher (~3.4 KB) with the 56-byte wrapper body.claude-desktopthenexecs itself in an infinite loop — 99% CPU, no Electron child, no logs. The installer reports success because the in-treeCOWORK_WRAPPER_OKcheck only validatesclaude-cowork. Confirmed on upgrades to currentmaster.The fix: Write the wrapper to a temp file in the same directory and
mv -fit into place.mvreplaces the symlink itself rather than writing through it, atomically, with no unconditionalrmon a live path — so the legacy symlink is broken cleanly andclaude-desktopis never touched.Why this variant over
rm -f+cat@rmorison's original fix (
rm -fbefore thecat) is correct and bounded. This PR uses the temp-file +mvform instead because it avoids deleting a live path entirely and is atomic (no window where the alias is missing or half-written). Same outcome, slightly more defensive.Test plan
bash -n install.shclaude-coworksymlinked →claude-desktop), run the patched alias-writing block, and confirm:claude-desktopretains the full launcher (not clobbered),claude-coworkis a real file (the wrapper), both are regular files (neither a symlink), and no temp files leak.https://claude.ai/code/session_01JN4faDKhhBv6qGFvPWRF21
Generated by Claude Code