Skip to content

fix(bin): single bin + lazy pho symlink so unpinned npx works#12

Merged
citron (lcandy2) merged 2 commits into
mainfrom
fix/npx-single-bin
Apr 29, 2026
Merged

fix(bin): single bin + lazy pho symlink so unpinned npx works#12
citron (lcandy2) merged 2 commits into
mainfrom
fix/npx-single-bin

Conversation

@lcandy2
Copy link
Copy Markdown
Member

@lcandy2 citron (lcandy2) commented Apr 29, 2026

Problem. After publishing 0.1.0, npx @photon-ai/photon --version (unpinned) fails with sh: photon: command not found. Pinned forms (@latest, @0.1.0) work; bunx works; bun add -g then photon works. The break is npm 11's npx skipping bin auto-resolve when bin has multiple keys, even if one matches the package's post-scope name. Confirmed against control: npx @vercel/ncc (single bin) resolves unpinned; ours doesn't.

Fix

  1. bin: { photon, pho }bin: "./dist/photon.js". npm registers the single bin under the package's post-scope name (photon). Unpinned npx @photon-ai/photon now auto-resolves.

  2. Lazy pho symlink at runtime (src/lib/pho-alias.ts). Walks the standard bin-dir layouts adjacent to the running script — local node_modules/.bin, bun-global <root>/bin, npm-global <prefix>/bin — finds the existing photon bin, and creates pho as a relative symlink next to it. First photon invocation after install creates it; subsequent runs short-circuit on existsSync(pho).

Why not postinstall?

Tried. Works for npm/yarn/pnpm but Bun blocks postinstall scripts by default:

$ bun add /tmp/photon-ai-photon-0.1.0.tgz
installed @photon-ai/photon@0.1.0 with binaries:
 - photon

111 packages installed [85.00ms]

Blocked 1 postinstall. Run `bun pm untrusted` for details.

bun add -g is documented as the primary install path. Silently dropping pho for the majority of users isn't acceptable, and bun pm trust @photon-ai/photon is too much friction. Runtime creation works uniformly across all package managers without trust opt-in.

Cost

After pho exists, two existsSync calls per photon invocation (~microseconds). Errors are swallowed — pho is convenience, never load-bearing.

Verified locally

$ bun run build && npm pack --pack-destination /tmp
$ cd $(mktemp -d) && echo '{"name":"test","type":"module"}' > package.json

# bun install path (would-be primary)
$ bun add /tmp/photon-ai-photon-0.1.0.tgz
$ ls node_modules/.bin/ph*
node_modules/.bin/photon -> ../@photon-ai/photon/dist/photon.js
$ ./node_modules/.bin/photon --version
0.1.0
$ ls -la node_modules/.bin/ph*
node_modules/.bin/pho -> ./photon
node_modules/.bin/photon -> ../@photon-ai/photon/dist/photon.js
$ ./node_modules/.bin/pho --version
0.1.0

# npm install path
$ rm -rf node_modules && npm install /tmp/photon-ai-photon-0.1.0.tgz
$ ./node_modules/.bin/photon --version && ls node_modules/.bin/pho
0.1.0
node_modules/.bin/pho -> ./photon

Test plan

  • bunx tsc --noEmit clean
  • bun run build produces dist/photon.js (~0.38 MB)
  • bun add from tarball: photon works, first run creates pho
  • npm install from tarball: same
  • Symlink + process.argv[1] references survived minify (verified in tarball)
  • After merge → 0.1.1 publish → npx @photon-ai/photon --version (unpinned) returns 0.1.1
  • After merge → bun add -g @photon-ai/photonphoton loginpho ls works

Triggers a release

Add release label after review to publish 0.1.1.

🤖 Generated with Claude Code


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

  • New Features

    • The pho command alias is now created automatically the first time you run photon; the CLI ensures the alias at startup.
  • Documentation

    • README updated to explicitly state that the pho alias is created automatically on first run.

Reduce package.json#bin from { photon, pho } to a single string
"./dist/photon.js" so npm 11's `npx @photon-ai/photon` (no version
pin) auto-resolves the binary. With multiple bin entries, npx skips
auto-resolve even when one matches the post-scope name, leaving
users with `sh: photon: command not found` unless they pin a
version. Verified against control: `@vercel/ncc` (single bin) works
unpinned; multi-bin scoped packages don't.

Restore the `pho` shortcut at runtime instead of declaring it in
`bin`. On first invocation, `ensurePhoAlias()` walks the standard
bin-dir layouts (local node_modules/.bin, bun-global, npm-global)
adjacent to the running script, finds the existing `photon` bin,
and creates `pho` as a relative symlink. Postinstall would have
been the obvious choice but Bun blocks postinstall by default,
which would silently strip `pho` from `bun add -g` users — our
primary install path. Runtime creation costs two existsSync calls
per launch and self-heals if `pho` is removed.

Verified locally: npm install + bun add from a packed tarball both
behave identically — `photon` available immediately, `pho` created
on first `photon` run.

Co-authored-by: Orca <help@stably.ai>
Copilot AI review requested due to automatic review settings April 29, 2026 19:22
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

The PR removes the explicit pho bin entry and adds runtime alias creation: package.json now registers only photon, src/index.ts calls ensurePhoAlias() at startup, and src/lib/pho-alias.ts creates a pho symlink on first execution; README updated to document this behavior.

Changes

Cohort / File(s) Summary
Package Configuration
package.json
Replaced dual bin object ("photon" and "pho") with a single string ./dist/photon.js, removing install-time pho registration.
CLI Initialization
src/index.ts
Imported and invoked ensurePhoAlias() during top-level startup (before update notifier).
Alias Management
src/lib/pho-alias.ts
Added exported ensurePhoAlias() to detect install layout, probe candidate bin dirs, and create a pho symlink pointing to photon if missing; errors are swallowed.
Documentation
README.md
Clarified that the pho alias is created automatically on first execution of photon after installation.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI as photon (entry)
  participant Alias as ensurePhoAlias()
  participant FS as Filesystem

  User->>CLI: run `photon`
  CLI->>Alias: invoke ensurePhoAlias()
  Alias->>CLI: inspect process.argv[1]
  Alias->>FS: probe candidate bin dirs (local, bun, npm global)
  alt photon binary found in candidate
    Alias->>FS: check `pho` with lstat
    alt `pho` missing
      Alias->>FS: create symlink `pho` -> `photon`
      FS-->>Alias: success (or error)
    else `pho` exists
      FS-->>Alias: no-op
    end
  else photon binary not found
    Alias-->>CLI: do nothing (silently ignore)
  end
  CLI-->>User: continue startup and run CLI
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped in at runtime, quiet and neat,
To link pho to photon — a small, sly feat.
No install-time fuss, no tangled threads,
Just a gentle symlink where the photon treads. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 changes: converting to a single bin entry and implementing lazy symlink creation for the pho alias to fix npx resolution issues.
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

Warning

Review ran into problems

🔥 Problems

These MCP integrations need to be re-authenticated in the Integrations settings: Linear


Review rate limit: 3/5 reviews remaining, refill in 16 minutes and 41 seconds.

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

Fixes unpinned npx @photon-ai/photon failing under npm 11 by switching the package to a single-bin declaration while preserving the pho alias via a best-effort runtime symlink created on first photon execution.

Changes:

  • Change package.json bin from a multi-key object (photon, pho) to a single string bin to restore npm 11 unpinned npx auto-resolve.
  • Add ensurePhoAlias() runtime hook to lazily create a pho symlink next to the resolved photon bin.
  • Update README to document that pho is created automatically after the first photon run.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/lib/pho-alias.ts New helper that locates the active bin directory and creates a pho symlink next to photon.
src/index.ts Calls ensurePhoAlias() at startup so the alias is created lazily.
package.json Switches to a single bin entry ("./dist/photon.js") to make unpinned npx work on npm 11.
README.md Documents the new lazy-creation behavior of the pho alias.

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

Comment thread src/lib/pho-alias.ts Outdated
Comment thread src/lib/pho-alias.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/pho-alias.ts (1)

45-47: Keep best-effort behavior, but surface failures in debug mode.

Swallowing all errors is fine for UX, but a debug-only trace would make alias issues diagnosable without affecting normal users.

🛠️ Proposed debug-only logging
-  } catch {
-    // best-effort
+  } catch (err) {
+    if (process.env.PHOTON_DEBUG === "1") {
+      console.error("[pho-alias] failed to ensure pho alias", err);
+    }
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/pho-alias.ts` around lines 45 - 47, Keep the best-effort swallow but
change the empty catch to capture the error and emit a debug-only trace: replace
the bare "catch { /* best-effort */ }" with "catch (err) { if (process.env.DEBUG
=== 'true' || process.env.NODE_ENV !== 'production') console.debug('pho-alias
error:', err); }" (or use the existing app logger if available) so the module's
alias-resolution catch block surfaces failures only in debug/dev environments;
ensure you reference the same catch in the pho-alias module and keep the swallow
behavior for production.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/pho-alias.ts`:
- Around line 24-35: The current logic computes bin-directory candidates
(candidates) even when running in source/dev mode; add an install-layout guard
so we only probe/write aliases when running from the installed package layout.
Before building/using candidates (where me, pkgRoot are computed), check for a
clear installed-layout signal—e.g., verify me points into the built dist
(contains "dist/photon.js") or that package metadata or expected dist layout
exists (use fs.existsSync on resolve(pkgRoot, "package.json") or
resolve(pkgRoot, "dist")), and return early if that check fails; only then
compute/use candidates and perform alias writes.

---

Nitpick comments:
In `@src/lib/pho-alias.ts`:
- Around line 45-47: Keep the best-effort swallow but change the empty catch to
capture the error and emit a debug-only trace: replace the bare "catch { /*
best-effort */ }" with "catch (err) { if (process.env.DEBUG === 'true' ||
process.env.NODE_ENV !== 'production') console.debug('pho-alias error:', err);
}" (or use the existing app logger if available) so the module's
alias-resolution catch block surfaces failures only in debug/dev environments;
ensure you reference the same catch in the pho-alias module and keep the swallow
behavior for production.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 89f7e5ee-3915-45f7-8ddd-b339aab67087

📥 Commits

Reviewing files that changed from the base of the PR and between b97fc98 and e9cfb75.

📒 Files selected for processing (4)
  • README.md
  • package.json
  • src/index.ts
  • src/lib/pho-alias.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🧰 Additional context used
📓 Path-based instructions (4)
package.json

📄 CodeRabbit inference engine (CLAUDE.md)

package.json: Use bun install instead of npm install, yarn install, or pnpm install in package.json
Use bun run <script> instead of npm run <script>, yarn run <script>, or pnpm run <script>

Files:

  • package.json
