Skip to content

fix: add postinstall permission fix for CLI and hook scripts (#330)#348

Merged
magyargergo merged 2 commits into
abhigyanpatwari:mainfrom
ShunsukeHayashi:fix/executable-permissions
Mar 18, 2026
Merged

fix: add postinstall permission fix for CLI and hook scripts (#330)#348
magyargergo merged 2 commits into
abhigyanpatwari:mainfrom
ShunsukeHayashi:fix/executable-permissions

Conversation

@ShunsukeHayashi

Copy link
Copy Markdown
Contributor

Problem

After npm install -g gitnexus, the CLI and hook scripts lack execute permissions (#330):

$ gitnexus serve
zsh: permission denied: gitnexus

Affected files (published as 644 instead of 755):

  • dist/cli/index.js — Main CLI entry point (has shebang)
  • hooks/claude/pre-tool-use.sh
  • hooks/claude/session-start.sh
  • hooks/claude/gitnexus-hook.cjs

Root Cause

  1. Git index — The hook and script files were tracked with mode 100644 instead of 100755
  2. dist/ files — Not tracked in git, so they inherit the build output permissions

Fix

1. scripts/fix-permissions.cjs (new)

A postinstall script that chmod +x the affected files on Unix systems. No-op on Windows.

const files = [
  'dist/cli/index.js',
  'hooks/claude/pre-tool-use.sh',
  'hooks/claude/session-start.sh',
  'hooks/claude/gitnexus-hook.cjs',
];
// chmod 755 each file if it exists

2. package.json postinstall update

"postinstall": "node scripts/patch-tree-sitter-swift.cjs && node scripts/fix-permissions.cjs"

3. Git index permissions

Set +x on hooks/ and scripts/ files in the git index so the published tarball includes correct permissions from the start.

After this fix

npm install -g gitnexus  # postinstall automatically fixes permissions
gitnexus serve           # ✅ works

Backward compatibility

  • Windows: script detects process.platform === 'win32' and exits immediately
  • Best-effort: if chmod fails (e.g., restricted filesystem), install still succeeds
  • Existing patch-tree-sitter-swift.cjs postinstall is preserved (chained with &&)

Related: #322 (similar permission issue on darwin/arm)

After npm global install, the CLI entry point and hook scripts lack execute
permissions because the published tarball preserves the source file modes.

Add scripts/fix-permissions.cjs that runs as part of postinstall to chmod +x
the affected files on Unix systems. No-op on Windows.

Also ensure hooks/ and scripts/ have +x in the git index so the published
tarball includes correct permissions.

Fixes #330
Related: #322 (similar permission issue on darwin/arm)
@vercel

vercel Bot commented Mar 18, 2026

Copy link
Copy Markdown

@ShunsukeHayashi is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@magyargergo

Copy link
Copy Markdown
Collaborator

@claude is there a way to fix this without having to have to run a post script? Research about related problems and see if there's an industry standard for this.

@claude

claude Bot commented Mar 18, 2026

Copy link
Copy Markdown

Claude finished @magyargergo's task in 1m 42s —— View job


Research: Fixing Permissions Without a Postinstall Script

  • Gather context
  • Research alternatives
  • Post findings

Yes — there are cleaner, industry-standard ways to handle this. Here's the breakdown:


The Two Categories of Files

This PR fixes two different categories of files, each with a better solution:

1. dist/cli/index.js — The CLI Entry Point

npm already handles this automatically via the bin field.

When npm installs a package that has a bin entry, it sets the execute bit (0755) on the target file as part of the install process. Since package.json already declares:

"bin": {
  "gitnexus": "dist/cli/index.js"
}

...npm should be making dist/cli/index.js executable during npm install -g. The postinstall chmod for this file is redundant. If users are still seeing permission denied, the underlying issue is likely that the dist/ build artifact had no shebang (#!/usr/bin/env node) or the file wasn't being rebuilt before publish — not a permissions gap that postinstall is the right fix for.

2. hooks/claude/*.sh / gitnexus-hook.cjs — The Hook Scripts

Fix the git index — which this PR already does correctly.

The git index now tracks these as 100755:

100755 hooks/claude/gitnexus-hook.cjs
100755 hooks/claude/pre-tool-use.sh
100755 hooks/claude/session-start.sh

npm preserves file permissions from the published tarball, and the tarball is built from the git-tracked files. So fixing the git mode is sufficient — no postinstall needed for these either.


The Industry-Standard Alternative for Build Artifacts: prepack

For dist/ files (which aren't git-tracked), the standard approach is a prepack script instead of postinstall. prepack runs on the publisher's machine before npm pack / npm publish, so correct permissions are baked into the tarball itself:

"prepack": "npm run build && chmod +x dist/cli/index.js"

Why prepack > postinstall:

postinstall prepack (+ git index fix)
Runs on Every installer's machine Publisher's machine once
Security surface Executes arbitrary code for all users No user-side execution
npm audit concerns Flagged by some security scanners None
Windows compatibility Needs platform guard Not needed (publisher is Unix)
Covers dist/ artifacts
Covers git-tracked scripts ✅ (redundant) ✅ via git index

The npm docs explicitly list prepack as running "before a tarball is packed." Many popular CLI packages (e.g., create-react-app, various @angular/cli releases) use this pattern.


Recommended Approach (no postinstall needed)

  1. Remove fix-permissions.cjs and drop it from postinstall
  2. Keep the git index fix from this PR (hooks/ files at 100755) — this is correct and necessary
  3. Add prepack to cover the CLI build artifact:
    "prepack": "npm run build && chmod +x dist/cli/index.js"
  4. Rely on npm's bin field to handle the symlink/wrapper (it sets 0755 automatically)

This eliminates user-side script execution entirely and is the approach used by the broader npm ecosystem.

Fix this →


@magyargergo

Copy link
Copy Markdown
Collaborator

Could you please tackle with this? 🙏

Per review feedback from @magyargergo and @claude:

- Remove fix-permissions.cjs postinstall script (security surface, redundant)
- Add prepack script: 'npm run build && chmod +x dist/cli/index.js'
- Keep git index 100755 for hooks/ (already correct from previous commit)
- Rely on npm bin field for CLI symlink permissions

prepack runs on the publisher's machine before npm pack/publish,
so correct permissions are baked into the tarball. No user-side
script execution needed.

This follows the npm ecosystem standard (prepack over postinstall)
as documented in npm docs life-cycle-scripts.
@ShunsukeHayashi

Copy link
Copy Markdown
Contributor Author

Good call @magyargergo, and great research by @claude.

Updated the PR:

  • Removed \ ix-permissions.cjs\ postinstall script entirely
  • Added \prepack\ script:
    pm run build && chmod +x dist/cli/index.js\
  • Kept the git index \100755\ fixes for hooks/ (still needed)

This follows the npm ecosystem standard — permissions are baked into the tarball at publish time, no user-side script execution needed.

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