@automagik/genie 4.260330.23 → 4.260330.25

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.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "genie",
13
- "version": "4.260330.23",
13
+ "version": "4.260330.25",
14
14
  "source": "./plugins/genie",
15
15
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, wish them into plans, make with parallel agents, ship as one team. A coding genie that grows with your project."
16
16
  }
package/dist/genie.js CHANGED
@@ -161,7 +161,7 @@ disable_paste_burst = true
161
161
  tell.location
162
162
  from (select lo_tell64($1) as location) tell
163
163
  ) seek
164
- `};return resolve2(lo),new Promise(async(r)=>finish=r);async function readable({highWaterMark=16384,start=0,end=1/0}={}){let max=end-start;return start&&await lo.seek(start),new Stream2.Readable({highWaterMark,async read(size){let l=size>max?size-max:size;max-=size;let[{data}]=await lo.read(l);if(this.push(data),data.length<size)this.push(null)}})}async function writable({highWaterMark=16384,start=0}={}){return start&&await lo.seek(start),new Stream2.Writable({highWaterMark,write(chunk,encoding,callback){lo.write(chunk).then(()=>callback(),callback)}})}}).catch(reject)})}var init_large=()=>{};var exports_src={};__export(exports_src,{default:()=>src_default});import os from"os";import fs from"fs";function Postgres(a,b2){let options=parseOptions(a,b2),subscribe=options.no_subscribe||Subscribe(Postgres,{...options}),ending=!1,queries=queue_default(),connecting=queue_default(),reserved=queue_default(),closed=queue_default(),ended=queue_default(),open=queue_default(),busy=queue_default(),full=queue_default(),queues={connecting,reserved,closed,ended,open,busy,full},connections=[...Array(options.max)].map(()=>connection_default(options,queues,{onopen,onend,onclose})),sql=Sql(handler);return Object.assign(sql,{get parameters(){return options.parameters},largeObject:largeObject.bind(null,sql),subscribe,CLOSE,END:CLOSE,PostgresError,options,reserve,listen,begin,close,end}),sql;function Sql(handler2){return handler2.debug=options.debug,Object.entries(options.types).reduce((acc,[name,type])=>{return acc[name]=(x)=>new Parameter(x,type.to),acc},typed),Object.assign(sql2,{types:typed,typed,unsafe,notify,array,json,file}),sql2;function typed(value,type){return new Parameter(value,type)}function sql2(strings,...args){return strings&&Array.isArray(strings.raw)?new Query(strings,args,handler2,cancel):typeof strings==="string"&&!args.length?new Identifier(options.transform.column.to?options.transform.column.to(strings):strings):new Builder(strings,args)}function unsafe(string,args=[],options2={}){return arguments.length===2&&!Array.isArray(args)&&(options2=args,args=[]),new Query([string],args,handler2,cancel,{prepare:!1,...options2,simple:"simple"in options2?options2.simple:args.length===0})}function file(path,args=[],options2={}){return arguments.length===2&&!Array.isArray(args)&&(options2=args,args=[]),new Query([],args,(query2)=>{fs.readFile(path,"utf8",(err,string)=>{if(err)return query2.reject(err);query2.strings=[string],handler2(query2)})},cancel,{...options2,simple:"simple"in options2?options2.simple:args.length===0})}}async function listen(name,fn,onlisten){let listener={fn,onlisten},sql2=listen.sql||(listen.sql=Postgres({...options,max:1,idle_timeout:null,max_lifetime:null,fetch_types:!1,onclose(){Object.entries(listen.channels).forEach(([name2,{listeners}])=>{delete listen.channels[name2],Promise.all(listeners.map((l)=>listen(name2,l.fn,l.onlisten).catch(()=>{})))})},onnotify(c,x){c in listen.channels&&listen.channels[c].listeners.forEach((l)=>l.fn(x))}})),channels=listen.channels||(listen.channels={});if(name in channels){channels[name].listeners.push(listener);let result2=await channels[name].result;return listener.onlisten&&listener.onlisten(),{state:result2.state,unlisten}}channels[name]={result:sql2`listen ${sql2.unsafe('"'+name.replace(/"/g,'""')+'"')}`,listeners:[listener]};let result=await channels[name].result;return listener.onlisten&&listener.onlisten(),{state:result.state,unlisten};async function unlisten(){if(name in channels===!1)return;if(channels[name].listeners=channels[name].listeners.filter((x)=>x!==listener),channels[name].listeners.length)return;return delete channels[name],sql2`unlisten ${sql2.unsafe('"'+name.replace(/"/g,'""')+'"')}`}}async function notify(channel,payload){return await sql`select pg_notify(${channel}, ${""+payload})`}async function reserve(){let queue=queue_default(),c=open.length?open.shift():await new Promise((resolve2,reject)=>{let query={reserve:resolve2,reject};queries.push(query),closed.length&&connect(closed.shift(),query)});move(c,reserved),c.reserved=()=>queue.length?c.execute(queue.shift()):move(c,reserved),c.reserved.release=!0;let sql2=Sql(handler2);return sql2.release=()=>{c.reserved=null,onopen(c)},sql2;function handler2(q){c.queue===full?queue.push(q):c.execute(q)||move(c,full)}}async function begin(options2,fn){!fn&&(fn=options2,options2="");let queries2=queue_default(),savepoints=0,connection2,prepare=null;try{return await sql.unsafe("begin "+options2.replace(/[^a-z ]/ig,""),[],{onexecute}).execute(),await Promise.race([scope(connection2,fn),new Promise((_,reject)=>connection2.onclose=reject)])}catch(error2){throw error2}async function scope(c,fn2,name){let sql2=Sql(handler2);sql2.savepoint=savepoint,sql2.prepare=(x)=>prepare=x.replace(/[^a-z0-9$-_. ]/gi);let uncaughtError,result;name&&await sql2`savepoint ${sql2(name)}`;try{if(result=await new Promise((resolve2,reject)=>{let x=fn2(sql2);Promise.resolve(Array.isArray(x)?Promise.all(x):x).then(resolve2,reject)}),uncaughtError)throw uncaughtError}catch(e){throw await(name?sql2`rollback to ${sql2(name)}`:sql2`rollback`),e instanceof PostgresError&&e.code==="25P02"&&uncaughtError||e}if(!name)prepare?await sql2`prepare transaction '${sql2.unsafe(prepare)}'`:await sql2`commit`;return result;function savepoint(name2,fn3){if(name2&&Array.isArray(name2.raw))return savepoint((sql3)=>sql3.apply(sql3,arguments));return arguments.length===1&&(fn3=name2,name2=null),scope(c,fn3,"s"+savepoints+++(name2?"_"+name2:""))}function handler2(q){q.catch((e)=>uncaughtError||(uncaughtError=e)),c.queue===full?queries2.push(q):c.execute(q)||move(c,full)}}function onexecute(c){connection2=c,move(c,reserved),c.reserved=()=>queries2.length?c.execute(queries2.shift()):move(c,reserved)}}function move(c,queue){return c.queue.remove(c),queue.push(c),c.queue=queue,queue===open?c.idleTimer.start():c.idleTimer.cancel(),c}function json(x){return new Parameter(x,3802)}function array(x,type){if(!Array.isArray(x))return array(Array.from(arguments));return new Parameter(x,type||(x.length?inferType(x)||25:0),options.shared.typeArrayMap)}function handler(query){if(ending)return query.reject(Errors.connection("CONNECTION_ENDED",options,options));if(open.length)return go(open.shift(),query);if(closed.length)return connect(closed.shift(),query);busy.length?go(busy.shift(),query):queries.push(query)}function go(c,query){return c.execute(query)?move(c,busy):move(c,full)}function cancel(query){return new Promise((resolve2,reject)=>{query.state?query.active?connection_default(options).cancel(query.state,resolve2,reject):query.cancelled={resolve:resolve2,reject}:(queries.remove(query),query.cancelled=!0,query.reject(Errors.generic("57014","canceling statement due to user request")),resolve2())})}async function end({timeout=null}={}){if(ending)return ending;await 1;let timer2;return ending=Promise.race([new Promise((r)=>timeout!==null&&(timer2=setTimeout(destroy,timeout*1000,r))),Promise.all(connections.map((c)=>c.end()).concat(listen.sql?listen.sql.end({timeout:0}):[],subscribe.sql?subscribe.sql.end({timeout:0}):[]))]).then(()=>clearTimeout(timer2))}async function close(){await Promise.all(connections.map((c)=>c.end()))}async function destroy(resolve2){await Promise.all(connections.map((c)=>c.terminate()));while(queries.length)queries.shift().reject(Errors.connection("CONNECTION_DESTROYED",options));resolve2()}function connect(c,query){return move(c,connecting),c.connect(query),c}function onend(c){move(c,ended)}function onopen(c){if(queries.length===0)return move(c,open);let max=Math.ceil(queries.length/(connecting.length+1)),ready=!0;while(ready&&queries.length&&max-- >0){let query=queries.shift();if(query.reserve)return query.reserve(c);ready=c.execute(query)}ready?move(c,busy):move(c,full)}function onclose(c,e){move(c,closed),c.reserved=null,c.onclose&&(c.onclose(e),c.onclose=null),options.onclose&&options.onclose(c.id),queries.length&&connect(c,queries.shift())}}function parseOptions(a,b2){if(a&&a.shared)return a;let env=process.env,o=(!a||typeof a==="string"?b2:a)||{},{url,multihost}=parseUrl(a),query=[...url.searchParams].reduce((a2,[b3,c])=>(a2[b3]=c,a2),{}),host=o.hostname||o.host||multihost||url.hostname||env.PGHOST||"localhost",port=o.port||url.port||env.PGPORT||5432,user=o.user||o.username||url.username||env.PGUSERNAME||env.PGUSER||osUsername();o.no_prepare&&(o.prepare=!1),query.sslmode&&(query.ssl=query.sslmode,delete query.sslmode),"timeout"in o&&(console.log("The timeout option is deprecated, use idle_timeout instead"),o.idle_timeout=o.timeout),query.sslrootcert==="system"&&(query.ssl="verify-full");let ints=["idle_timeout","connect_timeout","max_lifetime","max_pipeline","backoff","keep_alive"],defaults={max:globalThis.Cloudflare?3:10,ssl:!1,sslnegotiation:null,idle_timeout:null,connect_timeout:30,max_lifetime,max_pipeline:100,backoff,keep_alive:60,prepare:!0,debug:!1,fetch_types:!0,publications:"alltables",target_session_attrs:null};return{host:Array.isArray(host)?host:host.split(",").map((x)=>x.split(":")[0]),port:Array.isArray(port)?port:host.split(",").map((x)=>parseInt(x.split(":")[1]||port)),path:o.path||host.indexOf("/")>-1&&host+"/.s.PGSQL."+port,database:o.database||o.db||(url.pathname||"").slice(1)||env.PGDATABASE||user,user,pass:o.pass||o.password||url.password||env.PGPASSWORD||"",...Object.entries(defaults).reduce((acc,[k,d])=>{let value=k in o?o[k]:(k in query)?query[k]==="disable"||query[k]==="false"?!1:query[k]:env["PG"+k.toUpperCase()]||d;return acc[k]=typeof value==="string"&&ints.includes(k)?+value:value,acc},{}),connection:{application_name:env.PGAPPNAME||"postgres.js",...o.connection,...Object.entries(query).reduce((acc,[k,v])=>((k in defaults)||(acc[k]=v),acc),{})},types:o.types||{},target_session_attrs:tsa(o,url,env),onnotice:o.onnotice,onnotify:o.onnotify,onclose:o.onclose,onparameter:o.onparameter,socket:o.socket,transform:parseTransform(o.transform||{undefined:void 0}),parameters:{},shared:{retries:0,typeArrayMap:{}},...mergeUserTypes(o.types)}}function tsa(o,url,env){let x=o.target_session_attrs||url.searchParams.get("target_session_attrs")||env.PGTARGETSESSIONATTRS;if(!x||["read-write","read-only","primary","standby","prefer-standby"].includes(x))return x;throw Error("target_session_attrs "+x+" is not supported")}function backoff(retries){return(0.5+Math.random()/2)*Math.min(3**retries/100,20)}function max_lifetime(){return 60*(30+Math.random()*30)}function parseTransform(x){return{undefined:x.undefined,column:{from:typeof x.column==="function"?x.column:x.column&&x.column.from,to:x.column&&x.column.to},value:{from:typeof x.value==="function"?x.value:x.value&&x.value.from,to:x.value&&x.value.to},row:{from:typeof x.row==="function"?x.row:x.row&&x.row.from,to:x.row&&x.row.to}}}function parseUrl(url){if(!url||typeof url!=="string")return{url:{searchParams:new Map}};let host=url;host=host.slice(host.indexOf("://")+3).split(/[?/]/)[0],host=decodeURIComponent(host.slice(host.indexOf("@")+1));let urlObj=new URL(url.replace(host,host.split(",")[0]));return{url:{username:decodeURIComponent(urlObj.username),password:decodeURIComponent(urlObj.password),host:urlObj.host,hostname:urlObj.hostname,port:urlObj.port,pathname:urlObj.pathname,searchParams:urlObj.searchParams},multihost:host.indexOf(",")>-1&&host}}function osUsername(){try{return os.userInfo().username}catch(_){return process.env.USERNAME||process.env.USER||process.env.LOGNAME}}var src_default;var init_src=__esm(()=>{init_types2();init_connection();init_query();init_queue();init_errors3();init_large();Object.assign(Postgres,{PostgresError,toPascal,pascal,toCamel,camel,toKebab,kebab,fromPascal,fromCamel,fromKebab,BigInt:{to:20,from:[20],parse:(x)=>BigInt(x),serialize:(x)=>x.toString()}});src_default=Postgres});var exports_db={};__export(exports_db,{shutdown:()=>shutdown,resetConnection:()=>resetConnection,isConnected:()=>isConnected,isAvailable:()=>isAvailable,getLockfilePath:()=>getLockfilePath,getDataDir:()=>getDataDir,getConnection:()=>getConnection,getActivePort:()=>getActivePort,ensurePgserve:()=>ensurePgserve});import{execSync as execSync2,spawn as spawn2}from"child_process";import{existsSync as existsSync11,mkdirSync as mkdirSync5,readFileSync as readFileSync6,renameSync,unlinkSync as unlinkSync4,writeFileSync as writeFileSync5}from"fs";import{homedir as homedir11}from"os";import{join as join12}from"path";function maskCredentials(url){return url.replace(/\/\/.*@/,"//***@")}function selfHealPostgres(dataDir){try{execSync2(`pkill -9 -f "postgres.*${dataDir.replace(/\//g,"\\/")}" 2>/dev/null || true`,{stdio:"ignore",timeout:5000}),execSync2(`pkill -9 -f "pgserve.*${dataDir.replace(/\//g,"\\/")}" 2>/dev/null || true`,{stdio:"ignore",timeout:5000})}catch{}let pidFile=join12(dataDir,"postmaster.pid");if(existsSync11(pidFile))try{unlinkSync4(pidFile)}catch{}try{execSync2("ipcs -m 2>/dev/null | awk '$6 == 0 {print $2}' | xargs -I{} ipcrm -m {} 2>/dev/null || true",{stdio:"ignore",timeout:5000})}catch{}}function getPort(){let envPort=process.env.GENIE_PG_PORT;if(envPort){let parsed=Number.parseInt(envPort,10);if(!Number.isNaN(parsed)&&parsed>0&&parsed<65536)return parsed}return DEFAULT_PORT}async function isPostgresHealthy(port){try{return await Promise.race([(async()=>{let pg=(await Promise.resolve().then(() => (init_src(),exports_src))).default,probe=pg({host:DEFAULT_HOST,port,database:DB_NAME,username:"postgres",password:"postgres",max:1,connect_timeout:3,idle_timeout:1});try{return await probe`SELECT 1`,await probe.end({timeout:2}),!0}catch{try{await probe.end({timeout:1})}catch{}return!1}})(),new Promise((resolve2)=>{setTimeout(()=>resolve2(!1),4000).unref()})])}catch{return!1}}function readLockfile(){try{let content=readFileSync6(LOCKFILE_PATH,"utf-8").trim(),port=Number.parseInt(content,10);if(!Number.isNaN(port)&&port>0&&port<65536)return port}catch{}return null}function writeLockfile(port){try{mkdirSync5(GENIE_HOME2,{recursive:!0});let tmpPath=`${LOCKFILE_PATH}.tmp.${process.pid}`;writeFileSync5(tmpPath,String(port),"utf-8"),renameSync(tmpPath,LOCKFILE_PATH)}catch{}}function removeLockfile(){try{unlinkSync4(LOCKFILE_PATH)}catch{}}async function ensurePgserve(){if(ensurePromise)return ensurePromise;ensurePromise=_ensurePgserve();try{return await ensurePromise}finally{ensurePromise=null}}async function autoStartDaemon(){let pidPath=join12(GENIE_HOME2,"scheduler.pid");try{let pidStr=readFileSync6(pidPath,"utf-8").trim(),pid=Number.parseInt(pidStr,10);if(!Number.isNaN(pid)&&pid>0)try{process.kill(pid,0);return}catch{}}catch{}let bunPath=process.execPath??"bun",genieBin=process.argv[1]??"genie";spawn2(bunPath,[genieBin,"daemon","start"],{detached:!0,stdio:"ignore",env:{...process.env}}).unref()}async function _ensurePgserve(){if(activePort!==null)return activePort;let port=getPort(),portFromFile=readLockfile();if(portFromFile!==null&&await isPostgresHealthy(portFromFile))return activePort=portFromFile,process.env.GENIE_PG_AVAILABLE="true",portFromFile;if(await isPostgresHealthy(port))return activePort=port,process.env.GENIE_PG_AVAILABLE="true",writeLockfile(port),port;if(process.env.CI==="true")throw process.env.GENIE_PG_AVAILABLE="false",Error("pgserve not available in CI");if(process.env.GENIE_IS_DAEMON==="1"){mkdirSync5(DATA_DIR,{recursive:!0}),selfHealPostgres(DATA_DIR);try{let startedPort=await startPgserveOnPort(port);return registerExitHandler(),startedPort}catch(err){process.env.GENIE_PG_AVAILABLE="false";let message=err instanceof Error?err.message:String(err);throw Error(`pgserve failed to start: ${maskCredentials(message)}`)}}await autoStartDaemon();let deadline=Date.now()+16000;while(Date.now()<deadline){let p=readLockfile();if(p!==null&&await isPostgresHealthy(p))return activePort=p,process.env.GENIE_PG_AVAILABLE="true",p;await new Promise((r)=>setTimeout(r,500))}throw process.env.GENIE_PG_AVAILABLE="false",Error("Timed out waiting for daemon to start pgserve (16s). Run: genie daemon start")}function findPgserveBin(){try{let resolved=__require.resolve("pgserve/bin/pgserve-wrapper.cjs");if(existsSync11(resolved))return resolved}catch{}let globalBin=join12(homedir11(),".bun","bin","pgserve");if(existsSync11(globalBin))return globalBin;try{return execSync2("which pgserve",{encoding:"utf-8",timeout:3000}).trim()}catch{return"pgserve"}}async function startPgserveOnPort(port){mkdirSync5(DATA_DIR,{recursive:!0});let child=spawn2(findPgserveBin(),["--port",String(port),"--host",DEFAULT_HOST,"--data",DATA_DIR,"--log","warn","--no-stats","--no-cluster"],{detached:!0,stdio:"ignore"});child.unref(),pgserveChild=child;let deadline=Date.now()+15000;while(Date.now()<deadline){if(await isPostgresHealthy(port))return activePort=port,ownsLockfile=!0,process.env.GENIE_PG_AVAILABLE="true",writeLockfile(port),port;await new Promise((r)=>setTimeout(r,500))}try{child.kill("SIGTERM")}catch{}throw Error(`pgserve failed to start on port ${port} (timeout after 15s)`)}function registerExitHandler(){if(exitHandlerRegistered)return;exitHandlerRegistered=!0;let cleanup=()=>{if(pgserveChild){try{pgserveChild.kill("SIGTERM")}catch{}pgserveChild=null}if(ownsLockfile)removeLockfile(),ownsLockfile=!1};process.on("exit",cleanup),process.on("SIGINT",()=>{cleanup(),process.exit(130)}),process.on("SIGTERM",()=>{cleanup(),process.exit(143)})}async function getConnection(){if(sqlClient)try{return await sqlClient`SELECT 1`,sqlClient}catch{try{await sqlClient.end({timeout:2})}catch{}sqlClient=null,activePort=null}let port=await ensurePgserve(),postgres2=(await Promise.resolve().then(() => (init_src(),exports_src))).default,testSchema=process.env.GENIE_TEST_SCHEMA;sqlClient=postgres2({host:DEFAULT_HOST,port,database:DB_NAME,username:"postgres",password:"postgres",max:50,idle_timeout:1,connect_timeout:5,onnotice:()=>{},connection:{client_min_messages:"warning",...testSchema?{search_path:`${testSchema}, public`}:{}}});try{if(await runMigrations(sqlClient),!testSchema&&needsSeed())await runSeed(sqlClient)}catch(err){try{await sqlClient.end({timeout:2})}catch{}throw sqlClient=null,err}return sqlClient}function isConnected(){return sqlClient!==null}async function resetConnection(){if(sqlClient)await sqlClient.end({timeout:5}),sqlClient=null}async function isAvailable(){try{return await(await getConnection())`SELECT 1`,!0}catch{return!1}}async function shutdown(){if(sqlClient)await sqlClient.end({timeout:5}),sqlClient=null;if(ownsLockfile)removeLockfile(),ownsLockfile=!1}function getDataDir(){return DATA_DIR}function getActivePort(){return activePort??getPort()}function getLockfilePath(){return LOCKFILE_PATH}var DEFAULT_PORT=19642,DEFAULT_HOST="127.0.0.1",GENIE_HOME2,DATA_DIR,LOCKFILE_PATH,DB_NAME="genie",pgserveChild=null,sqlClient=null,activePort=null,ensurePromise=null,ownsLockfile=!1,exitHandlerRegistered=!1;var init_db=__esm(()=>{init_db_migrations();init_pg_seed();GENIE_HOME2=process.env.GENIE_HOME??join12(homedir11(),".genie"),DATA_DIR=join12(GENIE_HOME2,"data","pgserve"),LOCKFILE_PATH=join12(GENIE_HOME2,"pgserve.port")});var exports_audit={};__export(exports_audit,{recordAuditEvent:()=>recordAuditEvent,queryToolUsage:()=>queryToolUsage,queryTimeline:()=>queryTimeline,querySummary:()=>querySummary,queryErrorPatterns:()=>queryErrorPatterns,queryCostBreakdown:()=>queryCostBreakdown,queryAuditEvents:()=>queryAuditEvents,getActor:()=>getActor,generateTraceId:()=>generateTraceId});async function recordAuditEvent(entityType,entityId,eventType,actor,details){try{if(!await isAvailable())return;let sql=await getConnection();await sql`
164
+ `};return resolve2(lo),new Promise(async(r)=>finish=r);async function readable({highWaterMark=16384,start=0,end=1/0}={}){let max=end-start;return start&&await lo.seek(start),new Stream2.Readable({highWaterMark,async read(size){let l=size>max?size-max:size;max-=size;let[{data}]=await lo.read(l);if(this.push(data),data.length<size)this.push(null)}})}async function writable({highWaterMark=16384,start=0}={}){return start&&await lo.seek(start),new Stream2.Writable({highWaterMark,write(chunk,encoding,callback){lo.write(chunk).then(()=>callback(),callback)}})}}).catch(reject)})}var init_large=()=>{};var exports_src={};__export(exports_src,{default:()=>src_default});import os from"os";import fs from"fs";function Postgres(a,b2){let options=parseOptions(a,b2),subscribe=options.no_subscribe||Subscribe(Postgres,{...options}),ending=!1,queries=queue_default(),connecting=queue_default(),reserved=queue_default(),closed=queue_default(),ended=queue_default(),open=queue_default(),busy=queue_default(),full=queue_default(),queues={connecting,reserved,closed,ended,open,busy,full},connections=[...Array(options.max)].map(()=>connection_default(options,queues,{onopen,onend,onclose})),sql=Sql(handler);return Object.assign(sql,{get parameters(){return options.parameters},largeObject:largeObject.bind(null,sql),subscribe,CLOSE,END:CLOSE,PostgresError,options,reserve,listen,begin,close,end}),sql;function Sql(handler2){return handler2.debug=options.debug,Object.entries(options.types).reduce((acc,[name,type])=>{return acc[name]=(x)=>new Parameter(x,type.to),acc},typed),Object.assign(sql2,{types:typed,typed,unsafe,notify,array,json,file}),sql2;function typed(value,type){return new Parameter(value,type)}function sql2(strings,...args){return strings&&Array.isArray(strings.raw)?new Query(strings,args,handler2,cancel):typeof strings==="string"&&!args.length?new Identifier(options.transform.column.to?options.transform.column.to(strings):strings):new Builder(strings,args)}function unsafe(string,args=[],options2={}){return arguments.length===2&&!Array.isArray(args)&&(options2=args,args=[]),new Query([string],args,handler2,cancel,{prepare:!1,...options2,simple:"simple"in options2?options2.simple:args.length===0})}function file(path,args=[],options2={}){return arguments.length===2&&!Array.isArray(args)&&(options2=args,args=[]),new Query([],args,(query2)=>{fs.readFile(path,"utf8",(err,string)=>{if(err)return query2.reject(err);query2.strings=[string],handler2(query2)})},cancel,{...options2,simple:"simple"in options2?options2.simple:args.length===0})}}async function listen(name,fn,onlisten){let listener={fn,onlisten},sql2=listen.sql||(listen.sql=Postgres({...options,max:1,idle_timeout:null,max_lifetime:null,fetch_types:!1,onclose(){Object.entries(listen.channels).forEach(([name2,{listeners}])=>{delete listen.channels[name2],Promise.all(listeners.map((l)=>listen(name2,l.fn,l.onlisten).catch(()=>{})))})},onnotify(c,x){c in listen.channels&&listen.channels[c].listeners.forEach((l)=>l.fn(x))}})),channels=listen.channels||(listen.channels={});if(name in channels){channels[name].listeners.push(listener);let result2=await channels[name].result;return listener.onlisten&&listener.onlisten(),{state:result2.state,unlisten}}channels[name]={result:sql2`listen ${sql2.unsafe('"'+name.replace(/"/g,'""')+'"')}`,listeners:[listener]};let result=await channels[name].result;return listener.onlisten&&listener.onlisten(),{state:result.state,unlisten};async function unlisten(){if(name in channels===!1)return;if(channels[name].listeners=channels[name].listeners.filter((x)=>x!==listener),channels[name].listeners.length)return;return delete channels[name],sql2`unlisten ${sql2.unsafe('"'+name.replace(/"/g,'""')+'"')}`}}async function notify(channel,payload){return await sql`select pg_notify(${channel}, ${""+payload})`}async function reserve(){let queue=queue_default(),c=open.length?open.shift():await new Promise((resolve2,reject)=>{let query={reserve:resolve2,reject};queries.push(query),closed.length&&connect(closed.shift(),query)});move(c,reserved),c.reserved=()=>queue.length?c.execute(queue.shift()):move(c,reserved),c.reserved.release=!0;let sql2=Sql(handler2);return sql2.release=()=>{c.reserved=null,onopen(c)},sql2;function handler2(q){c.queue===full?queue.push(q):c.execute(q)||move(c,full)}}async function begin(options2,fn){!fn&&(fn=options2,options2="");let queries2=queue_default(),savepoints=0,connection2,prepare=null;try{return await sql.unsafe("begin "+options2.replace(/[^a-z ]/ig,""),[],{onexecute}).execute(),await Promise.race([scope(connection2,fn),new Promise((_,reject)=>connection2.onclose=reject)])}catch(error2){throw error2}async function scope(c,fn2,name){let sql2=Sql(handler2);sql2.savepoint=savepoint,sql2.prepare=(x)=>prepare=x.replace(/[^a-z0-9$-_. ]/gi);let uncaughtError,result;name&&await sql2`savepoint ${sql2(name)}`;try{if(result=await new Promise((resolve2,reject)=>{let x=fn2(sql2);Promise.resolve(Array.isArray(x)?Promise.all(x):x).then(resolve2,reject)}),uncaughtError)throw uncaughtError}catch(e){throw await(name?sql2`rollback to ${sql2(name)}`:sql2`rollback`),e instanceof PostgresError&&e.code==="25P02"&&uncaughtError||e}if(!name)prepare?await sql2`prepare transaction '${sql2.unsafe(prepare)}'`:await sql2`commit`;return result;function savepoint(name2,fn3){if(name2&&Array.isArray(name2.raw))return savepoint((sql3)=>sql3.apply(sql3,arguments));return arguments.length===1&&(fn3=name2,name2=null),scope(c,fn3,"s"+savepoints+++(name2?"_"+name2:""))}function handler2(q){q.catch((e)=>uncaughtError||(uncaughtError=e)),c.queue===full?queries2.push(q):c.execute(q)||move(c,full)}}function onexecute(c){connection2=c,move(c,reserved),c.reserved=()=>queries2.length?c.execute(queries2.shift()):move(c,reserved)}}function move(c,queue){return c.queue.remove(c),queue.push(c),c.queue=queue,queue===open?c.idleTimer.start():c.idleTimer.cancel(),c}function json(x){return new Parameter(x,3802)}function array(x,type){if(!Array.isArray(x))return array(Array.from(arguments));return new Parameter(x,type||(x.length?inferType(x)||25:0),options.shared.typeArrayMap)}function handler(query){if(ending)return query.reject(Errors.connection("CONNECTION_ENDED",options,options));if(open.length)return go(open.shift(),query);if(closed.length)return connect(closed.shift(),query);busy.length?go(busy.shift(),query):queries.push(query)}function go(c,query){return c.execute(query)?move(c,busy):move(c,full)}function cancel(query){return new Promise((resolve2,reject)=>{query.state?query.active?connection_default(options).cancel(query.state,resolve2,reject):query.cancelled={resolve:resolve2,reject}:(queries.remove(query),query.cancelled=!0,query.reject(Errors.generic("57014","canceling statement due to user request")),resolve2())})}async function end({timeout=null}={}){if(ending)return ending;await 1;let timer2;return ending=Promise.race([new Promise((r)=>timeout!==null&&(timer2=setTimeout(destroy,timeout*1000,r))),Promise.all(connections.map((c)=>c.end()).concat(listen.sql?listen.sql.end({timeout:0}):[],subscribe.sql?subscribe.sql.end({timeout:0}):[]))]).then(()=>clearTimeout(timer2))}async function close(){await Promise.all(connections.map((c)=>c.end()))}async function destroy(resolve2){await Promise.all(connections.map((c)=>c.terminate()));while(queries.length)queries.shift().reject(Errors.connection("CONNECTION_DESTROYED",options));resolve2()}function connect(c,query){return move(c,connecting),c.connect(query),c}function onend(c){move(c,ended)}function onopen(c){if(queries.length===0)return move(c,open);let max=Math.ceil(queries.length/(connecting.length+1)),ready=!0;while(ready&&queries.length&&max-- >0){let query=queries.shift();if(query.reserve)return query.reserve(c);ready=c.execute(query)}ready?move(c,busy):move(c,full)}function onclose(c,e){move(c,closed),c.reserved=null,c.onclose&&(c.onclose(e),c.onclose=null),options.onclose&&options.onclose(c.id),queries.length&&connect(c,queries.shift())}}function parseOptions(a,b2){if(a&&a.shared)return a;let env=process.env,o=(!a||typeof a==="string"?b2:a)||{},{url,multihost}=parseUrl(a),query=[...url.searchParams].reduce((a2,[b3,c])=>(a2[b3]=c,a2),{}),host=o.hostname||o.host||multihost||url.hostname||env.PGHOST||"localhost",port=o.port||url.port||env.PGPORT||5432,user=o.user||o.username||url.username||env.PGUSERNAME||env.PGUSER||osUsername();o.no_prepare&&(o.prepare=!1),query.sslmode&&(query.ssl=query.sslmode,delete query.sslmode),"timeout"in o&&(console.log("The timeout option is deprecated, use idle_timeout instead"),o.idle_timeout=o.timeout),query.sslrootcert==="system"&&(query.ssl="verify-full");let ints=["idle_timeout","connect_timeout","max_lifetime","max_pipeline","backoff","keep_alive"],defaults={max:globalThis.Cloudflare?3:10,ssl:!1,sslnegotiation:null,idle_timeout:null,connect_timeout:30,max_lifetime,max_pipeline:100,backoff,keep_alive:60,prepare:!0,debug:!1,fetch_types:!0,publications:"alltables",target_session_attrs:null};return{host:Array.isArray(host)?host:host.split(",").map((x)=>x.split(":")[0]),port:Array.isArray(port)?port:host.split(",").map((x)=>parseInt(x.split(":")[1]||port)),path:o.path||host.indexOf("/")>-1&&host+"/.s.PGSQL."+port,database:o.database||o.db||(url.pathname||"").slice(1)||env.PGDATABASE||user,user,pass:o.pass||o.password||url.password||env.PGPASSWORD||"",...Object.entries(defaults).reduce((acc,[k,d])=>{let value=k in o?o[k]:(k in query)?query[k]==="disable"||query[k]==="false"?!1:query[k]:env["PG"+k.toUpperCase()]||d;return acc[k]=typeof value==="string"&&ints.includes(k)?+value:value,acc},{}),connection:{application_name:env.PGAPPNAME||"postgres.js",...o.connection,...Object.entries(query).reduce((acc,[k,v])=>((k in defaults)||(acc[k]=v),acc),{})},types:o.types||{},target_session_attrs:tsa(o,url,env),onnotice:o.onnotice,onnotify:o.onnotify,onclose:o.onclose,onparameter:o.onparameter,socket:o.socket,transform:parseTransform(o.transform||{undefined:void 0}),parameters:{},shared:{retries:0,typeArrayMap:{}},...mergeUserTypes(o.types)}}function tsa(o,url,env){let x=o.target_session_attrs||url.searchParams.get("target_session_attrs")||env.PGTARGETSESSIONATTRS;if(!x||["read-write","read-only","primary","standby","prefer-standby"].includes(x))return x;throw Error("target_session_attrs "+x+" is not supported")}function backoff(retries){return(0.5+Math.random()/2)*Math.min(3**retries/100,20)}function max_lifetime(){return 60*(30+Math.random()*30)}function parseTransform(x){return{undefined:x.undefined,column:{from:typeof x.column==="function"?x.column:x.column&&x.column.from,to:x.column&&x.column.to},value:{from:typeof x.value==="function"?x.value:x.value&&x.value.from,to:x.value&&x.value.to},row:{from:typeof x.row==="function"?x.row:x.row&&x.row.from,to:x.row&&x.row.to}}}function parseUrl(url){if(!url||typeof url!=="string")return{url:{searchParams:new Map}};let host=url;host=host.slice(host.indexOf("://")+3).split(/[?/]/)[0],host=decodeURIComponent(host.slice(host.indexOf("@")+1));let urlObj=new URL(url.replace(host,host.split(",")[0]));return{url:{username:decodeURIComponent(urlObj.username),password:decodeURIComponent(urlObj.password),host:urlObj.host,hostname:urlObj.hostname,port:urlObj.port,pathname:urlObj.pathname,searchParams:urlObj.searchParams},multihost:host.indexOf(",")>-1&&host}}function osUsername(){try{return os.userInfo().username}catch(_){return process.env.USERNAME||process.env.USER||process.env.LOGNAME}}var src_default;var init_src=__esm(()=>{init_types2();init_connection();init_query();init_queue();init_errors3();init_large();Object.assign(Postgres,{PostgresError,toPascal,pascal,toCamel,camel,toKebab,kebab,fromPascal,fromCamel,fromKebab,BigInt:{to:20,from:[20],parse:(x)=>BigInt(x),serialize:(x)=>x.toString()}});src_default=Postgres});var exports_db={};__export(exports_db,{shutdown:()=>shutdown,resetConnection:()=>resetConnection,isConnected:()=>isConnected,isAvailable:()=>isAvailable,getLockfilePath:()=>getLockfilePath,getDataDir:()=>getDataDir,getConnection:()=>getConnection,getActivePort:()=>getActivePort,ensurePgserve:()=>ensurePgserve});import{execSync as execSync2,spawn as spawn2}from"child_process";import{existsSync as existsSync11,mkdirSync as mkdirSync5,readFileSync as readFileSync6,renameSync,unlinkSync as unlinkSync4,writeFileSync as writeFileSync5}from"fs";import{homedir as homedir11}from"os";import{join as join12}from"path";function maskCredentials(url){return url.replace(/\/\/.*@/,"//***@")}function selfHealPostgres(dataDir){try{execSync2(`pkill -9 -f "postgres.*${dataDir.replace(/\//g,"\\/")}" 2>/dev/null || true`,{stdio:"ignore",timeout:5000}),execSync2(`pkill -9 -f "pgserve.*${dataDir.replace(/\//g,"\\/")}" 2>/dev/null || true`,{stdio:"ignore",timeout:5000})}catch{}let pidFile=join12(dataDir,"postmaster.pid");if(existsSync11(pidFile))try{unlinkSync4(pidFile)}catch{}try{execSync2("ipcs -m 2>/dev/null | awk '$6 == 0 {print $2}' | xargs -I{} ipcrm -m {} 2>/dev/null || true",{stdio:"ignore",timeout:5000})}catch{}}function getPort(){let envPort=process.env.GENIE_PG_PORT;if(envPort){let parsed=Number.parseInt(envPort,10);if(!Number.isNaN(parsed)&&parsed>0&&parsed<65536)return parsed}return DEFAULT_PORT}async function isPostgresHealthy(port){try{return await Promise.race([(async()=>{let pg=(await Promise.resolve().then(() => (init_src(),exports_src))).default,probe=pg({host:DEFAULT_HOST,port,database:DB_NAME,username:"postgres",password:"postgres",max:1,connect_timeout:3,idle_timeout:1});try{return await probe`SELECT 1`,await probe.end({timeout:2}),!0}catch{try{await probe.end({timeout:1})}catch{}return!1}})(),new Promise((resolve2)=>{setTimeout(()=>resolve2(!1),4000).unref()})])}catch{return!1}}function readLockfile(){try{let content=readFileSync6(LOCKFILE_PATH,"utf-8").trim(),port=Number.parseInt(content,10);if(!Number.isNaN(port)&&port>0&&port<65536)return port}catch{}return null}function writeLockfile(port){try{mkdirSync5(GENIE_HOME2,{recursive:!0});let tmpPath=`${LOCKFILE_PATH}.tmp.${process.pid}`;writeFileSync5(tmpPath,String(port),"utf-8"),renameSync(tmpPath,LOCKFILE_PATH)}catch{}}function removeLockfile(){try{unlinkSync4(LOCKFILE_PATH)}catch{}}async function ensurePgserve(){if(ensurePromise)return ensurePromise;ensurePromise=_ensurePgserve();try{return await ensurePromise}finally{ensurePromise=null}}async function autoStartDaemon(){let pidPath=join12(GENIE_HOME2,"scheduler.pid");try{let pidStr=readFileSync6(pidPath,"utf-8").trim(),pid=Number.parseInt(pidStr,10);if(!Number.isNaN(pid)&&pid>0)try{process.kill(pid,0);return}catch{}}catch{}let bunPath=process.execPath??"bun",genieBin=process.argv[1]??"genie";spawn2(bunPath,[genieBin,"daemon","start"],{detached:!0,stdio:"ignore",env:{...process.env}}).unref()}async function _ensurePgserve(){if(activePort!==null)return activePort;let port=getPort(),portFromFile=readLockfile();if(portFromFile!==null&&await isPostgresHealthy(portFromFile))return activePort=portFromFile,process.env.GENIE_PG_AVAILABLE="true",portFromFile;if(await isPostgresHealthy(port))return activePort=port,process.env.GENIE_PG_AVAILABLE="true",writeLockfile(port),port;if(process.env.CI==="true")throw process.env.GENIE_PG_AVAILABLE="false",Error("pgserve not available in CI");if(process.env.GENIE_IS_DAEMON==="1"||process.env.GENIE_APP==="1"){mkdirSync5(DATA_DIR,{recursive:!0}),selfHealPostgres(DATA_DIR);try{let startedPort=await startPgserveOnPort(port);return registerExitHandler(),startedPort}catch(err){process.env.GENIE_PG_AVAILABLE="false";let message=err instanceof Error?err.message:String(err);throw Error(`pgserve failed to start: ${maskCredentials(message)}`)}}await autoStartDaemon();let deadline=Date.now()+16000;while(Date.now()<deadline){let p=readLockfile();if(p!==null&&await isPostgresHealthy(p))return activePort=p,process.env.GENIE_PG_AVAILABLE="true",p;await new Promise((r)=>setTimeout(r,500))}throw process.env.GENIE_PG_AVAILABLE="false",Error("Timed out waiting for daemon to start pgserve (16s). Run: genie daemon start")}function findPgserveBin(){try{let resolved=__require.resolve("pgserve/bin/pgserve-wrapper.cjs");if(existsSync11(resolved))return resolved}catch{}let globalBin=join12(homedir11(),".bun","bin","pgserve");if(existsSync11(globalBin))return globalBin;try{return execSync2("which pgserve",{encoding:"utf-8",timeout:3000}).trim()}catch{return"pgserve"}}async function startPgserveOnPort(port){mkdirSync5(DATA_DIR,{recursive:!0});let child=spawn2(findPgserveBin(),["--port",String(port),"--host",DEFAULT_HOST,"--data",DATA_DIR,"--log","warn","--no-stats","--no-cluster"],{detached:!0,stdio:"ignore"});child.unref(),pgserveChild=child;let deadline=Date.now()+15000;while(Date.now()<deadline){if(await isPostgresHealthy(port))return activePort=port,ownsLockfile=!0,process.env.GENIE_PG_AVAILABLE="true",writeLockfile(port),port;await new Promise((r)=>setTimeout(r,500))}try{child.kill("SIGTERM")}catch{}throw Error(`pgserve failed to start on port ${port} (timeout after 15s)`)}function registerExitHandler(){if(exitHandlerRegistered)return;exitHandlerRegistered=!0;let cleanup=()=>{if(pgserveChild){try{pgserveChild.kill("SIGTERM")}catch{}pgserveChild=null}if(ownsLockfile)removeLockfile(),ownsLockfile=!1};process.on("exit",cleanup),process.on("SIGINT",()=>{cleanup(),process.exit(130)}),process.on("SIGTERM",()=>{cleanup(),process.exit(143)})}async function getConnection(){if(sqlClient)try{return await sqlClient`SELECT 1`,sqlClient}catch{try{await sqlClient.end({timeout:2})}catch{}sqlClient=null,activePort=null}let port=await ensurePgserve(),postgres2=(await Promise.resolve().then(() => (init_src(),exports_src))).default,testSchema=process.env.GENIE_TEST_SCHEMA;sqlClient=postgres2({host:DEFAULT_HOST,port,database:DB_NAME,username:"postgres",password:"postgres",max:50,idle_timeout:1,connect_timeout:5,onnotice:()=>{},connection:{client_min_messages:"warning",...testSchema?{search_path:`${testSchema}, public`}:{}}});try{if(await runMigrations(sqlClient),!testSchema&&needsSeed())await runSeed(sqlClient)}catch(err){try{await sqlClient.end({timeout:2})}catch{}throw sqlClient=null,err}return sqlClient}function isConnected(){return sqlClient!==null}async function resetConnection(){if(sqlClient)await sqlClient.end({timeout:5}),sqlClient=null}async function isAvailable(){try{return await(await getConnection())`SELECT 1`,!0}catch{return!1}}async function shutdown(){if(sqlClient)await sqlClient.end({timeout:5}),sqlClient=null;if(ownsLockfile)removeLockfile(),ownsLockfile=!1}function getDataDir(){return DATA_DIR}function getActivePort(){return activePort??getPort()}function getLockfilePath(){return LOCKFILE_PATH}var DEFAULT_PORT=19642,DEFAULT_HOST="127.0.0.1",GENIE_HOME2,DATA_DIR,LOCKFILE_PATH,DB_NAME="genie",pgserveChild=null,sqlClient=null,activePort=null,ensurePromise=null,ownsLockfile=!1,exitHandlerRegistered=!1;var init_db=__esm(()=>{init_db_migrations();init_pg_seed();GENIE_HOME2=process.env.GENIE_HOME??join12(homedir11(),".genie"),DATA_DIR=join12(GENIE_HOME2,"data","pgserve"),LOCKFILE_PATH=join12(GENIE_HOME2,"pgserve.port")});var exports_audit={};__export(exports_audit,{recordAuditEvent:()=>recordAuditEvent,queryToolUsage:()=>queryToolUsage,queryTimeline:()=>queryTimeline,querySummary:()=>querySummary,queryErrorPatterns:()=>queryErrorPatterns,queryCostBreakdown:()=>queryCostBreakdown,queryAuditEvents:()=>queryAuditEvents,getActor:()=>getActor,generateTraceId:()=>generateTraceId});async function recordAuditEvent(entityType,entityId,eventType,actor,details){try{if(!await isAvailable())return;let sql=await getConnection();await sql`
165
165
  INSERT INTO audit_events (entity_type, entity_id, event_type, actor, details)
