Skip to content

fix: make pho alias work across all package managers#19

Merged
citron (lcandy2) merged 1 commit into
mainfrom
cursor/fix-pho-alias-across-package-managers
May 9, 2026
Merged

fix: make pho alias work across all package managers#19
citron (lcandy2) merged 1 commit into
mainfrom
cursor/fix-pho-alias-across-package-managers

Conversation

@lcandy2
Copy link
Copy Markdown
Member

@lcandy2 citron (lcandy2) commented May 9, 2026

Summary

  • Root cause: Bun fully resolves symlink chains for process.argv[1]. Under bun link, the double-symlink (~/.bun/bin/photon → node_modules/…/photon → dev-dir/dist/photon.js) resolved all the way to the dev directory, stripping node_modules from the path. The old regex guard failed, so pho was never created.
  • Fix: Replace the hardcoded node_modules regex + .. traversal approach with a realpathSync-based scan: check $PATH entries (plus well-known global bin fallbacks) for a photon whose realpath matches ours, then drop pho → ./photon next to it. No directory layout assumptions.
  • Result: Works across bun, npm, pnpm, yarn global installs, bun link, and local npx/bunx — tested all six layouts.

Test plan

  • bun add -g layout
  • bun link (double symlink)
  • npm install -g layout
  • pnpm add -g layout
  • yarn global add layout
  • local npx/bunx layout
  • Source-mode (bun run src/index.ts) does not trigger alias creation
  • Idempotent on repeated runs
  • Does not clobber existing pho binary
  • bun run typecheck passes

Made with Cursor


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

Summary by CodeRabbit

  • Refactor
    • Improved the alias resolution mechanism to be more reliable and straightforward, simplifying the directory lookup process.

Review Change Stack

The old approach matched `node_modules` in process.argv[1] and walked
hardcoded `..` levels to find the bin directory. This broke under
`bun link` (Bun fully resolves the symlink chain, stripping
node_modules from argv[1]) and assumed a scoped-package layout.

Replace with a realpath-based approach: scan $PATH plus well-known
global bin fallbacks for a `photon` entry whose realpath matches ours,
then drop `pho` next to it. Package-manager agnostic with no hardcoded
directory layouts.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copilot AI review requested due to automatic review settings May 9, 2026 06:38
@lcandy2 citron (lcandy2) added the release Just as it is label May 9, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f8d2da5c-8e60-4c20-b1f2-184e7e250c4a

📥 Commits

Reviewing files that changed from the base of the PR and between 0c5beaf and 642d568.

📒 Files selected for processing (1)
  • src/lib/pho-alias.ts
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file>
Bun automatically loads .env, so don't use dotenv
Use Bun.serve() with WebSockets, HTTPS, and routes instead of express
Use bun:sqlite for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of ws package
Prefer Bun.file over node:fs's readFile/writeFile for file operations
Use Bun.$ template literal for shell commands instead of execa
Use bun --hot to run server files with hot module reloading

Files:

  • src/lib/pho-alias.ts
**/*.{html,ts,tsx,css}

📄 CodeRabbit inference engine (CLAUDE.md)

Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild

Files:

  • src/lib/pho-alias.ts
**/*.{html,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • src/lib/pho-alias.ts
🔇 Additional comments (4)
src/lib/pho-alias.ts (4)

1-2: LGTM!

The imports are appropriate. Bun.file doesn't provide equivalents for these symlink and stat operations, so using node:fs is correct here.


4-23: Excellent documentation.

The JSDoc clearly explains the constraints (npm 11 npx, Bun postinstall behavior) and the chosen solution. The cost analysis at lines 20-22 is helpful for understanding the runtime impact.


29-46: LGTM!

The guard logic correctly excludes source mode and compiled binaries. The PATH-first approach with well-known fallbacks provides good coverage across package managers.


48-77: LGTM!

The implementation is well-designed:

  • Set-based deduplication avoids redundant syscalls
  • Using lstatSync correctly detects any existing file/symlink (including broken ones)
  • Relative symlink ./photon is portable
  • Early return after first match is correct since it corresponds to PATH precedence
  • Outer try/catch appropriately swallows errors for the documented "best-effort" behavior

📝 Walkthrough

Walkthrough

The PR refactors ensurePhoAlias() to locate the installed photon binary by scanning $PATH and home directory global bin locations for an entry matching the running dist/photon.js realpath, replacing the prior approach that derived candidate directories from a computed package root.

Changes

Photon Alias Installation Refactor

Layer / File(s) Summary
Imports & Dependencies
src/lib/pho-alias.ts
Removed existsSync, added delimiter to support $PATH parsing while keeping path and filesystem utilities for the new lookup strategy.
Strategy Documentation
src/lib/pho-alias.ts
Updated comments from "walk up to package root + try adjacent bin layouts" to "scan $PATH plus home-directory global bin fallbacks and match by realpath."
Core Implementation
src/lib/pho-alias.ts
Refactored ensurePhoAlias() to guard on process.argv[1] ending with dist/photon.js, compute its resolved realpath, build a de-duplicated candidate directory list from $PATH and home bins, then iterate directories to find a photon whose realpath matches and create the pho symlink.
Cleanup
src/lib/pho-alias.ts
Removed the escapeForRegex helper function that was used by the previous package-root-based detection logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • photon-hq/cli#3: Renamed CLI binaries to photon with pho alias and introduced dist/photon.js, which is the runtime entry point this refactored alias logic now guards and matches against.
  • photon-hq/cli#12: Concurrently modifies ensurePhoAlias() in the same file, implementing pho-symlink behavior but using package-root-derived directories; the two PRs represent different strategies for the same feature.
  • photon-hq/cli#11: Updates package.json to point CLI bins to dist/photon.js directly, which is the specific path this refactor now checks for via process.argv[1].

Poem

A rabbit hops through PATH with glee,🐰
Matching realpaths, wild and free,
No more package roots to trace—
Just symlinks in the right place! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing the pho alias to work across all package managers by refactoring the directory location logic.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/fix-pho-alias-across-package-managers

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the runtime pho alias installer to work across more package-manager layouts (notably Bun’s bun link double-symlink behavior) by locating the active photon bin directory via $PATH scanning and realpath matching instead of assuming a node_modules/.../dist/photon.js directory shape.

Changes:

  • Replace the previous node_modules-shape regex/traversal logic with a $PATH (plus fallback dirs) scan that finds a photon whose realpath matches the running script.
  • Create pho -> ./photon next to the matched photon entry, while remaining best-effort and non-clobbering.
Comments suppressed due to low confidence (1)

src/lib/pho-alias.ts:72

  • The lstatSync(pho) check treats any thrown error as “pho doesn’t exist” and proceeds to create the symlink. If lstatSync fails due to permissions or transient IO issues (not ENOENT), this will repeatedly attempt symlinkSync on every run and still fail. It would be more robust to only treat ENOENT as “absent” and return early for other errors.
      try {
        lstatSync(pho);
        return; // something already lives here; leave it alone
      } catch {
        // pho doesn't exist; safe to create
      }

      symlinkSync("./photon", pho);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/pho-alias.ts
@lcandy2 citron (lcandy2) merged commit 5645c23 into main May 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Just as it is

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants