Skip to content

fix(cli): symlink CLI binaries into /usr/local/bin for non-interactive hooks - #456

Open
alezander86 wants to merge 2 commits into
codemie-ai:mainfrom
alezander86:EPMCDME-13884_postinstall-universal-path-link
Open

fix(cli): symlink CLI binaries into /usr/local/bin for non-interactive hooks#456
alezander86 wants to merge 2 commits into
codemie-ai:mainfrom
alezander86:EPMCDME-13884_postinstall-universal-path-link

Conversation

@alezander86

Copy link
Copy Markdown
Contributor

Summary

SessionStart/SessionEnd hooks run codemie hook inside a plain non-interactive, non-login /usr/bin/bash -c "..." subshell, which never sources ~/.zshrc/~/.bashrc. postinstall.mjs only patched PATH via those rc files, so the hook failed with codemie: command not found even on a healthy install (codemie doctor green, codemie-claude working) — and reinstalling didn't help, since the binary was already correctly installed and linked.

Changes

  • scripts/postinstall.mjs: in addition to the existing rc-file PATH patch, symlink every CLI bin (read from package.json's bin field) into /usr/local/bin, which is on PATH for every shell regardless of interactivity/login status.
  • Skips a target that already points to the right symlink; warns (without clobbering) if a same-named file exists and isn't a CodeMie symlink.
  • Skipped entirely on win32.
  • If /usr/local/bin doesn't exist or isn't writable, prints the exact sudo ln -sf ... command(s) needed as a self-service manual fix — no reinstall required.
  • Existing rc-file/interactive-shell behavior (codemie doctor, codemie-claude) is unchanged.

Testing

  • Tests added/updated
  • Manual testing done — node --check scripts/postinstall.mjs, npm run typecheck, npm run build all pass; pre-commit hooks (lint-staged, typecheck, gitleaks) passed on commit.

Checklist

  • Code follows project standards
  • CI is green (npm run ci)
  • No merge conflicts with main

Ref: EPMCDME-13884, INC0000799603 / UR0002016875

@m-golovchin

Copy link
Copy Markdown
Collaborator

Checked manually, and tests pass
But have one comment from review

The fix: save-and-restore pattern

Current code (lines 105–107):
unlinkSync(target);
symlinkSync(source, target); // if this throws → old symlink is gone, nothing in its place

Fixed code:
const oldTarget = readlinkSync(target); // already confirmed it's a symlink
unlinkSync(target);
try {
symlinkSync(source, target);
} catch (createErr) {
try { symlinkSync(oldTarget, target); } catch { /* restore failed, fall through */ }
throw createErr; // re-throw so the outer catch adds the manual command
}

Why this is safer: If symlinkSync(source, target) fails (e.g., a momentary permissions race), the inner catch immediately puts the original symlink back. The outer catch at line 108 still fires (because createErr is re-thrown), so the manual sudo ln -sf ... hint is still printed. The user ends up with their old, working symlink intact rather than nothing.

The temp+rename alternative would be fully atomic (no window where the path is absent), but requires fs.renameSync and a temp filename, which is more code. For a postinstall script the save-and-restore pattern is the simpler, proportionate fix.

@alezander86

Oleksandr Taruraiev added 2 commits August 6, 2026 20:29
…e hooks

SessionStart/SessionEnd hooks run `codemie hook` in a plain non-interactive,
non-login `/usr/bin/bash -c "..."` subshell that never sources ~/.zshrc or
~/.bashrc, so the rc-file PATH patch postinstall.mjs relied on never took
effect there even though the user's own terminal worked fine.

postinstall.mjs now also symlinks every CLI bin (from package.json's `bin`
field) into /usr/local/bin, which every shell has on PATH regardless of
interactivity. Falls back to a printed manual `sudo ln -sf` hint when that
directory isn't writable, and skips existing non-symlink files instead of
clobbering them. Existing rc-file behavior is unchanged.

Ref: INC0000799603 / UR0002016875
@alezander86
alezander86 force-pushed the EPMCDME-13884_postinstall-universal-path-link branch from 9dfbdfc to a927699 Compare August 6, 2026 17:30
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.

2 participants