166
166
  VALUES (${entityType}, ${entityId}, ${eventType}, ${actor??null}, ${sql.json(details??{})})
167
167
  `}catch{}}function parseSince(since){let match=since.match(/^(\d+)([smhd])$/);if(!match)return since;let amount=Number.parseInt(match[1],10),unit=match[2],ms={s:1000,m:60000,h:3600000,d:86400000}[unit]??3600000;return new Date(Date.now()-amount*ms).toISOString()}async function queryAuditEvents(options={}){let sql=await getConnection(),conditions=[],values2=[],paramIdx=1;if(options.type)conditions.push(`event_type = $${paramIdx++}`),values2.push(options.type);if(options.entity)conditions.push(`(entity_type = $${paramIdx} OR entity_id = $${paramIdx})`),paramIdx++,values2.push(options.entity);if(options.since)conditions.push(`created_at >= $${paramIdx++}::timestamptz`),values2.push(parseSince(options.since));if(options.errorsOnly)conditions.push("event_type LIKE '%error%' OR (details::text LIKE '%error%')");let where=conditions.length>0?`WHERE ${conditions.join(" AND ")}`:"",limit=options.limit??50;return await sql.unsafe(`SELECT id, entity_type, entity_id, event_type, actor, details, created_at
@@ -1362,18 +1362,18 @@ Define your agent's mission here. What is their primary goal? What do they own?
1362
1362
  `,failedCount++,deps.log({timestamp:now.toISOString(),level:"warn",event:"orphan_run_failed",run_id:run.id,worker_id:run.worker_id,dead_heartbeats:threshold})}if(failedCount>0)deps.log({timestamp:now.toISOString(),level:"info",event:"orphan_reconciliation_completed",failed_count:failedCount});return failedCount}async function collectMachineSnapshot(deps){let sql=await deps.getConnection(),now=deps.now(),snapshotId=deps.generateId(),workers=await deps.listWorkers(),activeWorkers=workers.filter((w)=>!["done","error","suspended"].includes(w.state)).length,teams=new Set(workers.filter((w)=>w.team).map((w)=>w.team)),tmuxSessions=await deps.countTmuxSessions(),cpuPercent=null,memoryMb=null;try{let mem=process.memoryUsage();memoryMb=Math.round(mem.rss/1024/1024)}catch{}try{let cpus=(await import("os")).cpus();if(cpus.length>0){let total=cpus.reduce((acc,cpu)=>{let t=Object.values(cpu.times).reduce((a,b2)=>a+b2,0);return acc+t-cpu.times.idle},0),totalAll=cpus.reduce((acc,cpu)=>acc+Object.values(cpu.times).reduce((a,b2)=>a+b2,0),0);cpuPercent=totalAll>0?Math.round(total/totalAll*100):null}}catch{}await sql`