**/*.{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/index.ts
  • 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/index.ts
  • 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/index.ts
  • src/lib/pho-alias.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install`
Learnt from: CR
Repo: photon-hq/spectrum-cloud PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-16T22:15:43.438Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install`
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json scripts
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>`
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>`
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>` for running scripts
Learnt from: lcandy2
Repo: photon-hq/cli PR: 11
File: package.json:14-23
Timestamp: 2026-04-29T19:00:22.283Z
Learning: In photon-hq/cli, the `LICENSE` file was intentionally removed (commit 9c451da) per project direction. The `license` field is deliberately absent from `package.json`, and `LICENSE` is not included in the `files` array. Do not flag the missing `license` field or absent LICENSE file as issues in this repository.
📚 Learning: 2026-04-29T19:00:22.283Z
Learnt from: lcandy2
Repo: photon-hq/cli PR: 11
File: package.json:14-23
Timestamp: 2026-04-29T19:00:22.283Z
Learning: In photon-hq/cli, the `LICENSE` file was intentionally removed (commit 9c451da) per project direction. The `license` field is deliberately absent from `package.json`, and `LICENSE` is not included in the `files` array. Do not flag the missing `license` field or absent LICENSE file as issues in this repository.

Applied to files:

  • README.md
  • package.json
📚 Learning: 2026-04-28T03:08:36.661Z
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>`

Applied to files:

  • package.json
📚 Learning: 2026-03-10T22:49:19.048Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>` for running scripts

Applied to files:

  • package.json
📚 Learning: 2026-04-04T19:01:14.591Z
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install`

Applied to files:

  • package.json
  • src/lib/pho-alias.ts
📚 Learning: 2026-04-28T03:08:36.661Z
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json

Applied to files:

  • package.json
  • src/lib/pho-alias.ts
📚 Learning: 2026-03-10T22:49:19.048Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json scripts

Applied to files:

  • package.json
  • src/lib/pho-alias.ts
🔍 Remote MCP Linear

Based on my search of the Linear workspace, I was unable to find specific issues directly related to this PR about the @photon-ai/photon CLI package and the unpinned npx issue. The searches returned results for other Photon-related projects (like @photon-ai/uri, Cosmos CLI, and iMessage Kit) and general package management issues, but nothing specifically addressing this PR's scope.

Summary: Additional Context Found

While Linear searches did not return issues specifically about this PR's photon CLI package or the npx binary resolution problem, I can provide context based on what's been documented:

Key Implementation Considerations for Code Review:

The PR addresses a known npm packaging constraint:

  • Root cause clarity: npm 11's npx skips bin auto-resolve when package.json#bin has multiple keys. The fix converts from an object definition to a single string, allowing auto-resolution for unpinned npx @photon-ai/photon``

Review Focus Areas:

  1. Runtime ensurePhoAlias() Implementation (src/lib/pho-alias.ts):

    • Symlink creation strategy searches three bin directory candidates: local node_modules/.bin, bun global bin, npm global prefix bin
    • Error handling silently swallows all failures (filesystem errors, missing directories, EACCES, etc.)
    • Relative symlink ./photon assumes flat bin directory structure
    • Runs on every invocation but short-circuits via existence check (two existsSync calls overhead per run)
  2. Integration Point (src/index.ts):

    • ensurePhoAlias() executes before update notifier
    • No explicit error handling at call site (inherent to the swallowing strategy)
  3. Package.json Change:

    • Breaking change from object to string bin format
    • README clarified that pho alias is created at runtime on first execution
  4. Testing Verification (documented as pending):

    • Symlink creation confirmed with bun add and npm install from tarball
    • Minification and packed tarball compatibility verified
    • Missing: unpinned npx @photon-ai/photon --version verification (requires 0.1.1 release)
    • Missing: global bun add verification

Known Design Trade-offs:

  • Rejected postinstall script approach due to Bun's default security blocking; runtime creation ensures consistency across npm/bun/yarn
  • Silent error swallowing is intentional to avoid breaking the CLI on permission issues or missing directories
🔇 Additional comments (3)
README.md (1)

34-35: Docs update is clear and aligned with runtime alias behavior.

Good clarification that pho is created on first photon execution after install.

src/index.ts (1)

24-29: Startup integration point looks correct.

Calling ensurePhoAlias() at top-level before the rest of CLI flow is the right placement for lazy alias setup.

package.json (1)

24-24: Single-bin mapping is the right fix for unpinned npx @photon-ai/photon`` resolution.

This change cleanly preserves the package-name command path while aliasing is handled at runtime.

Comment thread src/lib/pho-alias.ts
…ard source mode

Three review findings on PR #12:

- bun add -g actually puts the bin at ~/.bun/bin/photon (5 levels up
  from pkgRoot), not ~/.bun/install/global/bin. Verified by running
  the install end-to-end: before this fix, no candidate matched and
  pho was silently dropped for the very install path the PR claims
  to fix. Updated candidates: local node_modules/.bin (2 up), npm
  global <prefix>/bin (4 up), bun global ~/.bun/bin (5 up).

- existsSync follows symlinks, so a broken pho link wasn't detected
  and the subsequent symlinkSync would throw EEXIST. Switched to
  lstatSync, which sees the link entry itself. If anything is at
  the pho path (broken or not), leave it alone.

- Source-mode runs (bun run src/index.ts) were probing candidate
  bin dirs uselessly. Added a regex guard that early-exits unless
  process.argv[1] matches the installed-package shape
  .../node_modules/.../dist/photon.js.

Co-authored-by: Orca <help@stably.ai>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/lib/pho-alias.ts (1)

64-66: Consider opt-in debug logging in the outer catch.

Silently swallowing all failures makes support/debugging harder when alias creation unexpectedly fails.

🔧 Optional tweak
-  } catch {
-    // best-effort
+  } catch (error) {
+    if (process.env.PHOTON_DEBUG === "1") {
+      console.warn("[photon] failed to ensure `pho` alias:", error);
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/pho-alias.ts` around lines 64 - 66, The outer catch in
src/lib/pho-alias.ts currently swallows all errors; change it to perform opt-in
debug logging so failures are visible when needed — detect a debug toggle (e.g.,
process.env.DEBUG_PHO_ALIAS or an existing logger object) and log the caught
error (error message/stack) inside the catch before continuing (use
console.debug or processLogger.debug if available); keep behavior unchanged when
the toggle is off so this remains best-effort.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/lib/pho-alias.ts`:
- Around line 64-66: The outer catch in src/lib/pho-alias.ts currently swallows
all errors; change it to perform opt-in debug logging so failures are visible
when needed — detect a debug toggle (e.g., process.env.DEBUG_PHO_ALIAS or an
existing logger object) and log the caught error (error message/stack) inside
the catch before continuing (use console.debug or processLogger.debug if
available); keep behavior unchanged when the toggle is off so this remains
best-effort.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a375a8d-00cf-4d57-ba88-f9a8da84524b

📥 Commits

Reviewing files that changed from the base of the PR and between e9cfb75 and 5c8ffd9.

📒 Files selected for processing (1)
  • src/lib/pho-alias.ts
📜 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
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install`
Learnt from: CR
Repo: photon-hq/spectrum-cloud PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-16T22:15:43.438Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install`
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json scripts
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>`
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>`
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>` for running scripts
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Use `bunx <package> <command>` instead of `npx <package> <command>`
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Use `bunx <package> <command>` instead of `npx <package> <command>`
Learnt from: CR
Repo: photon-hq/billing PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-17T02:57:19.075Z
Learning: Run `bun x ultracite fix` before committing to ensure compliance with Ultracite code standards
Learnt from: lcandy2
Repo: photon-hq/cli PR: 11
File: package.json:14-23
Timestamp: 2026-04-29T19:00:22.283Z
Learning: In photon-hq/cli, the `LICENSE` file was intentionally removed (commit 9c451da) per project direction. The `license` field is deliberately absent from `package.json`, and `LICENSE` is not included in the `files` array. Do not flag the missing `license` field or absent LICENSE file as issues in this repository.
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `bun <file>` instead of `node <file>` or `ts-node <file>`
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `bun <file>` instead of `node <file>` or `ts-node <file>`
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `bun <file>` instead of `node <file>` or `ts-node <file>` for running TypeScript and JavaScript files
📚 Learning: 2026-04-29T19:00:22.283Z
Learnt from: lcandy2
Repo: photon-hq/cli PR: 11
File: package.json:14-23
Timestamp: 2026-04-29T19:00:22.283Z
Learning: In photon-hq/cli, the `LICENSE` file was intentionally removed (commit 9c451da) per project direction. The `license` field is deliberately absent from `package.json`, and `LICENSE` is not included in the `files` array. Do not flag the missing `license` field or absent LICENSE file as issues in this repository.

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-04T19:01:14.591Z
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install`

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-28T03:08:36.661Z
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-03-10T22:49:19.048Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` in package.json scripts

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-28T03:08:36.661Z
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>`

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-03-10T22:49:19.048Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>` for running scripts

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-27T17:21:55.053Z
Learnt from: lcandy2
Repo: photon-hq/dashboard PR: 57
File: biome.jsonc:4-6
Timestamp: 2026-04-27T17:21:55.053Z
Learning: In `photon-hq/dashboard` (`biome.jsonc`), the `files.includes` array intentionally uses only negated glob patterns (e.g., `["!**/drizzle/meta", "!.claude", "!**/.next"]`) without a leading positive `"**"` pattern. This is a deliberate choice; do not flag it as requiring a positive base pattern.

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-04T19:01:14.591Z
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Use `bunx <package> <command>` instead of `npx <package> <command>`

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-04T19:01:14.591Z
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `bun <file>` instead of `node <file>` or `ts-node <file>`

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-16T22:15:43.439Z
Learnt from: CR
Repo: photon-hq/spectrum-cloud PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-16T22:15:43.439Z
Learning: Applies to **/*.ts?(x) : Use `Bun.$` for shell commands instead of `execa`

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-04T19:01:14.591Z
Learnt from: CR
Repo: photon-hq/explore-send-imessage-app PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-04-04T19:01:14.591Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `Bun.$` for shell command execution instead of `execa`

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-03-10T22:49:19.048Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `bun <file>` instead of `node <file>` or `ts-node <file>` for running TypeScript and JavaScript files

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-28T03:08:36.661Z
Learnt from: CR
Repo: photon-hq/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-28T03:08:36.661Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `Bun.$` template literal for shell commands instead of execa

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-03-10T22:49:19.048Z
Learnt from: CR
Repo: photon-hq/advanced-imessage-kit PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2026-03-10T22:49:19.048Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `Bun.$` template tag for shell commands instead of execa library

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-17T02:57:19.075Z
Learnt from: CR
Repo: photon-hq/billing PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-17T02:57:19.075Z
Learning: Run `bun x ultracite fix` before committing to ensure compliance with Ultracite code standards

Applied to files:

  • src/lib/pho-alias.ts
📚 Learning: 2026-04-13T23:00:20.897Z
Learnt from: CR
Repo: photon-hq/spectrum-ts PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-13T23:00:20.897Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : Use top-level regex literals instead of creating them in loops in JavaScript/TypeScript

Applied to files:

  • src/lib/pho-alias.ts
🪛 ast-grep (0.42.1)
src/lib/pho-alias.ts

[warning] 31-33: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
${escapeForRegex(sep)}node_modules${escapeForRegex(sep)}.+${escapeForRegex(sep)}dist${escapeForRegex(sep)}photon\\.js$,
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🔍 Remote MCP Linear

Summary of additional Linear search results

  • Ran workspace issue searches for "photon" and "pho". Results returned many unrelated design and engineering issues (e.g., ENG-918, ENG-471, DES-62, etc.) but I did not find any Linear issue that documents the unpinned npx / package.json#bin multi-key problem or the runtime pho-alias implementation referenced in this PR.,

Sources

  • Linear list_issues search for "photon" —
  • Linear list_issues search for "pho" —
🔇 Additional comments (2)
src/lib/pho-alias.ts (2)

30-45: Installed-layout guard and candidate probing look correct.

The early guard prevents source-mode side effects, and the candidate order sensibly prefers local bin before global locations.


52-63: lstatSync gate avoids clobbering existing pho entries.

Good call to treat any existing pho path (including broken symlinks) as authoritative and exit safely.

@lcandy2 citron (lcandy2) merged commit d19f951 into main Apr 29, 2026
1 check passed
citron (lcandy2) added a commit that referenced this pull request May 5, 2026
Aligns with industry convention (@vercel/cli, @supabase/cli,
@dokploy/cli, etc.) and matches the binary's role rather than
duplicating the name.

The previous reasoning behind @photon-ai/photon (PR #11) was that the
post-scope segment must match the bin name for unpinned `npx <pkg>` to
auto-resolve. That turns out to have been an overly strict reading of
PR #12's findings — the actual rule npm 11 uses is "single bin
auto-resolves regardless of name match." Confirmed empirically against
@dokploy/cli, which has bin `dokploy` (not `cli`) and works unpinned.

Bin shape switches from string-form to single-key object:
  "bin": "./dist/photon.js"
→ "bin": { "photon": "./dist/photon.js" }
This keeps the binary registered as `photon` (string form would have
made it `cli`, breaking every existing user invocation).

Verified locally against @photon-ai/cli@0.1.3 tarball:
- bun add -g: installs `photon`, lazy `pho` symlink fires on first run
- npm install (local): same
- `npx --yes <tarball>` resolves to 0.1.3 via single-bin auto-resolve

Existing `@photon-ai/photon` users will keep getting 0.1.3 (final
version on the old name). Migration path: re-install with
`bun add -g @photon-ai/cli`.
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