prjct-cli 2.50.0 → 2.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.50.1] - 2026-06-20
4
+
5
+ ### Security
6
+ - **Removed the runtime Bun auto-installer (supply-chain hardening).** The CLI no longer runs `scripts/ensure-bun.sh` (an unpinned, unverified `curl -fsSL https://bun.sh/install | bash`) on first invocation when only Node is present — `bin/prjct` now falls straight back to Node (a first-class runtime). The script is deleted and dropped from the published tarball (`files[]`), so the package ships **zero `curl | bash`**. The install-time variant was already gone (no `postinstall` since #391); this closes the same pattern on the runtime path. Addresses the OSV **MAL-2026-4647** false-positive (Amazon Inspector "alternate-runtime-dropper", pinned to 2.21.0) at its root. Bun is still preferred when already installed; users who want it install it themselves (https://bun.sh).
7
+
3
8
  ## [2.50.0] - 2026-06-20
4
9
 
5
10
  ### Added
package/bin/prjct CHANGED
@@ -153,9 +153,22 @@ run_with_node() {
153
153
  fi
154
154
  fi
155
155
 
156
- # prjct uses node's built-in `node:sqlite` (zero native deps, no postinstall).
157
- # On Node 22.5–23.x it lives behind `--experimental-sqlite`; on Node 24+ it's
158
- # unflagged and the flag is a tolerated no-op. Exporting it (vs passing as an
156
+ # prjct uses node's built-in `node:sqlite` (zero native deps, no postinstall),
157
+ # which requires Node >=22.5. Below that and since we no longer auto-install
158
+ # Bun (MAL-2026-4647) prjct can't run: fail with a clear, actionable message
159
+ # instead of a cryptic `--experimental-sqlite` flag error.
160
+ NODE_VER="$(node -v 2>/dev/null | sed 's/^v//')"
161
+ NODE_MAJ="${NODE_VER%%.*}"
162
+ NODE_MIN_REST="${NODE_VER#*.}"
163
+ NODE_MIN="${NODE_MIN_REST%%.*}"
164
+ if [ "${NODE_MAJ:-0}" -lt 22 ] || { [ "${NODE_MAJ:-0}" -eq 22 ] && [ "${NODE_MIN:-0}" -lt 5 ]; }; then
165
+ echo "Error: prjct needs Node >=22.5 (for node:sqlite) or Bun — found Node ${NODE_VER:-unknown}."
166
+ echo " Upgrade Node (https://nodejs.org) or install Bun (https://bun.sh)."
167
+ exit 1
168
+ fi
169
+
170
+ # On Node 22.5–23.x node:sqlite lives behind `--experimental-sqlite`; on Node
171
+ # 24+ it's unflagged and the flag is a tolerated no-op. Exporting it (vs an
159
172
  # argv flag) means the daemon + any node child prjct spawns inherit it too.
160
173
  # Prepended so a user's existing NODE_OPTIONS is preserved.
161
174
  export NODE_OPTIONS="--experimental-sqlite${NODE_OPTIONS:+ $NODE_OPTIONS}"
@@ -167,21 +180,14 @@ main() {
167
180
  # ALWAYS ensure setup first (self-healing)
168
181
  ensure_setup
169
182
 
170
- # Prefer bun if available (faster, native TS support)
183
+ # Prefer bun if available (faster, native TS support); otherwise fall back to
184
+ # node. We deliberately do NOT auto-install bun: fetching an unpinned
185
+ # `curl | bash` installer at runtime is a supply-chain risk (it triggered the
186
+ # MAL-2026-4647 false-positive). Node is a first-class runtime here — if a
187
+ # user wants bun for speed, they install it themselves (https://bun.sh).
171
188
  if check_bun; then
172
189
  run_with_bun "$@"
173
190
  elif check_node; then
174
- # Bun not found but Node available — try auto-install Bun (one-time)
175
- ENSURE_BUN="$ROOT_DIR/scripts/ensure-bun.sh"
176
- if [ -f "$ENSURE_BUN" ]; then
177
- sh "$ENSURE_BUN"
178
- # Re-source PATH in case Bun was just installed
179
- export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
180
- export PATH="$BUN_INSTALL/bin:$PATH"
181
- if check_bun; then
182
- run_with_bun "$@"
183
- fi
184
- fi
185
191
  run_with_node "$@"
186
192
  else
187
193
  echo "Error: Neither bun nor node is installed."
@@ -1800,7 +1800,7 @@ ${bs.dim("Providers:")}`}var zH,oy=h(()=>{"use strict";zH={ready:bs.green("\u271
1800
1800
  ${s.dim("Run 'prjct start' to configure (CLI providers)")}
1801
1801
  ${s.dim("Run 'prjct init' to configure (Cursor/Windsurf IDE)")}
1802
1802
  ${s.cyan("https://prjct.app")}
1803
- `)}else return!1;return!0}var YP=h(()=>{"use strict";c(YH,"runBinCommand")});var QP=uA((Mce,QH)=>{QH.exports={name:"prjct-cli",version:"2.50.0",description:"Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",main:"dist/bin/prjct.mjs",bin:{prjct:"bin/prjct"},publishConfig:{access:"public",registry:"https://registry.npmjs.org"},scripts:{build:"node scripts/build.js","build:node":"node scripts/build.js",release:"node scripts/release.js","release:patch":"node scripts/release.js patch","release:minor":"node scripts/release.js minor","release:major":"node scripts/release.js major",prepare:"lefthook install","update-commands":`bun -e "const installer = require('./core/infrastructure/command-installer'); installer.syncCommands().then(r => console.log('Commands updated:', r)).catch(e => console.error('Error:', e.message))"`,"install-global":"./scripts/install.sh",update:"./scripts/update.sh",test:"bun test","test:watch":"bun test --watch","test:coverage":"bun test --coverage",typecheck:"tsc --noEmit -p core/tsconfig.json","typecheck:watch":"tsc --noEmit -p core/tsconfig.json --watch",validate:"bun scripts/validate-commands.js",lint:"biome lint .","lint:fix":"biome lint --write .",knip:"knip","lint:meta":"bun core/cli/lint-meta-commentary.ts",format:"biome format --write .","format:check":"biome format .",check:"biome check .","check:fix":"biome check --write .","test:e2e":"bun test core/__tests__/e2e/"},keywords:["claude-code","gemini-cli","ai-agents","context-layer","developer-tools","ai-assistant","productivity","mcp","llm","coding-agents"],author:"prjct.app",license:"MIT",dependencies:{"@clack/prompts":"1.0.0","@modelcontextprotocol/sdk":"1.29.0",chalk:"4.1.2",chokidar:"5.0.0","jsonc-parser":"3.3.1",zod:"3.25.76"},overrides:{"path-to-regexp":"8.4.0","brace-expansion":"5.0.5","fast-uri":">=3.1.2",hono:">=4.12.18","ip-address":">=10.1.1"},devDependencies:{"@biomejs/biome":"2.3.13","@types/bun":"latest","@types/chokidar":"2.1.7","@types/node":"^22",esbuild:"0.28.1",knip:"6.3.1",lefthook:"2.1.0",typescript:"5.9.3"},repository:{type:"git",url:"git+https://github.com/jlopezlira/prjct-cli.git"},bugs:{url:"https://github.com/jlopezlira/prjct-cli/issues"},homepage:"https://prjct.app",packageManager:"bun@1.2.23",engines:{bun:">=1.0.0",node:">=22.5.0"},files:["assets/","bin/prjct","dist/","templates/","scripts/ensure-bun.sh","scripts/install.sh","LICENSE","README.md","CHANGELOG.md"],prepublishOnly:"node scripts/build.js"}});var s1={};import ZP from"node:os";import gu from"node:path";import vs from"chalk";async function ZH(){let[r,...e]=process.argv.slice(2);if(["-v","--version","version"].includes(r)){let n=await Promise.resolve().then(()=>dA(QP()));await n1(n.version),process.exit(0)}if(["-h","--help",void 0].includes(r)&&(r1(),process.exit(0)),r&&lu(r)&&!We.getByName(r)){let n=cu[r];n&&(g.failWithHint({message:`'prjct ${r}' was removed in v2. ${n.note}`,hint:`Use: ${n.replacement}`}),process.exit(1))}if(r&&!We.getByName(r)&&!(e.length===0&&Vi(r)!==null)){let s=[r,...e.filter(i=>!i.startsWith("-"))].join(" "),o=e.filter(i=>i.startsWith("-"));r="capture",e=[s,...o]}let t=e.includes("--md");t||g.start();try{let n=We.getByName(r);if(!n){let m=Vi(r),f=m?`Did you mean 'prjct ${m}'? Run 'prjct --help' for all commands`:"Run 'prjct --help' to see available commands";g.failWithHint(qo("UNKNOWN_COMMAND",{message:`Unknown command: ${r}`,hint:f})),t||g.end(),process.exit(1)}if(n.deprecated){let m=n.replacedBy?`Use 'prjct ${n.replacedBy}' instead`:"Run 'prjct --help' to see available commands";g.failWithHint({message:`Command '${r}' is deprecated`,hint:m}),t||g.end(),process.exit(1)}n.implemented||(g.failWithHint({message:`Command '${r}' is not yet implemented`,hint:"Run 'prjct --help' to see available commands",docs:"https://github.com/jlopezlira/prjct-cli"}),t||g.end(),process.exit(1));let{parsedArgs:s,options:o}=t1(n,e),i=!process.stdin.isTTY||o.md===!0||o.json===!0;n.requiresLlm&&!i&&(g.failWithHint({message:`'prjct ${r}' requires an AI agent to process its output`,hint:`Use 'p. ${r}' inside Claude/Cursor, or add --md flag`}),t||g.end(),process.exit(1));let a=e1(n,s);a&&(g.failWithHint(a),t||g.end(),process.exit(1));let l=null,u=Date.now();try{l=await P.getProjectId(process.cwd()),l&&(await Ni.expireIfStale(l),await Ni.touch(l))}catch{}let d=new gs,p;if(r==="analyze")p=await d.analyze(o);else if(r==="setup")p=await d.setup(o);else if(r==="update"||r==="upgrade")p=await d.update(o);else{let m=s.join(" ")||null,f=o.md===!0,y=We.getByName(r);if(y?.optionSchema)p=await We.executeWithOptions(r,m,process.cwd(),au(o,y.optionSchema));else{let k={spec:c(b=>uu(d,(b??"").trim().split(/\s+/).filter(Boolean),o,process.cwd()),"spec"),"audit-spec":c(b=>b?d.specAudit(b,process.cwd(),{md:f,lenses:o.lenses?String(o.lenses):void 0}):Promise.resolve({success:!1,error:"audit-spec requires a spec id"}),"audit-spec"),init:c(b=>d.init({idea:b,yes:o.yes===!0,pack:o.pack?String(o.pack):void 0,persona:o.persona?String(o.persona):void 0}),"init"),sync:c(()=>d.sync(process.cwd(),{preview:o.preview===!0||o["dry-run"]===!0,yes:o.yes===!0,json:o.json===!0,md:f,package:o.package?String(o.package):void 0,full:o.full===!0}),"sync"),"analysis-save-llm":c(b=>b?d.saveLlmAnalysis(b,process.cwd(),{md:f}):Promise.resolve({success:!1,error:"analysis-save-llm requires a JSON payload as positional arg"}),"analysis-save-llm"),regen:c(()=>d.regenVault(process.cwd(),{md:f}),"regen"),start:c(()=>d.start(),"start"),login:c(()=>d.login({md:f,url:o.url?String(o.url):void 0}),"login"),logout:c(()=>d.logout(),"logout"),auth:c(b=>d.auth(b,{md:f}),"auth")}[r];if(k)p=await k(m);else throw new Error(`Command '${r}' has no handler`)}}if(l){let m=Date.now()-u;try{await Ni.trackCommand(l,r,m)}catch{}try{await Li.recordTiming(l,"command_duration",m,{command:r});let f=globalThis.__perfStartNs;if(f){let y=Number(process.hrtime.bigint()-f)/1e6;await Li.recordTiming(l,"startup_time",y)}await Li.recordMemory(l,{command:r})}catch{}}p?.message&&console.log(p.message),t||g.end(),process.exit(p?.success?0:1)}catch(n){console.error("Error:",w(n)),process.env.DEBUG&&console.error(xu(n)),t||g.end(),process.exit(1)}}function e1(r,e){if(!r.params)return null;let t=r.params.match(/<[^>]+>/g);if(!t||t.length===0)return null;if(e.length<t.length){let n=t.map(o=>o.slice(1,-1)).join(", "),s=r.usage.terminal||`prjct ${r.name} ${r.params}`;return qo("MISSING_PARAM",{message:`Missing required parameter: ${n}`,hint:`Usage: ${s}`})}return null}function t1(r,e){let t=[],n={};for(let s=0;s<e.length;s++){let o=e[s];if(o.startsWith("--")){let i=o.slice(2);s+1<e.length&&!e[s+1].startsWith("--")?n[i]=e[++s]:n[i]=!0}else t.push(o)}return{parsedArgs:t,options:n}}async function n1(r){let e=await qn(),t=gu.join(ZP.homedir(),".claude","commands","p.md"),n=gu.join(ZP.homedir(),".gemini","commands","p.toml"),[s,o,i,a]=await Promise.all([E(t),E(n),E(gu.join(process.cwd(),".cursor","commands","sync.md")),E(gu.join(process.cwd(),".cursor"))]),l=await Jo();console.log(sy(r)),console.log(Io("Claude Code",e.claude.installed?s?"ready":"installed":"missing",e.claude.version?vs.dim(` (v${e.claude.version})`):"")),console.log(Io("Gemini CLI",e.gemini.installed?o?"ready":"installed":"missing",e.gemini.version?vs.dim(` (v${e.gemini.version})`):"")),console.log(Io("Antigravity",l.installed?l.skillInstalled?"ready":"detected":"missing",l.installed&&!l.skillInstalled?` ${vs.dim("(run prjct start)")}`:"")),console.log(Io("Cursor IDE",i?"ready":a?"detected":"missing",i?` ${vs.dim("(use /sync, /task)")}`:a?` ${vs.dim("(run prjct init)")}`:"","\u25CB no .cursor/ folder")),console.log(`
1803
+ `)}else return!1;return!0}var YP=h(()=>{"use strict";c(YH,"runBinCommand")});var QP=uA((Mce,QH)=>{QH.exports={name:"prjct-cli",version:"2.50.1",description:"Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",main:"dist/bin/prjct.mjs",bin:{prjct:"bin/prjct"},publishConfig:{access:"public",registry:"https://registry.npmjs.org"},scripts:{build:"node scripts/build.js","build:node":"node scripts/build.js",release:"node scripts/release.js","release:patch":"node scripts/release.js patch","release:minor":"node scripts/release.js minor","release:major":"node scripts/release.js major",prepare:"lefthook install","update-commands":`bun -e "const installer = require('./core/infrastructure/command-installer'); installer.syncCommands().then(r => console.log('Commands updated:', r)).catch(e => console.error('Error:', e.message))"`,"install-global":"./scripts/install.sh",update:"./scripts/update.sh",test:"bun test","test:watch":"bun test --watch","test:coverage":"bun test --coverage",typecheck:"tsc --noEmit -p core/tsconfig.json","typecheck:watch":"tsc --noEmit -p core/tsconfig.json --watch",validate:"bun scripts/validate-commands.js",lint:"biome lint .","lint:fix":"biome lint --write .",knip:"knip","lint:meta":"bun core/cli/lint-meta-commentary.ts",format:"biome format --write .","format:check":"biome format .",check:"biome check .","check:fix":"biome check --write .","test:e2e":"bun test core/__tests__/e2e/"},keywords:["claude-code","gemini-cli","ai-agents","context-layer","developer-tools","ai-assistant","productivity","mcp","llm","coding-agents"],author:"prjct.app",license:"MIT",dependencies:{"@clack/prompts":"1.0.0","@modelcontextprotocol/sdk":"1.29.0",chalk:"4.1.2",chokidar:"5.0.0","jsonc-parser":"3.3.1",zod:"3.25.76"},overrides:{"path-to-regexp":"8.4.0","brace-expansion":"5.0.5","fast-uri":">=3.1.2",hono:">=4.12.18","ip-address":">=10.1.1"},devDependencies:{"@biomejs/biome":"2.3.13","@types/bun":"latest","@types/chokidar":"2.1.7","@types/node":"^22",esbuild:"0.28.1",knip:"6.3.1",lefthook:"2.1.0",typescript:"5.9.3"},repository:{type:"git",url:"git+https://github.com/jlopezlira/prjct-cli.git"},bugs:{url:"https://github.com/jlopezlira/prjct-cli/issues"},homepage:"https://prjct.app",packageManager:"bun@1.2.23",engines:{bun:">=1.0.0",node:">=22.5.0"},files:["assets/","bin/prjct","dist/","templates/","scripts/install.sh","LICENSE","README.md","CHANGELOG.md"],prepublishOnly:"node scripts/build.js"}});var s1={};import ZP from"node:os";import gu from"node:path";import vs from"chalk";async function ZH(){let[r,...e]=process.argv.slice(2);if(["-v","--version","version"].includes(r)){let n=await Promise.resolve().then(()=>dA(QP()));await n1(n.version),process.exit(0)}if(["-h","--help",void 0].includes(r)&&(r1(),process.exit(0)),r&&lu(r)&&!We.getByName(r)){let n=cu[r];n&&(g.failWithHint({message:`'prjct ${r}' was removed in v2. ${n.note}`,hint:`Use: ${n.replacement}`}),process.exit(1))}if(r&&!We.getByName(r)&&!(e.length===0&&Vi(r)!==null)){let s=[r,...e.filter(i=>!i.startsWith("-"))].join(" "),o=e.filter(i=>i.startsWith("-"));r="capture",e=[s,...o]}let t=e.includes("--md");t||g.start();try{let n=We.getByName(r);if(!n){let m=Vi(r),f=m?`Did you mean 'prjct ${m}'? Run 'prjct --help' for all commands`:"Run 'prjct --help' to see available commands";g.failWithHint(qo("UNKNOWN_COMMAND",{message:`Unknown command: ${r}`,hint:f})),t||g.end(),process.exit(1)}if(n.deprecated){let m=n.replacedBy?`Use 'prjct ${n.replacedBy}' instead`:"Run 'prjct --help' to see available commands";g.failWithHint({message:`Command '${r}' is deprecated`,hint:m}),t||g.end(),process.exit(1)}n.implemented||(g.failWithHint({message:`Command '${r}' is not yet implemented`,hint:"Run 'prjct --help' to see available commands",docs:"https://github.com/jlopezlira/prjct-cli"}),t||g.end(),process.exit(1));let{parsedArgs:s,options:o}=t1(n,e),i=!process.stdin.isTTY||o.md===!0||o.json===!0;n.requiresLlm&&!i&&(g.failWithHint({message:`'prjct ${r}' requires an AI agent to process its output`,hint:`Use 'p. ${r}' inside Claude/Cursor, or add --md flag`}),t||g.end(),process.exit(1));let a=e1(n,s);a&&(g.failWithHint(a),t||g.end(),process.exit(1));let l=null,u=Date.now();try{l=await P.getProjectId(process.cwd()),l&&(await Ni.expireIfStale(l),await Ni.touch(l))}catch{}let d=new gs,p;if(r==="analyze")p=await d.analyze(o);else if(r==="setup")p=await d.setup(o);else if(r==="update"||r==="upgrade")p=await d.update(o);else{let m=s.join(" ")||null,f=o.md===!0,y=We.getByName(r);if(y?.optionSchema)p=await We.executeWithOptions(r,m,process.cwd(),au(o,y.optionSchema));else{let k={spec:c(b=>uu(d,(b??"").trim().split(/\s+/).filter(Boolean),o,process.cwd()),"spec"),"audit-spec":c(b=>b?d.specAudit(b,process.cwd(),{md:f,lenses:o.lenses?String(o.lenses):void 0}):Promise.resolve({success:!1,error:"audit-spec requires a spec id"}),"audit-spec"),init:c(b=>d.init({idea:b,yes:o.yes===!0,pack:o.pack?String(o.pack):void 0,persona:o.persona?String(o.persona):void 0}),"init"),sync:c(()=>d.sync(process.cwd(),{preview:o.preview===!0||o["dry-run"]===!0,yes:o.yes===!0,json:o.json===!0,md:f,package:o.package?String(o.package):void 0,full:o.full===!0}),"sync"),"analysis-save-llm":c(b=>b?d.saveLlmAnalysis(b,process.cwd(),{md:f}):Promise.resolve({success:!1,error:"analysis-save-llm requires a JSON payload as positional arg"}),"analysis-save-llm"),regen:c(()=>d.regenVault(process.cwd(),{md:f}),"regen"),start:c(()=>d.start(),"start"),login:c(()=>d.login({md:f,url:o.url?String(o.url):void 0}),"login"),logout:c(()=>d.logout(),"logout"),auth:c(b=>d.auth(b,{md:f}),"auth")}[r];if(k)p=await k(m);else throw new Error(`Command '${r}' has no handler`)}}if(l){let m=Date.now()-u;try{await Ni.trackCommand(l,r,m)}catch{}try{await Li.recordTiming(l,"command_duration",m,{command:r});let f=globalThis.__perfStartNs;if(f){let y=Number(process.hrtime.bigint()-f)/1e6;await Li.recordTiming(l,"startup_time",y)}await Li.recordMemory(l,{command:r})}catch{}}p?.message&&console.log(p.message),t||g.end(),process.exit(p?.success?0:1)}catch(n){console.error("Error:",w(n)),process.env.DEBUG&&console.error(xu(n)),t||g.end(),process.exit(1)}}function e1(r,e){if(!r.params)return null;let t=r.params.match(/<[^>]+>/g);if(!t||t.length===0)return null;if(e.length<t.length){let n=t.map(o=>o.slice(1,-1)).join(", "),s=r.usage.terminal||`prjct ${r.name} ${r.params}`;return qo("MISSING_PARAM",{message:`Missing required parameter: ${n}`,hint:`Usage: ${s}`})}return null}function t1(r,e){let t=[],n={};for(let s=0;s<e.length;s++){let o=e[s];if(o.startsWith("--")){let i=o.slice(2);s+1<e.length&&!e[s+1].startsWith("--")?n[i]=e[++s]:n[i]=!0}else t.push(o)}return{parsedArgs:t,options:n}}async function n1(r){let e=await qn(),t=gu.join(ZP.homedir(),".claude","commands","p.md"),n=gu.join(ZP.homedir(),".gemini","commands","p.toml"),[s,o,i,a]=await Promise.all([E(t),E(n),E(gu.join(process.cwd(),".cursor","commands","sync.md")),E(gu.join(process.cwd(),".cursor"))]),l=await Jo();console.log(sy(r)),console.log(Io("Claude Code",e.claude.installed?s?"ready":"installed":"missing",e.claude.version?vs.dim(` (v${e.claude.version})`):"")),console.log(Io("Gemini CLI",e.gemini.installed?o?"ready":"installed":"missing",e.gemini.version?vs.dim(` (v${e.gemini.version})`):"")),console.log(Io("Antigravity",l.installed?l.skillInstalled?"ready":"detected":"missing",l.installed&&!l.skillInstalled?` ${vs.dim("(run prjct start)")}`:"")),console.log(Io("Cursor IDE",i?"ready":a?"detected":"missing",i?` ${vs.dim("(use /sync, /task)")}`:a?` ${vs.dim("(run prjct init)")}`:"","\u25CB no .cursor/ folder")),console.log(`
1804
1804
  ${vs.dim("Run 'prjct start' for Claude/Gemini, 'prjct init' for Cursor")}
1805
1805
  ${vs.cyan("https://prjct.app")}
1806
1806
  `)}function r1(){console.log(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "2.50.0",
3
+ "version": "2.50.1",
4
4
  "description": "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
5
5
  "main": "dist/bin/prjct.mjs",
6
6
  "bin": {
@@ -94,7 +94,6 @@
94
94
  "bin/prjct",
95
95
  "dist/",
96
96
  "templates/",
97
- "scripts/ensure-bun.sh",
98
97
  "scripts/install.sh",
99
98
  "LICENSE",
100
99
  "README.md",
@@ -1,44 +0,0 @@
1
- #!/bin/sh
2
- # Auto-install Bun if not present (best-effort, never fails)
3
- #
4
- # Used by:
5
- # - scripts/postinstall.js (npm install)
6
- # - bin/prjct (first CLI invocation)
7
- #
8
- # NEVER exits with error — graceful fallback to Node.js
9
-
10
- # Already installed? Done.
11
- if command -v bun >/dev/null 2>&1; then
12
- exit 0
13
- fi
14
-
15
- # Also check ~/.bun/bin (may not be in PATH yet)
16
- if [ -x "${BUN_INSTALL:-$HOME/.bun}/bin/bun" ]; then
17
- exit 0
18
- fi
19
-
20
- # Only attempt on supported platforms
21
- case "$(uname -s)" in
22
- Darwin|Linux) ;;
23
- *)
24
- echo " Bun auto-install not supported on $(uname -s). Using Node.js."
25
- exit 0
26
- ;;
27
- esac
28
-
29
- echo " Installing Bun for optimal performance..."
30
-
31
- # Official Bun installer (non-interactive)
32
- if curl -fsSL https://bun.sh/install | bash >/dev/null 2>&1; then
33
- # Source the new PATH
34
- export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
35
- export PATH="$BUN_INSTALL/bin:$PATH"
36
-
37
- if command -v bun >/dev/null 2>&1; then
38
- echo " ✓ Bun $(bun --version) installed"
39
- exit 0
40
- fi
41
- fi
42
-
43
- echo " Bun auto-install skipped. Using Node.js (still works fine)."
44
- exit 0