1363
1363
  INSERT INTO machine_snapshots (id, active_workers, active_teams, tmux_sessions, cpu_percent, memory_mb, created_at)
1364
1364
  VALUES (${snapshotId}, ${activeWorkers}, ${teams.size}, ${tmuxSessions}, ${cpuPercent}, ${memoryMb}, ${now})
1365
- `,deps.log({timestamp:now.toISOString(),level:"debug",event:"machine_snapshot",active_workers:activeWorkers,active_teams:teams.size,tmux_sessions:tmuxSessions,cpu_percent:cpuPercent,memory_mb:memoryMb})}async function emitWorkerEvents(deps){let workers=await deps.listWorkers(),now=deps.now().toISOString(),currentIds=new Set;for(let worker of workers){currentIds.add(worker.id);let prev=previousWorkerStates.get(worker.id),repoPath=worker.repoPath??process.cwd();if(!prev)await deps.publishEvent(`genie.agent.${worker.id}.spawned`,{timestamp:now,kind:"state",agent:worker.id,team:worker.team,text:`Agent ${worker.id} spawned`,data:{state:worker.state},source:"registry"},repoPath);else if(prev.state!==worker.state){if(await deps.publishEvent(`genie.agent.${worker.id}.state`,{timestamp:now,kind:"state",agent:worker.id,team:worker.team,text:`Agent ${worker.id} state: ${prev.state} \u2192 ${worker.state}`,data:{previousState:prev.state,state:worker.state},source:"registry"},repoPath),worker.state==="done"&&worker.wishSlug&&worker.groupNumber!=null)await deps.publishEvent(`genie.wish.${worker.wishSlug}.group.${worker.groupNumber}.done`,{timestamp:now,kind:"system",agent:worker.id,team:worker.team,text:`Wish ${worker.wishSlug} group ${worker.groupNumber} completed by ${worker.id}`,data:{wishSlug:worker.wishSlug,groupNumber:worker.groupNumber},source:"registry"},repoPath)}previousWorkerStates.set(worker.id,{...worker})}for(let[id,prev]of previousWorkerStates)if(!currentIds.has(id))await deps.publishEvent(`genie.agent.${id}.killed`,{timestamp:now,kind:"state",agent:id,team:prev.team,text:`Agent ${id} killed`,data:{lastState:prev.state},source:"registry"},prev.repoPath??process.cwd()),previousWorkerStates.delete(id)}function _resetWorkerStatesForTesting(){previousWorkerStates.clear()}function startInboxWatcherIfEnabled(deps){let pollMs=getInboxPollIntervalMs();if(pollMs===0)return deps.log({timestamp:deps.now().toISOString(),level:"info",event:"inbox_watcher_disabled"}),null;return deps.log({timestamp:deps.now().toISOString(),level:"info",event:"inbox_watcher_started",poll_interval_ms:pollMs}),startInboxWatcher()}function startDaemon(configOverrides,depsOverrides){let config=resolveConfig(configOverrides),deps={...createDefaultDeps(),...depsOverrides},daemonId=deps.generateId(),running2=!0,pollTimeout=null,pollResolve=null,listenConnection=null,heartbeatTimer=null,orphanTimer=null,inboxWatcherHandle=null,captureFallbackTimer=null,eventRouterHandle=null,stop=()=>{if(running2=!1,pollTimeout)clearTimeout(pollTimeout),pollTimeout=null;if(pollResolve)pollResolve(),pollResolve=null;if(heartbeatTimer)clearInterval(heartbeatTimer),heartbeatTimer=null;if(orphanTimer)clearInterval(orphanTimer),orphanTimer=null;if(inboxWatcherHandle)stopInboxWatcher(inboxWatcherHandle),inboxWatcherHandle=null;if(listenConnection)listenConnection.end().catch(()=>{}),listenConnection=null;if(captureFallbackTimer)clearInterval(captureFallbackTimer),captureFallbackTimer=null;eventRouterHandle?.stop().catch(()=>{}),eventRouterHandle=null,Promise.resolve().then(() => (init_session_filewatch(),exports_session_filewatch)).then((m)=>m.stopFilewatch()).catch(()=>{}),Promise.resolve().then(() => (init_session_backfill(),exports_session_backfill)).then((m)=>m.stopBackfill()).catch(()=>{}),Promise.resolve().then(() => (init_db(),exports_db)).then(({getLockfilePath:getLockfilePath2})=>{try{__require("fs").unlinkSync(getLockfilePath2())}catch{}}).catch(()=>{})},processTriggers=async()=>{try{let claimed=await claimDueTriggers(deps,config,daemonId);if(claimed.length===0)return;if(claimed.length>config.jitterThreshold){let jitterMs=deps.jitter(config.maxJitterMs);deps.log({timestamp:deps.now().toISOString(),level:"info",event:"jitter_applied",count:claimed.length,jitter_ms:jitterMs}),await deps.sleep(jitterMs)}for(let trigger of claimed){if(!running2)break;await fireTrigger(deps,trigger,daemonId)}}catch(err){let message=err instanceof Error?err.message:String(err);deps.log({timestamp:deps.now().toISOString(),level:"error",event:"process_cycle_error",error:message})}};async function setupListenNotify(d,onTrigger){try{let sql=await d.getConnection();return await sql.listen("genie_trigger_due",async()=>{if(!running2)return;await onTrigger()}),d.log({timestamp:d.now().toISOString(),level:"info",event:"listen_started",channel:"genie_trigger_due"}),sql}catch(err){let message=err instanceof Error?err.message:String(err);return d.log({timestamp:d.now().toISOString(),level:"warn",event:"listen_failed",error:message}),null}}function startOrphanTimer(d,cfg){return setInterval(async()=>{if(!running2)return;try{await reconcileOrphans(d,cfg)}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"orphan_reconciliation_error",error:message})}},cfg.orphanCheckIntervalMs)}async function startEventRouterSafe(d){try{let handle=await startEventRouter();return d.log({timestamp:d.now().toISOString(),level:"info",event:"event_router_started"}),handle}catch(err){let message=err instanceof Error?err.message:String(err);return d.log({timestamp:d.now().toISOString(),level:"warn",event:"event_router_start_failed",error:message}),null}}async function initSessionCapture(d,cfg){try{let captureSql=await d.getConnection(),{startFilewatch:startFilewatch2}=await Promise.resolve().then(() => (init_session_filewatch(),exports_session_filewatch)),{startBackfill:startBackfill2}=await Promise.resolve().then(() => (init_session_backfill(),exports_session_backfill));if(!await startFilewatch2(captureSql)){let{ingestFileFull:ingestFileFull2,discoverAllJsonlFiles:discoverAllJsonlFiles2,buildWorkerMap:buildWorkerMap2}=await Promise.resolve().then(() => (init_session_capture(),exports_session_capture));d.log({timestamp:d.now().toISOString(),level:"warn",event:"filewatch_failed_fallback_polling"});let timer2=setInterval(async()=>{if(!running2)return;try{let files=await discoverAllJsonlFiles2(),workerMap=await buildWorkerMap2(captureSql);for(let f of files)await ingestFileFull2(captureSql,f.sessionId,f.jsonlPath,f.projectPath,0,{parentSessionId:f.parentSessionId,isSubagent:f.isSubagent,workerMap})}catch{}},cfg.heartbeatIntervalMs);return startBackfill2(captureSql).catch((err)=>{let msg=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"backfill_error",error:msg})}),timer2}return startBackfill2(captureSql).catch((err)=>{let msg=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"backfill_error",error:msg})}),null}catch(err){let message=err instanceof Error?err.message:String(err);return d.log({timestamp:d.now().toISOString(),level:"warn",event:"session_capture_init_failed",error:message}),null}}async function runHeartbeat(d){if(!running2)return;try{await collectHeartbeats(d),await collectMachineSnapshot(d),await emitWorkerEvents(d);try{let retSql=await d.getConnection();await retSql`DELETE FROM heartbeats WHERE created_at < now() - interval '7 days'`,await retSql`DELETE FROM machine_snapshots WHERE created_at < now() - interval '30 days'`,await retSql`DELETE FROM audit_events WHERE entity_type LIKE 'otel_%' AND created_at < now() - interval '30 days'`}catch{}}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"heartbeat_error",error:message})}}let done=(async()=>{deps.log({timestamp:deps.now().toISOString(),level:"info",event:"daemon_started",daemon_id:daemonId,max_concurrent:config.maxConcurrent,poll_interval_ms:config.pollIntervalMs});try{await recoverOnStartup(deps,daemonId,config)}catch(err){let message=err instanceof Error?err.message:String(err);deps.log({timestamp:deps.now().toISOString(),level:"error",event:"recovery_error",error:message})}listenConnection=await setupListenNotify(deps,processTriggers),heartbeatTimer=setInterval(()=>runHeartbeat(deps),config.heartbeatIntervalMs),orphanTimer=startOrphanTimer(deps,config),inboxWatcherHandle=startInboxWatcherIfEnabled(deps),eventRouterHandle=await startEventRouterSafe(deps),captureFallbackTimer=await initSessionCapture(deps,config),await processTriggers();while(running2){if(await new Promise((resolve5)=>{pollResolve=resolve5,pollTimeout=setTimeout(resolve5,config.pollIntervalMs)}),pollResolve=null,!running2)break;await processTriggers()}deps.log({timestamp:deps.now().toISOString(),level:"info",event:"daemon_stopped",daemon_id:daemonId})})();return{stop,done,daemonId}}var RESUME_COOLDOWN_MS=60000,DEFAULT_MAX_RESUME_ATTEMPTS=3,previousWorkerStates;var init_scheduler_daemon=__esm(()=>{init_cron();init_event_router();init_inbox_watcher();init_run_spec();previousWorkerStates=new Map});var exports_serve={};__export(exports_serve,{registerServeCommands:()=>registerServeCommands,isTuiSessionReady:()=>isTuiSessionReady,isServeRunning:()=>isServeRunning,ensureTuiSession:()=>ensureTuiSession,autoStartServe:()=>autoStartServe});import{execSync as execSync6,spawn as spawn3}from"child_process";import{existsSync as existsSync24,mkdirSync as mkdirSync8,readFileSync as readFileSync11,unlinkSync as unlinkSync5,writeFileSync as writeFileSync8}from"fs";import{homedir as homedir20}from"os";import{join as join31}from"path";function genieHome(){return process.env.GENIE_HOME??join31(homedir20(),".genie")}function servePidPath(){return join31(genieHome(),"serve.pid")}function genieTmuxConf(){return[join31(genieHome(),"tmux.conf")].find((p)=>existsSync24(p))??"/dev/null"}function readServePid(){let path2=servePidPath();if(!existsSync24(path2))return null;let raw=readFileSync11(path2,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function writeServePid(pid){mkdirSync8(genieHome(),{recursive:!0}),writeFileSync8(servePidPath(),String(pid),"utf-8")}function removeServePid(){let path2=servePidPath();if(existsSync24(path2))try{unlinkSync5(path2)}catch{}}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function genieTmux(subcmd){return`tmux -L ${GENIE_SOCKET} -f ${genieTmuxConf()} ${subcmd}`}function tuiTmux(subcmd){return`tmux ${subcmd}`}function isGenieTmuxRunning(){try{return execSync6(genieTmux("list-sessions"),{stdio:"ignore"}),!0}catch{return!1}}function applyTuiStyle(){let cmds=[`set-option -t ${TUI_SESSION} pane-border-style 'fg=${TUI_STYLE.inactiveBorder}'`,`set-option -t ${TUI_SESSION} pane-active-border-style 'fg=${TUI_STYLE.activeBorder}'`,`set-option -t ${TUI_SESSION} mouse off`,`set-option -t ${TUI_SESSION} status off`,`set-option -t ${TUI_SESSION} pane-border-status off`];for(let cmd of cmds)try{execSync6(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function setupTuiKeybindings(){let bindings=[`bind-key -T ${KEY_TABLE} Tab if-shell "[ '#{pane_index}' = '0' ]" "select-pane -t ${TUI_SESSION}:0.1" "select-pane -t ${TUI_SESSION}:0.0" \\; switch-client -T ${KEY_TABLE}`,`bind-key -T ${KEY_TABLE} C-b if-shell "[ $(tmux display-message -p '#\\{pane_width\\}' -t ${TUI_SESSION}:0.0) -gt 5 ]" "resize-pane -t ${TUI_SESSION}:0.0 -x 0" "resize-pane -t ${TUI_SESSION}:0.0 -x ${NAV_WIDTH}" \\; switch-client -T ${KEY_TABLE}`,`bind-key -T ${KEY_TABLE} C-q detach-client`,`bind-key -T ${KEY_TABLE} 'C-\\\\' detach-client`,`set-hook -t ${TUI_SESSION} client-session-changed "switch-client -T ${KEY_TABLE}"`];for(let cmd of bindings)try{execSync6(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function startTuiTmuxServer(){try{execSync6(tuiTmux(`has-session -t ${TUI_SESSION}`),{stdio:"ignore"});let panes2=execSync6(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
1366
- `);return{leftPane:panes2[0],rightPane:panes2[1]||panes2[0]}}catch{}let cols=120;execSync6(tuiTmux(`new-session -d -s ${TUI_SESSION} -x ${cols} -y ${40} -e GENIE_TUI_PANE=left`),{stdio:"ignore"}),execSync6(tuiTmux(`split-window -h -t ${TUI_SESSION}:0 -l ${cols-NAV_WIDTH-1}`),{stdio:"ignore"});let panes=execSync6(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
1365
+ `,deps.log({timestamp:now.toISOString(),level:"debug",event:"machine_snapshot",active_workers:activeWorkers,active_teams:teams.size,tmux_sessions:tmuxSessions,cpu_percent:cpuPercent,memory_mb:memoryMb})}async function emitWorkerEvents(deps){let workers=await deps.listWorkers(),now=deps.now().toISOString(),currentIds=new Set;for(let worker of workers){currentIds.add(worker.id);let prev=previousWorkerStates.get(worker.id),repoPath=worker.repoPath??process.cwd();if(!prev)await deps.publishEvent(`genie.agent.${worker.id}.spawned`,{timestamp:now,kind:"state",agent:worker.id,team:worker.team,text:`Agent ${worker.id} spawned`,data:{state:worker.state},source:"registry"},repoPath);else if(prev.state!==worker.state){if(await deps.publishEvent(`genie.agent.${worker.id}.state`,{timestamp:now,kind:"state",agent:worker.id,team:worker.team,text:`Agent ${worker.id} state: ${prev.state} \u2192 ${worker.state}`,data:{previousState:prev.state,state:worker.state},source:"registry"},repoPath),worker.state==="done"&&worker.wishSlug&&worker.groupNumber!=null)await deps.publishEvent(`genie.wish.${worker.wishSlug}.group.${worker.groupNumber}.done`,{timestamp:now,kind:"system",agent:worker.id,team:worker.team,text:`Wish ${worker.wishSlug} group ${worker.groupNumber} completed by ${worker.id}`,data:{wishSlug:worker.wishSlug,groupNumber:worker.groupNumber},source:"registry"},repoPath)}previousWorkerStates.set(worker.id,{...worker})}for(let[id,prev]of previousWorkerStates)if(!currentIds.has(id))await deps.publishEvent(`genie.agent.${id}.killed`,{timestamp:now,kind:"state",agent:id,team:prev.team,text:`Agent ${id} killed`,data:{lastState:prev.state},source:"registry"},prev.repoPath??process.cwd()),previousWorkerStates.delete(id)}function _resetWorkerStatesForTesting(){previousWorkerStates.clear()}function startInboxWatcherIfEnabled(deps){let pollMs=getInboxPollIntervalMs();if(pollMs===0)return deps.log({timestamp:deps.now().toISOString(),level:"info",event:"inbox_watcher_disabled"}),null;return deps.log({timestamp:deps.now().toISOString(),level:"info",event:"inbox_watcher_started",poll_interval_ms:pollMs}),startInboxWatcher()}function startDaemon(configOverrides,depsOverrides){let config=resolveConfig(configOverrides),deps={...createDefaultDeps(),...depsOverrides},daemonId=deps.generateId(),running2=!0,pollTimeout=null,pollResolve=null,listenConnection=null,heartbeatTimer=null,orphanTimer=null,inboxWatcherHandle=null,captureFallbackTimer=null,eventRouterHandle=null,stop=()=>{if(running2=!1,pollTimeout)clearTimeout(pollTimeout),pollTimeout=null;if(pollResolve)pollResolve(),pollResolve=null;if(heartbeatTimer)clearInterval(heartbeatTimer),heartbeatTimer=null;if(orphanTimer)clearInterval(orphanTimer),orphanTimer=null;if(inboxWatcherHandle)stopInboxWatcher(inboxWatcherHandle),inboxWatcherHandle=null;if(listenConnection)listenConnection.end().catch(()=>{}),listenConnection=null;if(captureFallbackTimer)clearInterval(captureFallbackTimer),captureFallbackTimer=null;eventRouterHandle?.stop().catch(()=>{}),eventRouterHandle=null,Promise.resolve().then(() => (init_session_filewatch(),exports_session_filewatch)).then((m)=>m.stopFilewatch()).catch(()=>{}),Promise.resolve().then(() => (init_session_backfill(),exports_session_backfill)).then((m)=>m.stopBackfill()).catch(()=>{}),Promise.resolve().then(() => (init_db(),exports_db)).then(({getLockfilePath:getLockfilePath2})=>{try{__require("fs").unlinkSync(getLockfilePath2())}catch{}}).catch(()=>{})},processTriggers=async()=>{try{let claimed=await claimDueTriggers(deps,config,daemonId);if(claimed.length===0)return;if(claimed.length>config.jitterThreshold){let jitterMs=deps.jitter(config.maxJitterMs);deps.log({timestamp:deps.now().toISOString(),level:"info",event:"jitter_applied",count:claimed.length,jitter_ms:jitterMs}),await deps.sleep(jitterMs)}for(let trigger of claimed){if(!running2)break;await fireTrigger(deps,trigger,daemonId)}}catch(err){let message=err instanceof Error?err.message:String(err);deps.log({timestamp:deps.now().toISOString(),level:"error",event:"process_cycle_error",error:message})}};async function setupListenNotify(d,onTrigger){try{let sql=await d.getConnection();return await sql.listen("genie_trigger_due",async()=>{if(!running2)return;await onTrigger()}),d.log({timestamp:d.now().toISOString(),level:"info",event:"listen_started",channel:"genie_trigger_due"}),sql}catch(err){let message=err instanceof Error?err.message:String(err);return d.log({timestamp:d.now().toISOString(),level:"warn",event:"listen_failed",error:message}),null}}function startOrphanTimer(d,cfg){return setInterval(async()=>{if(!running2)return;try{await reconcileOrphans(d,cfg)}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"orphan_reconciliation_error",error:message})}},cfg.orphanCheckIntervalMs)}async function startEventRouterSafe(d){try{let handle=await startEventRouter();return d.log({timestamp:d.now().toISOString(),level:"info",event:"event_router_started"}),handle}catch(err){let message=err instanceof Error?err.message:String(err);return d.log({timestamp:d.now().toISOString(),level:"warn",event:"event_router_start_failed",error:message}),null}}async function initSessionCapture(d,cfg){try{let captureSql=await d.getConnection(),{startFilewatch:startFilewatch2}=await Promise.resolve().then(() => (init_session_filewatch(),exports_session_filewatch)),{startBackfill:startBackfill2}=await Promise.resolve().then(() => (init_session_backfill(),exports_session_backfill));if(!await startFilewatch2(captureSql)){let{ingestFileFull:ingestFileFull2,discoverAllJsonlFiles:discoverAllJsonlFiles2,buildWorkerMap:buildWorkerMap2}=await Promise.resolve().then(() => (init_session_capture(),exports_session_capture));d.log({timestamp:d.now().toISOString(),level:"warn",event:"filewatch_failed_fallback_polling"});let timer2=setInterval(async()=>{if(!running2)return;try{let files=await discoverAllJsonlFiles2(),workerMap=await buildWorkerMap2(captureSql);for(let f of files)await ingestFileFull2(captureSql,f.sessionId,f.jsonlPath,f.projectPath,0,{parentSessionId:f.parentSessionId,isSubagent:f.isSubagent,workerMap})}catch{}},cfg.heartbeatIntervalMs);return startBackfill2(captureSql).catch((err)=>{let msg=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"backfill_error",error:msg})}),timer2}return startBackfill2(captureSql).catch((err)=>{let msg=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"backfill_error",error:msg})}),null}catch(err){let message=err instanceof Error?err.message:String(err);return d.log({timestamp:d.now().toISOString(),level:"warn",event:"session_capture_init_failed",error:message}),null}}async function runHeartbeat(d){if(!running2)return;try{await collectHeartbeats(d),await collectMachineSnapshot(d),await emitWorkerEvents(d);try{let retSql=await d.getConnection();await retSql`DELETE FROM heartbeats WHERE created_at < now() - interval '7 days'`,await retSql`DELETE FROM machine_snapshots WHERE created_at < now() - interval '30 days'`,await retSql`DELETE FROM audit_events WHERE entity_type LIKE 'otel_%' AND created_at < now() - interval '30 days'`}catch{}}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"heartbeat_error",error:message})}}let done=(async()=>{deps.log({timestamp:deps.now().toISOString(),level:"info",event:"daemon_started",daemon_id:daemonId,max_concurrent:config.maxConcurrent,poll_interval_ms:config.pollIntervalMs});try{await recoverOnStartup(deps,daemonId,config)}catch(err){let message=err instanceof Error?err.message:String(err);deps.log({timestamp:deps.now().toISOString(),level:"error",event:"recovery_error",error:message})}listenConnection=await setupListenNotify(deps,processTriggers),heartbeatTimer=setInterval(()=>runHeartbeat(deps),config.heartbeatIntervalMs),orphanTimer=startOrphanTimer(deps,config),inboxWatcherHandle=startInboxWatcherIfEnabled(deps),eventRouterHandle=await startEventRouterSafe(deps),captureFallbackTimer=await initSessionCapture(deps,config),await processTriggers();while(running2){if(await new Promise((resolve5)=>{pollResolve=resolve5,pollTimeout=setTimeout(resolve5,config.pollIntervalMs)}),pollResolve=null,!running2)break;await processTriggers()}deps.log({timestamp:deps.now().toISOString(),level:"info",event:"daemon_stopped",daemon_id:daemonId})})();return{stop,done,daemonId}}var RESUME_COOLDOWN_MS=60000,DEFAULT_MAX_RESUME_ATTEMPTS=3,previousWorkerStates;var init_scheduler_daemon=__esm(()=>{init_cron();init_event_router();init_inbox_watcher();init_run_spec();previousWorkerStates=new Map});var exports_serve={};__export(exports_serve,{registerServeCommands:()=>registerServeCommands,isTuiSessionReady:()=>isTuiSessionReady,isServeRunning:()=>isServeRunning,ensureTuiSession:()=>ensureTuiSession,autoStartServe:()=>autoStartServe});import{execSync as execSync6,spawn as spawn3}from"child_process";import{existsSync as existsSync24,mkdirSync as mkdirSync8,readFileSync as readFileSync11,unlinkSync as unlinkSync5,writeFileSync as writeFileSync8}from"fs";import{homedir as homedir20}from"os";import{join as join31}from"path";function genieHome(){return process.env.GENIE_HOME??join31(homedir20(),".genie")}function servePidPath(){return join31(genieHome(),"serve.pid")}function genieTmuxConf(){return[join31(genieHome(),"tmux.conf")].find((p)=>existsSync24(p))??"/dev/null"}function readServePid(){let path2=servePidPath();if(!existsSync24(path2))return null;let raw=readFileSync11(path2,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function writeServePid(pid){mkdirSync8(genieHome(),{recursive:!0}),writeFileSync8(servePidPath(),String(pid),"utf-8")}function removeServePid(){let path2=servePidPath();if(existsSync24(path2))try{unlinkSync5(path2)}catch{}}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function genieTmux(subcmd){return`tmux -L ${GENIE_SOCKET} -f ${genieTmuxConf()} ${subcmd}`}function tuiTmuxConf(){return[join31(genieHome(),"tui-tmux.conf")].find((p)=>existsSync24(p))??"/dev/null"}function tuiTmux(subcmd){return`tmux -L genie-tui -f ${tuiTmuxConf()} ${subcmd}`}function isGenieTmuxRunning(){try{return execSync6(genieTmux("list-sessions"),{stdio:"ignore"}),!0}catch{return!1}}function applyTuiStyle(){let cmds=[`set-option -t ${TUI_SESSION} pane-border-style 'fg=${TUI_STYLE.inactiveBorder}'`,`set-option -t ${TUI_SESSION} pane-active-border-style 'fg=${TUI_STYLE.activeBorder}'`,`set-option -t ${TUI_SESSION} mouse on`,`set-option -t ${TUI_SESSION} status off`,`set-option -t ${TUI_SESSION} pane-border-status off`];for(let cmd of cmds)try{execSync6(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function setupTuiKeybindings(){let bindings=[`bind-key -T root Tab if-shell "[ '#{pane_index}' = '0' ]" "select-pane -t ${TUI_SESSION}:0.1" "select-pane -t ${TUI_SESSION}:0.0"`,`bind-key -T root C-b if-shell "[ $(tmux display-message -p '#\\{pane_width\\}' -t ${TUI_SESSION}:0.0) -gt 5 ]" "resize-pane -t ${TUI_SESSION}:0.0 -x 0" "resize-pane -t ${TUI_SESSION}:0.0 -x ${NAV_WIDTH}"`,`bind-key -T root C-t send-keys -t ${TUI_SESSION}:0.1 C-b c`,"bind-key -T root C-q detach-client"];for(let cmd of bindings)try{execSync6(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function startTuiTmuxServer(){try{execSync6(tuiTmux(`has-session -t ${TUI_SESSION}`),{stdio:"ignore"});let panes2=execSync6(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
1366
+ `);return{leftPane:panes2[0],rightPane:panes2[1]||panes2[0]}}catch{}let cols=120;execSync6(tuiTmux(`new-session -d -s ${TUI_SESSION} -x ${cols} -y ${40}`),{stdio:"ignore"}),execSync6(tuiTmux(`split-window -h -t ${TUI_SESSION}:0 -l ${cols-NAV_WIDTH-1}`),{stdio:"ignore"});let panes=execSync6(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
1367
1367
  `);applyTuiStyle(),setupTuiKeybindings();try{execSync6(tuiTmux(`select-pane -t ${panes[0]}`),{stdio:"ignore"})}catch{}return{leftPane:panes[0],rightPane:panes[1]||panes[0]}}function sendTuiLaunchScript(leftPane,rightPane,workspaceRoot){let home=genieHome(),bunPath=process.execPath||"bun",genieBin=process.argv[1]||"genie",scriptPath=join31(home,"tui-launch.sh"),envVars=["GENIE_TUI_PANE=left",`GENIE_TUI_RIGHT=${rightPane}`];if(workspaceRoot)envVars.push(`GENIE_TUI_WORKSPACE=${workspaceRoot}`);let content=`#!/bin/sh
1368
1368
  export ${envVars.join(`
1369
1369
  export `)}
1370
1370
  exec ${bunPath} ${genieBin}
1371
- `;writeFileSync8(scriptPath,content,{mode:493});try{execSync6(tuiTmux(`send-keys -t '${leftPane}' '${scriptPath}' Enter`),{stdio:"ignore"})}catch{}}function killTuiSession(){try{execSync6(`tmux kill-session -t ${TUI_SESSION} 2>/dev/null`,{stdio:"ignore"})}catch{}}function listAgentSessions(){try{return execSync6(genieTmux("list-sessions -F '#{session_name}'"),{encoding:"utf-8"}).trim().split(`
1372
- `).filter(Boolean)}catch{return[]}}function isServeRunning(){let pid=readServePid();return pid!==null&&isProcessAlive(pid)}async function autoStartServe(){if(isServeRunning())return;let bunPath=process.execPath??"bun",genieBin=process.argv[1]??"genie",{spawn:spawnChild}=await import("child_process");spawnChild(bunPath,[genieBin,"serve","--foreground"],{detached:!0,stdio:"ignore",env:{...process.env,GENIE_IS_DAEMON:"1"}}).unref();let deadline=Date.now()+15000;while(Date.now()<deadline)if(await new Promise((resolve5)=>setTimeout(resolve5,500)),isServeRunning()&&isTuiSessionReady())return;if(!isServeRunning())throw Error("genie serve failed to start within 15s. Run `genie serve` manually.")}function isTuiSessionReady(){try{return execSync6(`tmux has-session -t ${TUI_SESSION} 2>/dev/null`,{stdio:"ignore"}),!0}catch{return!1}}function ensureTuiSession(workspaceRoot){if(isTuiSessionReady())return;let{leftPane,rightPane}=startTuiTmuxServer();sendTuiLaunchScript(leftPane,rightPane,workspaceRoot)}async function startAgentSync(){try{let{findWorkspace:findWorkspace2}=(init_workspace(),__toCommonJS(exports_workspace)),ws=findWorkspace2();if(!ws)return null;let{syncAgentDirectory:syncAgentDirectory2,watchAgentDirectory:watchAgentDirectory2}=await Promise.resolve().then(() => (init_agent_sync(),exports_agent_sync)),syncResult=await syncAgentDirectory2(ws.root);if(syncResult.registered.length+syncResult.updated.length>0)console.log(` Agent sync: ${syncResult.registered.length} registered, ${syncResult.updated.length} updated`);let watcher2=watchAgentDirectory2(ws.root,{onSync:(name,action)=>{console.log(` [agent-watcher] ${name}: ${action}`)}});if(watcher2)console.log(" Agent watcher started (watching agents/ directory)");return watcher2}catch(err){let msg=err instanceof Error?err.message:String(err);return console.error(` Agent sync failed: ${msg}`),null}}async function startForeground(){let existingPid=readServePid();if(existingPid&&isProcessAlive(existingPid))console.log(`genie serve already running (PID ${existingPid})`),process.exit(0);if(existingPid)removeServePid();process.env.GENIE_IS_DAEMON="1",writeServePid(process.pid),console.log(`genie serve starting (PID ${process.pid})`),console.log(" Starting pgserve...");try{let{ensurePgserve:ensurePgserve2}=await Promise.resolve().then(() => (init_db(),exports_db)),port=await ensurePgserve2();console.log(` pgserve ready on port ${port}`)}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(` pgserve failed: ${msg}`)}let sessions=listAgentSessions();if(sessions.length>0)console.log(` Agent server (-L ${GENIE_SOCKET}): ${sessions.length} sessions`);else console.log(` Agent server (-L ${GENIE_SOCKET}): no sessions yet (created on first spawn)`);handles.agentWatcher=await startAgentSync(),console.log(" Setting up TUI session...");let{leftPane,rightPane}=startTuiTmuxServer(),ws=(()=>{try{let{findWorkspace:findWorkspace2}=(init_workspace(),__toCommonJS(exports_workspace));return findWorkspace2()}catch{return null}})();sendTuiLaunchScript(leftPane,rightPane,ws?.root),console.log(" TUI server ready (session: genie-tui)"),console.log(" Starting scheduler daemon...");try{let{startDaemon:startDaemon2}=await Promise.resolve().then(() => (init_scheduler_daemon(),exports_scheduler_daemon));handles.schedulerHandle=startDaemon2(),console.log(" Scheduler started (includes event-router + inbox-watcher)")}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(` Scheduler failed: ${msg}`)}console.log(`
1371
+ `;writeFileSync8(scriptPath,content,{mode:493});try{execSync6(tuiTmux(`send-keys -t '${leftPane}' '${scriptPath}' Enter`),{stdio:"ignore"})}catch{}}function killTuiSession(){try{execSync6(tuiTmux("kill-server"),{stdio:"ignore"})}catch{}}function listAgentSessions(){try{return execSync6(genieTmux("list-sessions -F '#{session_name}'"),{encoding:"utf-8"}).trim().split(`
1372
+ `).filter(Boolean)}catch{return[]}}function isServeRunning(){let pid=readServePid();return pid!==null&&isProcessAlive(pid)}async function autoStartServe(){if(isServeRunning())return;let bunPath=process.execPath??"bun",genieBin=process.argv[1]??"genie",{spawn:spawnChild}=await import("child_process");spawnChild(bunPath,[genieBin,"serve","--foreground"],{detached:!0,stdio:"ignore",env:{...process.env,GENIE_IS_DAEMON:"1"}}).unref();let deadline=Date.now()+15000;while(Date.now()<deadline)if(await new Promise((resolve5)=>setTimeout(resolve5,500)),isServeRunning()&&isTuiSessionReady())return;if(!isServeRunning())throw Error("genie serve failed to start within 15s. Run `genie serve` manually.")}function isTuiSessionReady(){try{return execSync6(tuiTmux(`has-session -t ${TUI_SESSION}`),{stdio:"ignore"}),!0}catch{return!1}}function ensureTuiSession(workspaceRoot){if(isTuiSessionReady())return;let{leftPane,rightPane}=startTuiTmuxServer();sendTuiLaunchScript(leftPane,rightPane,workspaceRoot)}async function startAgentSync(){try{let{findWorkspace:findWorkspace2}=(init_workspace(),__toCommonJS(exports_workspace)),ws=findWorkspace2();if(!ws)return null;let{syncAgentDirectory:syncAgentDirectory2,watchAgentDirectory:watchAgentDirectory2}=await Promise.resolve().then(() => (init_agent_sync(),exports_agent_sync)),syncResult=await syncAgentDirectory2(ws.root);if(syncResult.registered.length+syncResult.updated.length>0)console.log(` Agent sync: ${syncResult.registered.length} registered, ${syncResult.updated.length} updated`);let watcher2=watchAgentDirectory2(ws.root,{onSync:(name,action)=>{console.log(` [agent-watcher] ${name}: ${action}`)}});if(watcher2)console.log(" Agent watcher started (watching agents/ directory)");return watcher2}catch(err){let msg=err instanceof Error?err.message:String(err);return console.error(` Agent sync failed: ${msg}`),null}}async function startForeground(){let existingPid=readServePid();if(existingPid&&isProcessAlive(existingPid))console.log(`genie serve already running (PID ${existingPid})`),process.exit(0);if(existingPid)removeServePid();process.env.GENIE_IS_DAEMON="1",writeServePid(process.pid),console.log(`genie serve starting (PID ${process.pid})`),console.log(" Starting pgserve...");try{let{ensurePgserve:ensurePgserve2}=await Promise.resolve().then(() => (init_db(),exports_db)),port=await ensurePgserve2();console.log(` pgserve ready on port ${port}`)}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(` pgserve failed: ${msg}`)}let sessions=listAgentSessions();if(sessions.length>0)console.log(` Agent server (-L ${GENIE_SOCKET}): ${sessions.length} sessions`);else console.log(` Agent server (-L ${GENIE_SOCKET}): no sessions yet (created on first spawn)`);handles.agentWatcher=await startAgentSync(),console.log(" Setting up TUI session...");let{leftPane,rightPane}=startTuiTmuxServer(),ws=(()=>{try{let{findWorkspace:findWorkspace2}=(init_workspace(),__toCommonJS(exports_workspace));return findWorkspace2()}catch{return null}})();sendTuiLaunchScript(leftPane,rightPane,ws?.root),console.log(" TUI server ready (session: genie-tui)"),console.log(" Starting scheduler daemon...");try{let{startDaemon:startDaemon2}=await Promise.resolve().then(() => (init_scheduler_daemon(),exports_scheduler_daemon));handles.schedulerHandle=startDaemon2(),console.log(" Scheduler started (includes event-router + inbox-watcher)")}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(` Scheduler failed: ${msg}`)}console.log(`
1373
1373
  genie serve is running. Press Ctrl+C to stop.`);let shutdown2=()=>{console.log(`
1374
- Shutting down genie serve...`),handles.agentWatcher?.close(),handles.schedulerHandle?.stop(),killTuiSession(),removeServePid(),console.log("genie serve stopped.")};if(process.on("SIGTERM",()=>{shutdown2(),process.exit(143)}),process.on("SIGINT",()=>{shutdown2(),process.exit(130)}),handles.schedulerHandle)await handles.schedulerHandle.done;else await new Promise(()=>{});removeServePid()}async function startBackground(){let existingPid=readServePid();if(existingPid&&isProcessAlive(existingPid))console.log(`genie serve already running (PID ${existingPid})`),process.exit(0);if(existingPid)removeServePid();let bunPath=process.execPath??"bun",genieBin=process.argv[1]??"genie",child=spawn3(bunPath,[genieBin,"serve","--foreground"],{detached:!0,stdio:"ignore",env:{...process.env,GENIE_IS_DAEMON:"1"}});if(child.unref(),child.pid)if(await new Promise((resolve5)=>setTimeout(resolve5,1000)),isProcessAlive(child.pid))console.log(`genie serve started (PID ${child.pid})`);else console.error("Error: genie serve exited immediately."),process.exit(1);else console.error("Error: failed to spawn genie serve"),process.exit(1)}async function stopServe(){let pid=readServePid();if(!pid){console.log("genie serve is not running (no PID file).");return}if(!isProcessAlive(pid)){console.log(`Stale PID file (PID ${pid} not running). Cleaning up.`),removeServePid(),killTuiSession();return}console.log(`Stopping genie serve (PID ${pid})...`);try{process.kill(-pid,"SIGTERM")}catch{try{process.kill(pid,"SIGTERM")}catch{}}let deadline=Date.now()+1e4;while(Date.now()<deadline&&isProcessAlive(pid))await new Promise((resolve5)=>setTimeout(resolve5,250));if(isProcessAlive(pid)){console.log("Did not stop within 10s. Sending SIGKILL.");try{process.kill(-pid,"SIGKILL")}catch{try{process.kill(pid,"SIGKILL")}catch{}}}killTuiSession(),removeServePid(),console.log("genie serve stopped.")}async function printPgserveStatus(){try{let{isAvailable:isAvailable2,getActivePort:getActivePort2}=await Promise.resolve().then(() => (init_db(),exports_db)),dbOk=await isAvailable2();console.log(` pgserve: ${dbOk?`healthy (port ${getActivePort2()})`:"unreachable"}`)}catch{console.log(" pgserve: unavailable")}}function printTmuxStatus(){let agentRunning=isGenieTmuxRunning(),sessions=agentRunning?listAgentSessions():[];if(console.log(` tmux -L ${GENIE_SOCKET}: ${agentRunning?`running (${sessions.length} sessions)`:"stopped"}`),sessions.length>0)console.log(` ${sessions.join(", ")}`);let tuiReady=isTuiSessionReady();console.log(` TUI session: ${tuiReady?"running":"stopped"}`)}async function printDaemonStatus(serveRunning){try{let schedulerPidPath=join31(genieHome(),"scheduler.pid");if(existsSync24(schedulerPidPath)){let sPid=Number.parseInt(readFileSync11(schedulerPidPath,"utf-8").trim(),10),sAlive=!Number.isNaN(sPid)&&isProcessAlive(sPid);console.log(` scheduler: ${sAlive?`running (PID ${sPid})`:"stopped"}`)}else if(serveRunning)console.log(" scheduler: integrated (in-process)");else console.log(" scheduler: stopped")}catch{console.log(" scheduler: unknown")}try{let{getInboxPollIntervalMs:getInboxPollIntervalMs2}=await Promise.resolve().then(() => (init_inbox_watcher(),exports_inbox_watcher)),pollMs=getInboxPollIntervalMs2();if(pollMs===0)console.log(" inbox: disabled");else console.log(` inbox: ${serveRunning?"watching":"stopped"} (poll ${pollMs/1000}s)`)}catch{console.log(" inbox: unavailable")}}async function statusServe(){let pid=readServePid(),running2=pid!==null&&isProcessAlive(pid);if(console.log(`
1375
- Genie Serve`),console.log("\u2500".repeat(50)),console.log(` Status: ${running2?"running":"stopped"}`),running2&&pid)console.log(` PID: ${pid}`);await printPgserveStatus(),printTmuxStatus(),await printDaemonStatus(running2),console.log(` PID file: ${servePidPath()}`),console.log("")}function registerServeCommands(program2){let serve=program2.command("serve").description("Start all genie infrastructure (pgserve, tmux, scheduler)");serve.command("start",{isDefault:!0}).description("Start genie serve").option("--daemon","Run in background").option("--foreground","Run in foreground (default)").action(async(options)=>{if(options.daemon)await startBackground();else await startForeground()}),serve.command("stop").description("Stop genie serve and all services").action(async()=>{await stopServe()}),serve.command("status").description("Show service health").action(async()=>{await statusServe()})}var GENIE_SOCKET="genie",TUI_SESSION="genie-tui",NAV_WIDTH=30,KEY_TABLE="genie-tui",TUI_STYLE,handles;var init_serve=__esm(()=>{TUI_STYLE={activeBorder:"#7c3aed",inactiveBorder:"#414868"};handles={schedulerHandle:null,agentWatcher:null}});var exports_tmux2={};__export(exports_tmux2,{attachTuiSession:()=>attachTuiSession,attachProjectWindow:()=>attachProjectWindow});import{execSync as execSync7,spawnSync as spawnSync2}from"child_process";function resolveRightPane(rightPane){try{return execSync7(`tmux display-message -t ${rightPane} -p ''`,{stdio:"ignore"}),rightPane}catch{try{let panes=execSync7(`tmux list-panes -t ${SESSION_NAME}:0 -F '#{pane_id}'`,{encoding:"utf-8"}).trim().split(`
1376
- `);return panes[1]||panes[0]}catch{return rightPane}}}function ensureAgentSession(sessionName){let agentTmux=`tmux -L ${GENIE_AGENT_SOCKET}`;try{execSync7(`${agentTmux} has-session -t '${sessionName}' 2>/dev/null`,{stdio:"ignore"})}catch{try{execSync7(`${agentTmux} new-session -d -s '${sessionName}'`,{stdio:"ignore"})}catch{}}}function attachProjectWindow(rightPane,targetSession,windowIndex){if(targetSession===SESSION_NAME)return;let pane=resolveRightPane(rightPane);if(ensureAgentSession(targetSession),windowIndex!==void 0)try{let agentTmux=`tmux -L ${GENIE_AGENT_SOCKET}`;execSync7(`${agentTmux} select-window -t '${targetSession}:${windowIndex}'`,{stdio:"ignore"})}catch{}try{let attachCmd=`tmux -L ${GENIE_AGENT_SOCKET} attach-session -t '${targetSession}'`;execSync7(`tmux respawn-pane -k -t ${pane} "TMUX='' while true; do ${attachCmd} 2>/dev/null; sleep 0.5; done"`,{stdio:"ignore"})}catch{}}function attachTuiSession(){spawnSync2("tmux",["attach-session","-t",SESSION_NAME],{stdio:"inherit"})}var SESSION_NAME="genie-tui",GENIE_AGENT_SOCKET="genie";var init_tmux2=()=>{};function onBridgeEvent(handler){return listeners.add(handler),()=>{listeners.delete(handler)}}function emit2(event){for(let handler of listeners)handler(event)}async function listAgents2(){return(await getConnection())`
1374
+ Shutting down genie serve...`),handles.agentWatcher?.close(),handles.schedulerHandle?.stop(),killTuiSession(),removeServePid(),console.log("genie serve stopped.")};if(process.on("SIGTERM",()=>{shutdown2(),process.exit(143)}),process.on("SIGINT",()=>{shutdown2(),process.exit(130)}),handles.schedulerHandle)await handles.schedulerHandle.done;else await new Promise(()=>{});removeServePid()}async function startBackground(){let existingPid=readServePid();if(existingPid&&isProcessAlive(existingPid))console.log(`genie serve already running (PID ${existingPid})`),process.exit(0);if(existingPid)removeServePid();let bunPath=process.execPath??"bun",genieBin=process.argv[1]??"genie",child=spawn3(bunPath,[genieBin,"serve","--foreground"],{detached:!0,stdio:"ignore",env:{...process.env,GENIE_IS_DAEMON:"1"}});if(child.unref(),child.pid)if(await new Promise((resolve5)=>setTimeout(resolve5,1000)),isProcessAlive(child.pid))console.log(`genie serve started (PID ${child.pid})`);else console.error("Error: genie serve exited immediately."),process.exit(1);else console.error("Error: failed to spawn genie serve"),process.exit(1)}async function stopServe(){let pid=readServePid();if(!pid){console.log("genie serve is not running (no PID file).");return}if(!isProcessAlive(pid)){console.log(`Stale PID file (PID ${pid} not running). Cleaning up.`),removeServePid(),killTuiSession();return}console.log(`Stopping genie serve (PID ${pid})...`);try{process.kill(-pid,"SIGTERM")}catch{try{process.kill(pid,"SIGTERM")}catch{}}let deadline=Date.now()+1e4;while(Date.now()<deadline&&isProcessAlive(pid))await new Promise((resolve5)=>setTimeout(resolve5,250));if(isProcessAlive(pid)){console.log("Did not stop within 10s. Sending SIGKILL.");try{process.kill(-pid,"SIGKILL")}catch{try{process.kill(pid,"SIGKILL")}catch{}}}killTuiSession(),removeServePid(),console.log("genie serve stopped.")}async function printPgserveStatus(){try{let{isAvailable:isAvailable2,getActivePort:getActivePort2}=await Promise.resolve().then(() => (init_db(),exports_db)),dbOk=await isAvailable2();console.log(` pgserve: ${dbOk?`healthy (port ${getActivePort2()})`:"unreachable"}`)}catch{console.log(" pgserve: unavailable")}}function printTmuxStatus(){let agentRunning=isGenieTmuxRunning(),sessions=agentRunning?listAgentSessions():[];if(console.log(` tmux -L ${GENIE_SOCKET}: ${agentRunning?`running (${sessions.length} sessions)`:"stopped"}`),sessions.length>0)console.log(` ${sessions.join(", ")}`);let tuiReady=isTuiSessionReady();console.log(` tmux -L genie-tui: ${tuiReady?"running":"stopped"}`)}async function printDaemonStatus(serveRunning){try{let schedulerPidPath=join31(genieHome(),"scheduler.pid");if(existsSync24(schedulerPidPath)){let sPid=Number.parseInt(readFileSync11(schedulerPidPath,"utf-8").trim(),10),sAlive=!Number.isNaN(sPid)&&isProcessAlive(sPid);console.log(` scheduler: ${sAlive?`running (PID ${sPid})`:"stopped"}`)}else if(serveRunning)console.log(" scheduler: integrated (in-process)");else console.log(" scheduler: stopped")}catch{console.log(" scheduler: unknown")}try{let{getInboxPollIntervalMs:getInboxPollIntervalMs2}=await Promise.resolve().then(() => (init_inbox_watcher(),exports_inbox_watcher)),pollMs=getInboxPollIntervalMs2();if(pollMs===0)console.log(" inbox: disabled");else console.log(` inbox: ${serveRunning?"watching":"stopped"} (poll ${pollMs/1000}s)`)}catch{console.log(" inbox: unavailable")}}async function statusServe(){let pid=readServePid(),running2=pid!==null&&isProcessAlive(pid);if(console.log(`
1375
+ Genie Serve`),console.log("\u2500".repeat(50)),console.log(` Status: ${running2?"running":"stopped"}`),running2&&pid)console.log(` PID: ${pid}`);await printPgserveStatus(),printTmuxStatus(),await printDaemonStatus(running2),console.log(` PID file: ${servePidPath()}`),console.log("")}function registerServeCommands(program2){let serve=program2.command("serve").description("Start all genie infrastructure (pgserve, tmux, scheduler)");serve.command("start",{isDefault:!0}).description("Start genie serve").option("--daemon","Run in background").option("--foreground","Run in foreground (default)").action(async(options)=>{if(options.daemon)await startBackground();else await startForeground()}),serve.command("stop").description("Stop genie serve and all services").action(async()=>{await stopServe()}),serve.command("status").description("Show service health").action(async()=>{await statusServe()})}var GENIE_SOCKET="genie",TUI_SESSION="genie-tui",NAV_WIDTH=30,TUI_STYLE,handles;var init_serve=__esm(()=>{TUI_STYLE={activeBorder:"#7c3aed",inactiveBorder:"#414868"};handles={schedulerHandle:null,agentWatcher:null}});var exports_tmux2={};__export(exports_tmux2,{attachTuiSession:()=>attachTuiSession,attachProjectWindow:()=>attachProjectWindow});import{execSync as execSync7,spawnSync as spawnSync2}from"child_process";function resolveRightPane(rightPane){try{return execSync7(`${TMUX} display-message -t ${rightPane} -p ''`,{stdio:"ignore"}),rightPane}catch{try{let panes=execSync7(`${TMUX} list-panes -t ${SESSION_NAME}:0 -F '#{pane_id}'`,{encoding:"utf-8"}).trim().split(`
1376
+ `);return panes[1]||panes[0]}catch{return rightPane}}}function attachProjectWindow(rightPane,targetSession,windowIndex){if(targetSession===SESSION_NAME)return;let pane=resolveRightPane(rightPane),agentTmux=`tmux -L ${GENIE_AGENT_SOCKET}`;try{execSync7(`${agentTmux} has-session -t '${targetSession}' 2>/dev/null`,{stdio:"ignore"})}catch{return}if(windowIndex!==void 0)try{execSync7(`${agentTmux} select-window -t '${targetSession}:${windowIndex}'`,{stdio:"ignore"})}catch{}try{execSync7(`${agentTmux} set-option -t '${targetSession}' status off 2>/dev/null`,{stdio:"ignore"})}catch{}try{let cmd=`while true; do TMUX='' ${agentTmux} attach-session -t '${targetSession}' 2>/dev/null; sleep 0.3; done`;execSync7(`${TMUX} respawn-pane -k -t ${pane} "bash -c '${cmd}'"`,{stdio:"ignore"})}catch{}try{execSync7(`${TMUX} select-pane -t ${SESSION_NAME}:0.0`,{stdio:"ignore"})}catch{}}function attachTuiSession(){spawnSync2("tmux",["-L",TMUX_SOCKET,"-f",TUI_TMUX_CONF,"attach-session","-t",SESSION_NAME],{stdio:"inherit"})}var SESSION_NAME="genie-tui",TMUX_SOCKET="genie-tui",GENIE_AGENT_SOCKET="genie",TUI_TMUX_CONF,TMUX;var init_tmux2=__esm(()=>{TUI_TMUX_CONF=(()=>{let{existsSync:existsSync25}=__require("fs"),tuiConf=`${process.env.GENIE_HOME??`${process.env.HOME}/.genie`}/tui-tmux.conf`;return existsSync25(tuiConf)?tuiConf:"/dev/null"})(),TMUX=`tmux -L ${TMUX_SOCKET} -f ${TUI_TMUX_CONF}`});function onBridgeEvent(handler){return listeners.add(handler),()=>{listeners.delete(handler)}}function emit2(event){for(let handler of listeners)handler(event)}async function listAgents2(){return(await getConnection())`
1377
1377
  SELECT a.id, a.custom_name, a.role, a.team, a.title, a.state,
1378
1378
  a.reports_to, a.current_executor_id, a.started_at
1379
1379
  FROM agents a
@@ -1468,7 +1468,7 @@ Genie Serve`),console.log("\u2500".repeat(50)),console.log(` Status: ${runn
1468
1468
  `]);return{agents:{online:Number(agentRows[0].online),total:Number(agentRows[0].total)},tasks:{active:Number(taskRows[0].active),backlog:Number(taskRows[0].backlog),done:Number(taskRows[0].done),total:Number(taskRows[0].total)},teams:{active:Number(teamRows[0].active),total:Number(teamRows[0].total)}}}async function startListening(){if(listenersActive)return;listenersActive=!0;let sql=await getConnection(),executorListener=await sql.listen("genie_executor_state",(payload)=>{try{emit2({type:"executor-state-changed",payload:JSON.parse(payload)})}catch{emit2({type:"executor-state-changed",payload:{raw:payload}})}}),taskListener=await sql.listen("genie_task_stage",(payload)=>{try{emit2({type:"task-stage-changed",payload:JSON.parse(payload)})}catch{emit2({type:"task-stage-changed",payload:{raw:payload}})}}),eventListener=await sql.listen("genie_runtime_event",(payload)=>{try{emit2({type:"runtime-event",payload:JSON.parse(payload)})}catch{emit2({type:"runtime-event",payload:{raw:payload}})}});stopFns=[()=>executorListener.unlisten(),()=>taskListener.unlisten(),()=>eventListener.unlisten()]}async function stopListening(){listenersActive=!1,await Promise.allSettled(stopFns.map((fn)=>fn())),stopFns=[]}var listeners,listenersActive=!1,stopFns;var init_pg_bridge=__esm(()=>{init_db();listeners=new Set;stopFns=[]});import{randomUUID as randomUUID6}from"crypto";function onPtyData(cb){dataCallback=cb}function onPtyExit(cb){exitCallback=cb}async function spawnForAgent(agentName,opts={}){let{buildClaudeCommand:buildClaudeCommand3}=await Promise.resolve().then(() => (init_provider_adapters(),exports_provider_adapters)),{createExecutor:createExecutor2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),{findOrCreateAgent:findOrCreateAgent2,setCurrentExecutor:setCurrentExecutor2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),cols=opts.cols??120,rows=opts.rows??40,cwd=opts.cwd??process.cwd(),launch=buildClaudeCommand3({provider:"claude",team:"app",role:"engineer",name:agentName}),agent=await findOrCreateAgent2(agentName,"app","engineer"),executor=await createExecutor2(agent.id,"app-pty","process",{repoPath:cwd,state:"spawning",metadata:{command:launch.command,source:"genie-app"}});await setCurrentExecutor2(agent.id,executor.id);let env={...process.env,...launch.env,GENIE_APP_PTY:"true"},session=spawnPty(launch.command,{cwd,cols,rows,env,agentId:agent.id,executorId:executor.id,taskId:opts.taskId??null}),{updateExecutorState:updateExecutorState2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry));return await updateExecutorState2(executor.id,"running"),session}function spawnBash(cwd){let shell=process.env.SHELL??"/bin/bash";return spawnPty(shell,{cwd:cwd??process.cwd(),cols:120,rows:40,env:process.env,agentId:null,executorId:null,taskId:null})}function writeTerminal(sessionId,data){let session=sessions.get(sessionId);if(!session||session.state!=="running")return!1;return session.pty.write(data),!0}function resizeTerminal(sessionId,cols,rows){let session=sessions.get(sessionId);if(!session||session.state!=="running")return!1;return session.pty.resize(cols,rows),session.cols=cols,session.rows=rows,!0}async function killTerminal(sessionId){let session=sessions.get(sessionId);if(!session)return!1;if(session.pty.kill(),session.state="exited",session.executorId)try{let{updateExecutorState:updateExecutorState2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry));await updateExecutorState2(session.executorId,"terminated")}catch{}return sessions.delete(sessionId),!0}async function killAll(){let ids=[...sessions.keys()];await Promise.allSettled(ids.map((id)=>killTerminal(id)))}async function pipeStdout(stdout,sessionId,ptyHandle){let reader=stdout.getReader(),decoder=new TextDecoder;try{while(!0){let{done,value}=await reader.read();if(done)break;let text=decoder.decode(value);if(dataCallback)dataCallback(sessionId,text);if(ptyHandle.onData)ptyHandle.onData(text)}}catch{}}function onProcExit(sessionId,code,ptyHandle){let session=sessions.get(sessionId);if(session)session.state="exited";if(exitCallback)exitCallback(sessionId,code);if(ptyHandle.onExit)ptyHandle.onExit(code);handlePtyExit(sessionId,code)}function spawnPty(command,opts){let sessionId=randomUUID6(),ptyHandle;try{let bunPty=(()=>{throw new Error("Cannot require module "+"bun-pty");})(),parts=command.split(" "),raw=bunPty.spawn(parts,{cwd:opts.cwd,env:opts.env,cols:opts.cols,rows:opts.rows});ptyHandle={write:(data)=>raw.write(data),resize:(cols,rows)=>raw.resize(cols,rows),kill:()=>raw.kill(),onData:null,onExit:null},raw.onData=(data)=>{if(dataCallback)dataCallback(sessionId,data);if(ptyHandle.onData)ptyHandle.onData(data)},raw.onExit=(code)=>{let session2=sessions.get(sessionId);if(session2)session2.state="exited";if(exitCallback)exitCallback(sessionId,code);if(ptyHandle.onExit)ptyHandle.onExit(code);handlePtyExit(sessionId,code)}}catch{let parts=command.split(" "),proc=Bun.spawn(parts,{cwd:opts.cwd,env:opts.env,stdin:"pipe",stdout:"pipe",stderr:"pipe"});ptyHandle={write:(data)=>proc.stdin.write(data),resize:()=>{},kill:()=>proc.kill(),onData:null,onExit:null},pipeStdout(proc.stdout,sessionId,ptyHandle),proc.exited.then((code)=>onProcExit(sessionId,code,ptyHandle))}let session={id:sessionId,pty:ptyHandle,agentId:opts.agentId,executorId:opts.executorId,taskId:opts.taskId,command,state:"running",cols:opts.cols,rows:opts.rows,createdAt:new Date().toISOString()};return sessions.set(sessionId,session),session}function handlePtyExit(sessionId,code){let session=sessions.get(sessionId);if(!session?.executorId)return;(async()=>{try{let{updateExecutorState:updateExecutorState2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry));await updateExecutorState2(session.executorId,code===0?"done":"error")}catch{}})()}var sessions,dataCallback=null,exitCallback=null;var init_pty=__esm(()=>{sessions=new Map});import{existsSync as existsSync25,mkdirSync as mkdirSync9,readFileSync as readFileSync12,writeFileSync as writeFileSync9}from"fs";import{homedir as homedir21}from"os";import{join as join32}from"path";function ensureAppHome(){if(!existsSync25(APP_HOME))mkdirSync9(APP_HOME,{recursive:!0})}function readRegistry(){if(ensureAppHome(),!existsSync25(REGISTRY_PATH))return[];try{return JSON.parse(readFileSync12(REGISTRY_PATH,"utf-8"))}catch{return[]}}function writeRegistry(workspaces){ensureAppHome(),writeFileSync9(REGISTRY_PATH,JSON.stringify(workspaces,null,2))}async function listWorkspaces(){return readRegistry()}async function initWorkspace(basePath,name,pgUrl){let resolvedName=name??basePath.split("/").pop()??"workspace",now=new Date().toISOString(),genieDir=join32(basePath,".genie");if(!existsSync25(genieDir))mkdirSync9(genieDir,{recursive:!0});let wsConfig={name:resolvedName,pgUrl:pgUrl??null,created:now};writeFileSync9(join32(genieDir,"workspace.json"),JSON.stringify(wsConfig,null,2));let registry=readRegistry(),existing=registry.findIndex((w)=>w.path===basePath),entry={path:basePath,name:resolvedName,pgUrl:pgUrl??null,created:now,lastOpened:now};if(existing>=0)registry[existing]=entry;else registry.push(entry);return writeRegistry(registry),entry}async function openWorkspace(path2){let registry=readRegistry(),idx=registry.findIndex((w)=>w.path===path2);if(idx<0)throw Error(`Workspace not found in registry: ${path2}`);let wsConfigPath=join32(path2,".genie","workspace.json");if(!existsSync25(wsConfigPath))throw Error(`Workspace config missing at ${wsConfigPath}`);return registry[idx].lastOpened=new Date().toISOString(),writeRegistry(registry),registry[idx]}async function removeWorkspace(path2){let registry=readRegistry(),filtered=registry.filter((w)=>w.path!==path2);if(filtered.length===registry.length)return!1;return writeRegistry(filtered),!0}var APP_HOME,REGISTRY_PATH;var init_workspace2=__esm(()=>{APP_HOME=join32(homedir21(),".genie-app"),REGISTRY_PATH=join32(APP_HOME,"workspaces.json")});async function list_agents(){return listAgents2()}async function show_agent(params){return showAgent(params.id)}async function list_tasks(params){return listTasks2(params.boardId)}async function kanban_board(params){return kanbanBoard(params.boardId)}async function list_boards(){return listBoards()}async function move_task(params){return{ok:await moveTask2(params.taskId,params.columnName)}}async function list_teams(){return listTeams3()}async function dashboard_stats(){return dashboardStats()}async function stream_events(params){return streamEvents(params)}async function spawn_terminal(params){if(params.agentName){let session2=await spawnForAgent(params.agentName,{cwd:params.cwd,cols:params.cols,rows:params.rows});return{sessionId:session2.id,agentId:session2.agentId,executorId:session2.executorId}}return{sessionId:spawnBash(params.cwd).id,agentId:null,executorId:null}}function write_terminal(params){return{ok:writeTerminal(params.sessionId,params.data)}}function resize_terminal(params){return{ok:resizeTerminal(params.sessionId,params.cols,params.rows)}}async function kill_terminal(params){return{ok:await killTerminal(params.sessionId)}}async function list_workspaces(){return listWorkspaces()}async function open_workspace(params){return openWorkspace(params.path)}async function init_workspace3(params){return initWorkspace(params.path,params.name,params.pgUrl)}async function remove_workspace(params){return{ok:await removeWorkspace(params.path)}}var commands;var init_ipc=__esm(()=>{init_pg_bridge();init_pty();init_workspace2();commands={list_agents,show_agent,list_tasks,kanban_board,list_boards,move_task,list_teams,dashboard_stats,stream_events,spawn_terminal,write_terminal,resize_terminal,kill_terminal,list_workspaces,open_workspace,init_workspace:init_workspace3,remove_workspace}});var exports_src_backend={};async function start(){console.log("[genie-app] Starting backend sidecar..."),await getConnection(),console.log("[genie-app] PG connected"),await startListening(),console.log("[genie-app] PG LISTEN active (executor_state, task_stage, runtime_event)"),onPtyData((sessionId,data)=>{emitToFrontend("pty-data",{sessionId,data})}),onPtyExit((sessionId,code)=>{emitToFrontend("pty-exit",{sessionId,code})}),onBridgeEvent((event)=>{emitToFrontend(event.type,event.payload)}),listenForCommands(),console.log("[genie-app] Backend ready")}function emitToFrontend(type2,payload){let message=JSON.stringify({type:"event",event:type2,payload});process.stdout.write(`${message}
1469
1469
  `)}function listenForCommands(){let buffer2="";process.stdin.setEncoding("utf-8"),process.stdin.on("data",(chunk)=>{buffer2+=chunk;let lines=buffer2.split(`
1470
1470
  `);buffer2=lines.pop()??"";for(let line of lines){if(!line.trim())continue;handleCommand(line)}}),process.stdin.on("end",()=>{shutdown2()})}async function handleCommand(line){let id;try{let msg=JSON.parse(line);id=msg.id;let handler=commands[msg.command];if(!handler){respond(id,null,`Unknown command: ${msg.command}`);return}let result=await handler(msg.params);respond(id,result,null)}catch(err){let message=err instanceof Error?err.message:String(err);respond(id,null,message)}}function respond(id,result,error2){let message=JSON.stringify({type:"response",id,result,error:error2});process.stdout.write(`${message}
1471
- `)}async function shutdown2(){if(shutdownRequested)return;shutdownRequested=!0,console.log("[genie-app] Shutting down..."),await killAll(),await stopListening(),console.log("[genie-app] Shutdown complete"),process.exit(0)}var shutdownRequested=!1;var init_src_backend=__esm(()=>{init_db();init_ipc();init_pg_bridge();init_pty();process.on("SIGTERM",()=>void shutdown2());process.on("SIGINT",()=>void shutdown2());start().catch((err)=>{console.error("[genie-app] Fatal error:",err),process.exit(1)})});var exports_board_service={};__export(exports_board_service,{updateColumn:()=>updateColumn,updateBoard:()=>updateBoard,reorderColumns:()=>reorderColumns,removeColumn:()=>removeColumn,reconcileBoard:()=>reconcileBoard,listBoards:()=>listBoards2,importBoard:()=>importBoard,getColumns:()=>getColumns,getBoard:()=>getBoard,exportBoard:()=>exportBoard,deleteBoard:()=>deleteBoard,createBoard:()=>createBoard,addColumn:()=>addColumn});function str3(v){return v!=null?String(v):null}function strOrDefault2(v,def){return v!=null?String(v):def}function parseJsonb(val,fallback){if(val==null)return fallback;if(typeof val==="string")try{return JSON.parse(val)}catch{return fallback}return val}function mapBoard(row){return{id:row.id,name:row.name,projectId:str3(row.project_id),description:str3(row.description),status:strOrDefault2(row.status,"active"),archivedAt:str3(row.archived_at),columns:parseJsonb(row.columns,[]),config:parseJsonb(row.config,{}),createdAt:strOrDefault2(row.created_at,""),updatedAt:strOrDefault2(row.updated_at,"")}}function generateColumnId(){return crypto.randomUUID()}function fillColumnDefaults(col,position){return{id:col.id??generateColumnId(),name:col.name??`column-${position}`,label:col.label??col.name??`Column ${position}`,gate:col.gate??"human",action:col.action??null,auto_advance:col.auto_advance??!1,transitions:col.transitions??[],roles:col.roles??["*"],color:col.color??"#94a3b8",parallel:col.parallel??!1,on_fail:col.on_fail??null,position}}async function createBoard(input){let sql=await getConnection(),columns=[];if(input.fromTemplate){let tmplRows=await sql`
1471
+ `)}async function shutdown2(){if(shutdownRequested)return;shutdownRequested=!0,console.log("[genie-app] Shutting down..."),await killAll(),await stopListening(),console.log("[genie-app] Shutdown complete"),process.exit(0)}var shutdownRequested=!1;var init_src_backend=__esm(()=>{init_db();init_ipc();init_pg_bridge();init_pty();process.env.GENIE_APP="1";process.on("SIGTERM",()=>void shutdown2());process.on("SIGINT",()=>void shutdown2());start().catch((err)=>{console.error("[genie-app] Fatal error:",err),process.exit(1)})});var exports_board_service={};__export(exports_board_service,{updateColumn:()=>updateColumn,updateBoard:()=>updateBoard,reorderColumns:()=>reorderColumns,removeColumn:()=>removeColumn,reconcileBoard:()=>reconcileBoard,listBoards:()=>listBoards2,importBoard:()=>importBoard,getColumns:()=>getColumns,getBoard:()=>getBoard,exportBoard:()=>exportBoard,deleteBoard:()=>deleteBoard,createBoard:()=>createBoard,addColumn:()=>addColumn});function str3(v){return v!=null?String(v):null}function strOrDefault2(v,def){return v!=null?String(v):def}function parseJsonb(val,fallback){if(val==null)return fallback;if(typeof val==="string")try{return JSON.parse(val)}catch{return fallback}return val}function mapBoard(row){return{id:row.id,name:row.name,projectId:str3(row.project_id),description:str3(row.description),status:strOrDefault2(row.status,"active"),archivedAt:str3(row.archived_at),columns:parseJsonb(row.columns,[]),config:parseJsonb(row.config,{}),createdAt:strOrDefault2(row.created_at,""),updatedAt:strOrDefault2(row.updated_at,"")}}function generateColumnId(){return crypto.randomUUID()}function fillColumnDefaults(col,position){return{id:col.id??generateColumnId(),name:col.name??`column-${position}`,label:col.label??col.name??`Column ${position}`,gate:col.gate??"human",action:col.action??null,auto_advance:col.auto_advance??!1,transitions:col.transitions??[],roles:col.roles??["*"],color:col.color??"#94a3b8",parallel:col.parallel??!1,on_fail:col.on_fail??null,position}}async function createBoard(input){let sql=await getConnection(),columns=[];if(input.fromTemplate){let tmplRows=await sql`
1472
1472
  SELECT columns FROM board_templates WHERE name = ${input.fromTemplate} OR id = ${input.fromTemplate} LIMIT 1
1473
1473
  `;if(tmplRows.length===0)throw Error(`Template not found: ${input.fromTemplate}`);columns=tmplRows[0].columns.map((col,i2)=>({...col,id:generateColumnId(),position:i2}))}else if(input.columns)columns=input.columns.map((col,i2)=>fillColumnDefaults(col,i2));let rows=await sql`
1474
1474
  INSERT INTO boards (name, project_id, description, columns)
@@ -2071,7 +2071,7 @@ $ bun add react-devtools-core@7 -d
2071
2071
  AND a.ended_at IS NULL
2072
2072
  ORDER BY a.started_at DESC
2073
2073
  `).map(mapAssignment)}function mapExecutor(row){let meta=row.metadata;return{id:String(row.id),agentId:String(row.agent_id),agentName:row.agent_name?String(row.agent_name):null,provider:String(row.provider),transport:String(row.transport),pid:row.pid!=null?Number(row.pid):null,tmuxSession:row.tmux_session?String(row.tmux_session):null,tmuxPaneId:row.tmux_pane_id?String(row.tmux_pane_id):null,state:String(row.state),metadata:typeof meta==="string"?JSON.parse(meta):meta??{},startedAt:row.started_at instanceof Date?row.started_at.toISOString():String(row.started_at),role:row.role?String(row.role):null,team:row.team?String(row.team):null}}function mapAssignment(row){return{id:String(row.id),executorId:String(row.executor_id),taskId:row.task_id?String(row.task_id):null,taskTitle:row.task_title?String(row.task_title):null,wishSlug:row.wish_slug?String(row.wish_slug):null,groupNumber:row.group_number!=null?Number(row.group_number):null,startedAt:row.started_at instanceof Date?row.started_at.toISOString():String(row.started_at)}}import{execSync as execSync16}from"child_process";function execQuiet(cmd){try{return execSync16(cmd,{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return""}}function parsePaneLine(parts){let[sessionName,winIdxStr,winName,winActive,winPanes,paneIdxStr,paneId,panePidStr,paneCmd,paneTitle,paneSize,sessAttached,sessWindows,sessCreated,paneDead]=parts;return{sessionName,winIdxStr,session:{name:sessionName,attached:sessAttached==="1",windowCount:Number.parseInt(sessWindows,10)||0,created:Number.parseInt(sessCreated,10)||0},window:{sessionName,index:Number.parseInt(winIdxStr,10)||0,name:winName,active:winActive==="1",paneCount:Number.parseInt(winPanes,10)||0},pane:{sessionName,windowIndex:Number.parseInt(winIdxStr,10)||0,paneIndex:Number.parseInt(paneIdxStr,10)||0,paneId,pid:Number.parseInt(panePidStr,10)||0,command:paneCmd,title:paneTitle,size:paneSize,isDead:paneDead==="1"}}}function getTmuxInventory(){let paneOutput=execQuiet("tmux -L genie list-panes -a -F '#{session_name}|#{window_index}|#{window_name}|#{window_active}|#{window_panes}|#{pane_index}|#{pane_id}|#{pane_pid}|#{pane_current_command}|#{pane_title}|#{pane_width}x#{pane_height}|#{session_attached}|#{session_windows}|#{session_created}|#{pane_dead}'");if(!paneOutput)return[];let sessionMap=new Map,windowMap=new Map;for(let line of paneOutput.split(`
2074
- `)){if(!line)continue;let parts=line.split("|");if(parts.length<15)continue;let parsed=parsePaneLine(parts);if(!sessionMap.has(parsed.sessionName))sessionMap.set(parsed.sessionName,{...parsed.session,windows:[]});let winKey=`${parsed.sessionName}:${parsed.winIdxStr}`;if(!windowMap.has(winKey)){let win={...parsed.window,panes:[]};windowMap.set(winKey,win),sessionMap.get(parsed.sessionName)?.windows.push(win)}windowMap.get(winKey)?.panes.push(parsed.pane)}return Array.from(sessionMap.values()).sort((a,b3)=>a.name.localeCompare(b3.name))}function isPidAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function allClaudePanes(sessions2){return sessions2.flatMap((s)=>s.windows.flatMap((w2)=>w2.panes)).filter((p)=>p.command==="claude"||p.title.includes("claude"))}function detectGaps(executors,sessions2){let deadPidExecutors=executors.filter((e)=>e.pid!=null&&!isPidAlive(e.pid)),executorPaneIds=new Set(executors.map((e)=>e.tmuxPaneId).filter(Boolean)),claudePanes=allClaudePanes(sessions2),orphanPanes=claudePanes.filter((p)=>!executorPaneIds.has(p.paneId)),linkedCount=executors.filter((e)=>e.tmuxPaneId&&!deadPidExecutors.some((d2)=>d2.id===e.id)).length,deadPaneCount=sessions2.flatMap((s)=>s.windows.flatMap((w2)=>w2.panes)).filter((p)=>p.isDead).length;return{deadPidExecutors,orphanPanes,linkedCount,totalExecutors:executors.length,totalClaudePanes:claudePanes.length,deadPaneCount}}async function collectDiagnostics(){let{loadExecutors:loadExecutors2,loadAssignments:loadAssignments2}=await Promise.resolve().then(() => exports_db2),sessions2=getTmuxInventory(),executors=await loadExecutors2(),executorIds=executors.map((e)=>e.id),assignments=await loadAssignments2(executorIds),gaps=detectGaps(executors,sessions2);return{sessions:sessions2,executors,assignments,gaps,timestamp:Date.now()}}var init_diagnostics=()=>{};function buildSessionTree(snapshot){let executorByPaneId=new Map;for(let exec3 of snapshot.executors)if(exec3.tmuxPaneId)executorByPaneId.set(exec3.tmuxPaneId,exec3);return snapshot.sessions.filter((s)=>s.name!=="genie-tui").map((session)=>sessionToNode(session,executorByPaneId))}function buildWorkspaceTree(input){let{agentNames,sessions:sessions2,executors}=input,sessionByName=new Map;for(let s of sessions2)if(s.name!=="genie-tui")sessionByName.set(s.name,s);let executorByPaneId=new Map;for(let exec3 of executors)if(exec3.tmuxPaneId)executorByPaneId.set(exec3.tmuxPaneId,exec3);let executorsByAgent=new Map;for(let exec3 of executors){let name=exec3.agentName??exec3.metadata?.agentName;if(typeof name==="string"){let list2=executorsByAgent.get(name)??[];list2.push(exec3),executorsByAgent.set(name,list2)}}let nodes=agentNames.map((name)=>buildAgentNode(name,sessionByName.get(name),executorsByAgent.get(name)??[],executorByPaneId)),agentNameSet=new Set(agentNames);for(let[name,session]of sessionByName)if(!agentNameSet.has(name))nodes.push(sessionToNode(session,executorByPaneId));return nodes}function countClaudePanes(session){return session.windows.reduce((sum,w2)=>sum+w2.panes.filter((p)=>p.command==="claude"||p.title.includes("claude")).length,0)}function buildAgentNode(name,session,agentExecutors,executorByPaneId){let wsState=deriveWsAgentState(session,agentExecutors),children=[];if(session)for(let win of session.windows){if(win.index===0)continue;children.push(windowToNode(session.name,win,executorByPaneId))}return{id:`agent:${name}`,type:"agent",label:name,depth:0,expanded:children.length>0,children,data:{sessionName:name,windowCount:session?session.windows.length:0},activePanes:session?countClaudePanes(session):0,agentState:agentExecutors.length>0?deriveExecutorState(agentExecutors):void 0,wsAgentState:wsState}}function deriveWsAgentState(session,agentExecutors){if(!session)return"stopped";for(let exec3 of agentExecutors){if(exec3.state==="error"||exec3.state==="terminated")return"error";if(exec3.state==="spawning")return"spawning"}return"running"}function deriveExecutorState(execs){for(let e of execs)if(e.state==="working")return"working";for(let e of execs)if(e.state==="permission")return"permission";for(let e of execs)if(e.state==="error"||e.state==="terminated")return"error";return"idle"}function sessionToNode(session,executorMap){let claudePanes=session.windows.reduce((sum,w2)=>sum+w2.panes.filter((p)=>p.command==="claude"||p.title.includes("claude")).length,0);return{id:`session:${session.name}`,type:"session",label:session.name,depth:0,expanded:!0,children:session.windows.map((w2)=>windowToNode(session.name,w2,executorMap)),data:{attached:session.attached,windowCount:session.windowCount},activePanes:claudePanes,agentState:void 0,wsAgentState:void 0}}function windowToNode(sessionName,window2,executorMap){let activePanes=window2.panes.filter((p)=>!p.isDead&&(p.command==="claude"||p.title.includes("claude"))).length;return{id:`window:${sessionName}:${window2.index}`,type:"window",label:window2.name,depth:1,expanded:!0,children:window2.panes.map((p)=>paneToNode(sessionName,window2.index,p,executorMap)),data:{active:window2.active,paneCount:window2.paneCount},activePanes,agentState:void 0,wsAgentState:void 0}}function paneToNode(sessionName,windowIndex,pane,executorMap){let executor=executorMap.get(pane.paneId),isClaude=pane.command==="claude"||pane.title.includes("claude");return{id:`pane:${pane.paneId}`,type:"pane",label:derivePaneLabel(pane,executor,isClaude),depth:2,expanded:!1,children:[],data:{command:pane.command,isDead:pane.isDead,pid:pane.pid,size:pane.size,paneId:pane.paneId,sessionName,windowIndex},activePanes:0,agentState:derivePaneState(pane,executor),wsAgentState:void 0}}function derivePaneLabel(pane,executor,isClaude){if(executor?.agentName&&executor?.team)return`${executor.team}/${executor.agentName}`;if(executor?.agentName)return executor.agentName;if(isClaude)return"claude";return pane.command}function derivePaneState(pane,executor){if(pane.isDead)return"error";if(!executor)return;let s=executor.state;if(s==="working")return"working";if(s==="idle"||s==="spawning")return"idle";if(s==="permission")return"permission";if(s==="error"||s==="terminated")return"error";return}function getSessionTarget(node){if(node.type==="agent")return{sessionName:node.data.sessionName};if(node.type==="session")return{sessionName:node.label};if(node.type==="window"){let parts=node.id.split(":");return{sessionName:parts[1],windowIndex:Number(parts[2])}}if(node.type==="pane"){let data=node.data;return{sessionName:data.sessionName,windowIndex:data.windowIndex}}return null}var palette,icons;var init_theme2=__esm(()=>{palette={purple:"#a855f7",violet:"#7c3aed",cyan:"#22d3ee",emerald:"#34d399",bg:"#1a1028",bgLight:"#241838",bgLighter:"#2e2048",text:"#e2e8f0",textDim:"#94a3b8",textMuted:"#64748b",border:"#414868",borderActive:"#7c3aed",scrollTrack:"#414868",scrollThumb:"#7aa2f7",active:"#22d3ee",success:"#34d399",warning:"#fbbf24",error:"#f87171",idle:"#94a3b8"},icons={org:"\u25C6",project:"\u25B8",projectOpen:"\u25BE",board:"\u2261",boardOpen:"\u2261",column:"\u2502",task:"\u25CB",taskActive:"\u25CF",taskDone:"\u2713",agent:"\u25B6",collapsed:"\u25B8",expanded:"\u25BE"}});function flattenTree(nodes){let result=[];function walk(node,depth){if(result.push({node,depth,visible:!0}),node.expanded)for(let child of node.children)walk(child,depth+1)}for(let node of nodes)walk(node,0);return result}function toggleNode(nodes,id){return nodes.map((node)=>{if(node.id===id)return{...node,expanded:!node.expanded};return{...node,children:toggleNode(node.children,id)}})}var import_jsx_dev_runtime2;var init_jsx_dev_runtime=__esm(()=>{import_jsx_dev_runtime2=__toESM(require_react_jsx_dev_runtime_development(),1)});function getNodeIcon(node){if(node.type==="agent")return getAgentIcon(node);switch(node.type){case"session":return node.data.attached?"\u25B6":"\u25B8";case"window":return node.data.active?"\u25A0":"\u25A1";case"pane":return getPaneIcon(node);default:return" "}}function getAgentIcon(node){switch(node.wsAgentState){case"running":return"\u25CF";case"stopped":return"\u25CB";case"error":return"\u2298";case"spawning":return"\u231B";default:return"\u25CB"}}function getPaneIcon(node){if(node.data.isDead)return"\u2718";if(node.agentState==="working")return"\u25CF";if(node.agentState==="idle")return"\u25CB";if(node.agentState==="permission")return"\u26A0";if(node.agentState==="error")return"\u2718";if(node.data.command==="claude")return"\u25C6";return"\u25CB"}function getNodeColor(node){if(node.type==="agent")return getAgentColor(node);switch(node.type){case"session":return node.data.attached?palette.emerald:palette.textDim;case"window":return node.data.active?palette.cyan:palette.text;case"pane":return getPaneColor(node);default:return palette.text}}function getAgentColor(node){switch(node.wsAgentState){case"running":return palette.emerald;case"stopped":return palette.textDim;case"error":return palette.error;case"spawning":return palette.warning;default:return palette.textDim}}function getPaneColor(node){if(node.data.isDead)return palette.error;if(node.agentState==="working")return palette.cyan;if(node.agentState==="permission")return palette.warning;if(node.agentState==="error")return palette.error;if(node.agentState==="idle")return palette.textDim;if(node.data.command==="claude")return palette.cyan;return palette.textDim}function getNodeSuffix(node){if(node.type==="agent"){let wc=node.data.windowCount;if(wc>1)return` (${wc} windows)`;if(wc===1)return" (1 window)";return""}if(node.type==="session"||node.type==="pane"){let count=node.activePanes;if(count>0)return` ${icons.agent}${count}`}return""}function getStateColor(state){switch(state){case"working":return palette.cyan;case"idle":return palette.textDim;case"permission":return palette.warning;case"error":return palette.error;default:return palette.textMuted}}var import_react13,TreeNodeRow;var init_TreeNode=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react13=__toESM(require_react_development(),1),TreeNodeRow=import_react13.memo(function({node,selected,onSelect,onToggle}){let indent=" ".repeat(node.depth),hasChildren=node.children.length>0,expandIcon=hasChildren?node.expanded?icons.expanded:icons.collapsed:" ",icon=getNodeIcon(node),color=getNodeColor(node),suffix=getNodeSuffix(node);return import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",backgroundColor:selected?palette.violet:void 0,onMouseDown:()=>{if(onSelect(node.id),hasChildren)onToggle(node.id)},children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[indent,expandIcon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:color,children:[icon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:selected?"#ffffff":palette.text,children:node.label},void 0,!1,void 0,this),suffix?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:suffix},void 0,!1,void 0,this):null,node.agentState?import_jsx_dev_runtime2.jsxDEV("span",{fg:getStateColor(node.agentState),children:[" ",node.agentState]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)})});function Nav({onTmuxSessionSelect,workspaceRoot,initialAgent}){let[diagnostics,setDiagnostics]=import_react15.useState(null),[sessionTree,setSessionTree]=import_react15.useState([]),[selectedIndex,setSelectedIndex]=import_react15.useState(0),lastTarget=import_react15.useRef(null),genieHome3=import_react15.useRef(process.env.GENIE_HOME??`${process.env.HOME}/.genie`);import_react15.useEffect(()=>{let active=!0;async function refresh(){try{let snap=await collectDiagnostics();if(active)setDiagnostics(snap)}catch(err){console.error("TUI: diagnostics failed:",err)}}refresh();let timer2=setInterval(refresh,2000);return()=>{active=!1,clearInterval(timer2)}},[]),import_react15.useEffect(()=>{if(!diagnostics)return;let newTree;if(workspaceRoot){let agentNames=scanAgents2(workspaceRoot);newTree=buildWorkspaceTree({agentNames,sessions:diagnostics.sessions,executors:diagnostics.executors})}else newTree=buildSessionTree(diagnostics);setSessionTree((prev)=>mergeExpandedState(prev,newTree))},[diagnostics,workspaceRoot]);let flatNodes=import_react15.useMemo(()=>flattenTree(sessionTree),[sessionTree]);import_react15.useEffect(()=>{if(flatNodes.length>0&&selectedIndex>=flatNodes.length)setSelectedIndex(flatNodes.length-1)},[flatNodes.length,selectedIndex]);let[pendingAgent,setPendingAgent]=import_react15.useState(initialAgent);import_react15.useEffect(()=>{if(!diagnostics)return;try{let fs3=__require("fs"),agentFile=`${genieHome3.current}/tui-initial-agent`;if(fs3.existsSync(agentFile)){let agent=fs3.readFileSync(agentFile,"utf-8").trim();if(fs3.unlinkSync(agentFile),agent)setPendingAgent(agent)}}catch{}},[diagnostics]),import_react15.useEffect(()=>{if(!pendingAgent||flatNodes.length===0)return;let idx=flatNodes.findIndex((n)=>n.node.id===`agent:${pendingAgent}`);if(idx>=0)setSelectedIndex(idx),setPendingAgent(void 0)},[pendingAgent,flatNodes]),import_react15.useEffect(()=>{let current=flatNodes[selectedIndex]?.node;if(!current)return;let target=getSessionTarget(current);if(!target)return;let key=`${target.sessionName}:${target.windowIndex??""}`;if(key===lastTarget.current)return;if(lastTarget.current=key,current.type==="agent"&&current.wsAgentState!=="running")return;onTmuxSessionSelect(target.sessionName,target.windowIndex)},[selectedIndex,flatNodes,onTmuxSessionSelect]);let handleSelect=import_react15.useCallback((id)=>{let idx=flatNodes.findIndex((n)=>n.node.id===id);if(idx>=0)setSelectedIndex(idx)},[flatNodes]),handleToggle=import_react15.useCallback((id)=>{setSessionTree((prev)=>toggleNode(prev,id))},[]),handleVerticalNav=import_react15.useCallback((keyName2)=>{let rowCount=flatNodes.length;if(rowCount===0)return;if(keyName2==="up"||keyName2==="k")setSelectedIndex((prev)=>prev===0?rowCount-1:prev-1);else if(keyName2==="down"||keyName2==="j")setSelectedIndex((prev)=>prev>=rowCount-1?0:prev+1)},[flatNodes.length]),handleExpandCollapse=import_react15.useCallback((keyName2)=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if((keyName2==="right"||keyName2==="l")&&node.children.length>0&&!node.expanded)handleToggle(node.id);else if((keyName2==="left"||keyName2==="h")&&node.expanded)handleToggle(node.id)},[flatNodes,selectedIndex,handleToggle]),handleEnter=import_react15.useCallback(()=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if(node.type==="agent"){if(node.wsAgentState!=="running")spawnAgent(node.label);let target2=getSessionTarget(node);if(target2)onTmuxSessionSelect(target2.sessionName,target2.windowIndex);return}if(node.children.length>0)handleToggle(node.id);let target=getSessionTarget(node);if(target)onTmuxSessionSelect(target.sessionName,target.windowIndex)},[flatNodes,selectedIndex,handleToggle,onTmuxSessionSelect]);useKeyboard((key)=>{if(key.name==="up"||key.name==="k"||key.name==="down"||key.name==="j")handleVerticalNav(key.name);else if(key.name==="right"||key.name==="l"||key.name==="left"||key.name==="h")handleExpandCollapse(key.name);else if(key.name==="enter"||key.name==="return")handleEnter()});let agentCount=workspaceRoot?sessionTree.filter((n)=>n.type==="agent").length:diagnostics?.sessions.length??0,runningCount=workspaceRoot?sessionTree.filter((n)=>n.wsAgentState==="running").length:diagnostics?.sessions.reduce((sum,s)=>sum+s.windows.reduce((ws,w2)=>ws+w2.panes.length,0),0)??0,headerLabel=workspaceRoot?"Agents":"Sessions";return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",backgroundColor:palette.bg,children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLight,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.purple,children:headerLabel},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[" ",workspaceRoot?`${runningCount}/${agentCount}`:`${agentCount}s ${runningCount}p`]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("scrollbox",{focused:!0,height:"100%",style:{scrollbarOptions:{showArrows:!1,trackOptions:{foregroundColor:palette.scrollThumb,backgroundColor:palette.scrollTrack}}},children:flatNodes.map((flat,i2)=>import_jsx_dev_runtime2.jsxDEV(TreeNodeRow,{node:flat.node,selected:i2===selectedIndex,onSelect:handleSelect,onToggle:handleToggle},flat.node.id,!1,void 0,this))},void 0,!1,void 0,this):import_jsx_dev_runtime2.jsxDEV("box",{flexGrow:1,justifyContent:"center",alignItems:"center",children:import_jsx_dev_runtime2.jsxDEV("text",{fg:palette.textDim,children:"Collecting..."},void 0,!1,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLight,children:import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:["\u2191\u2193",":nav ","\u2190\u2192",":expand Enter:",workspaceRoot?"spawn/attach":"attach"]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function spawnAgent(name){try{let{spawn:spawn4}=__require("child_process"),{join:join53,resolve:resolve10}=__require("path"),{existsSync:existsSync40}=__require("fs"),wsRoot=process.env.GENIE_TUI_WORKSPACE,cwd;if(wsRoot){let agentDir=resolve10(join53(wsRoot,"agents",name));if(existsSync40(agentDir))cwd=agentDir}spawn4("genie",["spawn",name],{detached:!0,stdio:"ignore",cwd}).unref()}catch{}}function mergeExpandedState(oldTree,newTree){if(oldTree.length===0)return newTree;let oldState=new Map;function collect(nodes){for(let n of nodes)oldState.set(n.id,n.expanded),collect(n.children)}collect(oldTree);function apply(nodes){return nodes.map((n)=>({...n,expanded:oldState.has(n.id)?oldState.get(n.id):n.expanded,children:apply(n.children)}))}return apply(newTree)}var import_react15;var init_Nav=__esm(async()=>{init_workspace();init_diagnostics();init_theme2();init_TreeNode();init_jsx_dev_runtime();await init_react();import_react15=__toESM(require_react_development(),1)});function App({rightPane,workspaceRoot,initialAgent}){let handleTmuxSessionSelect=import_react16.useCallback((sessionName,windowIndex)=>{if(!rightPane)return;attachProjectWindow(rightPane,sessionName,windowIndex)},[rightPane]);return import_jsx_dev_runtime2.jsxDEV(Nav,{onTmuxSessionSelect:handleTmuxSessionSelect,workspaceRoot,initialAgent},void 0,!1,void 0,this)}var import_react16;var init_app=__esm(async()=>{init_tmux2();init_jsx_dev_runtime();await init_Nav();import_react16=__toESM(require_react_development(),1)});var exports_render={};__export(exports_render,{renderNav:()=>renderNav});async function renderNav(){let rightPane=process.env.GENIE_TUI_RIGHT||void 0,workspaceRoot=process.env.GENIE_TUI_WORKSPACE||void 0,initialAgent=process.env.GENIE_TUI_AGENT||void 0,renderer=await createCliRenderer({exitOnCtrlC:!1,useMouse:!0});createRoot(renderer).render(import_jsx_dev_runtime2.jsxDEV(App,{rightPane,workspaceRoot,initialAgent},void 0,!1,void 0,this)),await new Promise((resolve10)=>{renderer.once("destroy",resolve10)})}var init_render=__esm(async()=>{init_jsx_dev_runtime();await __promiseAll([init_core(),init_react(),init_app()])});var exports_tui={};__export(exports_tui,{launchTui:()=>launchTui});async function launchTui(){let{renderNav:renderNav2}=await init_render().then(() => exports_render);await renderNav2()}var import__=__toESM(require_commander(),1),{program,createCommand,createArgument,createOption,CommanderError,InvalidArgumentError,InvalidOptionArgumentError,Command,Argument,Option,Help}=import__.default;import{existsSync as existsSync3,unlinkSync as unlinkSync2}from"fs";import{homedir as homedir3}from"os";import{join as join3}from"path";var{$:$2}=globalThis.Bun;import{existsSync,unlinkSync}from"fs";import{homedir}from"os";import{join}from"path";var CLAUDE_DIR=join(homedir(),".claude"),CLAUDE_HOOKS_DIR=join(CLAUDE_DIR,"hooks"),CLAUDE_SETTINGS_FILE=join(CLAUDE_DIR,"settings.json"),GENIE_HOOK_SCRIPT_NAME="genie-bash-hook.sh";function getClaudeSettingsPath(){return CLAUDE_SETTINGS_FILE}function getGenieHookScriptPath(){return join(CLAUDE_HOOKS_DIR,GENIE_HOOK_SCRIPT_NAME)}function hookScriptExists(){return existsSync(getGenieHookScriptPath())}function removeHookScript(){let scriptPath=getGenieHookScriptPath();if(existsSync(scriptPath))unlinkSync(scriptPath)}function contractClaudePath(path){let home=homedir();if(path.startsWith(`${home}/`))return`~${path.slice(home.length)}`;if(path===home)return"~";return path}init_genie_config2();var{$}=globalThis.Bun;async function checkCommand(cmd){try{let cmdPath=(await $`which ${cmd}`.quiet().text()).trim();if(!cmdPath)return{exists:!1};let version;try{let firstLine=(await $`${cmd} --version`.quiet().text()).split(`
2074
+ `)){if(!line)continue;let parts=line.split("|");if(parts.length<15)continue;let parsed=parsePaneLine(parts);if(!sessionMap.has(parsed.sessionName))sessionMap.set(parsed.sessionName,{...parsed.session,windows:[]});let winKey=`${parsed.sessionName}:${parsed.winIdxStr}`;if(!windowMap.has(winKey)){let win={...parsed.window,panes:[]};windowMap.set(winKey,win),sessionMap.get(parsed.sessionName)?.windows.push(win)}windowMap.get(winKey)?.panes.push(parsed.pane)}return Array.from(sessionMap.values()).sort((a,b3)=>a.name.localeCompare(b3.name))}function isPidAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function allClaudePanes(sessions2){return sessions2.flatMap((s)=>s.windows.flatMap((w2)=>w2.panes)).filter((p)=>p.command==="claude"||p.title.includes("claude"))}function detectGaps(executors,sessions2){let deadPidExecutors=executors.filter((e)=>e.pid!=null&&!isPidAlive(e.pid)),executorPaneIds=new Set(executors.map((e)=>e.tmuxPaneId).filter(Boolean)),claudePanes=allClaudePanes(sessions2),orphanPanes=claudePanes.filter((p)=>!executorPaneIds.has(p.paneId)),linkedCount=executors.filter((e)=>e.tmuxPaneId&&!deadPidExecutors.some((d2)=>d2.id===e.id)).length,deadPaneCount=sessions2.flatMap((s)=>s.windows.flatMap((w2)=>w2.panes)).filter((p)=>p.isDead).length;return{deadPidExecutors,orphanPanes,linkedCount,totalExecutors:executors.length,totalClaudePanes:claudePanes.length,deadPaneCount}}async function collectDiagnostics(){let{loadExecutors:loadExecutors2,loadAssignments:loadAssignments2}=await Promise.resolve().then(() => exports_db2),sessions2=getTmuxInventory(),executors=await loadExecutors2(),executorIds=executors.map((e)=>e.id),assignments=await loadAssignments2(executorIds),gaps=detectGaps(executors,sessions2);return{sessions:sessions2,executors,assignments,gaps,timestamp:Date.now()}}var init_diagnostics=()=>{};function buildSessionTree(snapshot){let executorByPaneId=new Map;for(let exec3 of snapshot.executors)if(exec3.tmuxPaneId)executorByPaneId.set(exec3.tmuxPaneId,exec3);return snapshot.sessions.filter((s)=>s.name!=="genie-tui").map((session)=>sessionToNode(session,executorByPaneId))}function buildWorkspaceTree(input){let{agentNames,sessions:sessions2,executors}=input,sessionByName=new Map;for(let s of sessions2)if(s.name!=="genie-tui")sessionByName.set(s.name,s);let executorByPaneId=new Map;for(let exec3 of executors)if(exec3.tmuxPaneId)executorByPaneId.set(exec3.tmuxPaneId,exec3);let executorsByAgent=new Map;for(let exec3 of executors){let name=exec3.agentName??exec3.metadata?.agentName;if(typeof name==="string"){let list2=executorsByAgent.get(name)??[];list2.push(exec3),executorsByAgent.set(name,list2)}}let nodes=agentNames.map((name)=>buildAgentNode(name,sessionByName.get(name),executorsByAgent.get(name)??[],executorByPaneId)),agentNameSet=new Set(agentNames);for(let[name,session]of sessionByName)if(!agentNameSet.has(name))nodes.push(sessionToNode(session,executorByPaneId));return nodes}function countClaudePanes(session){return session.windows.reduce((sum,w2)=>sum+w2.panes.filter((p)=>p.command==="claude"||p.title.includes("claude")).length,0)}function buildAgentNode(name,session,agentExecutors,executorByPaneId){let wsState=deriveWsAgentState(session,agentExecutors),children=[];if(session)for(let win of session.windows){if(win.index===0)continue;children.push(windowToNode(session.name,win,executorByPaneId))}return{id:`agent:${name}`,type:"agent",label:name,depth:0,expanded:children.length>0,children,data:{sessionName:name,windowCount:session?session.windows.length:0},activePanes:session?countClaudePanes(session):0,agentState:agentExecutors.length>0?deriveExecutorState(agentExecutors):void 0,wsAgentState:wsState}}function deriveWsAgentState(session,agentExecutors){if(!session)return"stopped";for(let exec3 of agentExecutors){if(exec3.state==="error"||exec3.state==="terminated")return"error";if(exec3.state==="spawning")return"spawning"}return"running"}function deriveExecutorState(execs){for(let e of execs)if(e.state==="working")return"working";for(let e of execs)if(e.state==="permission")return"permission";for(let e of execs)if(e.state==="error"||e.state==="terminated")return"error";return"idle"}function sessionToNode(session,executorMap){let claudePanes=session.windows.reduce((sum,w2)=>sum+w2.panes.filter((p)=>p.command==="claude"||p.title.includes("claude")).length,0);return{id:`session:${session.name}`,type:"session",label:session.name,depth:0,expanded:!0,children:session.windows.map((w2)=>windowToNode(session.name,w2,executorMap)),data:{attached:session.attached,windowCount:session.windowCount},activePanes:claudePanes,agentState:void 0,wsAgentState:void 0}}function windowToNode(sessionName,window2,executorMap){let activePanes=window2.panes.filter((p)=>!p.isDead&&(p.command==="claude"||p.title.includes("claude"))).length;return{id:`window:${sessionName}:${window2.index}`,type:"window",label:window2.name,depth:1,expanded:!0,children:window2.panes.map((p)=>paneToNode(sessionName,window2.index,p,executorMap)),data:{active:window2.active,paneCount:window2.paneCount},activePanes,agentState:void 0,wsAgentState:void 0}}function paneToNode(sessionName,windowIndex,pane,executorMap){let executor=executorMap.get(pane.paneId),isClaude=pane.command==="claude"||pane.title.includes("claude");return{id:`pane:${pane.paneId}`,type:"pane",label:derivePaneLabel(pane,executor,isClaude),depth:2,expanded:!1,children:[],data:{command:pane.command,isDead:pane.isDead,pid:pane.pid,size:pane.size,paneId:pane.paneId,sessionName,windowIndex},activePanes:0,agentState:derivePaneState(pane,executor),wsAgentState:void 0}}function derivePaneLabel(pane,executor,isClaude){if(executor?.agentName&&executor?.team)return`${executor.team}/${executor.agentName}`;if(executor?.agentName)return executor.agentName;if(isClaude)return"claude";return pane.command}function derivePaneState(pane,executor){if(pane.isDead)return"error";if(!executor)return;let s=executor.state;if(s==="working")return"working";if(s==="idle"||s==="spawning")return"idle";if(s==="permission")return"permission";if(s==="error"||s==="terminated")return"error";return}function getSessionTarget(node){if(node.type==="agent")return{sessionName:node.data.sessionName};if(node.type==="session")return{sessionName:node.label};if(node.type==="window"){let parts=node.id.split(":");return{sessionName:parts[1],windowIndex:Number(parts[2])}}if(node.type==="pane"){let data=node.data;return{sessionName:data.sessionName,windowIndex:data.windowIndex}}return null}var palette,icons;var init_theme2=__esm(()=>{palette={purple:"#a855f7",violet:"#7c3aed",cyan:"#22d3ee",emerald:"#34d399",bg:"#1a1028",bgLight:"#241838",bgLighter:"#2e2048",text:"#e2e8f0",textDim:"#94a3b8",textMuted:"#64748b",border:"#414868",borderActive:"#7c3aed",scrollTrack:"#414868",scrollThumb:"#7aa2f7",active:"#22d3ee",success:"#34d399",warning:"#fbbf24",error:"#f87171",idle:"#94a3b8"},icons={org:"\u25C6",project:"\u25B8",projectOpen:"\u25BE",board:"\u2261",boardOpen:"\u2261",column:"\u2502",task:"\u25CB",taskActive:"\u25CF",taskDone:"\u2713",agent:"\u25B6",collapsed:"\u25B8",expanded:"\u25BE"}});function flattenTree(nodes){let result=[];function walk(node,depth){if(result.push({node,depth,visible:!0}),node.expanded)for(let child of node.children)walk(child,depth+1)}for(let node of nodes)walk(node,0);return result}function toggleNode(nodes,id){return nodes.map((node)=>{if(node.id===id)return{...node,expanded:!node.expanded};return{...node,children:toggleNode(node.children,id)}})}var import_jsx_dev_runtime2;var init_jsx_dev_runtime=__esm(()=>{import_jsx_dev_runtime2=__toESM(require_react_jsx_dev_runtime_development(),1)});function getNodeIcon(node){if(node.type==="agent")return getAgentIcon(node);switch(node.type){case"session":return node.data.attached?"\u25B6":"\u25B8";case"window":return node.data.active?"\u25A0":"\u25A1";case"pane":return getPaneIcon(node);default:return" "}}function getAgentIcon(node){switch(node.wsAgentState){case"running":return"\u25CF";case"stopped":return"\u25CB";case"error":return"\u2298";case"spawning":return"\u231B";default:return"\u25CB"}}function getPaneIcon(node){if(node.data.isDead)return"\u2718";if(node.agentState==="working")return"\u25CF";if(node.agentState==="idle")return"\u25CB";if(node.agentState==="permission")return"\u26A0";if(node.agentState==="error")return"\u2718";if(node.data.command==="claude")return"\u25C6";return"\u25CB"}function getNodeColor(node){if(node.type==="agent")return getAgentColor(node);switch(node.type){case"session":return node.data.attached?palette.emerald:palette.textDim;case"window":return node.data.active?palette.cyan:palette.text;case"pane":return getPaneColor(node);default:return palette.text}}function getAgentColor(node){switch(node.wsAgentState){case"running":return palette.emerald;case"stopped":return palette.textDim;case"error":return palette.error;case"spawning":return palette.warning;default:return palette.textDim}}function getPaneColor(node){if(node.data.isDead)return palette.error;if(node.agentState==="working")return palette.cyan;if(node.agentState==="permission")return palette.warning;if(node.agentState==="error")return palette.error;if(node.agentState==="idle")return palette.textDim;if(node.data.command==="claude")return palette.cyan;return palette.textDim}function getNodeSuffix(node){if(node.type==="agent"){let wc=node.data.windowCount;if(wc>1)return` (${wc} windows)`;if(wc===1)return" (1 window)";return""}if(node.type==="session"||node.type==="pane"){let count=node.activePanes;if(count>0)return` ${icons.agent}${count}`}return""}function getStateColor(state){switch(state){case"working":return palette.cyan;case"idle":return palette.textDim;case"permission":return palette.warning;case"error":return palette.error;default:return palette.textMuted}}var import_react13,TreeNodeRow;var init_TreeNode=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react13=__toESM(require_react_development(),1),TreeNodeRow=import_react13.memo(function({node,selected,onSelect,onToggle}){let indent=" ".repeat(node.depth),hasChildren=node.children.length>0,expandIcon=hasChildren?node.expanded?icons.expanded:icons.collapsed:" ",icon=getNodeIcon(node),color=getNodeColor(node),suffix=getNodeSuffix(node);return import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",backgroundColor:selected?palette.violet:void 0,onMouseDown:()=>{if(onSelect(node.id),hasChildren)onToggle(node.id)},children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[indent,expandIcon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:color,children:[icon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:selected?"#ffffff":palette.text,children:node.label},void 0,!1,void 0,this),suffix?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:suffix},void 0,!1,void 0,this):null,node.agentState?import_jsx_dev_runtime2.jsxDEV("span",{fg:getStateColor(node.agentState),children:[" ",node.agentState]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)})});function Nav({onTmuxSessionSelect,workspaceRoot,initialAgent}){let[diagnostics,setDiagnostics]=import_react15.useState(null),[sessionTree,setSessionTree]=import_react15.useState([]),[selectedIndex,setSelectedIndex]=import_react15.useState(0),lastTarget=import_react15.useRef(null),initialSelectDone=import_react15.useRef(!1);import_react15.useEffect(()=>{let active=!0;async function refresh(){try{let snap=await collectDiagnostics();if(active)setDiagnostics(snap)}catch(err){console.error("TUI: diagnostics failed:",err)}}refresh();let timer2=setInterval(refresh,2000);return()=>{active=!1,clearInterval(timer2)}},[]),import_react15.useEffect(()=>{if(!diagnostics)return;let newTree;if(workspaceRoot){let agentNames=scanAgents2(workspaceRoot);newTree=buildWorkspaceTree({agentNames,sessions:diagnostics.sessions,executors:diagnostics.executors})}else newTree=buildSessionTree(diagnostics);setSessionTree((prev)=>mergeExpandedState(prev,newTree))},[diagnostics,workspaceRoot]);let flatNodes=import_react15.useMemo(()=>flattenTree(sessionTree),[sessionTree]);import_react15.useEffect(()=>{if(flatNodes.length>0&&selectedIndex>=flatNodes.length)setSelectedIndex(flatNodes.length-1)},[flatNodes.length,selectedIndex]),import_react15.useEffect(()=>{if(!initialAgent||initialSelectDone.current||flatNodes.length===0)return;let idx=flatNodes.findIndex((n)=>n.node.id===`agent:${initialAgent}`);if(idx>=0)setSelectedIndex(idx),initialSelectDone.current=!0},[initialAgent,flatNodes]),import_react15.useEffect(()=>{let current=flatNodes[selectedIndex]?.node;if(!current)return;let target=getSessionTarget(current);if(!target)return;let key=`${target.sessionName}:${target.windowIndex??""}`;if(key===lastTarget.current)return;if(lastTarget.current=key,current.type==="agent"&&current.wsAgentState!=="running")return;onTmuxSessionSelect(target.sessionName,target.windowIndex)},[selectedIndex,flatNodes,onTmuxSessionSelect]);let handleSelect=import_react15.useCallback((id)=>{let idx=flatNodes.findIndex((n)=>n.node.id===id);if(idx>=0)setSelectedIndex(idx)},[flatNodes]),handleToggle=import_react15.useCallback((id)=>{setSessionTree((prev)=>toggleNode(prev,id))},[]),handleVerticalNav=import_react15.useCallback((keyName2)=>{let rowCount=flatNodes.length;if(rowCount===0)return;if(keyName2==="up"||keyName2==="k")setSelectedIndex((prev)=>prev===0?rowCount-1:prev-1);else if(keyName2==="down"||keyName2==="j")setSelectedIndex((prev)=>prev>=rowCount-1?0:prev+1)},[flatNodes.length]),handleExpandCollapse=import_react15.useCallback((keyName2)=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if((keyName2==="right"||keyName2==="l")&&node.children.length>0&&!node.expanded)handleToggle(node.id);else if((keyName2==="left"||keyName2==="h")&&node.expanded)handleToggle(node.id)},[flatNodes,selectedIndex,handleToggle]),handleEnter=import_react15.useCallback(()=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if(node.type==="agent"){if(node.wsAgentState==="stopped")spawnAgent(node.label);let target2=getSessionTarget(node);if(target2)onTmuxSessionSelect(target2.sessionName,target2.windowIndex);return}if(node.children.length>0)handleToggle(node.id);let target=getSessionTarget(node);if(target)onTmuxSessionSelect(target.sessionName,target.windowIndex)},[flatNodes,selectedIndex,handleToggle,onTmuxSessionSelect]);useKeyboard((key)=>{if(key.name==="up"||key.name==="k"||key.name==="down"||key.name==="j")handleVerticalNav(key.name);else if(key.name==="right"||key.name==="l"||key.name==="left"||key.name==="h")handleExpandCollapse(key.name);else if(key.name==="enter"||key.name==="return")handleEnter()});let agentCount=workspaceRoot?sessionTree.filter((n)=>n.type==="agent").length:diagnostics?.sessions.length??0,runningCount=workspaceRoot?sessionTree.filter((n)=>n.wsAgentState==="running").length:diagnostics?.sessions.reduce((sum,s)=>sum+s.windows.reduce((ws,w2)=>ws+w2.panes.length,0),0)??0,headerLabel=workspaceRoot?"Agents":"Sessions";return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",backgroundColor:palette.bg,children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLight,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.purple,children:headerLabel},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[" ",workspaceRoot?`${runningCount}/${agentCount}`:`${agentCount}s ${runningCount}p`]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("scrollbox",{focused:!0,height:"100%",style:{scrollbarOptions:{showArrows:!1,trackOptions:{foregroundColor:palette.scrollThumb,backgroundColor:palette.scrollTrack}}},children:flatNodes.map((flat,i2)=>import_jsx_dev_runtime2.jsxDEV(TreeNodeRow,{node:flat.node,selected:i2===selectedIndex,onSelect:handleSelect,onToggle:handleToggle},flat.node.id,!1,void 0,this))},void 0,!1,void 0,this):import_jsx_dev_runtime2.jsxDEV("box",{flexGrow:1,justifyContent:"center",alignItems:"center",children:import_jsx_dev_runtime2.jsxDEV("text",{fg:palette.textDim,children:"Collecting..."},void 0,!1,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLight,children:import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:["\u2191\u2193",":nav ","\u2190\u2192",":expand Enter:",workspaceRoot?"spawn/attach":"attach"]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function spawnAgent(name){try{let{spawn:spawn4}=__require("child_process"),{join:join53,resolve:resolve10}=__require("path"),{existsSync:existsSync40}=__require("fs"),wsRoot=process.env.GENIE_TUI_WORKSPACE,cwd;if(wsRoot){let agentDir=resolve10(join53(wsRoot,"agents",name));if(existsSync40(agentDir))cwd=agentDir}spawn4("genie",["spawn",name,"--session",name],{detached:!0,stdio:"ignore",cwd}).unref()}catch{}}function mergeExpandedState(oldTree,newTree){if(oldTree.length===0)return newTree;let oldState=new Map;function collect(nodes){for(let n of nodes)oldState.set(n.id,n.expanded),collect(n.children)}collect(oldTree);function apply(nodes){return nodes.map((n)=>({...n,expanded:oldState.has(n.id)?oldState.get(n.id):n.expanded,children:apply(n.children)}))}return apply(newTree)}var import_react15;var init_Nav=__esm(async()=>{init_workspace();init_diagnostics();init_theme2();init_TreeNode();init_jsx_dev_runtime();await init_react();import_react15=__toESM(require_react_development(),1)});function App({rightPane,workspaceRoot,initialAgent}){let handleTmuxSessionSelect=import_react16.useCallback((sessionName,windowIndex)=>{if(!rightPane)return;attachProjectWindow(rightPane,sessionName,windowIndex)},[rightPane]);return import_jsx_dev_runtime2.jsxDEV(Nav,{onTmuxSessionSelect:handleTmuxSessionSelect,workspaceRoot,initialAgent},void 0,!1,void 0,this)}var import_react16;var init_app=__esm(async()=>{init_tmux2();init_jsx_dev_runtime();await init_Nav();import_react16=__toESM(require_react_development(),1)});var exports_render={};__export(exports_render,{renderNav:()=>renderNav});async function renderNav(){let rightPane=process.env.GENIE_TUI_RIGHT||void 0,workspaceRoot=process.env.GENIE_TUI_WORKSPACE||void 0,initialAgent=process.env.GENIE_TUI_AGENT||void 0,renderer=await createCliRenderer({exitOnCtrlC:!1,useMouse:!0});createRoot(renderer).render(import_jsx_dev_runtime2.jsxDEV(App,{rightPane,workspaceRoot,initialAgent},void 0,!1,void 0,this)),await new Promise((resolve10)=>{renderer.once("destroy",resolve10)})}var init_render=__esm(async()=>{init_jsx_dev_runtime();await __promiseAll([init_core(),init_react(),init_app()])});var exports_tui={};__export(exports_tui,{launchTui:()=>launchTui});async function launchTui(){let{renderNav:renderNav2}=await init_render().then(() => exports_render);await renderNav2()}var import__=__toESM(require_commander(),1),{program,createCommand,createArgument,createOption,CommanderError,InvalidArgumentError,InvalidOptionArgumentError,Command,Argument,Option,Help}=import__.default;import{existsSync as existsSync3,unlinkSync as unlinkSync2}from"fs";import{homedir as homedir3}from"os";import{join as join3}from"path";var{$:$2}=globalThis.Bun;import{existsSync,unlinkSync}from"fs";import{homedir}from"os";import{join}from"path";var CLAUDE_DIR=join(homedir(),".claude"),CLAUDE_HOOKS_DIR=join(CLAUDE_DIR,"hooks"),CLAUDE_SETTINGS_FILE=join(CLAUDE_DIR,"settings.json"),GENIE_HOOK_SCRIPT_NAME="genie-bash-hook.sh";function getClaudeSettingsPath(){return CLAUDE_SETTINGS_FILE}function getGenieHookScriptPath(){return join(CLAUDE_HOOKS_DIR,GENIE_HOOK_SCRIPT_NAME)}function hookScriptExists(){return existsSync(getGenieHookScriptPath())}function removeHookScript(){let scriptPath=getGenieHookScriptPath();if(existsSync(scriptPath))unlinkSync(scriptPath)}function contractClaudePath(path){let home=homedir();if(path.startsWith(`${home}/`))return`~${path.slice(home.length)}`;if(path===home)return"~";return path}init_genie_config2();var{$}=globalThis.Bun;async function checkCommand(cmd){try{let cmdPath=(await $`which ${cmd}`.quiet().text()).trim();if(!cmdPath)return{exists:!1};let version;try{let firstLine=(await $`${cmd} --version`.quiet().text()).split(`
2075
2075
  `)[0].trim(),versionMatch=firstLine.match(/(\d+\.[\d.]+[a-z0-9-]*)/i);version=versionMatch?versionMatch[1]:firstLine.slice(0,50)}catch{try{let firstLine=(await $`${cmd} -v`.quiet().text()).split(`
2076
2076
  `)[0].trim(),versionMatch=firstLine.match(/(\d+\.[\d.]+[a-z0-9-]*)/i);version=versionMatch?versionMatch[1]:firstLine.slice(0,50)}catch{}}return{exists:!0,version,path:cmdPath}}catch{return{exists:!1}}}function printSectionHeader(title){console.log(),console.log(`\x1B[1m${title}:\x1B[0m`)}function printCheckResult(result){let icon={pass:"\x1B[32m\u2713\x1B[0m",fail:"\x1B[31m\u2717\x1B[0m",warn:"\x1B[33m!\x1B[0m"}[result.status],message=result.message?` ${result.message}`:"";if(console.log(` ${icon} ${result.name}${message}`),result.suggestion)console.log(` \x1B[2m${result.suggestion}\x1B[0m`)}async function checkPrerequisites(){let results=[],tmuxCheck=await checkCommand("tmux");if(tmuxCheck.exists)results.push({name:"tmux",status:"pass",message:tmuxCheck.version||""});else results.push({name:"tmux",status:"fail",suggestion:"Install with: brew install tmux (or apt install tmux)"});let jqCheck=await checkCommand("jq");if(jqCheck.exists)results.push({name:"jq",status:"pass",message:jqCheck.version||""});else results.push({name:"jq",status:"fail",suggestion:"Install with: brew install jq (or apt install jq)"});let bunCheck=await checkCommand("bun");if(bunCheck.exists)results.push({name:"bun",status:"pass",message:bunCheck.version||""});else results.push({name:"bun",status:"fail",suggestion:"Install with: curl -fsSL https://bun.sh/install | bash"});let claudeCheck=await checkCommand("claude");if(claudeCheck.exists)results.push({name:"Claude Code",status:"pass",message:claudeCheck.version||""});else results.push({name:"Claude Code",status:"warn",suggestion:"Install with: npm install -g @anthropic-ai/claude-code"});return results}async function checkConfiguration(){let results=[];if(genieConfigExists())results.push({name:"Genie config exists",status:"pass",message:contractClaudePath(getGenieConfigPath())});else results.push({name:"Genie config exists",status:"warn",message:"not found",suggestion:"Run: genie setup"});if(isSetupComplete())results.push({name:"Setup complete",status:"pass"});else results.push({name:"Setup complete",status:"warn",message:"not completed",suggestion:"Run: genie setup"});let claudeSettingsPath=getClaudeSettingsPath();if(existsSync3(claudeSettingsPath))results.push({name:"Claude settings exists",status:"pass",message:contractClaudePath(claudeSettingsPath)});else results.push({name:"Claude settings exists",status:"warn",message:"not found",suggestion:"Claude Code creates this on first run"});return results}async function checkTmux(){let results=[];try{if((await $2`tmux -L genie list-sessions 2>/dev/null`.quiet()).exitCode===0)results.push({name:"Server running",status:"pass"});else return results.push({name:"Server running",status:"warn",message:"no sessions",suggestion:"Start with: tmux new-session -d -s genie"}),results}catch{return results.push({name:"Server running",status:"warn",message:"could not check"}),results}let sessionName=(await loadGenieConfig()).session.name;try{if((await $2`tmux -L genie has-session -t ${sessionName} 2>/dev/null`.quiet()).exitCode===0)results.push({name:`Session '${sessionName}' exists`,status:"pass"});else results.push({name:`Session '${sessionName}' exists`,status:"warn",suggestion:`Start with: tmux new-session -d -s ${sessionName}`})}catch{results.push({name:`Session '${sessionName}' exists`,status:"warn",message:"could not check"})}return results}async function checkWorkerProfiles(){let results=[];if(!genieConfigExists())return results.push({name:"Worker profiles",status:"warn",message:"no genie config",suggestion:"Run: genie setup"}),results;let config=await loadGenieConfig(),profiles=config.workerProfiles;if(!profiles||Object.keys(profiles).length===0)return results.push({name:"Worker profiles",status:"pass",message:"none configured (using defaults)"}),results;let totalProfiles=Object.keys(profiles).length;results.push({name:"Profiles configured",status:"pass",message:`${totalProfiles} profile${totalProfiles===1?"":"s"}`});for(let name of Object.keys(profiles))results.push({name:`Profile '${name}'`,status:"pass",message:"claude (direct)"});if(config.defaultWorkerProfile)if(profiles[config.defaultWorkerProfile])results.push({name:"Default profile",status:"pass",message:config.defaultWorkerProfile});else results.push({name:"Default profile",status:"warn",message:`'${config.defaultWorkerProfile}' not found`,suggestion:"Run: genie profiles default <profile>"});return results}function runCheckSection(label,results,counts){printSectionHeader(label);for(let result of results){if(printCheckResult(result),result.status==="fail")counts.errors=!0;if(result.status==="warn")counts.warnings=!0}}async function doctorCommand(options){if(options?.fix){await doctorFix();return}console.log(),console.log("\x1B[1mGenie Doctor\x1B[0m"),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`);let counts={errors:!1,warnings:!1};if(runCheckSection("Prerequisites",await checkPrerequisites(),counts),runCheckSection("Configuration",await checkConfiguration(),counts),runCheckSection("Tmux",await checkTmux(),counts),runCheckSection("Worker Profiles",await checkWorkerProfiles(),counts),console.log(),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`),counts.errors)console.log("\x1B[31mSome checks failed.\x1B[0m Run \x1B[36mgenie setup\x1B[0m to fix.");else if(counts.warnings)console.log("\x1B[33mSome warnings detected.\x1B[0m Everything should still work.");else console.log("\x1B[32mAll checks passed!\x1B[0m");if(console.log(),counts.errors)process.exit(1)}async function killStalePostgres(){console.log(" Killing stale postgres processes...");try{let{execSync}=await import("child_process");execSync('pkill -9 -f "postgres.*pgserve" 2>/dev/null || true',{stdio:"ignore",timeout:5000}),console.log(" \x1B[32m\u2713\x1B[0m Stale postgres processes killed")}catch{console.log(" \x1B[33m!\x1B[0m Could not kill stale postgres processes")}}async function cleanSharedMemory(){console.log(" Cleaning shared memory...");try{let{execSync}=await import("child_process");execSync("ipcs -m 2>/dev/null | awk '$6 == 0 {print $2}' | xargs -I{} ipcrm -m {} 2>/dev/null || true",{stdio:"ignore",timeout:5000}),console.log(" \x1B[32m\u2713\x1B[0m Shared memory cleaned")}catch{console.log(" \x1B[32m\u2713\x1B[0m No stale shared memory")}}async function stopExistingDaemon(pidFile){try{let{readFileSync:readFileSync2}=await import("fs"),pid=Number.parseInt(readFileSync2(pidFile,"utf-8").trim(),10);if(!Number.isNaN(pid)&&pid>0)try{process.kill(pid,"SIGTERM"),console.log(` \x1B[32m\u2713\x1B[0m Stopped existing daemon (PID ${pid})`),await new Promise((r)=>setTimeout(r,1000))}catch{}}catch{}}function removeStaleFiles(genieHome,pidFile){let portFile=join3(genieHome,"pgserve.port"),postmasterPid=join3(genieHome,"data","pgserve","postmaster.pid");for(let file of[portFile,pidFile,postmasterPid])if(existsSync3(file))try{unlinkSync2(file),console.log(` \x1B[32m\u2713\x1B[0m Removed ${file}`)}catch{console.log(` \x1B[33m!\x1B[0m Could not remove ${file}`)}}async function restartDaemon(){console.log(" Restarting daemon...");try{let{spawn}=await import("child_process"),bunPath=process.execPath??"bun",genieBin=process.argv[1]??"genie";spawn(bunPath,[genieBin,"daemon","start"],{detached:!0,stdio:"ignore"}).unref(),await new Promise((resolve)=>setTimeout(resolve,2000)),console.log(" \x1B[32m\u2713\x1B[0m Daemon restart initiated")}catch{console.log(" \x1B[33m!\x1B[0m Could not restart daemon \u2014 run: genie daemon start")}}async function doctorFix(){console.log(`
2077
2077
  \x1B[1mGenie Doctor \u2014 Auto Fix\x1B[0m`),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m
@@ -2,7 +2,7 @@
2
2
  "id": "genie",
3
3
  "name": "Genie",
4
4
  "description": "Skills, agents, and hooks for the Genie CLI terminal orchestration toolkit",
5
- "version": "4.260330.23",
5
+ "version": "4.260330.25",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260330.23",
3
+ "version": "4.260330.25",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,6 +5,9 @@
5
5
  * listeners, and exposes IPC command handlers. Handles graceful shutdown.
6
6
  */
7
7
 
8
+ // Self-contained: the app backend owns pgserve directly (no daemon dependency)
9
+ process.env.GENIE_APP = '1';
10
+
8
11
  import { getConnection } from '../../../src/lib/db.js';
9
12
  import { commands } from './ipc.js';
10
13
  import * as pgBridge from './pg-bridge.js';
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260330.23",
3
+ "version": "4.260330.25",
4
4
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
5
5
  "author": {
6
6
  "name": "Namastex Labs"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260330.23",
3
+ "version": "4.260330.25",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
@@ -33,3 +33,8 @@ set -g pane-border-status off
33
33
  # Pane borders (visual only, no #() shell commands)
34
34
  set -g pane-border-style "fg=#0f3460"
35
35
  set -g pane-active-border-style "fg=#7b2ff7"
36
+
37
+ # Unbind prefix key — all keystrokes go directly to the active pane
38
+ # TUI keybindings are set in the root table by serve.ts
39
+ set -g prefix None
40
+ unbind C-b
package/src/lib/db.ts CHANGED
@@ -249,8 +249,8 @@ async function _ensurePgserve(): Promise<number> {
249
249
  throw new Error('pgserve not available in CI');
250
250
  }
251
251
 
252
- // 4a. If we ARE the daemon — spawn pgserve directly (daemon owns PG).
253
- if (process.env.GENIE_IS_DAEMON === '1') {
252
+ // 4a. If we ARE the daemon or the standalone app — spawn pgserve directly.
253
+ if (process.env.GENIE_IS_DAEMON === '1' || process.env.GENIE_APP === '1') {
254
254
  mkdirSync(DATA_DIR, { recursive: true });
255
255
  selfHealPostgres(DATA_DIR);
256
256
  try {
@@ -88,9 +88,15 @@ function genieTmux(subcmd: string): string {
88
88
  return `tmux -L ${GENIE_SOCKET} -f ${genieTmuxConf()} ${subcmd}`;
89
89
  }
90
90
 
91
- /** Plain tmux command (default server used for TUI session) */
91
+ /** TUI tmux config minimal, no shell probes, no prefix key interference */
92
+ function tuiTmuxConf(): string {
93
+ const candidates = [join(genieHome(), 'tui-tmux.conf')];
94
+ return candidates.find((p) => existsSync(p)) ?? '/dev/null';
95
+ }
96
+
97
+ /** TUI tmux command — uses -L genie-tui socket + minimal TUI config */
92
98
  function tuiTmux(subcmd: string): string {
93
- return `tmux ${subcmd}`;
99
+ return `tmux -L genie-tui -f ${tuiTmuxConf()} ${subcmd}`;
94
100
  }
95
101
 
96
102
  /** Check if a tmux server is running on a socket */
@@ -104,7 +110,6 @@ function isGenieTmuxRunning(): boolean {
104
110
  }
105
111
 
106
112
  const NAV_WIDTH = 30;
107
- const KEY_TABLE = 'genie-tui';
108
113
 
109
114
  /** Theme colors for TUI tmux styling */
110
115
  const TUI_STYLE = {
@@ -117,7 +122,7 @@ function applyTuiStyle(): void {
117
122
  const cmds = [
118
123
  `set-option -t ${TUI_SESSION} pane-border-style 'fg=${TUI_STYLE.inactiveBorder}'`,
119
124
  `set-option -t ${TUI_SESSION} pane-active-border-style 'fg=${TUI_STYLE.activeBorder}'`,
120
- `set-option -t ${TUI_SESSION} mouse off`,
125
+ `set-option -t ${TUI_SESSION} mouse on`,
121
126
  `set-option -t ${TUI_SESSION} status off`,
122
127
  `set-option -t ${TUI_SESSION} pane-border-status off`,
123
128
  ];
@@ -128,18 +133,17 @@ function applyTuiStyle(): void {
128
133
  }
129
134
  }
130
135
 
131
- /** Set up keybindings in a dedicated key table for TUI */
136
+ /** Set up keybindings in root table so they work immediately */
132
137
  function setupTuiKeybindings(): void {
133
138
  const bindings = [
134
139
  // Tab: toggle focus between left nav (pane 0) and right terminal (pane 1)
135
- `bind-key -T ${KEY_TABLE} Tab if-shell "[ '#{pane_index}' = '0' ]" "select-pane -t ${TUI_SESSION}:0.1" "select-pane -t ${TUI_SESSION}:0.0" \\; switch-client -T ${KEY_TABLE}`,
140
+ `bind-key -T root Tab if-shell "[ '#{pane_index}' = '0' ]" "select-pane -t ${TUI_SESSION}:0.1" "select-pane -t ${TUI_SESSION}:0.0"`,
136
141
  // Ctrl+B: toggle sidebar width (collapse/expand)
137
- `bind-key -T ${KEY_TABLE} C-b if-shell "[ $(tmux display-message -p '#\\{pane_width\\}' -t ${TUI_SESSION}:0.0) -gt 5 ]" "resize-pane -t ${TUI_SESSION}:0.0 -x 0" "resize-pane -t ${TUI_SESSION}:0.0 -x ${NAV_WIDTH}" \\; switch-client -T ${KEY_TABLE}`,
138
- // Ctrl+Q: detach from TUI (don't kill serve owns the session)
139
- `bind-key -T ${KEY_TABLE} C-q detach-client`,
140
- `bind-key -T ${KEY_TABLE} 'C-\\\\' detach-client`,
141
- // Activate key table on session attach
142
- `set-hook -t ${TUI_SESSION} client-session-changed "switch-client -T ${KEY_TABLE}"`,
142
+ `bind-key -T root C-b if-shell "[ $(tmux display-message -p '#\\{pane_width\\}' -t ${TUI_SESSION}:0.0) -gt 5 ]" "resize-pane -t ${TUI_SESSION}:0.0 -x 0" "resize-pane -t ${TUI_SESSION}:0.0 -x ${NAV_WIDTH}"`,
143
+ // Ctrl+T: new window in agent session (sends C-b c to the nested tmux in right pane)
144
+ `bind-key -T root C-t send-keys -t ${TUI_SESSION}:0.1 C-b c`,
145
+ // Ctrl+Q: detach from TUI
146
+ 'bind-key -T root C-q detach-client',
143
147
  ];
144
148
  for (const cmd of bindings) {
145
149
  try {
@@ -169,7 +173,7 @@ function startTuiTmuxServer(): { leftPane: string; rightPane: string } {
169
173
  const cols = 120;
170
174
  const rows = 40;
171
175
 
172
- execSync(tuiTmux(`new-session -d -s ${TUI_SESSION} -x ${cols} -y ${rows} -e GENIE_TUI_PANE=left`), {
176
+ execSync(tuiTmux(`new-session -d -s ${TUI_SESSION} -x ${cols} -y ${rows}`), {
173
177
  stdio: 'ignore',
174
178
  });
175
179
  execSync(tuiTmux(`split-window -h -t ${TUI_SESSION}:0 -l ${cols - NAV_WIDTH - 1}`), { stdio: 'ignore' });
@@ -210,10 +214,10 @@ function sendTuiLaunchScript(leftPane: string, rightPane: string, workspaceRoot?
210
214
  } catch {}
211
215
  }
212
216
 
213
- /** Kill the TUI session on the default tmux server */
217
+ /** Kill the TUI tmux server */
214
218
  function killTuiSession(): void {
215
219
  try {
216
- execSync(`tmux kill-session -t ${TUI_SESSION} 2>/dev/null`, { stdio: 'ignore' });
220
+ execSync(tuiTmux('kill-server'), { stdio: 'ignore' });
217
221
  } catch {
218
222
  // not running
219
223
  }
@@ -269,10 +273,10 @@ export async function autoStartServe(): Promise<void> {
269
273
  }
270
274
  }
271
275
 
272
- /** Check if the genie-tui session exists on the default tmux server */
276
+ /** Check if the genie-tui session exists on the TUI socket */
273
277
  export function isTuiSessionReady(): boolean {
274
278
  try {
275
- execSync(`tmux has-session -t ${TUI_SESSION} 2>/dev/null`, { stdio: 'ignore' });
279
+ execSync(tuiTmux(`has-session -t ${TUI_SESSION}`), { stdio: 'ignore' });
276
280
  return true;
277
281
  } catch {
278
282
  return false;
@@ -539,7 +543,7 @@ function printTmuxStatus(): void {
539
543
  }
540
544
 
541
545
  const tuiReady = isTuiSessionReady();
542
- console.log(` TUI session: ${tuiReady ? 'running' : 'stopped'}`);
546
+ console.log(` tmux -L genie-tui: ${tuiReady ? 'running' : 'stopped'}`);
543
547
  }
544
548
 
545
549
  /** Print scheduler and inbox status */
@@ -24,7 +24,7 @@ export function Nav({ onTmuxSessionSelect, workspaceRoot, initialAgent }: NavPro
24
24
  const [sessionTree, setSessionTree] = useState<TreeNode[]>([]);
25
25
  const [selectedIndex, setSelectedIndex] = useState(0);
26
26
  const lastTarget = useRef<string | null>(null);
27
- const genieHome = useRef(process.env.GENIE_HOME ?? `${process.env.HOME}/.genie`);
27
+ const initialSelectDone = useRef(false);
28
28
 
29
29
  // Refresh diagnostics every 2s
30
30
  useEffect(() => {
@@ -76,34 +76,15 @@ export function Nav({ onTmuxSessionSelect, workspaceRoot, initialAgent }: NavPro
76
76
  }
77
77
  }, [flatNodes.length, selectedIndex]);
78
78
 
79
- // File-based initial agent: thin client writes ~/.genie/tui-initial-agent before attaching.
80
- // Check on each diagnostics refresh (2s) so re-attach from a different agent dir works.
81
- const [pendingAgent, setPendingAgent] = useState<string | undefined>(initialAgent);
82
-
83
- useEffect(() => {
84
- if (!diagnostics) return;
85
- try {
86
- const fs = require('node:fs') as typeof import('node:fs');
87
- const agentFile = `${genieHome.current}/tui-initial-agent`;
88
- if (fs.existsSync(agentFile)) {
89
- const agent = fs.readFileSync(agentFile, 'utf-8').trim();
90
- fs.unlinkSync(agentFile);
91
- if (agent) setPendingAgent(agent);
92
- }
93
- } catch {
94
- // best-effort
95
- }
96
- }, [diagnostics]);
97
-
98
- // Apply pending agent selection (from prop or file)
79
+ // Initial agent pre-selection (once)
99
80
  useEffect(() => {
100
- if (!pendingAgent || flatNodes.length === 0) return;
101
- const idx = flatNodes.findIndex((n) => n.node.id === `agent:${pendingAgent}`);
81
+ if (!initialAgent || initialSelectDone.current || flatNodes.length === 0) return;
82
+ const idx = flatNodes.findIndex((n) => n.node.id === `agent:${initialAgent}`);
102
83
  if (idx >= 0) {
103
84
  setSelectedIndex(idx);
104
- setPendingAgent(undefined);
85
+ initialSelectDone.current = true;
105
86
  }
106
- }, [pendingAgent, flatNodes]);
87
+ }, [initialAgent, flatNodes]);
107
88
 
108
89
  // Auto-switch right pane when cursor moves to a new target
109
90
  useEffect(() => {
@@ -162,11 +143,12 @@ export function Nav({ onTmuxSessionSelect, workspaceRoot, initialAgent }: NavPro
162
143
  const node = flatNodes[selectedIndex]?.node;
163
144
  if (!node) return;
164
145
 
165
- // Agent node: spawn if not running, then attach
166
146
  if (node.type === 'agent') {
167
- if (node.wsAgentState !== 'running') {
147
+ // No session → spawn the agent (creates session + window 0 with Claude)
148
+ if (node.wsAgentState === 'stopped') {
168
149
  spawnAgent(node.label);
169
150
  }
151
+ // Attach right pane to the agent's session (existing or just-spawned)
170
152
  const target = getSessionTarget(node);
171
153
  if (target) onTmuxSessionSelect(target.sessionName, target.windowIndex);
172
154
  return;
@@ -255,26 +237,19 @@ export function Nav({ onTmuxSessionSelect, workspaceRoot, initialAgent }: NavPro
255
237
  );
256
238
  }
257
239
 
258
- /** Spawn a stopped agent by launching `genie spawn <name>` in its workspace directory */
240
+ /** Spawn a stopped agent by launching `genie spawn <name>` from its workspace directory */
259
241
  function spawnAgent(name: string): void {
260
242
  try {
261
243
  const { spawn } = require('node:child_process') as typeof import('node:child_process');
262
244
  const { join, resolve } = require('node:path') as typeof import('node:path');
263
245
  const { existsSync } = require('node:fs') as typeof import('node:fs');
264
-
265
- // Resolve agent CWD from workspace path
266
246
  const wsRoot = process.env.GENIE_TUI_WORKSPACE;
267
247
  let cwd: string | undefined;
268
248
  if (wsRoot) {
269
249
  const agentDir = resolve(join(wsRoot, 'agents', name));
270
250
  if (existsSync(agentDir)) cwd = agentDir;
271
251
  }
272
-
273
- spawn('genie', ['spawn', name], {
274
- detached: true,
275
- stdio: 'ignore',
276
- cwd,
277
- }).unref();
252
+ spawn('genie', ['spawn', name, '--session', name], { detached: true, stdio: 'ignore', cwd }).unref();
278
253
  } catch {
279
254
  // best-effort spawn
280
255
  }
package/src/tui/tmux.ts CHANGED
@@ -1,28 +1,30 @@
1
1
  /**
2
2
  * TUI tmux runtime helpers — attach, navigate, pane management.
3
3
  *
4
- * TUI session lives on the DEFAULT tmux server (no -L flag).
5
- * Agent sessions live on the genie server (-L genie).
6
- * This module bridges the two for the right-pane attach.
4
+ * Session creation is handled by `genie serve` (see serve.ts).
5
+ * This module only provides runtime operations for the TUI client.
7
6
  */
8
7
 
9
8
  import { execSync, spawnSync } from 'node:child_process';
10
9
 
11
10
  const SESSION_NAME = 'genie-tui';
12
- /** Genie's agent tmux socket — where all agents/teams/sessions live. */
11
+ const TMUX_SOCKET = 'genie-tui';
13
12
  const GENIE_AGENT_SOCKET = 'genie';
13
+ const TUI_TMUX_CONF = (() => {
14
+ const { existsSync } = require('node:fs') as typeof import('node:fs');
15
+ const home = process.env.GENIE_HOME ?? `${process.env.HOME}/.genie`;
16
+ const tuiConf = `${home}/tui-tmux.conf`;
17
+ return existsSync(tuiConf) ? tuiConf : '/dev/null';
18
+ })();
19
+ const TMUX = `tmux -L ${TMUX_SOCKET} -f ${TUI_TMUX_CONF}`;
14
20
 
15
- /**
16
- * Resolve the right pane ID — self-healing if the pane was killed/recreated.
17
- * Falls back to re-discovering from the session layout.
18
- */
19
21
  function resolveRightPane(rightPane: string): string {
20
22
  try {
21
- execSync(`tmux display-message -t ${rightPane} -p ''`, { stdio: 'ignore' });
23
+ execSync(`${TMUX} display-message -t ${rightPane} -p ''`, { stdio: 'ignore' });
22
24
  return rightPane;
23
25
  } catch {
24
26
  try {
25
- const panes = execSync(`tmux list-panes -t ${SESSION_NAME}:0 -F '#{pane_id}'`, { encoding: 'utf-8' })
27
+ const panes = execSync(`${TMUX} list-panes -t ${SESSION_NAME}:0 -F '#{pane_id}'`, { encoding: 'utf-8' })
26
28
  .trim()
27
29
  .split('\n');
28
30
  return panes[1] || panes[0];
@@ -32,49 +34,45 @@ function resolveRightPane(rightPane: string): string {
32
34
  }
33
35
  }
34
36
 
35
- /** Ensure a tmux session exists on the agent server, creating it if needed */
36
- function ensureAgentSession(sessionName: string): void {
37
+ /** Switch right pane to a specific agent session. NEVER kills the pane. */
38
+ export function attachProjectWindow(rightPane: string, targetSession: string, windowIndex?: number): void {
39
+ if (targetSession === SESSION_NAME) return;
40
+ const pane = resolveRightPane(rightPane);
37
41
  const agentTmux = `tmux -L ${GENIE_AGENT_SOCKET}`;
42
+
43
+ // Ensure agent session exists
38
44
  try {
39
- execSync(`${agentTmux} has-session -t '${sessionName}' 2>/dev/null`, { stdio: 'ignore' });
45
+ execSync(`${agentTmux} has-session -t '${targetSession}' 2>/dev/null`, { stdio: 'ignore' });
40
46
  } catch {
41
- try {
42
- execSync(`${agentTmux} new-session -d -s '${sessionName}'`, { stdio: 'ignore' });
43
- } catch {
44
- // race: another process created it
45
- }
47
+ return; // No session — don't create empty ones, don't kill the pane
46
48
  }
47
- }
48
49
 
49
- /** Switch right pane to a specific session window on the genie agent server */
50
- export function attachProjectWindow(rightPane: string, targetSession: string, windowIndex?: number): void {
51
- // Guard: never attach the TUI session to itself (causes infinite loop)
52
- if (targetSession === SESSION_NAME) return;
53
- const pane = resolveRightPane(rightPane);
54
- ensureAgentSession(targetSession);
55
50
  if (windowIndex !== undefined) {
56
51
  try {
57
- const agentTmux = `tmux -L ${GENIE_AGENT_SOCKET}`;
58
52
  execSync(`${agentTmux} select-window -t '${targetSession}:${windowIndex}'`, { stdio: 'ignore' });
59
- } catch {
60
- // window may not exist
61
- }
53
+ } catch {}
62
54
  }
55
+
56
+ // Hide green status bar
63
57
  try {
64
- // Respawn right pane with a while-true loop so it survives agent exit.
65
- // When the inner attach ends (agent exits or detach), the loop re-attaches.
66
- // If the session is destroyed, attach fails quickly and retries after sleep.
67
- // Selecting a different agent calls respawn-pane -k again, killing this loop.
68
- const attachCmd = `tmux -L ${GENIE_AGENT_SOCKET} attach-session -t '${targetSession}'`;
69
- execSync(`tmux respawn-pane -k -t ${pane} "TMUX='' while true; do ${attachCmd} 2>/dev/null; sleep 0.5; done"`, {
70
- stdio: 'ignore',
71
- });
72
- } catch {
73
- // pane doesn't exist or command failed
74
- }
58
+ execSync(`${agentTmux} set-option -t '${targetSession}' status off 2>/dev/null`, { stdio: 'ignore' });
59
+ } catch {}
60
+
61
+ // respawn-pane with a loop wrapper the pane process is bash running a loop,
62
+ // so if the attach ends (agent exit, detach), the loop retries and the pane survives.
63
+ try {
64
+ const cmd = `while true; do TMUX='' ${agentTmux} attach-session -t '${targetSession}' 2>/dev/null; sleep 0.3; done`;
65
+ execSync(`${TMUX} respawn-pane -k -t ${pane} "bash -c '${cmd}'"`, { stdio: 'ignore' });
66
+ } catch {}
67
+
68
+ // Restore focus to left pane
69
+ try {
70
+ execSync(`${TMUX} select-pane -t ${SESSION_NAME}:0.0`, { stdio: 'ignore' });
71
+ } catch {}
75
72
  }
76
73
 
77
- /** Attach to the TUI session on default tmux server (blocking call) */
78
74
  export function attachTuiSession(): void {
79
- spawnSync('tmux', ['attach-session', '-t', SESSION_NAME], { stdio: 'inherit' });
75
+ spawnSync('tmux', ['-L', TMUX_SOCKET, '-f', TUI_TMUX_CONF, 'attach-session', '-t', SESSION_NAME], {
76
+ stdio: 'inherit',
77
+ });
80
78
  }