Skip to content

fix(install): refuse to replace non-managed files or dirs during setup - #114

Open
56phil wants to merge 1 commit into
danielvm-git:mainfrom
56phil:fix/installer-clobber
Open

fix(install): refuse to replace non-managed files or dirs during setup#114
56phil wants to merge 1 commit into
danielvm-git:mainfrom
56phil:fix/installer-clobber

Conversation

@56phil

@56phil 56phil commented Aug 24, 2026

Copy link
Copy Markdown

Summary

linkSkills/linkDir/linkFile/linkHook in scripts/lib/install-helpers.js previously did fs.rmSync(dst, { force: true, recursive: true }) before symlinking — silently destroying any pre-existing file or directory at the destination. A user's own ~/.claude/hooks/rtk-rewrite.sh (installed by RTK itself) or a user-authored skills dir with a colliding name would be deleted without prompt, backup, or any check.

Change

Add assertReplaceable(dst), called by all four link functions before removal:

  • Destination absent → safe to create.
  • Destination is a symlink pointing back into this package (REPO_ROOT) → managed by bigpowers, safe to re-link.
  • Anything else (regular file, directory, foreign symlink) → throw with a clear message: "Refusing to replace ... Back it up or remove it manually, then re-run setup."

The recursive: true rmSync is dropped in favor of plain rmSync, since the only removable case is now a single managed symlink.

Verification

  • bash scripts/test-install-helpers.sh → ALL PASS (existing suite, no regressions).
  • Direct guard checks: blocks a non-managed file, blocks a foreign symlink, re-links a managed symlink, creates a new link.

Closes #112.

linkSkills/linkDir/linkFile/linkHook previously did rmSync(dst,
{recursive:true}) before symlinking, silently destroying any pre-existing
file or directory at the destination. A user's own ~/.claude/hooks/
rtk-rewrite.sh (installed by RTK itself) or a user-authored skills dir
with a colliding name would be deleted without prompt or backup.

Add assertReplaceable(): the destination may only be replaced when it is
absent or already a symlink pointing back into this package. Anything
else throws with a clear message. The recursive rmSync is dropped in
favor of plain rmSync since the only removable case is now a single
managed symlink.

Closes danielvm-git#112
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

install: claude setup silently replaces an existing ~/.claude/hooks/rtk-rewrite.sh with a different, incompatible hook

1 participant