#!/usr/bin/env bun
// @bun
import{execSync as v,spawn as D}from"child_process";import{existsSync as E,mkdirSync as k,openSync as S,readFileSync as f,writeFileSync as h}from"fs";import{homedir as u,platform as w}from"os";import{basename as m,dirname as H,join as V}from"path";import{fileURLToPath as g}from"url";var _=H(g(import.meta.url)),F=m(H(_))==="dist",I=F?H(H(_)):H(_),P=process.cwd(),j=process.env.BEACON_HOME||V(u(),".beacon"),y=V(j,"server.json"),O=process.env.PORT||"4319";if(!process.env.BEACON_MIGRATIONS_DIR)process.env.BEACON_MIGRATIONS_DIR=V(I,"drizzle");function Y(z){if(!F)return V(I,z);return V(I,"dist",z.replace(/\.ts$/,".js"))}var $=process.argv[2];if($==="mcp")await import(Y("bin/mcp.ts"));else if($==="hook")await import(Y("bin/hook.ts"));else if($==="plan")await import(Y("bin/plan.ts"));else if($==="prompt")await import(Y("bin/prompt.ts"));else if($==="stop-hook")await import(Y("bin/stop-hook.ts"));else if($==="stop")s();else if($==="setup")await A(R()||P);else if($==="doctor")await import(Y("bin/doctor.ts"));else if($==="uninstall")await import(Y("bin/uninstall.ts"));else await a();async function A(z,Q=!1){let{installInitSkill:q,installRefreshSkill:J,installCodexRepoSkills:W,ensureMcp:B,ensureWorkflowDoc:G}=await import(Y("lib/assets.ts")),{selfHealGlobal:L}=await import(Y("lib/global-install.ts")),{codexDetected:M}=await import(Y("lib/codex-install.ts")),Z=await L(),U=q(z),X=J(z);G(z);let K=B(z),T=M()?W(z):[];if(Q){if(K.added)console.log(`[beacon] registered Beacon MCP in ${K.path} \u2014 restart your agent CLI to use @beacon mentions.`)}else{console.log(`
  \u25C9 Beacon setup \xB7 ${z}`),console.log(`  \u2713 skill:  ${U}`),console.log(`  \u2713 skill:  ${X}`);for(let N of T)console.log(`  \u2713 skill:  ${N}`);console.log(`  ${K.added?"\u2713 added ":"\xB7 kept  "} Beacon MCP in ${K.path}`),console.log(`  \u2192 in this repo, run /beacon-init in your agent (Claude Code or Codex) to map the repo.
`)}return{initSkill:U,refreshSkill:X,mcp:K,codexSkills:T,heal:Z}}function R(){try{return v("git rev-parse --show-toplevel",{cwd:P,stdio:["ignore","pipe","ignore"]}).toString().trim()}catch{return""}}function b(z){try{return process.kill(z,0),!0}catch{return!1}}function x(z){try{return JSON.parse(f(z,"utf8"))}catch{return null}}async function C(z){try{return(await fetch(z)).ok}catch{return!1}}async function c(z,Q=60){for(let q=0;q<Q;q++){if(await C(z))return!0;await new Promise((J)=>setTimeout(J,500))}return!1}function t(){k(j,{recursive:!0});let z=S(V(j,"server.log"),"a"),Q={...process.env,PORT:O,BEACON_NO_OPEN:"1",BEACON_MIGRATIONS_DIR:V(I,"drizzle")};delete Q.BEACON_REPO,delete Q.BEACON_DATA_DIR,delete Q.DATABASE_URL;let J=D("bun",F?["run","start"]:["run","dev"],{cwd:I,env:Q,detached:!0,stdio:["ignore",z,z]});J.unref();let W={pid:J.pid??0,port:O};return h(y,JSON.stringify(W)),W}async function p(){let z=x(y);if(z&&b(z.pid)&&await C(`http://localhost:${z.port}/api/workspace`))return z.port;console.log("[beacon] starting the Beacon server\u2026");let{port:Q}=t();if(!await c(`http://localhost:${Q}/api/workspace`))console.log("[beacon] (server is taking a while \u2014 it may still be compiling)");return Q}function d(z){if(process.env.BEACON_NO_OPEN)return;let Q=w()==="darwin"?"open":w()==="win32"?"start":"xdg-open";try{v(`${Q} "${z}"`,{stdio:"ignore"})}catch{}}async function a(){let z=R()||P,{addWorkspace:Q,idForPath:q,dataDirFor:J,ensureWorkspaceDb:W}=await import(Y("lib/workspaces.ts")),B=q(z),G=J(B),L=!E(V(G,"db.sqlite"));if(L)console.log(`[beacon] first run for ${z} \u2014 creating database\u2026`);let M=await W(B);if(!M.ok)console.error(`[beacon] failed to provision database: ${M.error}`);if(L)console.log("[beacon] tip: already have code here? run `/beacon-init` in your agent (Claude Code or Codex) to map the project.");let{heal:Z}=await A(z,!0),U=[];if(Z.skillsAdded.length)U.push(`skills ${Z.skillsAdded.join(", ")}`);if(Z.hooksAdded)U.push(`${Z.hooksAdded} Claude Code hook${Z.hooksAdded===1?"":"s"}`);if(Z.claudeMdBlockTouched)U.push("global CLAUDE.md block");if(U.length)console.log(`[beacon] wired into ~/.claude/: ${U.join(" + ")}.`);let X=Z.codex;if(X){let N=[];if(X.skillsAdded.length)N.push(`skills ${X.skillsAdded.join(", ")}`);if(X.hooksAdded)N.push(`${X.hooksAdded} Codex hook${X.hooksAdded===1?"":"s"}`);if(X.agentsMdBlockTouched)N.push("global AGENTS.md block");if(X.mcp.added)N.push("MCP entry in config.toml");if(N.length)console.log(`[beacon] wired into ~/.codex/: ${N.join(" + ")}.`);if(X.mcp.error)console.log(`[beacon] codex MCP not wired: ${X.mcp.error}`)}Q(z);let K=await p(),T=`http://localhost:${K}/api/workspace/activate?id=${B}&redirect=/map`;console.log(`
  \u25C9 Beacon
  repo:  ${z}
  data:  ${G}
  url:   http://localhost:${K}
`),console.log("  (the server keeps running in the background \u2014 `beacon stop` to stop it)\n"),d(T)}function s(){let z=x(y);if(z&&b(z.pid))try{process.kill(z.pid),console.log(`[beacon] stopped the server (pid ${z.pid}).`)}catch{console.log("[beacon] could not stop the server.")}else console.log("[beacon] no server running.")}
