@automagik/genie 4.260408.1 → 4.260408.2

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.260408.1",
13
+ "version": "4.260408.2",
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
@@ -334,7 +334,7 @@ ${content}`;if(params.extraArgs){let fileIdx=params.extraArgs.indexOf("--append-
334
334
 
335
335
  ${readFileSync9(params.extraArgs[fileIdx+1],"utf-8")}`,params.extraArgs.splice(fileIdx,2)}writeFileSync9(promptFile,content);let flag=params.promptMode==="system"?"--system-prompt-file":"--append-system-prompt-file";parts.push(flag,escapeShellArg2(promptFile))}else if(params.systemPromptFile){let flag=params.promptMode==="system"?"--system-prompt-file":"--append-system-prompt-file";parts.push(flag,escapeShellArg2(params.systemPromptFile))}}function appendOtelEnv(env,params){if(!params.otelPort||process.env.OTEL_EXPORTER_OTLP_ENDPOINT)return;if(env.CLAUDE_CODE_ENABLE_TELEMETRY="1",env.OTEL_LOGS_EXPORTER="otlp",env.OTEL_METRICS_EXPORTER="otlp",env.OTEL_EXPORTER_OTLP_PROTOCOL="http/json",env.OTEL_EXPORTER_OTLP_ENDPOINT=`http://127.0.0.1:${params.otelPort}`,env.OTEL_LOG_TOOL_DETAILS="1",params.otelLogPrompts!==!1)env.OTEL_LOG_USER_PROMPTS="1";let resourceParts=[];if(params.role)resourceParts.push(`agent.name=${params.role}`);if(params.team)resourceParts.push(`team.name=${params.team}`);if(params.otelWishSlug)resourceParts.push(`wish.slug=${params.otelWishSlug}`);if(params.role)resourceParts.push(`agent.role=${params.role}`);if(resourceParts.length>0)env.OTEL_RESOURCE_ATTRIBUTES=resourceParts.join(",")}function buildClaudeCommand(params){preflightCheck("claude");let parts=[resolveShellBinary("claude")??"claude","--dangerously-skip-permissions"],env={};if(env.GENIE_WORKER="1",params.role)env.GENIE_AGENT_NAME=params.role;if(params.team)env.GENIE_TEAM=params.team;if(appendOtelEnv(env,params),params.nativeTeam?.enabled)appendNativeTeamFlags(parts,env,params.nativeTeam,params);if(params.resume)parts.push("--resume",escapeShellArg2(params.resume));else if(params.sessionId)parts.push("--session-id",escapeShellArg2(params.sessionId));if(params.role)parts.push("--agent",escapeShellArg2(params.role));if(params.model)parts.push("--model",escapeShellArg2(params.model));if(params.name)parts.push("--name",escapeShellArg2(params.name));appendSystemPromptFlags(parts,params);let hookEntry={type:"command",command:buildDispatchCommand(),timeout:15},hooksSettings=JSON.stringify({hooks:{PreToolUse:[{matcher:"*",hooks:[hookEntry]}],PostToolUse:[{matcher:"*",hooks:[hookEntry]}],UserPromptSubmit:[{hooks:[hookEntry]}],Stop:[{hooks:[hookEntry]}]}});if(parts.push("--settings",escapeShellArg2(hooksSettings)),params.extraArgs)for(let arg of params.extraArgs)parts.push(escapeShellArg2(arg));if(params.initialPrompt)parts.push(escapeShellArg2(params.initialPrompt));return{command:parts.join(" "),provider:"claude",env:Object.keys(env).length>0?env:void 0,meta:{role:params.role,skill:params.skill}}}function buildCodexCommand(params){preflightCheck("codex");let parts=["codex"];if(parts.push("--yolo"),parts.push("--no-alt-screen"),params.extraArgs)for(let arg of params.extraArgs)parts.push(escapeShellArg2(arg));let promptParts=[`Genie worker. Team: ${params.team}.`];if(params.role)promptParts.push(`Role: ${params.role}.`);if(params.skill)promptParts.push(`Execute the ${params.skill} skill instructions.`);let prompt2=promptParts.join(" ");return parts.push(escapeShellArg2(prompt2)),{command:parts.join(" "),provider:"codex",meta:{role:params.role,skill:params.skill}}}function buildLaunchCommand(params){let validated=validateSpawnParams(params);switch(validated.provider){case"claude":return buildClaudeCommand(validated);case"codex":return buildCodexCommand(validated);case"claude-sdk":return{command:"claude-sdk-in-process",provider:"claude-sdk",meta:{role:validated.role,skill:validated.skill}};default:throw Error(`Unknown provider "${validated.provider}". Valid providers: claude, codex, claude-sdk`)}}var CLAUDE_TEAM_COLORS,spawnParamsSchema;var init_provider_adapters=__esm(()=>{init_zod();init_inject();CLAUDE_TEAM_COLORS=["red","blue","green","yellow","purple","orange","pink","cyan"],spawnParamsSchema=exports_external.object({provider:exports_external.enum(["claude","codex","claude-sdk","app-pty"]),team:exports_external.string().min(1,"Team name is required"),role:exports_external.string().optional(),skill:exports_external.string().optional(),extraArgs:exports_external.array(exports_external.string()).optional(),nativeTeam:exports_external.object({enabled:exports_external.boolean(),parentSessionId:exports_external.string().optional(),color:exports_external.string().optional(),agentType:exports_external.string().optional(),planModeRequired:exports_external.boolean().optional(),permissionMode:exports_external.string().optional(),agentName:exports_external.string().optional()}).optional(),sessionId:exports_external.string().uuid().optional(),resume:exports_external.string().optional(),systemPromptFile:exports_external.string().optional(),systemPrompt:exports_external.string().optional(),promptMode:exports_external.enum(["system","append"]).optional(),model:exports_external.string().optional(),initialPrompt:exports_external.string().optional(),name:exports_external.string().optional(),otelPort:exports_external.number().optional(),otelLogPrompts:exports_external.boolean().optional(),otelWishSlug:exports_external.string().optional(),newWindow:exports_external.boolean().optional(),windowTarget:exports_external.string().optional()})});var exports_claude_native_teams={};__export(exports_claude_native_teams,{writeNativeInbox:()=>writeNativeInbox,unregisterNativeMember:()=>unregisterNativeMember,sanitizeTeamName:()=>sanitizeTeamName,resolveNativeMemberName:()=>resolveNativeMemberName,registerNativeMember:()=>registerNativeMember,registerAsTeamLead:()=>registerAsTeamLead,loadConfig:()=>loadConfig,listTeamsWithUnreadInbox:()=>listTeamsWithUnreadInbox,listTeams:()=>listTeams,isInsideClaudeCode:()=>isInsideClaudeCode,ensureNativeTeam:()=>ensureNativeTeam,discoverTeamName:()=>discoverTeamName,discoverClaudeParentSessionId:()=>discoverClaudeParentSessionId,deleteNativeTeam:()=>deleteNativeTeam,clearNativeInbox:()=>clearNativeInbox,assignColor:()=>assignColor});import{existsSync as existsSync14}from"fs";import{mkdir as mkdir3,open,readFile as readFile3,readdir as readdir2,rm,stat,unlink as unlink2,writeFile as writeFile2}from"fs/promises";import{homedir as homedir14}from"os";import{join as join15}from"path";function claudeConfigDir2(){return process.env.CLAUDE_CONFIG_DIR??join15(homedir14(),".claude")}function teamsBaseDir(){return join15(claudeConfigDir2(),"teams")}function sanitizeTeamName(name){return name.replace(/[^a-zA-Z0-9]/g,"-").toLowerCase()}async function listTeams(){try{return(await readdir2(teamsBaseDir())).filter((e)=>!e.startsWith("."))}catch{return[]}}function teamDir(teamName){return join15(teamsBaseDir(),sanitizeTeamName(teamName))}function configPath(teamName){return join15(teamDir(teamName),"config.json")}function inboxesDir(teamName){return join15(teamDir(teamName),"inboxes")}function inboxPath(teamName,agentName){return join15(inboxesDir(teamName),`${sanitizeTeamName(agentName)}.json`)}function lockPath(filePath){return`${filePath}.lock`}function isPidAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}async function acquireLock(path2){let lock=lockPath(path2),deadline=Date.now()+LOCK_TIMEOUT_MS;while(Date.now()<deadline)try{await writeFile2(lock,String(process.pid),{flag:"wx"});return}catch{try{let content=await readFile3(lock,"utf-8"),holderPid=Number.parseInt(content.trim(),10);if(!Number.isNaN(holderPid)&&!isPidAlive(holderPid)){try{await unlink2(lock)}catch{}continue}}catch{continue}let jitter=Math.floor(Math.random()*LOCK_POLL_MS);await new Promise((r)=>setTimeout(r,LOCK_POLL_MS+jitter))}console.warn(`[claude-native-teams] Force-acquiring stale lock: ${lock}`),await writeFile2(lock,String(process.pid))}async function releaseLock(path2){try{await unlink2(lockPath(path2))}catch{}}async function loadConfig(teamName){try{let content=await readFile3(configPath(teamName),"utf-8");return JSON.parse(content)}catch(err){if(err instanceof Error&&"code"in err&&err.code==="ENOENT")return null;let message=err instanceof Error?err.message:String(err);return console.warn(`[claude-native-teams] Failed to load config for "${teamName}": ${message}`),null}}async function saveConfig(teamName,config){await writeFile2(configPath(teamName),JSON.stringify(config,null,2))}async function countLeadSessionRefs(){let counts=new Map,teams=await listTeams();for(let team of teams){let leadSessionId=(await loadConfig(team))?.leadSessionId;if(!leadSessionId)continue;counts.set(leadSessionId,(counts.get(leadSessionId)??0)+1)}return counts}async function ensureNativeTeam(teamName,description,leadSessionId,leaderName){let dir=teamDir(teamName),inboxDir=inboxesDir(teamName);await mkdir3(dir,{recursive:!0}),await mkdir3(inboxDir,{recursive:!0}),ensureTeammateBypassPermissions();let existing=await loadConfig(teamName);if(existing)return existing;let sanitized=sanitizeTeamName(teamName),resolvedLeader=sanitizeTeamName(leaderName??teamName),config={name:sanitized,description,createdAt:Date.now(),leadAgentId:`${resolvedLeader}@${sanitized}`,leadSessionId,members:[]};return await saveConfig(teamName,config),config}async function registerNativeMember(teamName,member){let config=await loadConfig(teamName);if(!config)throw Error(`Native team "${teamName}" not found`);let sanitized=sanitizeTeamName(teamName),agentId=`${sanitizeTeamName(member.agentName)}@${sanitized}`;config.members=config.members.filter((m)=>m.agentId!==agentId),config.members.push({agentId,name:sanitizeTeamName(member.agentName),agentType:member.agentType??"general-purpose",joinedAt:Date.now(),tmuxPaneId:member.tmuxPaneId,cwd:member.cwd??process.cwd(),backendType:"tmux",color:member.color,planModeRequired:member.planModeRequired??!1,isActive:!0}),await saveConfig(teamName,config);let inbox=inboxPath(teamName,member.agentName);if(!existsSync14(inbox))await writeFile2(inbox,"[]")}async function unregisterNativeMember(teamName,agentName){let config=await loadConfig(teamName);if(!config)return;let sanitized=sanitizeTeamName(teamName),agentId=`${sanitizeTeamName(agentName)}@${sanitized}`,member=config.members.find((m)=>m.agentId===agentId);if(member)member.isActive=!1;await saveConfig(teamName,config)}async function writeNativeInbox(teamName,agentName,message){let path2=inboxPath(teamName,agentName);await mkdir3(inboxesDir(teamName),{recursive:!0}),await acquireLock(path2);try{let messages2=[];try{let content=await readFile3(path2,"utf-8");messages2=JSON.parse(content)}catch{}messages2.push(message),await writeFile2(path2,JSON.stringify(messages2,null,2))}finally{await releaseLock(path2)}}async function resolveNativeMemberName(teamName,genieWorkerId){let config=await loadConfig(teamName);if(!config||config.members.length===0)return null;let sanitizedId=sanitizeTeamName(genieWorkerId),sanitizedTeam=sanitizeTeamName(teamName),exactMatch=config.members.find((m)=>m.name===sanitizedId&&m.isActive);if(exactMatch)return exactMatch.name;let agentIdMatch=config.members.find((m)=>m.agentId===`${sanitizedId}@${sanitizedTeam}`&&m.isActive);if(agentIdMatch)return agentIdMatch.name;let teamPrefix=`${sanitizedTeam}-`;if(sanitizedId.startsWith(teamPrefix)){let stripped=sanitizedId.slice(teamPrefix.length),prefixMatch=config.members.find((m)=>m.name===stripped&&m.isActive);if(prefixMatch)return prefixMatch.name}let inactiveMatch=config.members.find((m)=>m.name===sanitizedId);if(inactiveMatch)return inactiveMatch.name;return null}async function assignColor(teamName){let config=await loadConfig(teamName);if(!config)return CLAUDE_TEAM_COLORS[0];let usedColors=new Set(config.members.map((m)=>m.color));for(let color of CLAUDE_TEAM_COLORS)if(!usedColors.has(color))return color;return CLAUDE_TEAM_COLORS[config.members.length%CLAUDE_TEAM_COLORS.length]}async function clearNativeInbox(teamName,agentName){let path2=inboxPath(teamName,agentName);await acquireLock(path2);try{await writeFile2(path2,"[]")}finally{await releaseLock(path2)}}async function deleteNativeTeam(teamName){let dir=teamDir(teamName);if(!existsSync14(dir))return!1;return await rm(dir,{recursive:!0,force:!0}),!0}function extractLeaderInboxName(config,teamName){if(!config?.leadAgentId)return teamName??"unknown";let atIdx=config.leadAgentId.indexOf("@");return atIdx>0?config.leadAgentId.slice(0,atIdx):teamName??"unknown"}async function scanTeamInbox(base,name){let config=null;try{let cfgContent=await readFile3(join15(base,name,"config.json"),"utf-8");config=JSON.parse(cfgContent)}catch{}let leaderInboxName=extractLeaderInboxName(config,name),inboxFile=join15(base,name,"inboxes",`${leaderInboxName}.json`),messages2;try{let content=await readFile3(inboxFile,"utf-8");messages2=JSON.parse(content)}catch{return null}if(!Array.isArray(messages2))return null;let unread=messages2.filter((m)=>m.read===!1);if(unread.length===0)return null;let workingDir=null;if(config){let leadMember=config.members.find((m)=>m.agentId===config?.leadAgentId||m.name===leaderInboxName);if(leadMember?.cwd)workingDir=leadMember.cwd}return{teamName:name,unreadCount:unread.length,workingDir,firstUnreadText:unread[0]?.text??null}}async function listTeamsWithUnreadInbox(){let base=teamsBaseDir(),teamDirs;try{teamDirs=await readdir2(base)}catch{return[]}let results=[];for(let name of teamDirs){let entry=await scanTeamInbox(base,name);if(entry)results.push(entry)}return results}function sanitizePath(p){return p.replace(/[^a-zA-Z0-9]/g,"-")}async function discoverClaudeSessionId(cwd){let envSessionId=process.env.CLAUDE_CODE_SESSION_ID;if(envSessionId)return envSessionId;let projectDir=join15(claudeConfigDir2(),"projects",sanitizePath(cwd??process.cwd()));try{let jsonls=(await readdir2(projectDir)).filter((e)=>e.endsWith(".jsonl"));if(jsonls.length===0)return null;let newest=null;for(let name of jsonls){let s=await stat(join15(projectDir,name));if(!newest||s.mtimeMs>newest.mtime)newest={name,mtime:s.mtimeMs}}if(!newest)return null;return newest.name.replace(".jsonl","")}catch{return null}}async function readSessionMetadata(filePath){let handle=null;try{handle=await open(filePath,"r");let buffer2=Buffer.alloc(8192),{bytesRead}=await handle.read(buffer2,0,buffer2.length,0),head=buffer2.toString("utf-8",0,bytesRead);for(let line of head.split(`
336
336
  `).slice(0,20)){let trimmed=line.trim();if(!trimmed)continue;try{let entry=JSON.parse(trimmed),teamName=typeof entry.teamName==="string"?entry.teamName:void 0,agentName=typeof entry.agentName==="string"?entry.agentName:void 0;if(teamName||agentName)return{teamName,agentName}}catch{}}}catch{return{}}finally{await handle?.close().catch(()=>{})}return{}}function rootScore(metadata){if(!metadata.teamName&&!metadata.agentName)return 2;if(metadata.teamName&&!metadata.agentName)return 1;return 0}function compareSessionRanking(a,b2,leadRefs){let aLeadRefs=leadRefs.get(a.name.replace(".jsonl",""))??0,bLeadRefs=leadRefs.get(b2.name.replace(".jsonl",""))??0;if(aLeadRefs!==bLeadRefs)return bLeadRefs-aLeadRefs;let aRoot=rootScore(a.metadata),bRoot=rootScore(b2.metadata);if(aRoot!==bRoot)return bRoot-aRoot;return b2.mtime-a.mtime}async function discoverClaudeParentSessionId(cwd){let envSessionId=process.env.CLAUDE_CODE_SESSION_ID;if(envSessionId)return envSessionId;let projectDir=join15(claudeConfigDir2(),"projects",sanitizePath(cwd??process.cwd()));try{let jsonls=(await readdir2(projectDir)).filter((e)=>e.endsWith(".jsonl"));if(jsonls.length===0)return null;let ranked=await Promise.all(jsonls.map(async(name)=>{let filePath=join15(projectDir,name),s=await stat(filePath),metadata=await readSessionMetadata(filePath);return{name,mtime:s.mtimeMs,metadata}})),leadRefs=await countLeadSessionRefs();return ranked.sort((a,b2)=>compareSessionRanking(a,b2,leadRefs)),ranked[0]?.name.replace(".jsonl","")??null}catch{return null}}function isInsideClaudeCode(){return process.env.CLAUDECODE==="1"}async function discoverTeamName(cwd){let envTeam=process.env.GENIE_TEAM;if(envTeam)return envTeam;let sessionId=await discoverClaudeSessionId(cwd);if(!sessionId)return null;let base=teamsBaseDir();try{let teams=await readdir2(base);for(let name of teams){let cfgPath=join15(base,name,"config.json");try{let content=await readFile3(cfgPath,"utf-8"),config=JSON.parse(content);if(config.leadSessionId===sessionId)return config.name}catch{}}}catch{}return null}async function registerAsTeamLead(teamName,opts){let sessionId=await discoverClaudeSessionId(opts?.cwd);if(!sessionId)throw Error("Could not discover Claude Code session ID. Are you running inside Claude Code with CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1?");let resolvedLeaderName=opts?.leaderName??teamName,config=await ensureNativeTeam(teamName,`Genie team: ${teamName}`,sessionId,resolvedLeaderName);if(config.leadSessionId!==sessionId)config.leadSessionId=sessionId,await saveConfig(teamName,config);let sanitized=sanitizeTeamName(teamName),leadAgentId=`${sanitizeTeamName(resolvedLeaderName)}@${sanitized}`,existingLead=config.members.find((m)=>m.agentId===leadAgentId),resolvedPaneId=opts?.tmuxPaneId??process.env.TMUX_PANE;if(!existingLead||!existingLead.isActive)await registerNativeMember(teamName,{agentName:resolvedLeaderName,agentType:"general-purpose",color:opts?.color??"blue",tmuxPaneId:resolvedPaneId,cwd:opts?.cwd??process.cwd()});else if(resolvedPaneId&&existingLead.tmuxPaneId!==resolvedPaneId)existingLead.tmuxPaneId=resolvedPaneId,await saveConfig(teamName,config);let inbox=inboxPath(teamName,resolvedLeaderName);if(!existsSync14(inbox))await writeFile2(inbox,"[]");let finalConfig=await loadConfig(teamName);if(!finalConfig)throw Error(`Failed to load config for team "${teamName}" after creation`);return{sessionId,config:finalConfig}}var LOCK_TIMEOUT_MS=5000,LOCK_POLL_MS=50;var init_claude_native_teams=__esm(()=>{init_claude_settings();init_provider_adapters()});var exports_team_lead_command={};__export(exports_team_lead_command,{shellQuote:()=>shellQuote,sessionExists:()=>sessionExists,ccProjectDirName:()=>ccProjectDirName,buildTeamLeadCommand:()=>buildTeamLeadCommand});import{readFileSync as readFileSync9,readdirSync as readdirSync3}from"fs";import{basename,join as join16}from"path";function shellQuote(s){return`'${s.replace(/'/g,"'\\''")}'`}function buildTeamLeadCommand(teamName,options){let sanitized=sanitizeTeamName(teamName),qTeam=shellQuote(sanitized),folderName=basename(process.cwd()),resolvedLeader=options?.leaderName??teamName,sanitizedLeader=sanitizeTeamName(resolvedLeader),parts=["GENIE_WORKER=1","CLAUDECODE=1","CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1",`GENIE_TEAM=${qTeam}`,`GENIE_AGENT_NAME=${shellQuote(folderName)}`,"claude",`--agent-id ${shellQuote(`${sanitizedLeader}@${sanitized}`)}`,`--agent-name ${shellQuote(sanitizedLeader)}`,`--team-name ${qTeam}`,"--agent-type team-lead","--dangerously-skip-permissions"];if(parts.push(`--name ${shellQuote(sanitized)}`),options?.continueName)parts.push(`--resume ${shellQuote(options.continueName)}`);else if(options?.sessionId)parts.push(`--session-id ${shellQuote(options.sessionId)}`);if(options?.systemPromptFile){let promptFlag=(options?.promptMode??loadGenieConfigSync().promptMode)==="system"?"--system-prompt-file":"--append-system-prompt-file";parts.push(`${promptFlag} ${shellQuote(options.systemPromptFile)}`)}return parts.join(" ")}function ccProjectDirName(dir){return dir.replace(/\//g,"-")}function fileHasSessionName(filePath,needle){try{let lines=readFileSync9(filePath,"utf-8").split(`
337
- `).slice(0,10);for(let line of lines){if(!line.includes("custom-title"))continue;let entry=JSON.parse(line);if(entry.type==="custom-title"&&entry.customTitle?.toLowerCase()===needle)return!0}}catch{}return!1}function sessionExists(name,cwd){try{let home=process.env.HOME??"/root",projectDir=ccProjectDirName(cwd??process.cwd()),projectPath=join16(home,".claude","projects",projectDir),files;try{files=readdirSync3(projectPath).filter((f)=>f.endsWith(".jsonl"))}catch{return!1}let needle=name.toLowerCase();return files.some((file)=>{let full=join16(projectPath,file);return fileHasSessionName(full,needle)||fileHasSessionName(full,`${needle}-${needle}`)})}catch{return!1}}var init_team_lead_command=__esm(()=>{init_claude_native_teams();init_genie_config2()});var exports_tmux_wrapper={};__export(exports_tmux_wrapper,{genieTmuxPrefix:()=>genieTmuxPrefix,genieTmuxCmd:()=>genieTmuxCmd,executeTmux:()=>executeTmux});import{exec as execCallback}from"child_process";import{existsSync as existsSync15,mkdirSync as mkdirSync8}from"fs";import{homedir as homedir15}from"os";import{join as join17}from"path";import{promisify}from"util";function resolveGenieTmuxConf(){let home=homedir15(),genieHome2=process.env.GENIE_HOME??join17(home,".genie");return[join17(genieHome2,"tmux.conf"),join17(__dirname,"..","..","scripts","tmux","genie.tmux.conf")].find((p)=>existsSync15(p))??"/dev/null"}function genieTmuxPrefix(){return["-L",GENIE_TMUX_SOCKET,"-f",resolveGenieTmuxConf()]}function genieTmuxCmd(subcommand){return`${tmuxBin()} ${genieTmuxPrefix().join(" ")} ${subcommand}`}function getLogDir(){let logDir=join17(homedir15(),".genie","logs","tmux");if(!existsSync15(logDir))mkdirSync8(logDir,{recursive:!0});return logDir}function stripVerboseFlags(args){return args.filter((arg)=>!/^-v+$/.test(arg))}function isTmuxDebugEnabled(){return process.env.GENIE_TMUX_DEBUG==="1"}async function executeTmux(args){let argList=typeof args==="string"?args.split(/\s+/).filter(Boolean):args,finalArgs=stripVerboseFlags(argList),debugMode=isTmuxDebugEnabled(),options={};if(debugMode)finalArgs=["-v",...finalArgs],options.cwd=getLogDir();finalArgs=[...genieTmuxPrefix(),...finalArgs];let command=`${tmuxBin()} ${finalArgs.join(" ")}`,{stdout}=await exec(command,options);return stdout.trim()}var __dirname="/home/runner/_work/genie/genie/src/lib",exec,GENIE_TMUX_SOCKET;var init_tmux_wrapper=__esm(()=>{init_ensure_tmux();exec=promisify(execCallback),GENIE_TMUX_SOCKET=process.env.GENIE_TMUX_SOCKET||"genie"});var exports_tmux={};__export(exports_tmux,{setWindowEnv:()=>setWindowEnv,resolveRepoSession:()=>resolveRepoSession,listWindows:()=>listWindows,listPanes:()=>listPanes,killWindow:()=>killWindow,killSession:()=>killSession,isPaneProcessRunning:()=>isPaneProcessRunning,isPaneAlive:()=>isPaneAlive,getWindowEnv:()=>getWindowEnv,getCurrentSessionName:()=>getCurrentSessionName,findWindowByName:()=>findWindowByName,findSessionByName:()=>findSessionByName,executeTmux:()=>executeTmux2,ensureTeamWindow:()=>ensureTeamWindow,createSession:()=>createSession,capturePaneContent:()=>capturePaneContent,applyPaneColor:()=>applyPaneColor,TmuxUnreachableError:()=>TmuxUnreachableError});import{basename as basename2}from"path";async function executeTmux2(tmuxCommand){let{executeTmux:wrapperExec}=await Promise.resolve().then(() => (init_tmux_wrapper(),exports_tmux_wrapper));try{return await wrapperExec(tmuxCommand)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);throw Error(`Failed to execute tmux command: ${message}`)}}async function getCurrentSessionName(hint){if(process.env.TMUX)try{return(await executeTmux2("display-message -p '#{session_name}'")).trim()||null}catch{return null}try{let sessions=await listSessions();if(sessions.length===0)return null;if(hint){let match=sessions.find((s)=>s.name.includes(hint));if(match)return match.name}return sessions[0].name}catch{return null}}async function listSessions(){try{let output=await executeTmux2("list-sessions -F '#{session_id}:#{session_name}:#{?session_attached,1,0}:#{session_windows}'");if(!output)return[];return output.split(`
337
+ `).slice(0,10);for(let line of lines){if(!line.includes("custom-title"))continue;let entry=JSON.parse(line);if(entry.type==="custom-title"&&entry.customTitle?.toLowerCase()===needle)return!0}}catch{}return!1}function sessionExists(name,cwd){try{let home=process.env.HOME??"/root",projectDir=ccProjectDirName(cwd??process.cwd()),projectPath=join16(home,".claude","projects",projectDir),files;try{files=readdirSync3(projectPath).filter((f)=>f.endsWith(".jsonl"))}catch{return!1}let needle=name.toLowerCase();return files.some((file)=>{let full=join16(projectPath,file);return fileHasSessionName(full,needle)||fileHasSessionName(full,`${needle}-${needle}`)})}catch{return!1}}var init_team_lead_command=__esm(()=>{init_claude_native_teams();init_genie_config2()});var exports_tmux_wrapper={};__export(exports_tmux_wrapper,{genieTmuxPrefix:()=>genieTmuxPrefix,genieTmuxCmd:()=>genieTmuxCmd,executeTmux:()=>executeTmux});import{exec as execCallback}from"child_process";import{existsSync as existsSync15,mkdirSync as mkdirSync8}from"fs";import{homedir as homedir15}from"os";import{join as join17}from"path";import{promisify}from"util";function resolveGenieTmuxConf(){let home=homedir15(),genieHome2=process.env.GENIE_HOME??join17(home,".genie");return[join17(genieHome2,"tmux.conf"),join17(__dirname,"..","..","scripts","tmux","genie.tmux.conf"),join17(home,".bun","install","global","node_modules","@automagik","genie","scripts","tmux","genie.tmux.conf")].find((p)=>existsSync15(p))??"/dev/null"}function genieTmuxPrefix(){return["-L",GENIE_TMUX_SOCKET,"-f",resolveGenieTmuxConf()]}function genieTmuxCmd(subcommand){return`${tmuxBin()} ${genieTmuxPrefix().join(" ")} ${subcommand}`}function getLogDir(){let logDir=join17(homedir15(),".genie","logs","tmux");if(!existsSync15(logDir))mkdirSync8(logDir,{recursive:!0});return logDir}function stripVerboseFlags(args){return args.filter((arg)=>!/^-v+$/.test(arg))}function isTmuxDebugEnabled(){return process.env.GENIE_TMUX_DEBUG==="1"}async function executeTmux(args){let argList=typeof args==="string"?args.split(/\s+/).filter(Boolean):args,finalArgs=stripVerboseFlags(argList),debugMode=isTmuxDebugEnabled(),options={};if(debugMode)finalArgs=["-v",...finalArgs],options.cwd=getLogDir();finalArgs=[...genieTmuxPrefix(),...finalArgs];let command=`${tmuxBin()} ${finalArgs.join(" ")}`,{stdout}=await exec(command,options);return stdout.trim()}var __dirname="/home/runner/_work/genie/genie/src/lib",exec,GENIE_TMUX_SOCKET;var init_tmux_wrapper=__esm(()=>{init_ensure_tmux();exec=promisify(execCallback),GENIE_TMUX_SOCKET=process.env.GENIE_TMUX_SOCKET||"genie"});var exports_tmux={};__export(exports_tmux,{setWindowEnv:()=>setWindowEnv,resolveRepoSession:()=>resolveRepoSession,listWindows:()=>listWindows,listPanes:()=>listPanes,killWindow:()=>killWindow,killSession:()=>killSession,isPaneProcessRunning:()=>isPaneProcessRunning,isPaneAlive:()=>isPaneAlive,getWindowEnv:()=>getWindowEnv,getCurrentSessionName:()=>getCurrentSessionName,findWindowByName:()=>findWindowByName,findSessionByName:()=>findSessionByName,executeTmux:()=>executeTmux2,ensureTeamWindow:()=>ensureTeamWindow,createSession:()=>createSession,capturePaneContent:()=>capturePaneContent,applyPaneColor:()=>applyPaneColor,TmuxUnreachableError:()=>TmuxUnreachableError});import{basename as basename2}from"path";async function executeTmux2(tmuxCommand){let{executeTmux:wrapperExec}=await Promise.resolve().then(() => (init_tmux_wrapper(),exports_tmux_wrapper));try{return await wrapperExec(tmuxCommand)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);throw Error(`Failed to execute tmux command: ${message}`)}}async function getCurrentSessionName(hint){if(process.env.TMUX)try{return(await executeTmux2("display-message -p '#{session_name}'")).trim()||null}catch{return null}try{let sessions=await listSessions();if(sessions.length===0)return null;if(hint){let match=sessions.find((s)=>s.name.includes(hint));if(match)return match.name}return sessions[0].name}catch{return null}}async function listSessions(){try{let output=await executeTmux2("list-sessions -F '#{session_id}:#{session_name}:#{?session_attached,1,0}:#{session_windows}'");if(!output)return[];return output.split(`
338
338
  `).map((line)=>{let[id,name,attached,windows]=line.split(":");return{id,name,attached:attached==="1",windows:Number.parseInt(windows,10)}})}catch(error2){if((error2 instanceof Error?error2.message:String(error2)).includes("no server running"))return[];throw error2}}async function findSessionByName(name){try{return(await listSessions()).find((session)=>session.name===name)||null}catch(_error){return null}}async function getWindowEnv(target,varName){try{let output=await executeTmux2(`show-environment -t ${shellQuote(target)} ${shellQuote(varName)}`),prefix=`${varName}=`;if(output?.startsWith(prefix))return output.slice(prefix.length).trim();return null}catch{return null}}async function setWindowEnv(target,varName,value){await executeTmux2(`set-environment -t ${shellQuote(target)} ${shellQuote(varName)} ${shellQuote(value)}`)}async function killSession(sessionId){await executeTmux2(`kill-session -t '${sessionId}'`)}async function listWindows(sessionId){try{let output=await executeTmux2(`list-windows -t '${sessionId}' -F '#{window_id}:#{window_name}:#{window_index}:#{?window_active,1,0}'`);if(!output)return[];return output.split(`
339
339
  `).map((line)=>{let[id,name,indexStr,active]=line.split(":");return{id,name,index:Number.parseInt(indexStr,10),active:active==="1",sessionId}})}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);if(message.includes("no server running")||message.includes("session not found"))return[];throw error2}}async function listPanes(windowId){try{let output=await executeTmux2(`list-panes -t '${windowId}' -F '#{pane_id}:#{pane_title}:#{?pane_active,1,0}'`);if(!output)return[];return output.split(`
340
340
  `).map((line)=>{let[id,title,active]=line.split(":");return{id,windowId,title,active:active==="1"}})}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);if(message.includes("no server running")||message.includes("window not found"))return[];throw error2}}async function capturePaneContent(paneId,lines=200,includeColors=!1){try{return await executeTmux2(`capture-pane -p ${includeColors?"-e":""} -t '${paneId}' -S -${lines} -E -`)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);if(message.includes("no server running")||message.includes("pane not found"))return"";throw error2}}async function createSession(name){return await executeTmux2(`new-session -d -s "${name}" -e LC_ALL=C.UTF-8 -e LANG=C.UTF-8`),findSessionByName(name)}async function createWindow(sessionId,name,workingDir){let cdFlag=workingDir?` -c '${workingDir.replace(/'/g,"'\\''")}'`:"",output=await executeTmux2(`new-window -d -P -F '#{window_id}:#{window_index}' -t '${sessionId}:' -n '${name}'${cdFlag}`),[windowId,indexStr]=output.trim().split(":");if(!windowId)return null;try{await executeTmux2(`set-window-option -t '${windowId}' automatic-rename off`)}catch{}return{id:windowId,name,index:Number.parseInt(indexStr,10)||0,active:!1,sessionId}}async function findWindowByName(sessionId,name){return(await listWindows(sessionId)).find((w)=>w.name===name)||null}async function ensureMasterWindow(session,masterName){try{let windows=await listWindows(session);if(windows.length<2)return;let masterWindow=windows.find((w)=>w.name===masterName);if(!masterWindow)return;let minIndex=Math.min(...windows.map((w)=>w.index));if(masterWindow.index===minIndex)return;await executeTmux2(`swap-window -s '${session}:${masterWindow.index}' -t '${session}:${minIndex}'`)}catch{}}async function ensureSessionExists(name){try{await executeTmux2(`new-session -d -s "${name}" -e LC_ALL=C.UTF-8 -e LANG=C.UTF-8`)}catch(error2){if((error2 instanceof Error?error2.message:String(error2)).includes("duplicate session"))return;throw error2}}async function ensureTeamWindow(session,teamName,workingDir){for(let attempt=0;attempt<3;attempt++)try{return await ensureTeamWindowOnce(session,teamName,workingDir)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);if(message.includes("no server running")||message.includes("server exited")||message.includes("error connecting")){if(attempt<2){let delay=250*2**attempt;console.warn(`[genie-tmux] tmux server unreachable (attempt ${attempt+1}/3), retrying in ${delay}ms...`),await new Promise((resolve2)=>setTimeout(resolve2,delay));continue}}throw error2}throw Error("Failed to ensure team window after 3 attempts")}async function ensureTeamWindowOnce(session,teamName,workingDir){await ensureSessionExists(session);let existing=await findWindowByName(session,teamName);if(existing){try{await executeTmux2(`set-window-option -t '${existing.id}' automatic-rename off`)}catch{}await rehydratePaneColorHook(existing.id);let panes2=await listPanes(existing.id),paneId2=panes2.length>0?panes2[0].id:`${session}:${teamName}.0`;return{windowId:existing.id,windowName:teamName,paneId:paneId2,created:!1}}let windowsBefore=await listWindows(session),masterBefore=windowsBefore.length>0?windowsBefore.reduce((a,b2)=>a.index<=b2.index?a:b2):null,newWindow=await createWindow(session,teamName,workingDir);if(!newWindow)throw Error(`Failed to create team window "${teamName}" in session "${session}"`);if(masterBefore)await ensureMasterWindow(session,masterBefore.name);await rehydratePaneColorHook(newWindow.id);let panes=await listPanes(newWindow.id),paneId=panes.length>0?panes[0].id:`${session}:${teamName}.0`;return{windowId:newWindow.id,windowName:teamName,paneId,created:!0}}function ensurePaneColorScript(){let{existsSync:existsSync16,writeFileSync:writeFileSync9,mkdirSync:mkdirSync9,chmodSync:chmodSync3}=__require("fs"),{dirname:dirname3}=__require("path");if(existsSync16(PANE_COLOR_SCRIPT))return;mkdirSync9(dirname3(PANE_COLOR_SCRIPT),{recursive:!0});let bin=tmuxBin();writeFileSync9(PANE_COLOR_SCRIPT,`#!/bin/bash
@@ -1657,18 +1657,18 @@ Stopped following`),process.exit(0)}),await new Promise(()=>{});return}let conte
1657
1657
  `,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`
1658
1658
  INSERT INTO machine_snapshots (id, active_workers, active_teams, tmux_sessions, cpu_percent, memory_mb, created_at)
1659
1659
  VALUES (${snapshotId}, ${activeWorkers}, ${teams.size}, ${tmuxSessions}, ${cpuPercent}, ${memoryMb}, ${now})
1660
- `,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,leaseRecoveryTimer=null,inboxWatcherHandle=null,captureFallbackTimer=null,eventRouterHandle=null,deliveryUnsub=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(leaseRecoveryTimer)clearInterval(leaseRecoveryTimer),leaseRecoveryTimer=null;if(inboxWatcherHandle)stopInboxWatcher(inboxWatcherHandle),inboxWatcherHandle=null;if(listenConnection)listenConnection.end().catch(()=>{}),listenConnection=null;if(captureFallbackTimer)clearInterval(captureFallbackTimer),captureFallbackTimer=null;if(eventRouterHandle?.stop().catch(()=>{}),eventRouterHandle=null,deliveryUnsub)deliveryUnsub().catch(()=>{}),deliveryUnsub=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 startLeaseRecoveryTimer(d,cfg,dId){return setInterval(async()=>{if(!running2)return;try{await reclaimExpiredLeases(d,dId)}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"lease_recovery_error",error:message})}},cfg.leaseRecoveryIntervalMs)}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),leaseRecoveryTimer=startLeaseRecoveryTimer(deps,config,daemonId),inboxWatcherHandle=startInboxWatcherIfEnabled(deps),eventRouterHandle=await startEventRouterSafe(deps);try{deliveryUnsub=await subscribeDelivery(async(toWorker,messageId)=>{try{let{deliverToPane:deliverToPane2}=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router));await deliverToPane2(toWorker,messageId)}catch{}}),deps.log({timestamp:deps.now().toISOString(),level:"info",event:"mailbox_delivery_listen_started"})}catch{}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_mailbox();init_run_spec();previousWorkerStates=new Map});var exports_serve={};__export(exports_serve,{registerServeCommands:()=>registerServeCommands,isTuiSessionReady:()=>isTuiSessionReady,isServeRunning:()=>isServeRunning,getTuiQuitBindingArgs:()=>getTuiQuitBindingArgs,getTuiKeybindings:()=>getTuiKeybindings,ensureTuiSession:()=>ensureTuiSession,autoStartServe:()=>autoStartServe});import{execSync as execSync9,spawn as spawn4,spawnSync as spawnSync3}from"child_process";import{existsSync as existsSync26,mkdirSync as mkdirSync11,readFileSync as readFileSync15,unlinkSync as unlinkSync7,writeFileSync as writeFileSync13}from"fs";import{homedir as homedir23}from"os";import{join as join35}from"path";function genieHome3(){return process.env.GENIE_HOME??join35(homedir23(),".genie")}function servePidPath(){return join35(genieHome3(),"serve.pid")}function genieTmuxConf(){return[join35(genieHome3(),"tmux.conf")].find((p)=>existsSync26(p))??"/dev/null"}function readServePid(){let path3=servePidPath();if(!existsSync26(path3))return null;let raw=readFileSync15(path3,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function writeServePid(pid){mkdirSync11(genieHome3(),{recursive:!0}),writeFileSync13(servePidPath(),String(pid),"utf-8")}function removeServePid(){let path3=servePidPath();if(existsSync26(path3))try{unlinkSync7(path3)}catch{}}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function genieTmux(subcmd){return`${tmuxBin()} -L ${GENIE_SOCKET} -f ${genieTmuxConf()} ${subcmd}`}function tuiTmuxConf(){return[join35(genieHome3(),"tui-tmux.conf")].find((p)=>existsSync26(p))??"/dev/null"}function tuiTmux(subcmd){return`${tmuxBin()} -L genie-tui -f ${tuiTmuxConf()} ${subcmd}`}function isGenieTmuxRunning(){try{return execSync9(genieTmux("list-sessions"),{stdio:"ignore"}),!0}catch{return!1}}function getTuiKeybindings(sessionName=TUI_SESSION){return[`bind-key -T root Tab if-shell "[ '#{pane_index}' = '0' ]" "select-pane -t ${sessionName}:0.1" "select-pane -t ${sessionName}:0.0"`,`bind-key -T root C-1 select-pane -t ${sessionName}:0.0`,`bind-key -T root C-2 select-pane -t ${sessionName}:0.1`,`bind-key -T root C-b if-shell "[ $(tmux display-message -p '#\\{pane_width\\}' -t ${sessionName}:0.0) -gt 5 ]" "resize-pane -t ${sessionName}:0.0 -x 0" "resize-pane -t ${sessionName}:0.0 -x ${NAV_WIDTH}"`,`bind-key -T root C-t select-pane -t ${sessionName}:0.0 \\; send-keys -t ${sessionName}:0.0 C-t`,"bind-key -T root C-d detach-client",`bind-key -T root C-q select-pane -t ${sessionName}:0.0 \\; send-keys -t ${sessionName}:0.0 C-q`]}function getTuiQuitBindingArgs(sessionName=TUI_SESSION){return["bind-key","-T","root","C-q","select-pane","-t",`${sessionName}:0.0`,"\\;","send-keys","-t",`${sessionName}:0.0`,"C-q"]}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{execSync9(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function setupTuiKeybindings(){for(let cmd of getTuiKeybindings())try{if(cmd.startsWith("bind-key -T root C-q "))spawnSync3(tmuxBin(),["-L","genie-tui","-f",tuiTmuxConf(),...getTuiQuitBindingArgs()],{stdio:"ignore"});else execSync9(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function startTuiTmuxServer(){try{execSync9(tuiTmux(`has-session -t ${TUI_SESSION}`),{stdio:"ignore"});let panes2=execSync9(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
1660
+ `,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,leaseRecoveryTimer=null,inboxWatcherHandle=null,captureFallbackTimer=null,eventRouterHandle=null,deliveryUnsub=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(leaseRecoveryTimer)clearInterval(leaseRecoveryTimer),leaseRecoveryTimer=null;if(inboxWatcherHandle)stopInboxWatcher(inboxWatcherHandle),inboxWatcherHandle=null;if(listenConnection)listenConnection.end().catch(()=>{}),listenConnection=null;if(captureFallbackTimer)clearInterval(captureFallbackTimer),captureFallbackTimer=null;if(eventRouterHandle?.stop().catch(()=>{}),eventRouterHandle=null,deliveryUnsub)deliveryUnsub().catch(()=>{}),deliveryUnsub=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 startLeaseRecoveryTimer(d,cfg,dId){return setInterval(async()=>{if(!running2)return;try{await reclaimExpiredLeases(d,dId)}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"lease_recovery_error",error:message})}},cfg.leaseRecoveryIntervalMs)}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),leaseRecoveryTimer=startLeaseRecoveryTimer(deps,config,daemonId),inboxWatcherHandle=startInboxWatcherIfEnabled(deps),eventRouterHandle=await startEventRouterSafe(deps);try{deliveryUnsub=await subscribeDelivery(async(toWorker,messageId)=>{try{let{deliverToPane:deliverToPane2}=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router));await deliverToPane2(toWorker,messageId)}catch{}}),deps.log({timestamp:deps.now().toISOString(),level:"info",event:"mailbox_delivery_listen_started"})}catch{}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_mailbox();init_run_spec();previousWorkerStates=new Map});var exports_serve={};__export(exports_serve,{registerServeCommands:()=>registerServeCommands,isTuiSessionReady:()=>isTuiSessionReady,isServeRunning:()=>isServeRunning,getTuiQuitBindingArgs:()=>getTuiQuitBindingArgs,getTuiKeybindings:()=>getTuiKeybindings,ensureTuiSession:()=>ensureTuiSession,autoStartServe:()=>autoStartServe});import{execSync as execSync9,spawn as spawn4,spawnSync as spawnSync3}from"child_process";import{existsSync as existsSync26,mkdirSync as mkdirSync11,readFileSync as readFileSync15,unlinkSync as unlinkSync7,writeFileSync as writeFileSync13}from"fs";import{homedir as homedir23}from"os";import{join as join35}from"path";function genieHome3(){return process.env.GENIE_HOME??join35(homedir23(),".genie")}function servePidPath(){return join35(genieHome3(),"serve.pid")}function readServePid(){let path3=servePidPath();if(!existsSync26(path3))return null;let raw=readFileSync15(path3,"utf-8").trim(),pid=Number.parseInt(raw,10);if(Number.isNaN(pid)||pid<=0)return null;return pid}function writeServePid(pid){mkdirSync11(genieHome3(),{recursive:!0}),writeFileSync13(servePidPath(),String(pid),"utf-8")}function removeServePid(){let path3=servePidPath();if(existsSync26(path3))try{unlinkSync7(path3)}catch{}}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function tuiTmuxConf(){return[join35(genieHome3(),"tui-tmux.conf")].find((p)=>existsSync26(p))??"/dev/null"}function tuiTmux(subcmd){return`${tmuxBin()} -L genie-tui -f ${tuiTmuxConf()} ${subcmd}`}function isGenieTmuxRunning(){try{return execSync9(genieTmuxCmd("list-sessions"),{stdio:"ignore"}),!0}catch{return!1}}function getTuiKeybindings(sessionName=TUI_SESSION){return[`bind-key -T root Tab if-shell "[ '#{pane_index}' = '0' ]" "select-pane -t ${sessionName}:0.1" "select-pane -t ${sessionName}:0.0"`,`bind-key -T root C-1 select-pane -t ${sessionName}:0.0`,`bind-key -T root C-2 select-pane -t ${sessionName}:0.1`,`bind-key -T root C-b if-shell "[ $(tmux display-message -p '#\\{pane_width\\}' -t ${sessionName}:0.0) -gt 5 ]" "resize-pane -t ${sessionName}:0.0 -x 0" "resize-pane -t ${sessionName}:0.0 -x ${NAV_WIDTH}"`,`bind-key -T root C-t select-pane -t ${sessionName}:0.0 \\; send-keys -t ${sessionName}:0.0 C-t`,"bind-key -T root C-d detach-client",`bind-key -T root C-q select-pane -t ${sessionName}:0.0 \\; send-keys -t ${sessionName}:0.0 C-q`]}function getTuiQuitBindingArgs(sessionName=TUI_SESSION){return["bind-key","-T","root","C-q","select-pane","-t",`${sessionName}:0.0`,"\\;","send-keys","-t",`${sessionName}:0.0`,"C-q"]}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{execSync9(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function setupTuiKeybindings(){for(let cmd of getTuiKeybindings())try{if(cmd.startsWith("bind-key -T root C-q "))spawnSync3(tmuxBin(),["-L","genie-tui","-f",tuiTmuxConf(),...getTuiQuitBindingArgs()],{stdio:"ignore"});else execSync9(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function startTuiTmuxServer(){try{execSync9(tuiTmux(`has-session -t ${TUI_SESSION}`),{stdio:"ignore"});let panes2=execSync9(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
1661
1661
  `);if(panes2.length>=2){try{execSync9(tuiTmux(`respawn-pane -k -t ${panes2[1]} 'cat'`),{stdio:"ignore"})}catch{}return{leftPane:panes2[0],rightPane:panes2[1]}}let cols2=Number.parseInt(execSync9(tuiTmux(`display-message -t ${TUI_SESSION}:0 -p '#{window_width}'`),{encoding:"utf-8"}).trim(),10)||120;execSync9(tuiTmux(`split-window -h -t ${TUI_SESSION}:0 -l ${cols2-NAV_WIDTH-1}`),{stdio:"ignore"});let refreshed=execSync9(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
1662
1662
  `);applyTuiStyle(),setupTuiKeybindings();try{execSync9(tuiTmux(`select-pane -t ${refreshed[0]}`),{stdio:"ignore"})}catch{}return{leftPane:refreshed[0],rightPane:refreshed[1]||refreshed[0]}}catch{}let cols=120;execSync9(tuiTmux(`new-session -d -s ${TUI_SESSION} -x ${cols} -y ${40}`),{stdio:"ignore"}),execSync9(tuiTmux(`split-window -h -t ${TUI_SESSION}:0 -l ${cols-NAV_WIDTH-1}`),{stdio:"ignore"});let panes=execSync9(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
1663
1663
  `);applyTuiStyle(),setupTuiKeybindings();try{execSync9(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=genieHome3(),bunPath=process.execPath||"bun",genieBin=process.argv[1]||"genie",scriptPath=join35(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
1664
1664
  export ${envVars.join(`
1665
1665
  export `)}
1666
1666
  exec ${bunPath} ${genieBin}
1667
- `;writeFileSync13(scriptPath,content,{mode:493});try{execSync9(tuiTmux(`send-keys -t '${leftPane}' '${scriptPath}' Enter`),{stdio:"ignore"})}catch{}}function killTuiSession(){try{execSync9(tuiTmux("kill-server"),{stdio:"ignore"})}catch{}}function listAgentSessions(){try{return execSync9(genieTmux("list-sessions -F '#{session_name}'"),{encoding:"utf-8"}).trim().split(`
1668
- `).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 execSync9(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,genieHome:genieHome4}=(init_workspace(),__toCommonJS(exports_workspace)),ws=findWorkspace2();if(!ws){let{join:join36}=__require("path"),configPath2=join36(genieHome4(),"config.json");return console.warn(` Agent sync: DISABLED \u2014 no workspace found from cwd or ${configPath2}`),console.warn(" Fix: `cd <workspace> && genie serve restart`, or run `genie init` to bootstrap one"),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 (workspace: ${ws.root})`);else console.log(` Agent sync: up to date (workspace: ${ws.root})`);if(syncResult.errors.length>0){console.warn(` Agent sync: ${syncResult.errors.length} error(s) \u2014 these agents were NOT registered:`);for(let e of syncResult.errors)console.warn(` ${e.name}: ${e.error}`)}let watcher2=watchAgentDirectory2(ws.root,{onSync:(name,action)=>{console.log(` [agent-watcher] ${name}: ${action}`)}});if(watcher2)console.log(" Agent watcher started (watching agents/ directory)");else console.warn(" Agent watcher: FAILED to start \u2014 new agents will not be auto-registered");return watcher2}catch(err){let msg=err instanceof Error?err.message:String(err);return console.error(` Agent sync failed: ${msg}`),null}}async function startForeground(headless){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);let mode=headless?"headless":"full";if(console.log(`genie serve starting (PID ${process.pid}, mode: ${mode})`),!headless)await ensureTmux();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}`);try{let{registerService:registerService2}=await Promise.resolve().then(() => (init_service_registry(),exports_service_registry));registerService2("pgserve-owner",process.pid)}catch{}}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(` pgserve failed: ${msg}`)}if(!headless){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)`)}if(handles.agentWatcher=await startAgentSync(),!headless){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)");try{let{registerService:registerService2}=await Promise.resolve().then(() => (init_service_registry(),exports_service_registry));registerService2("scheduler",process.pid)}catch{}}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(` Scheduler failed: ${msg}`)}console.log(`
1667
+ `;writeFileSync13(scriptPath,content,{mode:493});try{execSync9(tuiTmux(`send-keys -t '${leftPane}' '${scriptPath}' Enter`),{stdio:"ignore"})}catch{}}function killTuiSession(){try{execSync9(tuiTmux("kill-server"),{stdio:"ignore"})}catch{}}function listAgentSessions(){try{return execSync9(genieTmuxCmd("list-sessions -F '#{session_name}'"),{encoding:"utf-8"}).trim().split(`
1668
+ `).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 execSync9(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,genieHome:genieHome4}=(init_workspace(),__toCommonJS(exports_workspace)),ws=findWorkspace2();if(!ws){let{join:join36}=__require("path"),configPath2=join36(genieHome4(),"config.json");return console.warn(` Agent sync: DISABLED \u2014 no workspace found from cwd or ${configPath2}`),console.warn(" Fix: `cd <workspace> && genie serve restart`, or run `genie init` to bootstrap one"),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 (workspace: ${ws.root})`);else console.log(` Agent sync: up to date (workspace: ${ws.root})`);if(syncResult.errors.length>0){console.warn(` Agent sync: ${syncResult.errors.length} error(s) \u2014 these agents were NOT registered:`);for(let e of syncResult.errors)console.warn(` ${e.name}: ${e.error}`)}let watcher2=watchAgentDirectory2(ws.root,{onSync:(name,action)=>{console.log(` [agent-watcher] ${name}: ${action}`)}});if(watcher2)console.log(" Agent watcher started (watching agents/ directory)");else console.warn(" Agent watcher: FAILED to start \u2014 new agents will not be auto-registered");return watcher2}catch(err){let msg=err instanceof Error?err.message:String(err);return console.error(` Agent sync failed: ${msg}`),null}}async function startForeground(headless){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);let mode=headless?"headless":"full";if(console.log(`genie serve starting (PID ${process.pid}, mode: ${mode})`),!headless)await ensureTmux();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}`);try{let{registerService:registerService2}=await Promise.resolve().then(() => (init_service_registry(),exports_service_registry));registerService2("pgserve-owner",process.pid)}catch{}}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(` pgserve failed: ${msg}`)}if(!headless){let sessions=listAgentSessions();if(sessions.length>0)console.log(` Agent server (-L genie): ${sessions.length} sessions`);else console.log(" Agent server (-L genie): no sessions yet (created on first spawn)")}if(handles.agentWatcher=await startAgentSync(),!headless){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)");try{let{registerService:registerService2}=await Promise.resolve().then(() => (init_service_registry(),exports_service_registry));registerService2("scheduler",process.pid)}catch{}}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(` Scheduler failed: ${msg}`)}console.log(`
1669
1669
  genie serve is running (${mode}). ${headless?"Send SIGTERM to stop.":"Press Ctrl+C to stop."}`);let shutdownStarted=!1,shutdown2=()=>{if(shutdownStarted)return;shutdownStarted=!0,console.log(`
1670
- Shutting down genie serve...`),handles.agentWatcher?.close(),handles.schedulerHandle?.stop();try{let{killAllServices:killAllServices2}=(init_service_registry(),__toCommonJS(exports_service_registry));killAllServices2()}catch{}if(!headless)killTuiSession();try{let lockfilePath=join35(genieHome3(),"pgserve.port");if(existsSync26(lockfilePath))unlinkSync7(lockfilePath)}catch{}removeServePid(),console.log("genie serve stopped.")},forceKillShutdown=()=>{shutdown2(),setTimeout(()=>{console.error("Graceful shutdown timeout (10s). Force-killing remaining processes.");try{let{getRegisteredServices:getRegisteredServices2}=(init_service_registry(),__toCommonJS(exports_service_registry));for(let svc of getRegisteredServices2())try{process.kill(svc.pid,"SIGKILL")}catch{}}catch{}process.exit(1)},1e4).unref()};if(process.on("SIGTERM",()=>{forceKillShutdown(),process.exit(143)}),process.on("SIGINT",()=>{forceKillShutdown(),process.exit(130)}),handles.schedulerHandle)await handles.schedulerHandle.done;else await new Promise(()=>{});removeServePid()}async function startBackground(headless){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",args=[process.argv[1]??"genie","serve","--foreground"];if(headless)args.push("--headless");let child=spawn4(bunPath,args,{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=join35(genieHome3(),"scheduler.pid");if(existsSync26(schedulerPidPath)){let sPid=Number.parseInt(readFileSync15(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(`
1671
- 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)").option("--headless","Run without TUI (services only: pgserve, scheduler, inbox-watcher)").action(async(options)=>{if(options.daemon)await startBackground(options.headless);else await startForeground(options.headless)}),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(()=>{init_ensure_tmux();TUI_STYLE={activeBorder:"#7c3aed",inactiveBorder:"#414868"};handles={schedulerHandle:null,agentWatcher:null}});var exports_tmux2={};__export(exports_tmux2,{newAgentWindow:()=>newAgentWindow,hasProjectSession:()=>hasProjectSession,attachTuiSession:()=>attachTuiSession,attachProjectWindow:()=>attachProjectWindow});import{spawnSync as spawnSync4}from"child_process";function runTuiTmux(args,stdio="ignore"){return spawnSync4(TMUX_BIN,["-L",TMUX_SOCKET,"-f",TUI_TMUX_CONF,...args],{stdio})}function runTuiTmuxOutput(args){let result2=spawnSync4(TMUX_BIN,["-L",TMUX_SOCKET,"-f",TUI_TMUX_CONF,...args],{encoding:"utf-8"});return result2.status===0?result2.stdout.trim():null}function runAgentTmux(args,stdio="ignore"){return spawnSync4(TMUX_BIN,["-L",GENIE_AGENT_SOCKET,...args],{stdio})}function shellQuote3(value){return`'${value.replace(/'/g,"'\\''")}'`}function buildAttachLoop(targetSession){return`while true; do TMUX='' ${[TMUX_BIN,"-L",GENIE_AGENT_SOCKET,"attach-session","-t",targetSession].map(shellQuote3).join(" ")} 2>/dev/null; sleep 0.3; done`}function resolveRightPane(rightPane){if(runTuiTmux(["display-message","-t",rightPane,"-p",""]).status===0)return rightPane;let panes=runTuiTmuxOutput(["list-panes","-t",`${SESSION_NAME}:0`,"-F","#{pane_id}"])?.split(`
1670
+ Shutting down genie serve...`),handles.agentWatcher?.close(),handles.schedulerHandle?.stop();try{let{killAllServices:killAllServices2}=(init_service_registry(),__toCommonJS(exports_service_registry));killAllServices2()}catch{}if(!headless)killTuiSession();try{let lockfilePath=join35(genieHome3(),"pgserve.port");if(existsSync26(lockfilePath))unlinkSync7(lockfilePath)}catch{}removeServePid(),console.log("genie serve stopped.")},forceKillShutdown=()=>{shutdown2(),setTimeout(()=>{console.error("Graceful shutdown timeout (10s). Force-killing remaining processes.");try{let{getRegisteredServices:getRegisteredServices2}=(init_service_registry(),__toCommonJS(exports_service_registry));for(let svc of getRegisteredServices2())try{process.kill(svc.pid,"SIGKILL")}catch{}}catch{}process.exit(1)},1e4).unref()};if(process.on("SIGTERM",()=>{forceKillShutdown(),process.exit(143)}),process.on("SIGINT",()=>{forceKillShutdown(),process.exit(130)}),handles.schedulerHandle)await handles.schedulerHandle.done;else await new Promise(()=>{});removeServePid()}async function startBackground(headless){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",args=[process.argv[1]??"genie","serve","--foreground"];if(headless)args.push("--headless");let child=spawn4(bunPath,args,{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: ${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=join35(genieHome3(),"scheduler.pid");if(existsSync26(schedulerPidPath)){let sPid=Number.parseInt(readFileSync15(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(`
1671
+ 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)").option("--headless","Run without TUI (services only: pgserve, scheduler, inbox-watcher)").action(async(options)=>{if(options.daemon)await startBackground(options.headless);else await startForeground(options.headless)}),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 TUI_SESSION="genie-tui",NAV_WIDTH=30,TUI_STYLE,handles;var init_serve=__esm(()=>{init_ensure_tmux();init_tmux_wrapper();TUI_STYLE={activeBorder:"#7c3aed",inactiveBorder:"#414868"};handles={schedulerHandle:null,agentWatcher:null}});var exports_tmux2={};__export(exports_tmux2,{newAgentWindow:()=>newAgentWindow,hasProjectSession:()=>hasProjectSession,attachTuiSession:()=>attachTuiSession,attachProjectWindow:()=>attachProjectWindow});import{spawnSync as spawnSync4}from"child_process";function runTuiTmux(args,stdio="ignore"){return spawnSync4(TMUX_BIN,["-L",TMUX_SOCKET,"-f",TUI_TMUX_CONF,...args],{stdio})}function runTuiTmuxOutput(args){let result2=spawnSync4(TMUX_BIN,["-L",TMUX_SOCKET,"-f",TUI_TMUX_CONF,...args],{encoding:"utf-8"});return result2.status===0?result2.stdout.trim():null}function runAgentTmux(args,stdio="ignore"){return spawnSync4(TMUX_BIN,["-L",GENIE_AGENT_SOCKET,...args],{stdio})}function shellQuote3(value){return`'${value.replace(/'/g,"'\\''")}'`}function buildAttachLoop(targetSession){return`while true; do TMUX='' ${[TMUX_BIN,"-L",GENIE_AGENT_SOCKET,"attach-session","-t",targetSession].map(shellQuote3).join(" ")} 2>/dev/null; sleep 0.3; done`}function resolveRightPane(rightPane){if(runTuiTmux(["display-message","-t",rightPane,"-p",""]).status===0)return rightPane;let panes=runTuiTmuxOutput(["list-panes","-t",`${SESSION_NAME}:0`,"-F","#{pane_id}"])?.split(`
1672
1672
  `)??[];return panes[1]||panes[0]||rightPane}function hasProjectSession(targetSession){return runAgentTmux(["has-session","-t",targetSession]).status===0}function attachProjectWindow(rightPane,targetSession,windowIndex){if(targetSession===SESSION_NAME)return;let pane=resolveRightPane(rightPane);if(!hasProjectSession(targetSession))return;if(windowIndex!==void 0)runAgentTmux(["select-window","-t",`${targetSession}:${windowIndex}`]);runAgentTmux(["set-option","-t",targetSession,"status","off"]),runTuiTmux(["respawn-pane","-k","-t",pane,"sh","-lc",buildAttachLoop(targetSession)]),runTuiTmux(["select-pane","-t",`${SESSION_NAME}:0.0`])}function attachTuiSession(){if(process.env.TMUX)runTuiTmux(["switch-client","-t",SESSION_NAME],"inherit");else runTuiTmux(["attach-session","-t",SESSION_NAME],"inherit")}function nextRoleSuffix(baseName){let sessionName=baseName.replace(/\//g,"-"),output=spawnSync4(TMUX_BIN,["-L",GENIE_AGENT_SOCKET,"list-windows","-t",sessionName,"-F","#{window_name}"],{encoding:"utf-8"}),names=output.status===0?output.stdout.trim().split(`
1673
1673
  `):[],max=names.length+1,re=new RegExp(`^${baseName}-(\\d+)$`);for(let n of names){let m=n.match(re);if(m)max=Math.max(max,Number.parseInt(m[1],10)+1)}return max}function newAgentWindow(agentName){(async()=>{try{let{reconcileStaleSpawns:reconcileStaleSpawns2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));await reconcileStaleSpawns2()}catch{}let{spawn:spawn5}=__require("child_process"),{join:join36,resolve:resolve5}=__require("path"),{existsSync:existsSync27}=__require("fs"),bunPath=process.execPath||"bun",genieBin=process.argv[1],wsRoot=process.env.GENIE_TUI_WORKSPACE,cwd;if(wsRoot){let parentName=agentName.includes("/")?agentName.slice(0,agentName.indexOf("/")):agentName,agentDir=resolve5(join36(wsRoot,"agents",parentName));if(existsSync27(agentDir))cwd=agentDir}let suffix=nextRoleSuffix(agentName),role=`${agentName}-${suffix}`,sessionName=agentName.replace(/\//g,"-"),args=["spawn",agentName,"--role",role,"--session",sessionName,"--new-window"];(genieBin&&genieBin!=="genie"?spawn5(bunPath,[genieBin,...args],{detached:!0,stdio:"ignore",cwd}):spawn5("genie",args,{detached:!0,stdio:"ignore",cwd})).unref()})()}var SESSION_NAME="genie-tui",TMUX_SOCKET="genie-tui",GENIE_AGENT_SOCKET="genie",TUI_TMUX_CONF,TMUX_BIN;var init_tmux2=__esm(()=>{init_ensure_tmux();TUI_TMUX_CONF=(()=>{let{existsSync:existsSync27}=__require("fs"),tuiConf=`${process.env.GENIE_HOME??`${process.env.HOME}/.genie`}/tui-tmux.conf`;return existsSync27(tuiConf)?tuiConf:"/dev/null"})(),TMUX_BIN=tmuxBin()});var require_encoders=__commonJS((exports)=>{Object.defineProperty(exports,"__esModule",{value:!0});exports.TD=exports.TE=exports.Empty=void 0;exports.encode=encode;exports.decode=decode;exports.Empty=new Uint8Array(0);exports.TE=new TextEncoder;exports.TD=new TextDecoder;function concat(...bufs){let max=0;for(let i2=0;i2<bufs.length;i2++)max+=bufs[i2].length;let out=new Uint8Array(max),index=0;for(let i2=0;i2<bufs.length;i2++)out.set(bufs[i2],index),index+=bufs[i2].length;return out}function encode(...a){let bufs=[];for(let i2=0;i2<a.length;i2++)bufs.push(exports.TE.encode(a[i2]));if(bufs.length===0)return exports.Empty;if(bufs.length===1)return bufs[0];return concat(...bufs)}function decode(a){if(!a||a.length===0)return"";return exports.TD.decode(a)}});var require_nuid=__commonJS((exports)=>{Object.defineProperty(exports,"__esModule",{value:!0});exports.nuid=exports.Nuid=void 0;var digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",base=36,preLen=12,seqLen=10,maxSeq=3656158440062976,minInc=33,maxInc=333,totalLen=preLen+seqLen;function _getRandomValues(a){for(let i2=0;i2<a.length;i2++)a[i2]=Math.floor(Math.random()*255)}function fillRandom(a){var _a;if((_a=globalThis===null||globalThis===void 0?void 0:globalThis.crypto)===null||_a===void 0?void 0:_a.getRandomValues)globalThis.crypto.getRandomValues(a);else _getRandomValues(a)}class Nuid{constructor(){this.buf=new Uint8Array(totalLen),this.inited=!1}init(){this.inited=!0,this.setPre(),this.initSeqAndInc(),this.fillSeq()}initSeqAndInc(){this.seq=Math.floor(Math.random()*maxSeq),this.inc=Math.floor(Math.random()*(maxInc-minInc)+minInc)}setPre(){let cbuf=new Uint8Array(preLen);fillRandom(cbuf);for(let i2=0;i2<preLen;i2++){let di=cbuf[i2]%base;this.buf[i2]=digits.charCodeAt(di)}}fillSeq(){let n=this.seq;for(let i2=totalLen-1;i2>=preLen;i2--)this.buf[i2]=digits.charCodeAt(n%base),n=Math.floor(n/base)}next(){if(!this.inited)this.init();if(this.seq+=this.inc,this.seq>maxSeq)this.setPre(),this.initSeqAndInc();return this.fillSeq(),String.fromCharCode.apply(String,this.buf)}reset(){this.init()}}exports.Nuid=Nuid;exports.nuid=new Nuid});var require_core=__commonJS((exports)=>{var __awaiter=exports&&exports.__awaiter||function(thisArg,_arguments,P,generator){function adopt(value){return value instanceof P?value:new P(function(resolve5){resolve5(value)})}return new(P||(P=Promise))(function(resolve5,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator.throw(value))}catch(e){reject(e)}}function step(result2){result2.done?resolve5(result2.value):adopt(result2.value).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};Object.defineProperty(exports,"__esModule",{value:!0});exports.ServiceVerb=exports.DEFAULT_HOST=exports.DEFAULT_PORT=exports.ServiceError=exports.ServiceErrorCodeHeader=exports.ServiceErrorHeader=exports.ServiceResponseType=exports.RequestStrategy=exports.Match=exports.NatsError=exports.Messages=exports.ErrorCode=exports.DebugEvents=exports.Events=void 0;exports.isNatsError=isNatsError;exports.syncIterator=syncIterator;exports.createInbox=createInbox;var nuid_1=require_nuid(),Events;(function(Events2){Events2.Disconnect="disconnect",Events2.Reconnect="reconnect",Events2.Update="update",Events2.LDM="ldm",Events2.Error="error"})(Events||(exports.Events=Events={}));var DebugEvents;(function(DebugEvents2){DebugEvents2.Reconnecting="reconnecting",DebugEvents2.PingTimer="pingTimer",DebugEvents2.StaleConnection="staleConnection",DebugEvents2.ClientInitiatedReconnect="client initiated reconnect"})(DebugEvents||(exports.DebugEvents=DebugEvents={}));var ErrorCode;(function(ErrorCode2){ErrorCode2.ApiError="BAD API",ErrorCode2.BadAuthentication="BAD_AUTHENTICATION",ErrorCode2.BadCreds="BAD_CREDS",ErrorCode2.BadHeader="BAD_HEADER",ErrorCode2.BadJson="BAD_JSON",ErrorCode2.BadPayload="BAD_PAYLOAD",ErrorCode2.BadSubject="BAD_SUBJECT",ErrorCode2.Cancelled="CANCELLED",ErrorCode2.ConnectionClosed="CONNECTION_CLOSED",ErrorCode2.ConnectionDraining="CONNECTION_DRAINING",ErrorCode2.ConnectionRefused="CONNECTION_REFUSED",ErrorCode2.ConnectionTimeout="CONNECTION_TIMEOUT",ErrorCode2.Disconnect="DISCONNECT",ErrorCode2.InvalidOption="INVALID_OPTION",ErrorCode2.InvalidPayload="INVALID_PAYLOAD",ErrorCode2.MaxPayloadExceeded="MAX_PAYLOAD_EXCEEDED",ErrorCode2.NoResponders="503",ErrorCode2.NotFunction="NOT_FUNC",ErrorCode2.RequestError="REQUEST_ERROR",ErrorCode2.ServerOptionNotAvailable="SERVER_OPT_NA",ErrorCode2.SubClosed="SUB_CLOSED",ErrorCode2.SubDraining="SUB_DRAINING",ErrorCode2.Timeout="TIMEOUT",ErrorCode2.Tls="TLS",ErrorCode2.Unknown="UNKNOWN_ERROR",ErrorCode2.WssRequired="WSS_REQUIRED",ErrorCode2.JetStreamInvalidAck="JESTREAM_INVALID_ACK",ErrorCode2.JetStream404NoMessages="404",ErrorCode2.JetStream408RequestTimeout="408",ErrorCode2.JetStream409MaxAckPendingExceeded="409",ErrorCode2.JetStream409="409",ErrorCode2.JetStreamNotEnabled="503",ErrorCode2.JetStreamIdleHeartBeat="IDLE_HEARTBEAT",ErrorCode2.AuthorizationViolation="AUTHORIZATION_VIOLATION",ErrorCode2.AuthenticationExpired="AUTHENTICATION_EXPIRED",ErrorCode2.ProtocolError="NATS_PROTOCOL_ERR",ErrorCode2.PermissionsViolation="PERMISSIONS_VIOLATION",ErrorCode2.AuthenticationTimeout="AUTHENTICATION_TIMEOUT",ErrorCode2.AccountExpired="ACCOUNT_EXPIRED"})(ErrorCode||(exports.ErrorCode=ErrorCode={}));function isNatsError(err){return typeof err.code==="string"}class Messages{constructor(){this.messages=new Map,this.messages.set(ErrorCode.InvalidPayload,"Invalid payload type - payloads can be 'binary', 'string', or 'json'"),this.messages.set(ErrorCode.BadJson,"Bad JSON"),this.messages.set(ErrorCode.WssRequired,"TLS is required, therefore a secure websocket connection is also required")}static getMessage(s){return messages2.getMessage(s)}getMessage(s){return this.messages.get(s)||s}}exports.Messages=Messages;var messages2=new Messages;class NatsError extends Error{constructor(message,code,chainedError){super(message);this.name="NatsError",this.message=message,this.code=code,this.chainedError=chainedError}static errorForCode(code,chainedError){let m=Messages.getMessage(code);return new NatsError(m,code,chainedError)}isAuthError(){return this.code===ErrorCode.AuthenticationExpired||this.code===ErrorCode.AuthorizationViolation||this.code===ErrorCode.AccountExpired}isAuthTimeout(){return this.code===ErrorCode.AuthenticationTimeout}isPermissionError(){return this.code===ErrorCode.PermissionsViolation}isProtocolError(){return this.code===ErrorCode.ProtocolError}isJetStreamError(){return this.api_error!==void 0}jsError(){return this.api_error?this.api_error:null}}exports.NatsError=NatsError;var Match;(function(Match2){Match2[Match2.Exact=0]="Exact",Match2[Match2.CanonicalMIME=1]="CanonicalMIME",Match2[Match2.IgnoreCase=2]="IgnoreCase"})(Match||(exports.Match=Match={}));var RequestStrategy;(function(RequestStrategy2){RequestStrategy2.Timer="timer",RequestStrategy2.Count="count",RequestStrategy2.JitterTimer="jitterTimer",RequestStrategy2.SentinelMsg="sentinelMsg"})(RequestStrategy||(exports.RequestStrategy=RequestStrategy={}));function syncIterator(src){let iter=src[Symbol.asyncIterator]();return{next(){return __awaiter(this,void 0,void 0,function*(){let m=yield iter.next();if(m.done)return Promise.resolve(null);return Promise.resolve(m.value)})}}}var ServiceResponseType;(function(ServiceResponseType2){ServiceResponseType2.STATS="io.nats.micro.v1.stats_response",ServiceResponseType2.INFO="io.nats.micro.v1.info_response",ServiceResponseType2.PING="io.nats.micro.v1.ping_response"})(ServiceResponseType||(exports.ServiceResponseType=ServiceResponseType={}));exports.ServiceErrorHeader="Nats-Service-Error";exports.ServiceErrorCodeHeader="Nats-Service-Error-Code";class ServiceError extends Error{constructor(code,message){super(message);this.code=code}static isServiceError(msg){return ServiceError.toServiceError(msg)!==null}static toServiceError(msg){var _a,_b;let scode=((_a=msg===null||msg===void 0?void 0:msg.headers)===null||_a===void 0?void 0:_a.get(exports.ServiceErrorCodeHeader))||"";if(scode!==""){let code=parseInt(scode)||400,description=((_b=msg===null||msg===void 0?void 0:msg.headers)===null||_b===void 0?void 0:_b.get(exports.ServiceErrorHeader))||"";return new ServiceError(code,description.length?description:scode)}return null}}exports.ServiceError=ServiceError;function createInbox(prefix=""){if(prefix=prefix||"_INBOX",typeof prefix!=="string")throw Error("prefix must be a string");return prefix.split(".").forEach((v)=>{if(v==="*"||v===">")throw Error(`inbox prefixes cannot have wildcards '${prefix}'`)}),`${prefix}.${nuid_1.nuid.next()}`}exports.DEFAULT_PORT=4222;exports.DEFAULT_HOST="127.0.0.1";var ServiceVerb;(function(ServiceVerb2){ServiceVerb2.PING="PING",ServiceVerb2.STATS="STATS",ServiceVerb2.INFO="INFO"})(ServiceVerb||(exports.ServiceVerb=ServiceVerb={}))});var require_util=__commonJS((exports)=>{var __awaiter=exports&&exports.__awaiter||function(thisArg,_arguments,P,generator){function adopt(value){return value instanceof P?value:new P(function(resolve5){resolve5(value)})}return new(P||(P=Promise))(function(resolve5,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator.throw(value))}catch(e){reject(e)}}function step(result2){result2.done?resolve5(result2.value):adopt(result2.value).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})},__asyncValues=exports&&exports.__asyncValues||function(o){if(!Symbol.asyncIterator)throw TypeError("Symbol.asyncIterator is not defined.");var m=o[Symbol.asyncIterator],i2;return m?m.call(o):(o=typeof __values==="function"?__values(o):o[Symbol.iterator](),i2={},verb("next"),verb("throw"),verb("return"),i2[Symbol.asyncIterator]=function(){return this},i2);function verb(n){i2[n]=o[n]&&function(v){return new Promise(function(resolve5,reject){v=o[n](v),settle(resolve5,reject,v.done,v.value)})}}function settle(resolve5,reject,d,v){Promise.resolve(v).then(function(v2){resolve5({value:v2,done:d})},reject)}};Object.defineProperty(exports,"__esModule",{value:!0});exports.SimpleMutex=exports.Perf=void 0;exports.extend=extend3;exports.render=render;exports.timeout=timeout;exports.delay=delay;exports.deadline=deadline;exports.deferred=deferred;exports.debugDeferred=debugDeferred;exports.shuffle=shuffle;exports.collect=collect;exports.jitter=jitter;exports.backoffWithMax=backoffWithMax;exports.backoff=backoff2;exports.nanos=nanos;exports.millis=millis;var encoders_1=require_encoders(),core_1=require_core();function extend3(a,...b2){for(let i2=0;i2<b2.length;i2++){let o=b2[i2];Object.keys(o).forEach(function(k){a[k]=o[k]})}return a}function render(frame){return encoders_1.TD.decode(frame).replace(/\n/g,"\u240A").replace(/\r/g,"\u240D")}function timeout(ms,asyncTraces=!0){let err=asyncTraces?core_1.NatsError.errorForCode(core_1.ErrorCode.Timeout):null,methods,timer2,p=new Promise((_resolve,reject)=>{methods={cancel:()=>{if(timer2)clearTimeout(timer2)}},timer2=setTimeout(()=>{if(err===null)reject(core_1.NatsError.errorForCode(core_1.ErrorCode.Timeout));else reject(err)},ms)});return Object.assign(p,methods)}function delay(ms=0){let methods,p=new Promise((resolve5)=>{let timer2=setTimeout(()=>{resolve5()},ms);methods={cancel:()=>{if(timer2)clearTimeout(timer2)}}});return Object.assign(p,methods)}function deadline(p,millis2=1000){let err=Error("deadline exceeded"),d=deferred(),timer2=setTimeout(()=>d.reject(err),millis2);return Promise.race([p,d]).finally(()=>clearTimeout(timer2))}function deferred(){let methods={},p=new Promise((resolve5,reject)=>{methods={resolve:resolve5,reject}});return Object.assign(p,methods)}function debugDeferred(){let methods={},p=new Promise((resolve5,reject)=>{methods={resolve:(v)=>{console.trace("resolve",v),resolve5(v)},reject:(err)=>{console.trace("reject"),reject(err)}}});return Object.assign(p,methods)}function shuffle(a){for(let i2=a.length-1;i2>0;i2--){let j=Math.floor(Math.random()*(i2+1));[a[i2],a[j]]=[a[j],a[i2]]}return a}function collect(iter){return __awaiter(this,void 0,void 0,function*(){var _a,iter_1,iter_1_1,_b,e_1,_c,_d;let buf=[];try{for(_a=!0,iter_1=__asyncValues(iter);iter_1_1=yield iter_1.next(),_b=iter_1_1.done,!_b;_a=!0){_d=iter_1_1.value,_a=!1;let v=_d;buf.push(v)}}catch(e_1_1){e_1={error:e_1_1}}finally{try{if(!_a&&!_b&&(_c=iter_1.return))yield _c.call(iter_1)}finally{if(e_1)throw e_1.error}}return buf})}class Perf{constructor(){this.timers=new Map,this.measures=new Map}mark(key){this.timers.set(key,performance.now())}measure(key,startKey,endKey){let s=this.timers.get(startKey);if(s===void 0)throw Error(`${startKey} is not defined`);let e=this.timers.get(endKey);if(e===void 0)throw Error(`${endKey} is not defined`);this.measures.set(key,e-s)}getEntries(){let values2=[];return this.measures.forEach((v,k)=>{values2.push({name:k,duration:v})}),values2}}exports.Perf=Perf;class SimpleMutex{constructor(max=1){this.max=max,this.current=0,this.waiting=[]}lock(){if(this.current++,this.current<=this.max)return Promise.resolve();let d=deferred();return this.waiting.push(d),d}unlock(){this.current--;let d=this.waiting.pop();d===null||d===void 0||d.resolve()}}exports.SimpleMutex=SimpleMutex;function jitter(n){if(n===0)return 0;return Math.floor(n/2+Math.random()*n)}function backoffWithMax(max=30000){let a=[max];while(!0){let n=Math.floor(max/2);if(n<100){a.unshift(0);break}a.unshift(n),max=n}return backoff2(a)}function backoff2(policy=[0,250,250,500,500,3000,5000]){if(!Array.isArray(policy))policy=[0,250,250,500,500,3000,5000];let max=policy.length-1;return{backoff(attempt){return jitter(attempt>max?policy[max]:policy[attempt])}}}function nanos(millis2){return millis2*1e6}function millis(ns){return Math.floor(ns/1e6)}});var require_databuffer=__commonJS((exports)=>{Object.defineProperty(exports,"__esModule",{value:!0});exports.DataBuffer=void 0;var encoders_1=require_encoders();class DataBuffer{constructor(){this.buffers=[],this.byteLength=0}static concat(...bufs){let max=0;for(let i2=0;i2<bufs.length;i2++)max+=bufs[i2].length;let out=new Uint8Array(max),index=0;for(let i2=0;i2<bufs.length;i2++)out.set(bufs[i2],index),index+=bufs[i2].length;return out}static fromAscii(m){if(!m)m="";return encoders_1.TE.encode(m)}static toAscii(a){return encoders_1.TD.decode(a)}reset(){this.buffers.length=0,this.byteLength=0}pack(){if(this.buffers.length>1){let v=new Uint8Array(this.byteLength),index=0;for(let i2=0;i2<this.buffers.length;i2++)v.set(this.buffers[i2],index),index+=this.buffers[i2].length;this.buffers.length=0,this.buffers.push(v)}}shift(){if(this.buffers.length){let a=this.buffers.shift();if(a)return this.byteLength-=a.length,a}return new Uint8Array(0)}drain(n){if(this.buffers.length){this.pack();let v=this.buffers.pop();if(v){let max=this.byteLength;if(n===void 0||n>max)n=max;let d=v.subarray(0,n);if(max>n)this.buffers.push(v.subarray(n));return this.byteLength=max-n,d}}return new Uint8Array(0)}fill(a,...bufs){if(a)this.buffers.push(a),this.byteLength+=a.length;for(let i2=0;i2<bufs.length;i2++)if(bufs[i2]&&bufs[i2].length)this.buffers.push(bufs[i2]),this.byteLength+=bufs[i2].length}peek(){if(this.buffers.length)return this.pack(),this.buffers[0];return new Uint8Array(0)}size(){return this.byteLength}length(){return this.buffers.length}}exports.DataBuffer=DataBuffer});var require_transport=__commonJS((exports)=>{Object.defineProperty(exports,"__esModule",{value:!0});exports.LF=exports.CR=exports.CRLF=exports.CR_LF_LEN=exports.CR_LF=void 0;exports.setTransportFactory=setTransportFactory;exports.defaultPort=defaultPort;exports.getUrlParseFn=getUrlParseFn;exports.newTransport=newTransport;exports.getResolveFn=getResolveFn;exports.protoLen=protoLen;exports.extractProtocolMessage=extractProtocolMessage;var encoders_1=require_encoders(),core_1=require_core(),databuffer_1=require_databuffer(),transportConfig;function setTransportFactory(config){transportConfig=config}function defaultPort(){return transportConfig!==void 0&&transportConfig.defaultPort!==void 0?transportConfig.defaultPort:core_1.DEFAULT_PORT}function getUrlParseFn(){return transportConfig!==void 0&&transportConfig.urlParseFn?transportConfig.urlParseFn:void 0}function newTransport(){if(!transportConfig||typeof transportConfig.factory!=="function")throw Error("transport fn is not set");return transportConfig.factory()}function getResolveFn(){return transportConfig!==void 0&&transportConfig.dnsResolveFn?transportConfig.dnsResolveFn:void 0}exports.CR_LF=`\r
1674
1674
  `;exports.CR_LF_LEN=exports.CR_LF.length;exports.CRLF=databuffer_1.DataBuffer.fromAscii(exports.CR_LF);exports.CR=new Uint8Array(exports.CRLF)[0];exports.LF=new Uint8Array(exports.CRLF)[1];function protoLen(ba){for(let i2=0;i2<ba.length;i2++){let n=i2+1;if(ba.byteLength>n&&ba[i2]===exports.CR&&ba[n]===exports.LF)return n+1}return 0}function extractProtocolMessage(a){let len=protoLen(a);if(len>0){let out=new Uint8Array(a).slice(0,len);return encoders_1.TD.decode(out)}return""}});var require_ipparser=__commonJS((exports)=>{Object.defineProperty(exports,"__esModule",{value:!0});exports.ipV4=ipV4;exports.isIP=isIP;exports.parseIP=parseIP;var IPv4LEN=4,IPv6LEN=16,ASCII0=48,ASCII9=57,ASCIIA=65,ASCIIF=70,ASCIIa=97,ASCIIf=102,big=16777215;function ipV4(a,b2,c,d){let ip=new Uint8Array(IPv6LEN);return[0,0,0,0,0,0,0,0,0,0,255,255].forEach((v,idx)=>{ip[idx]=v}),ip[12]=a,ip[13]=b2,ip[14]=c,ip[15]=d,ip}function isIP(h){return parseIP(h)!==void 0}function parseIP(h){for(let i2=0;i2<h.length;i2++)switch(h[i2]){case".":return parseIPv4(h);case":":return parseIPv6(h)}return}function parseIPv4(s){let ip=new Uint8Array(IPv4LEN);for(let i2=0;i2<IPv4LEN;i2++){if(s.length===0)return;if(i2>0){if(s[0]!==".")return;s=s.substring(1)}let{n,c,ok}=dtoi(s);if(!ok||n>255)return;s=s.substring(c),ip[i2]=n}return ipV4(ip[0],ip[1],ip[2],ip[3])}function parseIPv6(s){let ip=new Uint8Array(IPv6LEN),ellipsis=-1;if(s.length>=2&&s[0]===":"&&s[1]===":"){if(ellipsis=0,s=s.substring(2),s.length===0)return ip}let i2=0;while(i2<IPv6LEN){let{n,c,ok}=xtoi(s);if(!ok||n>65535)return;if(c<s.length&&s[c]==="."){if(ellipsis<0&&i2!=IPv6LEN-IPv4LEN)return;if(i2+IPv4LEN>IPv6LEN)return;let ip4=parseIPv4(s);if(ip4===void 0)return;ip[i2]=ip4[12],ip[i2+1]=ip4[13],ip[i2+2]=ip4[14],ip[i2+3]=ip4[15],s="",i2+=IPv4LEN;break}if(ip[i2]=n>>8,ip[i2+1]=n,i2+=2,s=s.substring(c),s.length===0)break;if(s[0]!==":"||s.length==1)return;if(s=s.substring(1),s[0]===":"){if(ellipsis>=0)return;if(ellipsis=i2,s=s.substring(1),s.length===0)break}}if(s.length!==0)return;if(i2<IPv6LEN){if(ellipsis<0)return;let n=IPv6LEN-i2;for(let j=i2-1;j>=ellipsis;j--)ip[j+n]=ip[j];for(let j=ellipsis+n-1;j>=ellipsis;j--)ip[j]=0}else if(ellipsis>=0)return;return ip}function dtoi(s){let i2=0,n=0;for(i2=0;i2<s.length&&ASCII0<=s.charCodeAt(i2)&&s.charCodeAt(i2)<=ASCII9;i2++)if(n=n*10+(s.charCodeAt(i2)-ASCII0),n>=big)return{n:big,c:i2,ok:!1};if(i2===0)return{n:0,c:0,ok:!1};return{n,c:i2,ok:!0}}function xtoi(s){let n=0,i2=0;for(i2=0;i2<s.length;i2++){if(ASCII0<=s.charCodeAt(i2)&&s.charCodeAt(i2)<=ASCII9)n*=16,n+=s.charCodeAt(i2)-ASCII0;else if(ASCIIa<=s.charCodeAt(i2)&&s.charCodeAt(i2)<=ASCIIf)n*=16,n+=s.charCodeAt(i2)-ASCIIa+10;else if(ASCIIA<=s.charCodeAt(i2)&&s.charCodeAt(i2)<=ASCIIF)n*=16,n+=s.charCodeAt(i2)-ASCIIA+10;else break;if(n>=big)return{n:0,c:i2,ok:!1}}if(i2===0)return{n:0,c:i2,ok:!1};return{n,c:i2,ok:!0}}});var require_servers=__commonJS((exports)=>{var __awaiter=exports&&exports.__awaiter||function(thisArg,_arguments,P,generator){function adopt(value){return value instanceof P?value:new P(function(resolve5){resolve5(value)})}return new(P||(P=Promise))(function(resolve5,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator.throw(value))}catch(e){reject(e)}}function step(result2){result2.done?resolve5(result2.value):adopt(result2.value).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})};Object.defineProperty(exports,"__esModule",{value:!0});exports.Servers=exports.ServerImpl=void 0;exports.isIPV4OrHostname=isIPV4OrHostname;exports.hostPort=hostPort;var transport_1=require_transport(),util_1=require_util(),ipparser_1=require_ipparser(),core_1=require_core();function isIPV4OrHostname(hp){if(hp.indexOf("[")!==-1||hp.indexOf("::")!==-1)return!1;if(hp.indexOf(".")!==-1)return!0;if(hp.split(":").length<=2)return!0;return!1}function isIPV6(hp){return!isIPV4OrHostname(hp)}function filterIpv6MappedToIpv4(hp){let idx=hp.toUpperCase().indexOf("::FFFF:");if(idx!==-1&&hp.indexOf(".")!==-1){let ip=hp.substring(idx+7);return ip=ip.replace("[",""),ip.replace("]","")}return hp}function hostPort(u){if(u=u.trim(),u.match(/^(.*:\/\/)(.*)/m))u=u.replace(/^(.*:\/\/)(.*)/gm,"$2");if(u=filterIpv6MappedToIpv4(u),isIPV6(u)&&u.indexOf("[")===-1)u=`[${u}]`;let op=isIPV6(u)?u.match(/(]:)(\d+)/):u.match(/(:)(\d+)/),port=op&&op.length===3&&op[1]&&op[2]?parseInt(op[2]):core_1.DEFAULT_PORT,url=new URL(`${port===80?"https":"http"}://${u}`);url.port=`${port}`;let hostname=url.hostname;if(hostname.charAt(0)==="[")hostname=hostname.substring(1,hostname.length-1);return{listen:url.host,hostname,port}}class ServerImpl{constructor(u,gossiped=!1){this.src=u,this.tlsName="";let v=hostPort(u);this.listen=v.listen,this.hostname=v.hostname,this.port=v.port,this.didConnect=!1,this.reconnects=0,this.lastConnect=0,this.gossiped=gossiped}toString(){return this.listen}resolve(opts){return __awaiter(this,void 0,void 0,function*(){if(!opts.fn||opts.resolve===!1)return[this];let buf=[];if((0,ipparser_1.isIP)(this.hostname))return[this];else{let ips=yield opts.fn(this.hostname);if(opts.debug)console.log(`resolve ${this.hostname} = ${ips.join(",")}`);for(let ip of ips){let proto=this.port===80?"https":"http",url=new URL(`${proto}://${isIPV6(ip)?"["+ip+"]":ip}`);url.port=`${this.port}`;let ss=new ServerImpl(url.host,!1);ss.tlsName=this.hostname,buf.push(ss)}}if(opts.randomize)(0,util_1.shuffle)(buf);return this.resolves=buf,buf})}}exports.ServerImpl=ServerImpl;class Servers{constructor(listens=[],opts={}){this.firstSelect=!0,this.servers=[],this.tlsName="",this.randomize=opts.randomize||!1;let urlParseFn=(0,transport_1.getUrlParseFn)();if(listens){if(listens.forEach((hp)=>{hp=urlParseFn?urlParseFn(hp):hp,this.servers.push(new ServerImpl(hp))}),this.randomize)this.servers=(0,util_1.shuffle)(this.servers)}if(this.servers.length===0)this.addServer(`${core_1.DEFAULT_HOST}:${(0,transport_1.defaultPort)()}`,!1);this.currentServer=this.servers[0]}clear(){this.servers.length=0}updateTLSName(){let cs=this.getCurrentServer();if(!(0,ipparser_1.isIP)(cs.hostname))this.tlsName=cs.hostname,this.servers.forEach((s)=>{if(s.gossiped)s.tlsName=this.tlsName})}getCurrentServer(){return this.currentServer}addServer(u,implicit=!1){let urlParseFn=(0,transport_1.getUrlParseFn)();u=urlParseFn?urlParseFn(u):u;let s=new ServerImpl(u,implicit);if((0,ipparser_1.isIP)(s.hostname))s.tlsName=this.tlsName;this.servers.push(s)}selectServer(){if(this.firstSelect)return this.firstSelect=!1,this.currentServer;let t=this.servers.shift();if(t)this.servers.push(t),this.currentServer=t;return t}removeCurrentServer(){this.removeServer(this.currentServer)}removeServer(server2){if(server2){let index=this.servers.indexOf(server2);this.servers.splice(index,1)}}length(){return this.servers.length}next(){return this.servers.length?this.servers[0]:void 0}getServers(){return this.servers}update(info,encrypted){let added=[],deleted=[],urlParseFn=(0,transport_1.getUrlParseFn)(),discovered=new Map;if(info.connect_urls&&info.connect_urls.length>0)info.connect_urls.forEach((hp)=>{hp=urlParseFn?urlParseFn(hp,encrypted):hp;let s=new ServerImpl(hp,!0);discovered.set(hp,s)});let toDelete=[];return this.servers.forEach((s,index)=>{let u=s.listen;if(s.gossiped&&this.currentServer.listen!==u&&discovered.get(u)===void 0)toDelete.push(index);discovered.delete(u)}),toDelete.reverse(),toDelete.forEach((index)=>{let removed=this.servers.splice(index,1);deleted=deleted.concat(removed[0].listen)}),discovered.forEach((v,k)=>{this.servers.push(v),added.push(k)}),{added,deleted}}}exports.Servers=Servers});var require_queued_iterator=__commonJS((exports)=>{var __await=exports&&exports.__await||function(v){return this instanceof __await?(this.v=v,this):new __await(v)},__asyncGenerator=exports&&exports.__asyncGenerator||function(thisArg,_arguments,generator){if(!Symbol.asyncIterator)throw TypeError("Symbol.asyncIterator is not defined.");var g=generator.apply(thisArg,_arguments||[]),i2,q=[];return i2=Object.create((typeof AsyncIterator==="function"?AsyncIterator:Object).prototype),verb("next"),verb("throw"),verb("return",awaitReturn),i2[Symbol.asyncIterator]=function(){return this},i2;function awaitReturn(f){return function(v){return Promise.resolve(v).then(f,reject)}}function verb(n,f){if(g[n]){if(i2[n]=function(v){return new Promise(function(a,b2){q.push([n,v,a,b2])>1||resume(n,v)})},f)i2[n]=f(i2[n])}}function resume(n,v){try{step(g[n](v))}catch(e){settle(q[0][3],e)}}function step(r){r.value instanceof __await?Promise.resolve(r.value.v).then(fulfill,reject):settle(q[0][2],r)}function fulfill(value){resume("next",value)}function reject(value){resume("throw",value)}function settle(f,v){if(f(v),q.shift(),q.length)resume(q[0][0],q[0][1])}};Object.defineProperty(exports,"__esModule",{value:!0});exports.QueuedIteratorImpl=void 0;var util_1=require_util(),core_1=require_core();class QueuedIteratorImpl{constructor(){this.inflight=0,this.filtered=0,this.pendingFiltered=0,this.processed=0,this.received=0,this.noIterator=!1,this.done=!1,this.signal=(0,util_1.deferred)(),this.yields=[],this.iterClosed=(0,util_1.deferred)(),this.time=0,this.yielding=!1}[Symbol.asyncIterator](){return this.iterate()}push(v){if(this.done)return;if(typeof v==="function"){this.yields.push(v),this.signal.resolve();return}let{ingest,protocol}=this.ingestionFilterFn?this.ingestionFilterFn(v,this.ctx||this):{ingest:!0,protocol:!1};if(ingest){if(protocol)this.filtered++,this.pendingFiltered++;this.yields.push(v),this.signal.resolve()}}iterate(){return __asyncGenerator(this,arguments,function*(){if(this.noIterator)throw new core_1.NatsError("unsupported iterator",core_1.ErrorCode.ApiError);if(this.yielding)throw new core_1.NatsError("already yielding",core_1.ErrorCode.ApiError);this.yielding=!0;try{while(!0){if(this.yields.length===0)yield __await(this.signal);if(this.err)throw this.err;let yields=this.yields;this.inflight=yields.length,this.yields=[];for(let i2=0;i2<yields.length;i2++){if(typeof yields[i2]==="function"){let fn=yields[i2];try{fn()}catch(err){throw err}if(this.err)throw this.err;continue}if(this.protocolFilterFn?this.protocolFilterFn(yields[i2]):!0){this.processed++;let start=Date.now();if(yield yield __await(yields[i2]),this.time=Date.now()-start,this.dispatchedFn&&yields[i2])this.dispatchedFn(yields[i2])}else this.pendingFiltered--;this.inflight--}if(this.done)break;else if(this.yields.length===0)yields.length=0,this.yields=yields,this.signal=(0,util_1.deferred)()}}finally{this.stop()}})}stop(err){if(this.done)return;this.err=err,this.done=!0,this.signal.resolve(),this.iterClosed.resolve(err)}getProcessed(){return this.noIterator?this.received:this.processed}getPending(){return this.yields.length+this.inflight-this.pendingFiltered}getReceived(){return this.received-this.filtered}}exports.QueuedIteratorImpl=QueuedIteratorImpl});var require_headers=__commonJS((exports)=>{Object.defineProperty(exports,"__esModule",{value:!0});exports.MsgHdrsImpl=void 0;exports.canonicalMIMEHeaderKey=canonicalMIMEHeaderKey;exports.headers=headers;var encoders_1=require_encoders(),core_1=require_core();function canonicalMIMEHeaderKey(k){let upper=!0,buf=Array(k.length);for(let i2=0;i2<k.length;i2++){let c=k.charCodeAt(i2);if(c===58||c<33||c>126)throw new core_1.NatsError(`'${k[i2]}' is not a valid character for a header key`,core_1.ErrorCode.BadHeader);if(upper&&97<=c&&c<=122)c-=32;else if(!upper&&65<=c&&c<=90)c+=32;buf[i2]=c,upper=c==45}return String.fromCharCode(...buf)}function headers(code=0,description=""){if(code===0&&description!==""||code>0&&description==="")throw Error("setting status requires both code and description");return new MsgHdrsImpl(code,description)}var HEADER="NATS/1.0";class MsgHdrsImpl{constructor(code=0,description=""){this._code=code,this._description=description,this.headers=new Map}[Symbol.iterator](){return this.headers.entries()}size(){return this.headers.size}equals(mh){if(mh&&this.headers.size===mh.headers.size&&this._code===mh._code){for(let[k,v]of this.headers){let a=mh.values(k);if(v.length!==a.length)return!1;let vv=[...v].sort(),aa=[...a].sort();for(let i2=0;i2<vv.length;i2++)if(vv[i2]!==aa[i2])return!1}return!0}return!1}static decode(a){let mh=new MsgHdrsImpl,lines=encoders_1.TD.decode(a).split(`\r
@@ -2051,13 +2051,7 @@ ${answer}
2051
2051
  AND table_type = 'BASE TABLE'
2052
2052
  AND table_name NOT LIKE '_genie_%'
2053
2053
  ORDER BY table_name
2054
- `).map((r)=>r.table_name)}async function filterAvailableTables(sql,requested){let existing=new Set(await getAvailableTables(sql)),available=[],skipped=[];for(let table of requested)if(existing.has(table))available.push(table);else skipped.push(table);return{available,skipped}}var require_ignore=__commonJS((exports,module)=>{function makeArray(subject){return Array.isArray(subject)?subject:[subject]}var UNDEFINED=void 0,EMPTY="",SPACE=" ",ESCAPE="\\",REGEX_TEST_BLANK_LINE=/^\s+$/,REGEX_INVALID_TRAILING_BACKSLASH=/(?:[^\\]|^)\\$/,REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION=/^\\!/,REGEX_REPLACE_LEADING_EXCAPED_HASH=/^\\#/,REGEX_SPLITALL_CRLF=/\r?\n/g,REGEX_TEST_INVALID_PATH=/^\.{0,2}\/|^\.{1,2}$/,REGEX_TEST_TRAILING_SLASH=/\/$/,SLASH="/",TMP_KEY_IGNORE="node-ignore";if(typeof Symbol<"u")TMP_KEY_IGNORE=Symbol.for("node-ignore");var KEY_IGNORE=TMP_KEY_IGNORE,define2=(object,key,value)=>{return Object.defineProperty(object,key,{value}),value},REGEX_REGEXP_RANGE=/([0-z])-([0-z])/g,RETURN_FALSE=()=>!1,sanitizeRange=(range)=>range.replace(REGEX_REGEXP_RANGE,(match,from,to)=>from.charCodeAt(0)<=to.charCodeAt(0)?match:EMPTY),cleanRangeBackSlash=(slashes)=>{let{length}=slashes;return slashes.slice(0,length-length%2)},REPLACERS=[[/^\uFEFF/,()=>EMPTY],[/((?:\\\\)*?)(\\?\s+)$/,(_,m1,m2)=>m1+(m2.indexOf("\\")===0?SPACE:EMPTY)],[/(\\+?)\s/g,(_,m1)=>{let{length}=m1;return m1.slice(0,length-length%2)+SPACE}],[/[\\$.|*+(){^]/g,(match)=>`\\${match}`],[/(?!\\)\?/g,()=>"[^/]"],[/^\//,()=>"^"],[/\//g,()=>"\\/"],[/^\^*\\\*\\\*\\\//,()=>"^(?:.*\\/)?"],[/^(?=[^^])/,function(){return!/\/(?!$)/.test(this)?"(?:^|\\/)":"^"}],[/\\\/\\\*\\\*(?=\\\/|$)/g,(_,index,str5)=>index+6<str5.length?"(?:\\/[^\\/]+)*":"\\/.+"],[/(^|[^\\]+)(\\\*)+(?=.+)/g,(_,p1,p2)=>{let unescaped=p2.replace(/\\\*/g,"[^\\/]*");return p1+unescaped}],[/\\\\\\(?=[$.|*+(){^])/g,()=>ESCAPE],[/\\\\/g,()=>ESCAPE],[/(\\)?\[([^\]/]*?)(\\*)($|\])/g,(match,leadEscape,range,endEscape,close)=>leadEscape===ESCAPE?`\\[${range}${cleanRangeBackSlash(endEscape)}${close}`:close==="]"?endEscape.length%2===0?`[${sanitizeRange(range)}${endEscape}]`:"[]":"[]"],[/(?:[^*])$/,(match)=>/\/$/.test(match)?`${match}$`:`${match}(?=$|\\/$)`]],REGEX_REPLACE_TRAILING_WILDCARD=/(^|\\\/)?\\\*$/,MODE_IGNORE="regex",MODE_CHECK_IGNORE="checkRegex",UNDERSCORE="_",TRAILING_WILD_CARD_REPLACERS={[MODE_IGNORE](_,p1){return`${p1?`${p1}[^/]+`:"[^/]*"}(?=$|\\/$)`},[MODE_CHECK_IGNORE](_,p1){return`${p1?`${p1}[^/]*`:"[^/]*"}(?=$|\\/$)`}},makeRegexPrefix=(pattern)=>REPLACERS.reduce((prev,[matcher,replacer])=>prev.replace(matcher,replacer.bind(pattern)),pattern),isString=(subject)=>typeof subject==="string",checkPattern=(pattern)=>pattern&&isString(pattern)&&!REGEX_TEST_BLANK_LINE.test(pattern)&&!REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)&&pattern.indexOf("#")!==0,splitPattern=(pattern)=>pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);class IgnoreRule{constructor(pattern,mark,body,ignoreCase,negative,prefix){this.pattern=pattern,this.mark=mark,this.negative=negative,define2(this,"body",body),define2(this,"ignoreCase",ignoreCase),define2(this,"regexPrefix",prefix)}get regex(){let key=UNDERSCORE+MODE_IGNORE;if(this[key])return this[key];return this._make(MODE_IGNORE,key)}get checkRegex(){let key=UNDERSCORE+MODE_CHECK_IGNORE;if(this[key])return this[key];return this._make(MODE_CHECK_IGNORE,key)}_make(mode,key){let str5=this.regexPrefix.replace(REGEX_REPLACE_TRAILING_WILDCARD,TRAILING_WILD_CARD_REPLACERS[mode]),regex=this.ignoreCase?new RegExp(str5,"i"):new RegExp(str5);return define2(this,key,regex)}}var createRule=({pattern,mark},ignoreCase)=>{let negative=!1,body=pattern;if(body.indexOf("!")===0)negative=!0,body=body.substr(1);body=body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION,"!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH,"#");let regexPrefix=makeRegexPrefix(body);return new IgnoreRule(pattern,mark,body,ignoreCase,negative,regexPrefix)};class RuleManager{constructor(ignoreCase){this._ignoreCase=ignoreCase,this._rules=[]}_add(pattern){if(pattern&&pattern[KEY_IGNORE]){this._rules=this._rules.concat(pattern._rules._rules),this._added=!0;return}if(isString(pattern))pattern={pattern};if(checkPattern(pattern.pattern)){let rule=createRule(pattern,this._ignoreCase);this._added=!0,this._rules.push(rule)}}add(pattern){return this._added=!1,makeArray(isString(pattern)?splitPattern(pattern):pattern).forEach(this._add,this),this._added}test(path3,checkUnignored,mode){let ignored=!1,unignored=!1,matchedRule;this._rules.forEach((rule)=>{let{negative}=rule;if(unignored===negative&&ignored!==unignored||negative&&!ignored&&!unignored&&!checkUnignored)return;if(!rule[mode].test(path3))return;ignored=!negative,unignored=negative,matchedRule=negative?UNDEFINED:rule});let ret={ignored,unignored};if(matchedRule)ret.rule=matchedRule;return ret}}var throwError2=(message,Ctor)=>{throw new Ctor(message)},checkPath=(path3,originalPath,doThrow)=>{if(!isString(path3))return doThrow(`path must be a string, but got \`${originalPath}\``,TypeError);if(!path3)return doThrow("path must not be empty",TypeError);if(checkPath.isNotRelative(path3))return doThrow(`path should be a \`path.relative()\`d string, but got "${originalPath}"`,RangeError);return!0},isNotRelative=(path3)=>REGEX_TEST_INVALID_PATH.test(path3);checkPath.isNotRelative=isNotRelative;checkPath.convert=(p)=>p;class Ignore{constructor({ignorecase=!0,ignoreCase=ignorecase,allowRelativePaths=!1}={}){define2(this,KEY_IGNORE,!0),this._rules=new RuleManager(ignoreCase),this._strictPathCheck=!allowRelativePaths,this._initCache()}_initCache(){this._ignoreCache=Object.create(null),this._testCache=Object.create(null)}add(pattern){if(this._rules.add(pattern))this._initCache();return this}addPattern(pattern){return this.add(pattern)}_test(originalPath,cache,checkUnignored,slices){let path3=originalPath&&checkPath.convert(originalPath);return checkPath(path3,originalPath,this._strictPathCheck?throwError2:RETURN_FALSE),this._t(path3,cache,checkUnignored,slices)}checkIgnore(path3){if(!REGEX_TEST_TRAILING_SLASH.test(path3))return this.test(path3);let slices=path3.split(SLASH).filter(Boolean);if(slices.pop(),slices.length){let parent=this._t(slices.join(SLASH)+SLASH,this._testCache,!0,slices);if(parent.ignored)return parent}return this._rules.test(path3,!1,MODE_CHECK_IGNORE)}_t(path3,cache,checkUnignored,slices){if(path3 in cache)return cache[path3];if(!slices)slices=path3.split(SLASH).filter(Boolean);if(slices.pop(),!slices.length)return cache[path3]=this._rules.test(path3,checkUnignored,MODE_IGNORE);let parent=this._t(slices.join(SLASH)+SLASH,cache,checkUnignored,slices);return cache[path3]=parent.ignored?parent:this._rules.test(path3,checkUnignored,MODE_IGNORE)}ignores(path3){return this._test(path3,this._ignoreCache,!1).ignored}createFilter(){return(path3)=>!this.ignores(path3)}filter(paths){return makeArray(paths).filter(this.createFilter())}test(path3){return this._test(path3,this._testCache,!0)}}var factory=(options)=>new Ignore(options),isPathValid=(path3)=>checkPath(path3&&checkPath.convert(path3),path3,RETURN_FALSE),setupWindows=()=>{let makePosix=(str5)=>/^\\\\\?\\/.test(str5)||/["<>|\u0000-\u001F]+/u.test(str5)?str5:str5.replace(/\\/g,"/");checkPath.convert=makePosix;let REGEX_TEST_WINDOWS_PATH_ABSOLUTE=/^[a-z]:\//i;checkPath.isNotRelative=(path3)=>REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path3)||isNotRelative(path3)};if(typeof process<"u"&&process.platform==="win32")setupWindows();module.exports=factory;factory.default=factory;module.exports.isPathValid=isPathValid;define2(module.exports,Symbol.for("setupWindows"),setupWindows)});function isValid2(value){return typeof value==="string"&&VALID.has(value)}function resolveExecutorType(override){if(isValid2(override))return override;let env=process.env.GENIE_EXECUTOR;if(isValid2(env))return env;try{let persisted=loadGenieConfigSync().omni?.executor;if(isValid2(persisted))return persisted}catch{}return"tmux"}var VALID;var init_executor_config=__esm(()=>{init_genie_config2();VALID=new Set(["tmux","sdk"])});import{createHash as createHash4,randomUUID as randomUUID9}from"crypto";import{mkdirSync as mkdirSync20,writeFileSync as writeFileSync23}from"fs";import{homedir as homedir28}from"os";import{join as join52}from"path";function sanitizeWindowName2(chatId){let hash=createHash4("md5").update(chatId).digest("hex").slice(0,12);return`${chatId.replace(/[^a-zA-Z0-9]/g,"").slice(0,24)}-${hash}`||"chat"}class ClaudeCodeOmniExecutor{sessions=new Map;safePgCall=null;setSafePgCall(fn){this.safePgCall=fn}setNatsPublish(_fn){}async injectNudge(session,text){let nudgeText=`[system] ${text}`;await executeTmux2(`send-keys -t '${session.paneId}' ${shellQuote(nudgeText)} Enter`)}async spawn(agentName,chatId,env){let resolved=await resolve3(agentName);if(!resolved)throw Error(`Agent "${agentName}" not found in genie directory`);let entry=resolved.entry,tmuxSession=agentName,windowName=sanitizeWindowName2(chatId),{paneId,created}=await ensureTeamWindow(tmuxSession,windowName,entry.dir);if(created){let envVars={...env,GENIE_OMNI_CHAT_ID:chatId,GENIE_OMNI_AGENT:agentName},envPrefix=Object.entries(envVars).map(([k,v])=>`${k}=${shellQuote(v)}`).join(" "),systemPromptFile=join52(entry.dir,"AGENTS.md"),promptFlag=entry.promptMode==="system"?"--system-prompt-file":"--append-system-prompt-file",modelFlag=entry.model?`--model ${shellQuote(entry.model)}`:"",sessionId=randomUUID9(),cmd=[envPrefix,"claude",promptFlag,shellQuote(systemPromptFile),modelFlag,"--session-id",shellQuote(sessionId),"--dangerously-skip-permissions"].filter(Boolean).join(" ");await executeTmux2(`send-keys -t '${paneId}' ${shellQuote(cmd)} Enter`)}let sessionKey2=`${agentName}:${chatId}`,executorId=await this.registerInWorldA(agentName,chatId,env.OMNI_INSTANCE??"",tmuxSession,windowName,paneId);if(this.sessions.set(sessionKey2,{executorId}),executorId)await this.updateState(executorId,"running",chatId);let now=Date.now();return{id:sessionKey2,agentName,chatId,tmuxSession,tmuxWindow:windowName,paneId,createdAt:now,lastActivityAt:now}}async registerInWorldA(agentName,chatId,instanceId,tmuxSession,tmuxWindow,tmuxPaneId){if(!this.safePgCall)return null;let agent=await this.safePgCall("tmux-find-or-create-agent",()=>findOrCreateAgent(agentName,"omni","omni"),null,{chatId});if(!agent)return null;return(await this.safePgCall("tmux-create-executor",()=>createAndLinkExecutor(agent.id,"claude","tmux",{tmuxSession,tmuxWindow,tmuxPaneId,tmuxWindowId:null,metadata:{source:"omni",chat_id:chatId,instance_id:instanceId}}),null,{chatId}))?.id??null}async updateState(executorId,state,chatId){if(!this.safePgCall)return;await this.safePgCall("tmux-update-executor-state",()=>updateExecutorState(executorId,state),void 0,{executorId,chatId})}async deliver(session,message){let state=this.sessions.get(session.id);if(state?.executorId)await this.updateState(state.executorId,"working",session.chatId);let inboxDir=join52(process.env.CLAUDE_CONFIG_DIR??join52(homedir28(),".claude"),"teams",session.tmuxSession,"inboxes");mkdirSync20(inboxDir,{recursive:!0});let inboxFile=join52(inboxDir,`${sanitizeWindowName2(session.chatId)}.json`),messages2=[];try{let{readFileSync:readFileSync26}=await import("fs");messages2=JSON.parse(readFileSync26(inboxFile,"utf-8"))}catch{}if(messages2.push({from:message.sender||"whatsapp-user",text:message.content,summary:message.content.slice(0,120),timestamp:message.timestamp||new Date().toISOString(),read:!1}),writeFileSync23(inboxFile,JSON.stringify(messages2,null,2)),session.lastActivityAt=Date.now(),state?.executorId)await this.updateState(state.executorId,"idle",session.chatId)}async shutdown(session){let state=this.sessions.get(session.id);try{await killWindow(session.tmuxSession,session.tmuxWindow)}finally{if(state?.executorId&&this.safePgCall)await this.safePgCall("tmux-terminate-executor",()=>terminateExecutor(state.executorId),void 0,{executorId:state.executorId,chatId:session.chatId});this.sessions.delete(session.id)}}async isAlive(session){try{if(!await isPaneAlive(session.paneId))return!1;return await isPaneProcessRunning(session.paneId,"claude")}catch{return!1}}}var init_claude_code2=__esm(()=>{init_agent_directory();init_agent_registry();init_executor_registry();init_team_lead_command();init_tmux()});async function recordAuditEvent2(safePgCall,type2,attrs){let entityType=attrs.entity_type??"executor",entityId=attrs.entity_id??attrs.executor_id??"",actor=attrs.actor??attrs.agent_id??null,{entity_type:_et,entity_id:_eid,actor:_a,...details}=attrs;await safePgCall(`audit:${type2}`,(sql)=>sql`INSERT INTO audit_events (entity_type, entity_id, event_type, actor, details)
2055
- VALUES (${entityType}, ${entityId}, ${type2}, ${actor}, ${JSON.stringify(details)})`,void 0,{executorId:entityId,chatId:attrs.chat_id??""})}async function startSession(safePgCall,executorId,claudeSessionId,agentId,team,role,wishSlug){let sessionId=claudeSessionId??`sdk-${executorId}-${Date.now()}`;if(!await safePgCall("sdk-session-start",(sql)=>sql`INSERT INTO sessions (id, agent_id, executor_id, team, role, wish_slug, status, jsonl_path, project_path)
2056
- VALUES (${sessionId}, ${agentId}, ${executorId}, ${team??null}, ${role??null}, ${wishSlug??null}, 'active', '', '')
2057
- ON CONFLICT (id) DO NOTHING
2058
- RETURNING id`,null,{executorId,chatId:""}))return null;return sessionId}async function recordTurn(safePgCall,sessionId,turnIndex,role,content,toolName,timestamp2){let ts3=timestamp2??new Date().toISOString();await safePgCall("sdk-session-turn",(sql)=>sql`INSERT INTO session_content (session_id, turn_index, role, content, tool_name, timestamp)
2059
- VALUES (${sessionId}, ${turnIndex}, ${role}, ${content}, ${toolName??null}, ${ts3})
2060
- ON CONFLICT (session_id, turn_index) DO NOTHING`,void 0,{executorId:"",chatId:""})}async function updateTurnCount(safePgCall,sessionId,totalTurns){await safePgCall("sdk-session-turn-count",(sql)=>sql`UPDATE sessions SET total_turns = ${totalTurns}, updated_at = now() WHERE id = ${sessionId}`,void 0,{executorId:"",chatId:""})}async function endSession(safePgCall,sessionId,status="completed"){await safePgCall("sdk-session-end",(sql)=>sql`UPDATE sessions SET ended_at = now(), status = ${status}, updated_at = now() WHERE id = ${sessionId}`,void 0,{executorId:"",chatId:""})}function buildTurnBasedPrompt(senderName,instanceId,chatId){return`
2054
+ `).map((r)=>r.table_name)}async function filterAvailableTables(sql,requested){let existing=new Set(await getAvailableTables(sql)),available=[],skipped=[];for(let table of requested)if(existing.has(table))available.push(table);else skipped.push(table);return{available,skipped}}var require_ignore=__commonJS((exports,module)=>{function makeArray(subject){return Array.isArray(subject)?subject:[subject]}var UNDEFINED=void 0,EMPTY="",SPACE=" ",ESCAPE="\\",REGEX_TEST_BLANK_LINE=/^\s+$/,REGEX_INVALID_TRAILING_BACKSLASH=/(?:[^\\]|^)\\$/,REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION=/^\\!/,REGEX_REPLACE_LEADING_EXCAPED_HASH=/^\\#/,REGEX_SPLITALL_CRLF=/\r?\n/g,REGEX_TEST_INVALID_PATH=/^\.{0,2}\/|^\.{1,2}$/,REGEX_TEST_TRAILING_SLASH=/\/$/,SLASH="/",TMP_KEY_IGNORE="node-ignore";if(typeof Symbol<"u")TMP_KEY_IGNORE=Symbol.for("node-ignore");var KEY_IGNORE=TMP_KEY_IGNORE,define2=(object,key,value)=>{return Object.defineProperty(object,key,{value}),value},REGEX_REGEXP_RANGE=/([0-z])-([0-z])/g,RETURN_FALSE=()=>!1,sanitizeRange=(range)=>range.replace(REGEX_REGEXP_RANGE,(match,from,to)=>from.charCodeAt(0)<=to.charCodeAt(0)?match:EMPTY),cleanRangeBackSlash=(slashes)=>{let{length}=slashes;return slashes.slice(0,length-length%2)},REPLACERS=[[/^\uFEFF/,()=>EMPTY],[/((?:\\\\)*?)(\\?\s+)$/,(_,m1,m2)=>m1+(m2.indexOf("\\")===0?SPACE:EMPTY)],[/(\\+?)\s/g,(_,m1)=>{let{length}=m1;return m1.slice(0,length-length%2)+SPACE}],[/[\\$.|*+(){^]/g,(match)=>`\\${match}`],[/(?!\\)\?/g,()=>"[^/]"],[/^\//,()=>"^"],[/\//g,()=>"\\/"],[/^\^*\\\*\\\*\\\//,()=>"^(?:.*\\/)?"],[/^(?=[^^])/,function(){return!/\/(?!$)/.test(this)?"(?:^|\\/)":"^"}],[/\\\/\\\*\\\*(?=\\\/|$)/g,(_,index,str5)=>index+6<str5.length?"(?:\\/[^\\/]+)*":"\\/.+"],[/(^|[^\\]+)(\\\*)+(?=.+)/g,(_,p1,p2)=>{let unescaped=p2.replace(/\\\*/g,"[^\\/]*");return p1+unescaped}],[/\\\\\\(?=[$.|*+(){^])/g,()=>ESCAPE],[/\\\\/g,()=>ESCAPE],[/(\\)?\[([^\]/]*?)(\\*)($|\])/g,(match,leadEscape,range,endEscape,close)=>leadEscape===ESCAPE?`\\[${range}${cleanRangeBackSlash(endEscape)}${close}`:close==="]"?endEscape.length%2===0?`[${sanitizeRange(range)}${endEscape}]`:"[]":"[]"],[/(?:[^*])$/,(match)=>/\/$/.test(match)?`${match}$`:`${match}(?=$|\\/$)`]],REGEX_REPLACE_TRAILING_WILDCARD=/(^|\\\/)?\\\*$/,MODE_IGNORE="regex",MODE_CHECK_IGNORE="checkRegex",UNDERSCORE="_",TRAILING_WILD_CARD_REPLACERS={[MODE_IGNORE](_,p1){return`${p1?`${p1}[^/]+`:"[^/]*"}(?=$|\\/$)`},[MODE_CHECK_IGNORE](_,p1){return`${p1?`${p1}[^/]*`:"[^/]*"}(?=$|\\/$)`}},makeRegexPrefix=(pattern)=>REPLACERS.reduce((prev,[matcher,replacer])=>prev.replace(matcher,replacer.bind(pattern)),pattern),isString=(subject)=>typeof subject==="string",checkPattern=(pattern)=>pattern&&isString(pattern)&&!REGEX_TEST_BLANK_LINE.test(pattern)&&!REGEX_INVALID_TRAILING_BACKSLASH.test(pattern)&&pattern.indexOf("#")!==0,splitPattern=(pattern)=>pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);class IgnoreRule{constructor(pattern,mark,body,ignoreCase,negative,prefix){this.pattern=pattern,this.mark=mark,this.negative=negative,define2(this,"body",body),define2(this,"ignoreCase",ignoreCase),define2(this,"regexPrefix",prefix)}get regex(){let key=UNDERSCORE+MODE_IGNORE;if(this[key])return this[key];return this._make(MODE_IGNORE,key)}get checkRegex(){let key=UNDERSCORE+MODE_CHECK_IGNORE;if(this[key])return this[key];return this._make(MODE_CHECK_IGNORE,key)}_make(mode,key){let str5=this.regexPrefix.replace(REGEX_REPLACE_TRAILING_WILDCARD,TRAILING_WILD_CARD_REPLACERS[mode]),regex=this.ignoreCase?new RegExp(str5,"i"):new RegExp(str5);return define2(this,key,regex)}}var createRule=({pattern,mark},ignoreCase)=>{let negative=!1,body=pattern;if(body.indexOf("!")===0)negative=!0,body=body.substr(1);body=body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION,"!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH,"#");let regexPrefix=makeRegexPrefix(body);return new IgnoreRule(pattern,mark,body,ignoreCase,negative,regexPrefix)};class RuleManager{constructor(ignoreCase){this._ignoreCase=ignoreCase,this._rules=[]}_add(pattern){if(pattern&&pattern[KEY_IGNORE]){this._rules=this._rules.concat(pattern._rules._rules),this._added=!0;return}if(isString(pattern))pattern={pattern};if(checkPattern(pattern.pattern)){let rule=createRule(pattern,this._ignoreCase);this._added=!0,this._rules.push(rule)}}add(pattern){return this._added=!1,makeArray(isString(pattern)?splitPattern(pattern):pattern).forEach(this._add,this),this._added}test(path3,checkUnignored,mode){let ignored=!1,unignored=!1,matchedRule;this._rules.forEach((rule)=>{let{negative}=rule;if(unignored===negative&&ignored!==unignored||negative&&!ignored&&!unignored&&!checkUnignored)return;if(!rule[mode].test(path3))return;ignored=!negative,unignored=negative,matchedRule=negative?UNDEFINED:rule});let ret={ignored,unignored};if(matchedRule)ret.rule=matchedRule;return ret}}var throwError2=(message,Ctor)=>{throw new Ctor(message)},checkPath=(path3,originalPath,doThrow)=>{if(!isString(path3))return doThrow(`path must be a string, but got \`${originalPath}\``,TypeError);if(!path3)return doThrow("path must not be empty",TypeError);if(checkPath.isNotRelative(path3))return doThrow(`path should be a \`path.relative()\`d string, but got "${originalPath}"`,RangeError);return!0},isNotRelative=(path3)=>REGEX_TEST_INVALID_PATH.test(path3);checkPath.isNotRelative=isNotRelative;checkPath.convert=(p)=>p;class Ignore{constructor({ignorecase=!0,ignoreCase=ignorecase,allowRelativePaths=!1}={}){define2(this,KEY_IGNORE,!0),this._rules=new RuleManager(ignoreCase),this._strictPathCheck=!allowRelativePaths,this._initCache()}_initCache(){this._ignoreCache=Object.create(null),this._testCache=Object.create(null)}add(pattern){if(this._rules.add(pattern))this._initCache();return this}addPattern(pattern){return this.add(pattern)}_test(originalPath,cache,checkUnignored,slices){let path3=originalPath&&checkPath.convert(originalPath);return checkPath(path3,originalPath,this._strictPathCheck?throwError2:RETURN_FALSE),this._t(path3,cache,checkUnignored,slices)}checkIgnore(path3){if(!REGEX_TEST_TRAILING_SLASH.test(path3))return this.test(path3);let slices=path3.split(SLASH).filter(Boolean);if(slices.pop(),slices.length){let parent=this._t(slices.join(SLASH)+SLASH,this._testCache,!0,slices);if(parent.ignored)return parent}return this._rules.test(path3,!1,MODE_CHECK_IGNORE)}_t(path3,cache,checkUnignored,slices){if(path3 in cache)return cache[path3];if(!slices)slices=path3.split(SLASH).filter(Boolean);if(slices.pop(),!slices.length)return cache[path3]=this._rules.test(path3,checkUnignored,MODE_IGNORE);let parent=this._t(slices.join(SLASH)+SLASH,cache,checkUnignored,slices);return cache[path3]=parent.ignored?parent:this._rules.test(path3,checkUnignored,MODE_IGNORE)}ignores(path3){return this._test(path3,this._ignoreCache,!1).ignored}createFilter(){return(path3)=>!this.ignores(path3)}filter(paths){return makeArray(paths).filter(this.createFilter())}test(path3){return this._test(path3,this._testCache,!0)}}var factory=(options)=>new Ignore(options),isPathValid=(path3)=>checkPath(path3&&checkPath.convert(path3),path3,RETURN_FALSE),setupWindows=()=>{let makePosix=(str5)=>/^\\\\\?\\/.test(str5)||/["<>|\u0000-\u001F]+/u.test(str5)?str5:str5.replace(/\\/g,"/");checkPath.convert=makePosix;let REGEX_TEST_WINDOWS_PATH_ABSOLUTE=/^[a-z]:\//i;checkPath.isNotRelative=(path3)=>REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path3)||isNotRelative(path3)};if(typeof process<"u"&&process.platform==="win32")setupWindows();module.exports=factory;factory.default=factory;module.exports.isPathValid=isPathValid;define2(module.exports,Symbol.for("setupWindows"),setupWindows)});function isValid2(value){return typeof value==="string"&&VALID.has(value)}function resolveExecutorType(override){if(isValid2(override))return override;let env=process.env.GENIE_EXECUTOR;if(isValid2(env))return env;try{let persisted=loadGenieConfigSync().omni?.executor;if(isValid2(persisted))return persisted}catch{}return"tmux"}var VALID;var init_executor_config=__esm(()=>{init_genie_config2();VALID=new Set(["tmux","sdk"])});function buildTurnBasedPrompt(senderName,instanceId,chatId){return`
2061
2055
  # WhatsApp Turn-Based Conversation
2062
2056
 
2063
2057
  You are responding to a WhatsApp message from ${senderName}.
@@ -2085,7 +2079,13 @@ You have two equivalent ways to send a reply to the user:
2085
2079
  1. Use \`SendMessage(recipient: "omni", ...)\` for normal text replies.
2086
2080
  2. ALWAYS call \`omni done\` as your LAST action to close the turn \u2014 even if you already sent SendMessage replies, call \`omni done skip=true\`.
2087
2081
  3. Do NOT generate bare text as your reply \u2014 it will go nowhere. Use SendMessage or omni done.
2088
- `.trim()}async function loadSystemPrompt(entry){let identityPath=loadIdentity(entry);if(!identityPath)return;let{readFileSync:readFileSync26}=await import("fs");try{return readFileSync26(identityPath,"utf-8")}catch{return}}async function resolveSystemPrompt(entry,state,message,chatId){let prompt2=await loadSystemPrompt(entry),isTurnBased=Boolean(state.env.OMNI_INSTANCE);if(isTurnBased){let turnPrompt=buildTurnBasedPrompt(message.sender,state.env.OMNI_INSTANCE,state.env.OMNI_CHAT??chatId);prompt2=prompt2?`${turnPrompt}
2082
+ `.trim()}import{createHash as createHash4,randomUUID as randomUUID9}from"crypto";import{mkdirSync as mkdirSync20,writeFileSync as writeFileSync23}from"fs";import{homedir as homedir28}from"os";import{join as join52}from"path";function sanitizeWindowName2(chatId){let hash=createHash4("md5").update(chatId).digest("hex").slice(0,12);return`${chatId.replace(/[^a-zA-Z0-9]/g,"").slice(0,24)}-${hash}`||"chat"}function buildOmniSpawnParams(agentName,chatId,entry,env,initialMessage){let instanceId=env.OMNI_INSTANCE??"",senderName=env.OMNI_SENDER_NAME??"whatsapp-user",turnPrompt=buildTurnBasedPrompt(senderName,instanceId,chatId);return{provider:entry.provider??"claude",team:agentName,role:agentName,sessionId:randomUUID9(),model:entry.model,promptMode:entry.promptMode,systemPromptFile:join52(entry.dir,"AGENTS.md"),systemPrompt:turnPrompt,initialPrompt:initialMessage,nativeTeam:{enabled:!0,agentName,color:entry.color??void 0}}}class ClaudeCodeOmniExecutor{sessions=new Map;safePgCall=null;setSafePgCall(fn){this.safePgCall=fn}setNatsPublish(_fn){}async injectNudge(session,text){let nudgeText=`[system] ${text}`;await executeTmux2(`send-keys -t '${session.paneId}' ${shellQuote(nudgeText)} Enter`)}async spawn(agentName,chatId,env){let resolved=await resolve3(agentName);if(!resolved)throw Error(`Agent "${agentName}" not found in genie directory`);let entry=resolved.entry,tmuxSession=agentName,windowName=sanitizeWindowName2(chatId),{paneId,created}=await ensureTeamWindow(tmuxSession,windowName,entry.dir);if(created){let omniEnv={...env,GENIE_OMNI_CHAT_ID:chatId,GENIE_OMNI_AGENT:agentName},params=buildOmniSpawnParams(agentName,chatId,entry,omniEnv),launch=buildLaunchCommand(params),allEnv={...omniEnv,...launch.env},envPrefix=Object.entries(allEnv).map(([k,v])=>`${k}=${shellQuote(v)}`).join(" "),cmd=envPrefix?`${envPrefix} ${launch.command}`:launch.command;await executeTmux2(`send-keys -t '${paneId}' ${shellQuote(cmd)} Enter`)}let sessionKey2=`${agentName}:${chatId}`,executorId=await this.registerInWorldA(agentName,chatId,env.OMNI_INSTANCE??"",tmuxSession,windowName,paneId);if(this.sessions.set(sessionKey2,{executorId}),executorId)await this.updateState(executorId,"running",chatId);let now=Date.now();return{id:sessionKey2,agentName,chatId,tmuxSession,tmuxWindow:windowName,paneId,createdAt:now,lastActivityAt:now}}async registerInWorldA(agentName,chatId,instanceId,tmuxSession,tmuxWindow,tmuxPaneId){if(!this.safePgCall)return null;let agent=await this.safePgCall("tmux-find-or-create-agent",()=>findOrCreateAgent(agentName,"omni","omni"),null,{chatId});if(!agent)return null;return(await this.safePgCall("tmux-create-executor",()=>createAndLinkExecutor(agent.id,"claude","tmux",{tmuxSession,tmuxWindow,tmuxPaneId,tmuxWindowId:null,metadata:{source:"omni",chat_id:chatId,instance_id:instanceId}}),null,{chatId}))?.id??null}async updateState(executorId,state,chatId){if(!this.safePgCall)return;await this.safePgCall("tmux-update-executor-state",()=>updateExecutorState(executorId,state),void 0,{executorId,chatId})}async deliver(session,message){let state=this.sessions.get(session.id);if(state?.executorId)await this.updateState(state.executorId,"working",session.chatId);let inboxDir=join52(process.env.CLAUDE_CONFIG_DIR??join52(homedir28(),".claude"),"teams",session.tmuxSession,"inboxes");mkdirSync20(inboxDir,{recursive:!0});let inboxFile=join52(inboxDir,`${sanitizeWindowName2(session.chatId)}.json`),messages2=[];try{let{readFileSync:readFileSync26}=await import("fs");messages2=JSON.parse(readFileSync26(inboxFile,"utf-8"))}catch{}if(messages2.push({from:message.sender||"whatsapp-user",text:message.content,summary:message.content.slice(0,120),timestamp:message.timestamp||new Date().toISOString(),read:!1}),writeFileSync23(inboxFile,JSON.stringify(messages2,null,2)),session.lastActivityAt=Date.now(),state?.executorId)await this.updateState(state.executorId,"idle",session.chatId)}async shutdown(session){let state=this.sessions.get(session.id);try{await killWindow(session.tmuxSession,session.tmuxWindow)}finally{if(state?.executorId&&this.safePgCall)await this.safePgCall("tmux-terminate-executor",()=>terminateExecutor(state.executorId),void 0,{executorId:state.executorId,chatId:session.chatId});this.sessions.delete(session.id)}}async isAlive(session){try{if(!await isPaneAlive(session.paneId))return!1;return await isPaneProcessRunning(session.paneId,"claude")}catch{return!1}}}var init_claude_code2=__esm(()=>{init_agent_directory();init_agent_registry();init_executor_registry();init_provider_adapters();init_team_lead_command();init_tmux()});async function recordAuditEvent2(safePgCall,type2,attrs){let entityType=attrs.entity_type??"executor",entityId=attrs.entity_id??attrs.executor_id??"",actor=attrs.actor??attrs.agent_id??null,{entity_type:_et,entity_id:_eid,actor:_a,...details}=attrs;await safePgCall(`audit:${type2}`,(sql)=>sql`INSERT INTO audit_events (entity_type, entity_id, event_type, actor, details)
2083
+ VALUES (${entityType}, ${entityId}, ${type2}, ${actor}, ${JSON.stringify(details)})`,void 0,{executorId:entityId,chatId:attrs.chat_id??""})}async function startSession(safePgCall,executorId,claudeSessionId,agentId,team,role,wishSlug){let sessionId=claudeSessionId??`sdk-${executorId}-${Date.now()}`;if(!await safePgCall("sdk-session-start",(sql)=>sql`INSERT INTO sessions (id, agent_id, executor_id, team, role, wish_slug, status, jsonl_path, project_path)
2084
+ VALUES (${sessionId}, ${agentId}, ${executorId}, ${team??null}, ${role??null}, ${wishSlug??null}, 'active', '', '')
2085
+ ON CONFLICT (id) DO NOTHING
2086
+ RETURNING id`,null,{executorId,chatId:""}))return null;return sessionId}async function recordTurn(safePgCall,sessionId,turnIndex,role,content,toolName,timestamp2){let ts3=timestamp2??new Date().toISOString();await safePgCall("sdk-session-turn",(sql)=>sql`INSERT INTO session_content (session_id, turn_index, role, content, tool_name, timestamp)
2087
+ VALUES (${sessionId}, ${turnIndex}, ${role}, ${content}, ${toolName??null}, ${ts3})
2088
+ ON CONFLICT (session_id, turn_index) DO NOTHING`,void 0,{executorId:"",chatId:""})}async function updateTurnCount(safePgCall,sessionId,totalTurns){await safePgCall("sdk-session-turn-count",(sql)=>sql`UPDATE sessions SET total_turns = ${totalTurns}, updated_at = now() WHERE id = ${sessionId}`,void 0,{executorId:"",chatId:""})}async function endSession(safePgCall,sessionId,status="completed"){await safePgCall("sdk-session-end",(sql)=>sql`UPDATE sessions SET ended_at = now(), status = ${status}, updated_at = now() WHERE id = ${sessionId}`,void 0,{executorId:"",chatId:""})}async function loadSystemPrompt(entry){let identityPath=loadIdentity(entry);if(!identityPath)return;let{readFileSync:readFileSync26}=await import("fs");try{return readFileSync26(identityPath,"utf-8")}catch{return}}async function resolveSystemPrompt(entry,state,message,chatId){let prompt2=await loadSystemPrompt(entry),isTurnBased=Boolean(state.env.OMNI_INSTANCE);if(isTurnBased){let turnPrompt=buildTurnBasedPrompt(message.sender,state.env.OMNI_INSTANCE,state.env.OMNI_CHAT??chatId);prompt2=prompt2?`${turnPrompt}
2089
2089
 
2090
2090
  ${prompt2}`:turnPrompt}return{prompt:prompt2,isTurnBased}}function extractTextFromAssistant(msg){if(!msg.message)return[];return msg.message.content.filter((b2)=>b2.type==="text"&&b2.text).map((b2)=>b2.text)}async function collectQueryResult(queryMessages){let textParts=[],sessionId;try{for await(let msg of queryMessages){if(msg.type==="assistant")textParts.push(...extractTextFromAssistant(msg));if(msg.type==="result"&&msg.subtype==="success")sessionId=msg.session_id}}catch(err){if(err.name==="AbortError")return{text:""};throw err}return{text:textParts.join(`
2091
2091
  `).trim(),sessionId}}function buildReplyPayload(agent,chatId,instanceId,extra={}){return JSON.stringify({content:"",agent,chat_id:chatId,instance_id:instanceId,timestamp:new Date().toISOString(),...extra})}function resolveAction(params,env){if(params.skip)return{type:"skip",extra:{reason:params.reason},label:"Turn closed (skip)."};if(params.react)return{type:"react",extra:{react:String(params.react),message_id:env.OMNI_MESSAGE??""},label:`Reacted ${params.react} + turn closed.`};if(params.media)return{type:"media",extra:{content:params.caption?String(params.caption):params.text?String(params.text):"",media:String(params.media)},label:"Media sent + turn closed."};if(params.text)return{type:"text",extra:{content:String(params.text)},label:"Turn closed. Message delivered."};return{type:"skip",extra:{},label:"Turn closed (skip)."}}function handleDoneTool(params,env,natsPublish){let instanceId=env.OMNI_INSTANCE??"",chatId=env.OMNI_CHAT??"",agent=env.OMNI_AGENT??"",action=resolveAction(params,env);if(action.type==="skip"){if(natsPublish&&instanceId&&chatId)natsPublish(`omni.turn.done.${instanceId}.${chatId}`,JSON.stringify({action:"skip",...action.extra}));return action.label}if(!natsPublish||!instanceId||!chatId)return console.warn("[claude-sdk] No NATS publish available \u2014 reply dropped"),"Turn close attempted but NATS publish not available.";return natsPublish(`omni.reply.${instanceId}.${chatId}`,buildReplyPayload(agent,chatId,instanceId,action.extra)),action.label}function parseSendMessageInput(input){if(!input||typeof input!=="object")return{};let obj=input,recipient=typeof obj.recipient==="string"?obj.recipient:typeof obj.to==="string"?obj.to:void 0,body=typeof obj.message==="string"?obj.message:typeof obj.content==="string"?obj.content:void 0;return{recipient,body}}function createSendMessageOmniHook(env,natsPublish){return async(input)=>{let hookInput=input;if(hookInput.tool_name!=="SendMessage")return{};let{recipient,body}=parseSendMessageInput(hookInput.tool_input);if(recipient!=="omni")return{};let instanceId=env.OMNI_INSTANCE??"",chatId=env.OMNI_CHAT??"",agent=env.OMNI_AGENT??"";if(!instanceId||!chatId)return{};if(!body||body.trim()==="")return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:'SendMessage(recipient: "omni") requires a non-empty `message` field. Retry with the reply text.'}};if(!natsPublish)return console.warn("[claude-sdk] SendMessage(to: omni) intercepted but NATS publish unavailable \u2014 message dropped"),{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"Omni bridge unavailable \u2014 message could not be delivered."}};return natsPublish(`omni.reply.${instanceId}.${chatId}`,buildReplyPayload(agent,chatId,instanceId,{content:body})),{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"Message delivered to user via omni bridge."}}}}async function createDoneMcpServer(env,natsPublish){let{createSdkMcpServer,tool}=await import("@anthropic-ai/claude-agent-sdk");return createSdkMcpServer({name:"genie-omni-tools",tools:[tool("done","Close this turn. REQUIRED after processing the user message. Sends a final response, reacts, or skips. Call exactly once per turn.",{text:exports_external.string().optional().describe("Final message to the user"),media:exports_external.string().optional().describe("File path for media attachment"),caption:exports_external.string().optional().describe("Caption for media"),react:exports_external.string().optional().describe("Emoji reaction (instead of text)"),skip:exports_external.boolean().optional().describe("Close turn without sending anything"),reason:exports_external.string().optional().describe("Internal reason for skipping")},async(args)=>{return{content:[{type:"text",text:handleDoneTool(args,env,natsPublish)}]}})]})}class ClaudeSdkOmniExecutor{sessions=new Map;safePgCall=null;natsPublish=null;deliveryQueues=new Map;pendingNudges=new Map;setSafePgCall(fn){this.safePgCall=fn}setNatsPublish(fn){this.natsPublish=fn}async injectNudge(session,text){if(!this.sessions.has(session.id))return;this.pendingNudges.set(session.id,text)}async spawn(agentName,chatId,env){if(!await resolve3(agentName))throw Error(`Agent "${agentName}" not found in genie directory`);let provider=new ClaudeSdkProvider,abortController=new AbortController,sessionId=`${agentName}:${chatId}`,registration=await this.registerInWorldA(agentName,chatId,env.OMNI_INSTANCE??"");if(this.sessions.set(sessionId,{abortController,running:!0,provider,executorId:registration?.executorId??null,claudeSessionId:registration?.claudeSessionId,dbSessionId:null,turnIndex:0,env}),registration?.executorId)await this.updateState(registration.executorId,"running",chatId);let now=Date.now();return{id:sessionId,agentName,chatId,tmuxSession:"",tmuxWindow:"",paneId:`sdk-${chatId}`,createdAt:now,lastActivityAt:now}}async registerInWorldA(agentName,chatId,instanceId){if(!this.safePgCall)return null;let agent=await this.safePgCall("sdk-find-or-create-agent",()=>findOrCreateAgent(agentName,"omni","omni"),null,{chatId});if(!agent)return null;let existing=await this.safePgCall("sdk-find-existing-executor",()=>findLatestByMetadata({agentId:agent.id,source:"omni",chatId}),null,{chatId});if(existing)return await this.safePgCall("sdk-relink-executor",()=>relinkExecutorToAgent(existing.id,agent.id),void 0,{executorId:existing.id,chatId}),await recordAuditEvent2(this.safePgCall,"session.resumed",{executor_id:existing.id,agent_id:agentName,chat_id:chatId,claude_session_id:existing.claudeSessionId}),{executorId:existing.id,claudeSessionId:existing.claudeSessionId??void 0};let executor=await this.safePgCall("sdk-create-executor",()=>createAndLinkExecutor(agent.id,"claude","api",{claudeSessionId:void 0,metadata:{source:"omni",chat_id:chatId,instance_id:instanceId}}),null,{chatId});if(executor)await recordAuditEvent2(this.safePgCall,"session.created_fresh",{executor_id:executor.id,agent_id:agentName,chat_id:chatId});return executor?{executorId:executor.id}:null}async updateState(executorId,state,chatId){if(!this.safePgCall)return;await this.safePgCall("sdk-update-executor-state",()=>updateExecutorState(executorId,state),void 0,{executorId,chatId})}async deliver(session,message){let state=this.sessions.get(session.id);if(!state)throw Error(`No SDK session found for ${session.id}`);let current=(this.deliveryQueues.get(session.id)??Promise.resolve()).then(()=>this._processDelivery(session,state,message));this.deliveryQueues.set(session.id,current.catch(()=>{}))}async _processDelivery(session,state,message){let resolved=await resolve3(session.agentName);if(!resolved)throw Error(`Agent "${session.agentName}" not found in genie directory`);let entry=resolved.entry,permissionConfig=resolvePermissionConfig(entry.permissions),{prompt:systemPrompt,isTurnBased}=await resolveSystemPrompt(entry,state,message,session.chatId);if(state.executorId)await this.updateState(state.executorId,"working",session.chatId);if(this.safePgCall)await recordAuditEvent2(this.safePgCall,"deliver.start",{executor_id:state.executorId??session.id,agent_id:session.agentName,chat_id:message.chatId,instance_id:message.instanceId});let doneMcp=await createDoneMcpServer(state.env,this.natsPublish),sendMessageHooks=isTurnBased?{PreToolUse:[{matcher:"SendMessage",hooks:[createSendMessageOmniHook(state.env,this.natsPublish)]}]}:void 0,extraOptions={abortController:state.abortController,mcpServers:{"genie-omni-tools":doneMcp},...sendMessageHooks&&{hooks:sendMessageHooks}};if(state.claudeSessionId)extraOptions.resume=state.claudeSessionId;let queryContent=message.content,pendingNudge=this.pendingNudges.get(session.id);if(pendingNudge)queryContent=`[system] ${pendingNudge}
@@ -2953,7 +2953,7 @@ Bus `);for(let i2=1;i2<parts.length;i2++){let usb2=parseLinuxUsb(parts[i2]);resu
2953
2953
  \x1B[1mGenie Doctor \u2014 Auto Fix\x1B[0m`),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m
2954
2954
  `),await killStalePostgres(),await cleanSharedMemory();let genieHome2=process.env.GENIE_HOME??join4(homedir4(),".genie"),pidFile=join4(genieHome2,"scheduler.pid");await stopExistingDaemon(pidFile),removeStaleFiles(genieHome2,pidFile),await restartDaemon(),console.log(`
2955
2955
  \x1B[2m${"\u2500".repeat(40)}\x1B[0m`),console.log(`\x1B[32mFix complete.\x1B[0m Run \x1B[36mgenie doctor\x1B[0m to verify.
2956
- `)}init_setup();init_shortcuts();import{existsSync as existsSync7}from"fs";import{homedir as homedir8}from"os";import{join as join8}from"path";async function shortcutsShowCommand(){displayShortcuts();let home=homedir8(),tmuxConf=join8(home,".tmux.conf"),zshrc=join8(home,".zshrc"),bashrc=join8(home,".bashrc");if(console.log("Installation status:"),isShortcutsInstalled(tmuxConf))console.log(" \x1B[32m\u2713\x1B[0m tmux.conf");else console.log(" \x1B[33m-\x1B[0m tmux.conf");let shellRc=existsSync7(zshrc)?zshrc:bashrc;if(isShortcutsInstalled(shellRc))console.log(` \x1B[32m\u2713\x1B[0m ${shellRc.replace(home,"~")}`);else console.log(` \x1B[33m-\x1B[0m ${shellRc.replace(home,"~")}`);console.log(),console.log("Run \x1B[36mgenie shortcuts install\x1B[0m to install shortcuts."),console.log("Run \x1B[36mgenie shortcuts uninstall\x1B[0m to remove shortcuts."),console.log()}async function shortcutsInstallCommand(){await installShortcuts()}async function shortcutsUninstallCommand(){await uninstallShortcuts()}init_esm14();init_claude_settings();init_genie_config2();import{existsSync as existsSync8,lstatSync,rmSync as rmSync2,unlinkSync as unlinkSync5}from"fs";import{homedir as homedir9}from"os";import{join as join9}from"path";var ORCHESTRATION_RULES_PATH=join9(homedir9(),".claude","rules","genie-orchestration.md"),LOCAL_BIN=join9(homedir9(),".local","bin"),SYMLINKS=["genie","term"];function isGenieSymlink(path2){try{if(!existsSync8(path2))return!1;if(!lstatSync(path2).isSymbolicLink())return!1;return!0}catch{return!1}}function removeSymlinks(){let removed=[];for(let name of SYMLINKS){let symlinkPath=join9(LOCAL_BIN,name);if(isGenieSymlink(symlinkPath))try{unlinkSync5(symlinkPath),removed.push(name)}catch{}}return removed}function tryRemoveStep(label,successMsg,fn){console.log(`\x1B[2m${label}\x1B[0m`);try{fn(),console.log(` \x1B[32m+\x1B[0m ${successMsg}`)}catch(error){let message=error instanceof Error?error.message:String(error);console.log(` \x1B[33m!\x1B[0m ${label.replace("...","")} failed: ${message}`)}}function performUninstall(hasHookScript,existingSymlinks,genieDir,hasGenieDir){if(hasHookScript)tryRemoveStep("Removing hook script...","Hook script removed",()=>removeHookScript());if(existingSymlinks.length>0){console.log("\x1B[2mRemoving symlinks...\x1B[0m");let removed=removeSymlinks();if(removed.length>0)console.log(` \x1B[32m+\x1B[0m Removed: ${removed.join(", ")}`)}if(existsSync8(ORCHESTRATION_RULES_PATH))tryRemoveStep("Removing orchestration rules...","Orchestration rules removed (~/.claude/rules/genie-orchestration.md)",()=>unlinkSync5(ORCHESTRATION_RULES_PATH));if(hasGenieDir)tryRemoveStep("Removing genie directory...","Directory removed",()=>rmSync2(genieDir,{recursive:!0,force:!0}))}async function uninstallCommand(){console.log(),console.log("\x1B[1m\x1B[33m Uninstall Genie CLI\x1B[0m"),console.log();let genieDir=getGenieDir(),hasGenieDir=existsSync8(genieDir),hasHookScript=hookScriptExists(),hasOrchestrationRules=existsSync8(ORCHESTRATION_RULES_PATH),existingSymlinks=SYMLINKS.filter((name)=>isGenieSymlink(join9(LOCAL_BIN,name)));if(console.log("\x1B[2mThis will remove:\x1B[0m"),hasHookScript)console.log(" \x1B[31m-\x1B[0m Hook script (~/.claude/hooks/genie-bash-hook.sh)");if(hasOrchestrationRules)console.log(" \x1B[31m-\x1B[0m Orchestration rules (~/.claude/rules/genie-orchestration.md)");if(hasGenieDir)console.log(` \x1B[31m-\x1B[0m Genie directory (${contractPath(genieDir)})`);if(existingSymlinks.length>0)console.log(` \x1B[31m-\x1B[0m Symlinks from ~/.local/bin: ${existingSymlinks.join(", ")}`);if(console.log(),!hasGenieDir&&!hasHookScript&&!hasOrchestrationRules&&existingSymlinks.length===0){console.log("\x1B[33mNothing to uninstall.\x1B[0m"),console.log();return}if(!await esm_default4({message:"Are you sure you want to uninstall Genie CLI?",default:!1})){console.log(),console.log("\x1B[2mUninstall cancelled.\x1B[0m"),console.log();return}console.log(),performUninstall(hasHookScript,existingSymlinks,genieDir,hasGenieDir),console.log(),console.log("\x1B[32m+\x1B[0m Genie CLI uninstalled."),console.log(),console.log("\x1B[2mNote: If you installed via npm/bun, also run:\x1B[0m"),console.log(" \x1B[36mbun remove -g @automagik/genie\x1B[0m"),console.log(" \x1B[2mor\x1B[0m"),console.log(" \x1B[36mnpm uninstall -g @automagik/genie\x1B[0m"),console.log()}init_genie_config2();import{execSync as execSync2,spawn as spawn2}from"child_process";import{chmodSync as chmodSync2,copyFileSync as copyFileSync2,existsSync as existsSync9,mkdirSync as mkdirSync6,readFileSync as readFileSync6,readdirSync,rmSync as rmSync3,writeFileSync as writeFileSync7}from"fs";import{chmod,copyFile,mkdir,unlink}from"fs/promises";import{homedir as homedir10}from"os";import{join as join10}from"path";var GENIE_HOME=process.env.GENIE_HOME||join10(homedir10(),".genie"),GENIE_SRC=join10(GENIE_HOME,"src"),GENIE_BIN=join10(GENIE_HOME,"bin"),LOCAL_BIN2=join10(homedir10(),".local","bin");function log(message){console.log(`\x1B[32m\u25B8\x1B[0m ${message}`)}function success(message){console.log(`\x1B[32m\u2714\x1B[0m ${message}`)}function error(message){console.log(`\x1B[31m\u2716\x1B[0m ${message}`)}async function runCommand(command,args,cwd){return new Promise((resolve)=>{let output=[],child=spawn2(command,args,{cwd,stdio:["inherit","pipe","pipe"],env:{...process.env,FORCE_COLOR:"1"}});child.stdout?.on("data",(data)=>{let str=data.toString();output.push(str),process.stdout.write(str)}),child.stderr?.on("data",(data)=>{let str=data.toString();output.push(str),process.stderr.write(str)}),child.on("close",(code)=>{resolve({success:code===0,output:output.join("")})}),child.on("error",(err)=>{error(err.message),resolve({success:!1,output:err.message})})})}async function getGitInfo(cwd){try{let branchResult=await runCommandSilent("git",["rev-parse","--abbrev-ref","HEAD"],cwd),commitResult=await runCommandSilent("git",["rev-parse","--short","HEAD"],cwd),dateResult=await runCommandSilent("git",["log","-1","--format=%ci"],cwd);if(branchResult.success&&commitResult.success&&dateResult.success)return{branch:branchResult.output.trim(),commit:commitResult.output.trim(),commitDate:dateResult.output.trim().split(" ")[0]}}catch{}return null}async function runCommandSilent(command,args,cwd,timeoutMs=4000){return new Promise((resolve)=>{let output=[],settled=!1,child=spawn2(command,args,{cwd,stdio:["inherit","pipe","pipe"]}),timer=setTimeout(()=>{if(settled)return;settled=!0,child.kill("SIGTERM"),resolve({success:!1,output:`Timed out after ${timeoutMs}ms`})},timeoutMs);child.stdout?.on("data",(data)=>{output.push(data.toString())}),child.stderr?.on("data",(data)=>{output.push(data.toString())}),child.on("close",(code)=>{if(settled)return;settled=!0,clearTimeout(timer),resolve({success:code===0,output:output.join("")})}),child.on("error",(err)=>{if(settled)return;settled=!0,clearTimeout(timer),resolve({success:!1,output:err.message})})})}function detectFromBinaryPath(path2){if(path2.includes(".bun"))return"bun";if(path2.includes("node_modules"))return"npm";if(path2===join10(LOCAL_BIN2,"genie")||path2.startsWith(GENIE_BIN))return"source";return null}async function detectInstallationType(){if(genieConfigExists())try{let config=await loadGenieConfig();if(config.installMethod)return config.installMethod}catch{}if(existsSync9(join10(GENIE_SRC,".git")))return"source";let result2=await runCommandSilent("which",["genie"]);if(!result2.success)return"unknown";let detected=detectFromBinaryPath(result2.output.trim());if(detected)return detected;return(await runCommandSilent("which",["bun"])).success?"bun":"npm"}async function updateViaBun(channel){try{__require("fs").unlinkSync(join10(homedir10(),".bun","install","global","bun.lock"))}catch{}if(log(`Updating via bun (channel: ${channel})...`),!(await runCommand("bun",["add","-g","--force","--no-cache",`@automagik/genie@${channel}`])).success)return error("Failed to update via bun"),!1;return console.log(),success(`Genie CLI updated via bun (${channel})!`),!0}async function updateViaNpm(channel){if(log(`Updating via npm (channel: ${channel})...`),!(await runCommand("npm",["install","-g",`@automagik/genie@${channel}`])).success)return error("Failed to update via npm"),!1;return console.log(),success(`Genie CLI updated via npm (${channel})!`),!0}async function detectGlobalInstalls(){let found=new Set,[npmResult,bunResult]=await Promise.all([runCommandSilent("npm",["list","-g","@automagik/genie"]),runCommandSilent("bun",["pm","ls","-g"])]);if(npmResult.success&&!npmResult.output.includes("(empty)"))found.add("npm");if(bunResult.success&&bunResult.output.includes("@automagik/genie"))found.add("bun");return found}async function updateSource(){let beforeInfo=await getGitInfo(GENIE_SRC);if(beforeInfo)console.log(`Current: \x1B[2m${beforeInfo.branch}@${beforeInfo.commit} (${beforeInfo.commitDate})\x1B[0m`),console.log();if(log("Fetching latest changes..."),!(await runCommand("git",["fetch","origin"],GENIE_SRC)).success)error("Failed to fetch from origin"),process.exit(1);if(log("Resetting to origin/main..."),!(await runCommand("git",["reset","--hard","origin/main"],GENIE_SRC)).success)error("Failed to reset to origin/main"),process.exit(1);console.log();let afterInfo=await getGitInfo(GENIE_SRC);if(beforeInfo&&afterInfo&&beforeInfo.commit===afterInfo.commit){success("Already up to date!"),console.log();return}if(log("Installing dependencies..."),!(await runCommand("bun",["install"],GENIE_SRC)).success)error("Failed to install dependencies"),process.exit(1);if(console.log(),log("Building..."),!(await runCommand("bun",["run","build"],GENIE_SRC)).success)error("Failed to build"),process.exit(1);console.log(),log("Installing binaries...");try{await mkdir(GENIE_BIN,{recursive:!0}),await mkdir(LOCAL_BIN2,{recursive:!0});let binaries=["genie.js","term.js"],names=["genie","term"];for(let i=0;i<binaries.length;i++){let src=join10(GENIE_SRC,"dist",binaries[i]),binDest=join10(GENIE_BIN,binaries[i]),linkDest=join10(LOCAL_BIN2,names[i]);await copyFile(src,binDest),await chmod(binDest,493),await symlinkOrCopy(binDest,linkDest)}for(let legacy of["claudio.js","claudio"]){let legacyBin=join10(GENIE_BIN,legacy),legacyLink=join10(LOCAL_BIN2,legacy);try{await unlink(legacyBin)}catch{}try{await unlink(legacyLink)}catch{}}success("Binaries installed")}catch(err){error(`Failed to install binaries: ${err}`),process.exit(1)}if(console.log(),console.log("\x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m"),success("Genie CLI updated successfully!"),console.log(),afterInfo)console.log(`Version: \x1B[36m${afterInfo.branch}@${afterInfo.commit}\x1B[0m (${afterInfo.commitDate})`),console.log()}async function symlinkOrCopy(src,dest){let{symlink,unlink:unlink2}=await import("fs/promises");try{if(existsSync9(dest))await unlink2(dest);await symlink(src,dest)}catch{await copyFile(src,dest)}}function copyDirSync(src,dest){mkdirSync6(dest,{recursive:!0});for(let entry of readdirSync(src,{withFileTypes:!0})){let srcPath=join10(src,entry.name),destPath=join10(dest,entry.name);if(entry.isDirectory())copyDirSync(srcPath,destPath);else copyFileSync2(srcPath,destPath)}}async function resolveGlobalPkgDir(installType){if(installType==="bun"){let bunPath=join10(homedir10(),".bun","install","global","node_modules","@automagik","genie");if(existsSync9(bunPath))return bunPath}if(installType==="npm"){let npmRootResult=await runCommandSilent("npm",["root","-g"]);if(npmRootResult.success){let npmPath=join10(npmRootResult.output.trim(),"@automagik","genie");if(existsSync9(npmPath))return npmPath}}let bunFallback=join10(homedir10(),".bun","install","global","node_modules","@automagik","genie");if(existsSync9(bunFallback))return bunFallback;let npmRootFallback=await runCommandSilent("npm",["root","-g"]);if(npmRootFallback.success){let npmPath=join10(npmRootFallback.output.trim(),"@automagik","genie");if(existsSync9(npmPath))return npmPath}return null}function updatePluginRegistry(claudePlugins,cacheDir,version){let registryPath=join10(claudePlugins,"installed_plugins.json");try{if(!existsSync9(registryPath))return;let registry=JSON.parse(readFileSync6(registryPath,"utf-8")),entries=registry.plugins?.["genie@automagik"];if(!Array.isArray(entries))return;for(let entry of entries)if(entry.scope==="user")entry.installPath=cacheDir,entry.version=version,entry.lastUpdated=new Date().toISOString();writeFileSync7(registryPath,JSON.stringify(registry,null,2))}catch(err){log(`Registry update failed (non-fatal): ${err}`)}}function syncTmuxConf(tmuxScriptsSrc){mkdirSync6(GENIE_HOME,{recursive:!0});let tmuxConfSrc=join10(tmuxScriptsSrc,"genie.tmux.conf"),tmuxConfDest=join10(GENIE_HOME,"tmux.conf");if(existsSync9(tmuxConfSrc))try{copyFileSync2(tmuxConfSrc,tmuxConfDest),success(`Installed tmux config to ${tmuxConfDest}`);try{let{tmuxBin:tmuxBin2}=(init_ensure_tmux(),__toCommonJS(exports_ensure_tmux));execSync2(`${tmuxBin2()} -L genie source-file '${tmuxConfDest}'`,{stdio:"ignore"}),success("Reloaded genie tmux server configuration")}catch{}}catch{}let tuiConfSrc=join10(tmuxScriptsSrc,"tui-tmux.conf"),tuiConfDest=join10(GENIE_HOME,"tui-tmux.conf");if(existsSync9(tuiConfSrc))try{copyFileSync2(tuiConfSrc,tuiConfDest),success(`Installed TUI tmux config to ${tuiConfDest}`)}catch{}let osc52Src=join10(tmuxScriptsSrc,"osc52-copy.sh"),osc52Dest=join10(GENIE_HOME,"osc52-copy.sh");if(existsSync9(osc52Src))try{copyFileSync2(osc52Src,osc52Dest),chmodSync2(osc52Dest,493),success(`Installed OSC 52 clipboard helper to ${osc52Dest}`)}catch{}}function syncTmuxScripts(globalPkgDir){let tmuxScriptsSrc=join10(globalPkgDir,"scripts","tmux");if(!existsSync9(tmuxScriptsSrc))return;let scriptsDir=join10(GENIE_HOME,"scripts");mkdirSync6(scriptsDir,{recursive:!0});let scriptCount=0;for(let entry of readdirSync(tmuxScriptsSrc))if(entry.endsWith(".sh")||entry==="genie.tmux.conf"||entry==="tui-tmux.conf"){let src=join10(tmuxScriptsSrc,entry),dest=join10(scriptsDir,entry);copyFileSync2(src,dest);try{chmodSync2(dest,entry.endsWith(".sh")?493:420)}catch{}scriptCount++}if(scriptCount>0)success(`Refreshed ${scriptCount} tmux scripts at ${scriptsDir}`);syncTmuxConf(tmuxScriptsSrc)}function syncMarketplaceVersion(claudePlugins,version){let marketplacePath=join10(claudePlugins,"marketplaces","automagik",".claude-plugin","marketplace.json");try{if(!existsSync9(marketplacePath))return;let data=JSON.parse(readFileSync6(marketplacePath,"utf-8"));if(Array.isArray(data.plugins)){for(let plugin of data.plugins)if(plugin.name==="genie")plugin.version=version}writeFileSync7(marketplacePath,JSON.stringify(data,null,2)),success(`Updated marketplace.json to v${version}`)}catch(err){log(`Marketplace version update failed (non-fatal): ${err}`)}}function syncPluginPackageVersion(claudePlugins,version){let pkgPath=join10(claudePlugins,"marketplaces","automagik","plugins","genie","package.json");try{if(!existsSync9(pkgPath))return;let data=JSON.parse(readFileSync6(pkgPath,"utf-8"));data.version=version,writeFileSync7(pkgPath,JSON.stringify(data,null,2)),success(`Updated plugin package.json to v${version}`)}catch(err){log(`Plugin package.json update failed (non-fatal): ${err}`)}}function syncSkillsSymlink(claudePlugins,version){let skillsLink=join10(claudePlugins,"marketplaces","automagik","plugins","genie","skills"),cacheSkills=join10("..","..","..","..","cache","automagik","genie",version,"skills");try{let{symlinkSync,unlinkSync:unlinkSync6,lstatSync:lstatSync2}=__require("fs");try{lstatSync2(skillsLink),unlinkSync6(skillsLink)}catch{}symlinkSync(cacheSkills,skillsLink),success(`Skills symlink \u2192 cache/${version}/skills`)}catch(err){log(`Skills symlink update failed (non-fatal): ${err}`)}}async function syncPlugin(installType){log("Syncing Claude Code plugin...");let globalPkgDir=await resolveGlobalPkgDir(installType);if(!globalPkgDir){log("Could not find installed package \u2014 skipping plugin sync");return}let pluginSrc=join10(globalPkgDir,"plugins","genie");if(!existsSync9(pluginSrc)){log("Plugin source not found in package \u2014 skipping plugin sync");return}let version;try{version=JSON.parse(readFileSync6(join10(globalPkgDir,"package.json"),"utf-8")).version}catch{log("Could not read package version \u2014 skipping plugin sync");return}let claudePlugins=join10(homedir10(),".claude","plugins"),cacheDir=join10(claudePlugins,"cache","automagik","genie",version);try{if(existsSync9(cacheDir))rmSync3(cacheDir,{recursive:!0,force:!0});copyDirSync(pluginSrc,cacheDir);let skillsSrc=join10(globalPkgDir,"skills");if(existsSync9(skillsSrc)&&!existsSync9(join10(cacheDir,"skills")))copyDirSync(skillsSrc,join10(cacheDir,"skills"))}catch(err){error(`Failed to copy plugin: ${err}`);return}updatePluginRegistry(claudePlugins,cacheDir,version),syncMarketplaceVersion(claudePlugins,version),syncPluginPackageVersion(claudePlugins,version),syncSkillsSymlink(claudePlugins,version),syncTmuxScripts(globalPkgDir),success(`Plugin synced to v${version}`)}async function resolveChannel(options){if(options.next)return"next";if(options.stable)return"latest";if(genieConfigExists())try{let config=await loadGenieConfig();if(config.updateChannel)return config.updateChannel}catch{}return"latest"}async function persistChannel(channel){try{let config=await loadGenieConfig();config.updateChannel=channel,await saveGenieConfig(config)}catch{}}async function updateCommand(options={}){console.log(),console.log("\x1B[1m\uD83E\uDDDE Genie CLI Update\x1B[0m"),console.log("\x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m"),console.log();let channel=await resolveChannel(options);if(options.next||options.stable)await persistChannel(channel);let installType=await detectInstallationType();if(log(`Detected installation: ${installType}`),log(`Channel: ${channel}${channel==="next"?" (dev builds)":" (stable)"}`),console.log(),installType==="unknown")error("No Genie CLI installation found"),console.log(),console.log("Install method not configured. Please reinstall genie:"),console.log("\x1B[36m curl -fsSL https://raw.githubusercontent.com/automagik-dev/genie/main/install.sh | bash\x1B[0m"),console.log(),process.exit(1);if(installType==="source"){await updateSource();return}let globalInstalls=await detectGlobalInstalls(),primaryMethod=installType;if(!(primaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))process.exit(1);let secondaryMethod=primaryMethod==="bun"?"npm":"bun";if(globalInstalls.has(secondaryMethod)){if(console.log(),log(`Also updating ${secondaryMethod}-global install...`),!(secondaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))error(`Secondary update via ${secondaryMethod} failed (non-blocking)`)}await syncPlugin(installType)}init_version();import{execSync as execSync3}from"child_process";var MAX_COMMITS=5;function getRecentGitHistory(filePath,cwd){try{let trimmed=execSync3(`git log --oneline -n ${MAX_COMMITS} -- ${JSON.stringify(filePath)}`,{encoding:"utf-8",timeout:5000,cwd,stdio:["pipe","pipe","pipe"]}).trim();if(!trimmed)return null;return trimmed}catch{return null}}async function auditContext(payload){let input=payload.tool_input;if(!input)return;let filePath=input.file_path;if(!filePath)return;let cwd=payload.cwd??process.cwd(),history=getRecentGitHistory(filePath,cwd);if(!history)return;return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[audit-context] Recent git history for ${filePath}:
2956
+ `)}init_setup();init_shortcuts();import{existsSync as existsSync7}from"fs";import{homedir as homedir8}from"os";import{join as join8}from"path";async function shortcutsShowCommand(){displayShortcuts();let home=homedir8(),tmuxConf=join8(home,".tmux.conf"),zshrc=join8(home,".zshrc"),bashrc=join8(home,".bashrc");if(console.log("Installation status:"),isShortcutsInstalled(tmuxConf))console.log(" \x1B[32m\u2713\x1B[0m tmux.conf");else console.log(" \x1B[33m-\x1B[0m tmux.conf");let shellRc=existsSync7(zshrc)?zshrc:bashrc;if(isShortcutsInstalled(shellRc))console.log(` \x1B[32m\u2713\x1B[0m ${shellRc.replace(home,"~")}`);else console.log(` \x1B[33m-\x1B[0m ${shellRc.replace(home,"~")}`);console.log(),console.log("Run \x1B[36mgenie shortcuts install\x1B[0m to install shortcuts."),console.log("Run \x1B[36mgenie shortcuts uninstall\x1B[0m to remove shortcuts."),console.log()}async function shortcutsInstallCommand(){await installShortcuts()}async function shortcutsUninstallCommand(){await uninstallShortcuts()}init_esm14();init_claude_settings();init_genie_config2();import{existsSync as existsSync8,lstatSync,rmSync as rmSync2,unlinkSync as unlinkSync5}from"fs";import{homedir as homedir9}from"os";import{join as join9}from"path";var ORCHESTRATION_RULES_PATH=join9(homedir9(),".claude","rules","genie-orchestration.md"),LOCAL_BIN=join9(homedir9(),".local","bin"),SYMLINKS=["genie","term"];function isGenieSymlink(path2){try{if(!existsSync8(path2))return!1;if(!lstatSync(path2).isSymbolicLink())return!1;return!0}catch{return!1}}function removeSymlinks(){let removed=[];for(let name of SYMLINKS){let symlinkPath=join9(LOCAL_BIN,name);if(isGenieSymlink(symlinkPath))try{unlinkSync5(symlinkPath),removed.push(name)}catch{}}return removed}function tryRemoveStep(label,successMsg,fn){console.log(`\x1B[2m${label}\x1B[0m`);try{fn(),console.log(` \x1B[32m+\x1B[0m ${successMsg}`)}catch(error){let message=error instanceof Error?error.message:String(error);console.log(` \x1B[33m!\x1B[0m ${label.replace("...","")} failed: ${message}`)}}function performUninstall(hasHookScript,existingSymlinks,genieDir,hasGenieDir){if(hasHookScript)tryRemoveStep("Removing hook script...","Hook script removed",()=>removeHookScript());if(existingSymlinks.length>0){console.log("\x1B[2mRemoving symlinks...\x1B[0m");let removed=removeSymlinks();if(removed.length>0)console.log(` \x1B[32m+\x1B[0m Removed: ${removed.join(", ")}`)}if(existsSync8(ORCHESTRATION_RULES_PATH))tryRemoveStep("Removing orchestration rules...","Orchestration rules removed (~/.claude/rules/genie-orchestration.md)",()=>unlinkSync5(ORCHESTRATION_RULES_PATH));if(hasGenieDir)tryRemoveStep("Removing genie directory...","Directory removed",()=>rmSync2(genieDir,{recursive:!0,force:!0}))}async function uninstallCommand(){console.log(),console.log("\x1B[1m\x1B[33m Uninstall Genie CLI\x1B[0m"),console.log();let genieDir=getGenieDir(),hasGenieDir=existsSync8(genieDir),hasHookScript=hookScriptExists(),hasOrchestrationRules=existsSync8(ORCHESTRATION_RULES_PATH),existingSymlinks=SYMLINKS.filter((name)=>isGenieSymlink(join9(LOCAL_BIN,name)));if(console.log("\x1B[2mThis will remove:\x1B[0m"),hasHookScript)console.log(" \x1B[31m-\x1B[0m Hook script (~/.claude/hooks/genie-bash-hook.sh)");if(hasOrchestrationRules)console.log(" \x1B[31m-\x1B[0m Orchestration rules (~/.claude/rules/genie-orchestration.md)");if(hasGenieDir)console.log(` \x1B[31m-\x1B[0m Genie directory (${contractPath(genieDir)})`);if(existingSymlinks.length>0)console.log(` \x1B[31m-\x1B[0m Symlinks from ~/.local/bin: ${existingSymlinks.join(", ")}`);if(console.log(),!hasGenieDir&&!hasHookScript&&!hasOrchestrationRules&&existingSymlinks.length===0){console.log("\x1B[33mNothing to uninstall.\x1B[0m"),console.log();return}if(!await esm_default4({message:"Are you sure you want to uninstall Genie CLI?",default:!1})){console.log(),console.log("\x1B[2mUninstall cancelled.\x1B[0m"),console.log();return}console.log(),performUninstall(hasHookScript,existingSymlinks,genieDir,hasGenieDir),console.log(),console.log("\x1B[32m+\x1B[0m Genie CLI uninstalled."),console.log(),console.log("\x1B[2mNote: If you installed via npm/bun, also run:\x1B[0m"),console.log(" \x1B[36mbun remove -g @automagik/genie\x1B[0m"),console.log(" \x1B[2mor\x1B[0m"),console.log(" \x1B[36mnpm uninstall -g @automagik/genie\x1B[0m"),console.log()}init_genie_config2();import{execSync as execSync2,spawn as spawn2}from"child_process";import{chmodSync as chmodSync2,copyFileSync as copyFileSync2,existsSync as existsSync9,mkdirSync as mkdirSync6,readFileSync as readFileSync6,readdirSync,rmSync as rmSync3,writeFileSync as writeFileSync7}from"fs";import{chmod,copyFile,mkdir,unlink}from"fs/promises";import{homedir as homedir10}from"os";import{join as join10}from"path";var GENIE_HOME=process.env.GENIE_HOME||join10(homedir10(),".genie"),GENIE_SRC=join10(GENIE_HOME,"src"),GENIE_BIN=join10(GENIE_HOME,"bin"),LOCAL_BIN2=join10(homedir10(),".local","bin");function log(message){console.log(`\x1B[32m\u25B8\x1B[0m ${message}`)}function success(message){console.log(`\x1B[32m\u2714\x1B[0m ${message}`)}function error(message){console.log(`\x1B[31m\u2716\x1B[0m ${message}`)}async function runCommand(command,args,cwd){return new Promise((resolve)=>{let output=[],child=spawn2(command,args,{cwd,stdio:["inherit","pipe","pipe"],env:{...process.env,FORCE_COLOR:"1"}});child.stdout?.on("data",(data)=>{let str=data.toString();output.push(str),process.stdout.write(str)}),child.stderr?.on("data",(data)=>{let str=data.toString();output.push(str),process.stderr.write(str)}),child.on("close",(code)=>{resolve({success:code===0,output:output.join("")})}),child.on("error",(err)=>{error(err.message),resolve({success:!1,output:err.message})})})}async function getGitInfo(cwd){try{let branchResult=await runCommandSilent("git",["rev-parse","--abbrev-ref","HEAD"],cwd),commitResult=await runCommandSilent("git",["rev-parse","--short","HEAD"],cwd),dateResult=await runCommandSilent("git",["log","-1","--format=%ci"],cwd);if(branchResult.success&&commitResult.success&&dateResult.success)return{branch:branchResult.output.trim(),commit:commitResult.output.trim(),commitDate:dateResult.output.trim().split(" ")[0]}}catch{}return null}async function runCommandSilent(command,args,cwd,timeoutMs=4000){return new Promise((resolve)=>{let output=[],settled=!1,child=spawn2(command,args,{cwd,stdio:["inherit","pipe","pipe"]}),timer=setTimeout(()=>{if(settled)return;settled=!0,child.kill("SIGTERM"),resolve({success:!1,output:`Timed out after ${timeoutMs}ms`})},timeoutMs);child.stdout?.on("data",(data)=>{output.push(data.toString())}),child.stderr?.on("data",(data)=>{output.push(data.toString())}),child.on("close",(code)=>{if(settled)return;settled=!0,clearTimeout(timer),resolve({success:code===0,output:output.join("")})}),child.on("error",(err)=>{if(settled)return;settled=!0,clearTimeout(timer),resolve({success:!1,output:err.message})})})}function detectFromBinaryPath(path2){if(path2.includes(".bun"))return"bun";if(path2.includes("node_modules"))return"npm";if(path2===join10(LOCAL_BIN2,"genie")||path2.startsWith(GENIE_BIN))return"source";return null}async function detectInstallationType(){if(genieConfigExists())try{let config=await loadGenieConfig();if(config.installMethod)return config.installMethod}catch{}if(existsSync9(join10(GENIE_SRC,".git")))return"source";let result2=await runCommandSilent("which",["genie"]);if(!result2.success)return"unknown";let detected=detectFromBinaryPath(result2.output.trim());if(detected)return detected;return(await runCommandSilent("which",["bun"])).success?"bun":"npm"}async function updateViaBun(channel){try{__require("fs").unlinkSync(join10(homedir10(),".bun","install","global","bun.lock"))}catch{}if(log(`Updating via bun (channel: ${channel})...`),!(await runCommand("bun",["add","-g","--force","--no-cache",`@automagik/genie@${channel}`])).success)return error("Failed to update via bun"),!1;return console.log(),success(`Genie CLI updated via bun (${channel})!`),!0}async function updateViaNpm(channel){if(log(`Updating via npm (channel: ${channel})...`),!(await runCommand("npm",["install","-g",`@automagik/genie@${channel}`])).success)return error("Failed to update via npm"),!1;return console.log(),success(`Genie CLI updated via npm (${channel})!`),!0}async function detectGlobalInstalls(){let found=new Set,[npmResult,bunResult]=await Promise.all([runCommandSilent("npm",["list","-g","@automagik/genie"]),runCommandSilent("bun",["pm","ls","-g"])]);if(npmResult.success&&!npmResult.output.includes("(empty)"))found.add("npm");if(bunResult.success&&bunResult.output.includes("@automagik/genie"))found.add("bun");return found}async function updateSource(){let beforeInfo=await getGitInfo(GENIE_SRC);if(beforeInfo)console.log(`Current: \x1B[2m${beforeInfo.branch}@${beforeInfo.commit} (${beforeInfo.commitDate})\x1B[0m`),console.log();if(log("Fetching latest changes..."),!(await runCommand("git",["fetch","origin"],GENIE_SRC)).success)error("Failed to fetch from origin"),process.exit(1);if(log("Resetting to origin/main..."),!(await runCommand("git",["reset","--hard","origin/main"],GENIE_SRC)).success)error("Failed to reset to origin/main"),process.exit(1);console.log();let afterInfo=await getGitInfo(GENIE_SRC);if(beforeInfo&&afterInfo&&beforeInfo.commit===afterInfo.commit){success("Already up to date!"),console.log();return}if(log("Installing dependencies..."),!(await runCommand("bun",["install"],GENIE_SRC)).success)error("Failed to install dependencies"),process.exit(1);if(console.log(),log("Building..."),!(await runCommand("bun",["run","build"],GENIE_SRC)).success)error("Failed to build"),process.exit(1);console.log(),log("Installing binaries...");try{await mkdir(GENIE_BIN,{recursive:!0}),await mkdir(LOCAL_BIN2,{recursive:!0});let binaries=["genie.js","term.js"],names=["genie","term"];for(let i=0;i<binaries.length;i++){let src=join10(GENIE_SRC,"dist",binaries[i]),binDest=join10(GENIE_BIN,binaries[i]),linkDest=join10(LOCAL_BIN2,names[i]);await copyFile(src,binDest),await chmod(binDest,493),await symlinkOrCopy(binDest,linkDest)}for(let legacy of["claudio.js","claudio"]){let legacyBin=join10(GENIE_BIN,legacy),legacyLink=join10(LOCAL_BIN2,legacy);try{await unlink(legacyBin)}catch{}try{await unlink(legacyLink)}catch{}}success("Binaries installed")}catch(err){error(`Failed to install binaries: ${err}`),process.exit(1)}if(console.log(),console.log("\x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m"),success("Genie CLI updated successfully!"),console.log(),afterInfo)console.log(`Version: \x1B[36m${afterInfo.branch}@${afterInfo.commit}\x1B[0m (${afterInfo.commitDate})`),console.log()}async function symlinkOrCopy(src,dest){let{symlink,unlink:unlink2}=await import("fs/promises");try{if(existsSync9(dest))await unlink2(dest);await symlink(src,dest)}catch{await copyFile(src,dest)}}function copyDirSync(src,dest){mkdirSync6(dest,{recursive:!0});for(let entry of readdirSync(src,{withFileTypes:!0})){let srcPath=join10(src,entry.name),destPath=join10(dest,entry.name);if(entry.isDirectory())copyDirSync(srcPath,destPath);else copyFileSync2(srcPath,destPath)}}async function resolveGlobalPkgDir(installType){if(installType==="bun"){let bunPath=join10(homedir10(),".bun","install","global","node_modules","@automagik","genie");if(existsSync9(bunPath))return bunPath}if(installType==="npm"){let npmRootResult=await runCommandSilent("npm",["root","-g"]);if(npmRootResult.success){let npmPath=join10(npmRootResult.output.trim(),"@automagik","genie");if(existsSync9(npmPath))return npmPath}}let bunFallback=join10(homedir10(),".bun","install","global","node_modules","@automagik","genie");if(existsSync9(bunFallback))return bunFallback;let npmRootFallback=await runCommandSilent("npm",["root","-g"]);if(npmRootFallback.success){let npmPath=join10(npmRootFallback.output.trim(),"@automagik","genie");if(existsSync9(npmPath))return npmPath}return null}function updatePluginRegistry(claudePlugins,cacheDir,version){let registryPath=join10(claudePlugins,"installed_plugins.json");try{if(!existsSync9(registryPath))return;let registry=JSON.parse(readFileSync6(registryPath,"utf-8")),entries=registry.plugins?.["genie@automagik"];if(!Array.isArray(entries))return;for(let entry of entries)if(entry.scope==="user")entry.installPath=cacheDir,entry.version=version,entry.lastUpdated=new Date().toISOString();writeFileSync7(registryPath,JSON.stringify(registry,null,2))}catch(err){log(`Registry update failed (non-fatal): ${err}`)}}function syncTmuxConf(tmuxScriptsSrc){mkdirSync6(GENIE_HOME,{recursive:!0});let tmuxConfSrc=join10(tmuxScriptsSrc,"genie.tmux.conf"),tmuxConfDest=join10(GENIE_HOME,"tmux.conf");if(existsSync9(tmuxConfSrc))try{copyFileSync2(tmuxConfSrc,tmuxConfDest),success(`Installed tmux config to ${tmuxConfDest}`);try{let{tmuxBin:tmuxBin2}=(init_ensure_tmux(),__toCommonJS(exports_ensure_tmux));execSync2(`${tmuxBin2()} -L genie source-file '${tmuxConfDest}'`,{stdio:"ignore"}),success("Reloaded genie tmux server configuration")}catch{}}catch{}let tuiConfSrc=join10(tmuxScriptsSrc,"tui-tmux.conf"),tuiConfDest=join10(GENIE_HOME,"tui-tmux.conf");if(existsSync9(tuiConfSrc))try{copyFileSync2(tuiConfSrc,tuiConfDest),success(`Installed TUI tmux config to ${tuiConfDest}`)}catch{}let osc52Src=join10(tmuxScriptsSrc,"osc52-copy.sh"),osc52Dest=join10(GENIE_HOME,"scripts","osc52-copy.sh");if(existsSync9(osc52Src))try{copyFileSync2(osc52Src,osc52Dest),chmodSync2(osc52Dest,493),success(`Installed OSC 52 clipboard helper to ${osc52Dest}`)}catch{}}function syncTmuxScripts(globalPkgDir){let tmuxScriptsSrc=join10(globalPkgDir,"scripts","tmux");if(!existsSync9(tmuxScriptsSrc))return;let scriptsDir=join10(GENIE_HOME,"scripts");mkdirSync6(scriptsDir,{recursive:!0});let scriptCount=0;for(let entry of readdirSync(tmuxScriptsSrc))if(entry.endsWith(".sh")||entry==="genie.tmux.conf"||entry==="tui-tmux.conf"){let src=join10(tmuxScriptsSrc,entry),dest=join10(scriptsDir,entry);copyFileSync2(src,dest);try{chmodSync2(dest,entry.endsWith(".sh")?493:420)}catch{}scriptCount++}if(scriptCount>0)success(`Refreshed ${scriptCount} tmux scripts at ${scriptsDir}`);syncTmuxConf(tmuxScriptsSrc)}function syncMarketplaceVersion(claudePlugins,version){let marketplacePath=join10(claudePlugins,"marketplaces","automagik",".claude-plugin","marketplace.json");try{if(!existsSync9(marketplacePath))return;let data=JSON.parse(readFileSync6(marketplacePath,"utf-8"));if(Array.isArray(data.plugins)){for(let plugin of data.plugins)if(plugin.name==="genie")plugin.version=version}writeFileSync7(marketplacePath,JSON.stringify(data,null,2)),success(`Updated marketplace.json to v${version}`)}catch(err){log(`Marketplace version update failed (non-fatal): ${err}`)}}function syncPluginPackageVersion(claudePlugins,version){let pkgPath=join10(claudePlugins,"marketplaces","automagik","plugins","genie","package.json");try{if(!existsSync9(pkgPath))return;let data=JSON.parse(readFileSync6(pkgPath,"utf-8"));data.version=version,writeFileSync7(pkgPath,JSON.stringify(data,null,2)),success(`Updated plugin package.json to v${version}`)}catch(err){log(`Plugin package.json update failed (non-fatal): ${err}`)}}function syncSkillsSymlink(claudePlugins,version){let skillsLink=join10(claudePlugins,"marketplaces","automagik","plugins","genie","skills"),cacheSkills=join10("..","..","..","..","cache","automagik","genie",version,"skills");try{let{symlinkSync,unlinkSync:unlinkSync6,lstatSync:lstatSync2}=__require("fs");try{lstatSync2(skillsLink),unlinkSync6(skillsLink)}catch{}symlinkSync(cacheSkills,skillsLink),success(`Skills symlink \u2192 cache/${version}/skills`)}catch(err){log(`Skills symlink update failed (non-fatal): ${err}`)}}async function syncPlugin(installType){log("Syncing Claude Code plugin...");let globalPkgDir=await resolveGlobalPkgDir(installType);if(!globalPkgDir){log("Could not find installed package \u2014 skipping plugin sync");return}let pluginSrc=join10(globalPkgDir,"plugins","genie");if(!existsSync9(pluginSrc)){log("Plugin source not found in package \u2014 skipping plugin sync");return}let version;try{version=JSON.parse(readFileSync6(join10(globalPkgDir,"package.json"),"utf-8")).version}catch{log("Could not read package version \u2014 skipping plugin sync");return}let claudePlugins=join10(homedir10(),".claude","plugins"),cacheDir=join10(claudePlugins,"cache","automagik","genie",version);try{if(existsSync9(cacheDir))rmSync3(cacheDir,{recursive:!0,force:!0});copyDirSync(pluginSrc,cacheDir);let skillsSrc=join10(globalPkgDir,"skills");if(existsSync9(skillsSrc)&&!existsSync9(join10(cacheDir,"skills")))copyDirSync(skillsSrc,join10(cacheDir,"skills"))}catch(err){error(`Failed to copy plugin: ${err}`);return}updatePluginRegistry(claudePlugins,cacheDir,version),syncMarketplaceVersion(claudePlugins,version),syncPluginPackageVersion(claudePlugins,version),syncSkillsSymlink(claudePlugins,version),syncTmuxScripts(globalPkgDir),success(`Plugin synced to v${version}`)}async function resolveChannel(options){if(options.next)return"next";if(options.stable)return"latest";if(genieConfigExists())try{let config=await loadGenieConfig();if(config.updateChannel)return config.updateChannel}catch{}return"latest"}async function persistChannel(channel){try{let config=await loadGenieConfig();config.updateChannel=channel,await saveGenieConfig(config)}catch{}}async function updateCommand(options={}){console.log(),console.log("\x1B[1m\uD83E\uDDDE Genie CLI Update\x1B[0m"),console.log("\x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m"),console.log();let channel=await resolveChannel(options);if(options.next||options.stable)await persistChannel(channel);let installType=await detectInstallationType();if(log(`Detected installation: ${installType}`),log(`Channel: ${channel}${channel==="next"?" (dev builds)":" (stable)"}`),console.log(),installType==="unknown")error("No Genie CLI installation found"),console.log(),console.log("Install method not configured. Please reinstall genie:"),console.log("\x1B[36m curl -fsSL https://raw.githubusercontent.com/automagik-dev/genie/main/install.sh | bash\x1B[0m"),console.log(),process.exit(1);if(installType==="source"){await updateSource();return}let globalInstalls=await detectGlobalInstalls(),primaryMethod=installType;if(!(primaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))process.exit(1);let secondaryMethod=primaryMethod==="bun"?"npm":"bun";if(globalInstalls.has(secondaryMethod)){if(console.log(),log(`Also updating ${secondaryMethod}-global install...`),!(secondaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))error(`Secondary update via ${secondaryMethod} failed (non-blocking)`)}await syncPlugin(installType)}init_version();import{execSync as execSync3}from"child_process";var MAX_COMMITS=5;function getRecentGitHistory(filePath,cwd){try{let trimmed=execSync3(`git log --oneline -n ${MAX_COMMITS} -- ${JSON.stringify(filePath)}`,{encoding:"utf-8",timeout:5000,cwd,stdio:["pipe","pipe","pipe"]}).trim();if(!trimmed)return null;return trimmed}catch{return null}}async function auditContext(payload){let input=payload.tool_input;if(!input)return;let filePath=input.file_path;if(!filePath)return;let cwd=payload.cwd??process.cwd(),history=getRecentGitHistory(filePath,cwd);if(!history)return;return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[audit-context] Recent git history for ${filePath}:
2957
2957
  ${history}`}}}function buildSearchNames(recipient,dirEntry){let names=new Set([recipient]);if(dirEntry){if(names.add(dirEntry.entry.name),dirEntry.entry.roles)for(let role of dirEntry.entry.roles)names.add(role)}return names}function buildSpawnArgs(template){let args=["spawn","--provider",template.provider,"--team",template.team];if(template.role)args.push("--role",template.role);if(template.skill)args.push("--skill",template.skill);if(template.cwd)args.push("--cwd",template.cwd);if(template.extraArgs)args.push(...template.extraArgs);return args}async function isRecipientLeader(recipient,teamName){try{let{getTeam:getTeam2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),config=await getTeam2(teamName);return!!config?.leader&&recipient===config.leader}catch{return!1}}async function autoSpawn(payload){let input=payload.tool_input;if(!input||input.type!=="message")return;let recipient=input.recipient;if(!recipient)return;let teamName=process.env.GENIE_TEAM??payload.team_name;if(!teamName)return;if(recipient==="team-lead")return;if(await isRecipientLeader(recipient,teamName))return;try{let registryMod=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),tmuxMod=await Promise.resolve().then(() => (init_tmux(),exports_tmux)),directoryMod=await Promise.resolve().then(() => (init_agent_directory(),exports_agent_directory)),existing=(await registryMod.list()).find((a)=>(a.role===recipient||a.id===recipient)&&a.team===teamName);if(existing&&await tmuxMod.isPaneAlive(existing.paneId))return;let dirEntry=await directoryMod.resolve(recipient),templates=await registryMod.listTemplates(),searchNames=buildSearchNames(recipient,dirEntry),template=templates.find((t)=>{if(t.team!==teamName)return!1;return[...searchNames].some((q)=>t.id===q||t.role===q)});if(!template){if(dirEntry)console.error(`[genie-hook] Agent "${recipient}" is registered in directory but has no spawn template in team "${teamName}".`);return}let{spawnSync:spawnSync2}=__require("child_process");spawnSync2("genie",buildSpawnArgs(template),{timeout:1e4,stdio:"ignore",env:{...process.env,GENIE_TEAM:teamName}}),console.error(`[genie-hook] Auto-spawned "${recipient}" in team "${teamName}"`)}catch(err){let msg=err instanceof Error?err.message:String(err);return console.error(`[genie-hook] Auto-spawn failed for "${recipient}": ${msg}`),{hookSpecificOutput:{hookEventName:"PreToolUse",additionalContext:`auto-spawn warning: failed to spawn "${recipient}": ${msg}`}}}}import{existsSync as existsSync19}from"fs";import{basename as basename3,join as join21}from"path";var BRAIN_PKG="@khal-os/brain",BRAIN_DIR="node_modules/@khal-os/brain",enrichedSessions=new Set;function sessionKey(payload){return payload.session_id??`${process.pid}`}function isBrainAvailable(){return existsSync19(join21(BRAIN_DIR,"package.json"))}async function queryBrain(cwd){try{let brain=await import(BRAIN_PKG);if(!brain.search)return null;let projectName=basename3(cwd),results=await brain.search({query:`context for ${projectName}`,limit:5,minScore:0.5});if(!results||results.length===0)return null;return results.map((r)=>{return`- ${(r.content??r.text??"").slice(0,200)}`}).join(`
2958
2958
  `)}catch{return null}}async function brainInject(payload){let key=sessionKey(payload);if(enrichedSessions.has(key))return;if(enrichedSessions.add(key),!isBrainAvailable())return;let cwd=payload.cwd??process.cwd();try{let context=await queryBrain(cwd);if(!context)return;return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[brain-inject] Prior context from knowledge base:
2959
2959
  ${context}`}}}catch{return}}var DENY_PATTERNS=[{test:(cmd)=>/git\s+push\b/i.test(cmd)&&/(?:^|\s)(main|master)(?:\s|$)/.test(cmd),reason:"BLOCKED: Push to main/master is FORBIDDEN. Push to a feature branch and create a PR targeting dev."},{test:(cmd)=>/git\s+push\b/.test(cmd)&&/:(main|master)\b/.test(cmd),reason:"BLOCKED: Push refspec targeting main/master is FORBIDDEN."},{test:(cmd)=>/gh\s+pr\s+create\b/.test(cmd)&&!/(--base|-B)\s+\S/.test(cmd),reason:"BLOCKED: gh pr create requires explicit --base flag. Use: gh pr create --base dev (or --base main for releases)"},{test:(cmd)=>/gh\s+pr\s+merge\b/.test(cmd),reason:"BLOCKED: Agents may NOT merge PRs. Only humans merge via GitHub UI."},{test:(cmd)=>/git\s+checkout\s+(main|master)\s*[;&|]+\s*git\s+(commit|merge|cherry-pick|rebase|push|add)\b/.test(cmd),reason:"BLOCKED: Committing or mutating on main/master is FORBIDDEN. Work on feature branches."}];async function branchGuard(payload){let input=payload.tool_input;if(!input)return;let command=input.command;if(!command)return;if(!/\b(git|gh)\b/.test(command))return;for(let pattern of DENY_PATTERNS)if(pattern.test(command))return{decision:"deny",reason:pattern.reason};return}import{execSync as execSync5}from"child_process";import{statSync}from"fs";var STALENESS_THRESHOLD_SECS=120;function getLastCommitInfo(filePath,cwd){try{let trimmed=execSync5(`git log -1 --format="%at|%an|%s" -- ${JSON.stringify(filePath)}`,{encoding:"utf-8",timeout:5000,cwd,stdio:["pipe","pipe","pipe"]}).trim();if(!trimmed)return null;let[timestampStr,author,...messageParts]=trimmed.split("|"),timestamp2=Number.parseInt(timestampStr,10);if(Number.isNaN(timestamp2))return null;let age=Math.floor(Date.now()/1000)-timestamp2;return{author:author??"unknown",age,message:messageParts.join("|")}}catch{return null}}function getFileModAge(filePath){try{let stat2=statSync(filePath);return Math.floor((Date.now()-stat2.mtimeMs)/1000)}catch{return null}}function buildCommitWarning(filePath,commitInfo){return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[freshness] Stale read warning: ${filePath} was modified ${commitInfo.age}s ago by "${commitInfo.author}" (${commitInfo.message}). Contents may have changed since you last read it.`}}}function checkUncommittedChanges(filePath,cwd,diskAge){try{if(execSync5(`git status --porcelain -- ${JSON.stringify(filePath)}`,{encoding:"utf-8",timeout:5000,cwd,stdio:["pipe","pipe","pipe"]}).trim())return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[freshness] Stale read warning: ${filePath} has uncommitted changes (modified ${diskAge}s ago). Another agent may be editing this file concurrently.`}}}catch{}return}async function freshness(payload){let input=payload.tool_input;if(!input)return;let filePath=input.file_path;if(!filePath)return;let cwd=payload.cwd??process.cwd(),currentAgent=process.env.GENIE_AGENT_NAME,diskAge=getFileModAge(filePath);if(diskAge===null||diskAge>=STALENESS_THRESHOLD_SECS)return;let commitInfo=getLastCommitInfo(filePath,cwd);if(commitInfo&&commitInfo.age<STALENESS_THRESHOLD_SECS){if(currentAgent&&commitInfo.author.includes(currentAgent))return;return buildCommitWarning(filePath,commitInfo)}if(currentAgent)return checkUncommittedChanges(filePath,cwd,diskAge);return}async function identityInject(payload){let input=payload.tool_input;if(!input)return;let msgType=input.type;if(msgType&&msgType!=="message"&&msgType!=="broadcast")return;let agentName=process.env.GENIE_AGENT_NAME;if(!agentName)return;let contentField=input.content!==void 0?"content":"message",content=input[contentField];if(!content)return;if(content.startsWith(`[from:${agentName}]`))return;return{updatedInput:{...input,[contentField]:`[from:${agentName}] ${content}`}}}var NUDGE_PATTERNS=[{test:/tmux\s+capture-pane/,message:`If you're checking genie agent progress, use structured monitoring instead:
package/install.sh CHANGED
@@ -724,7 +724,7 @@ install_tmux_if_needed() {
724
724
  }
725
725
 
726
726
  configure_tmux_defaults() {
727
- local tmux_conf="$HOME/.tmux.conf"
727
+ local tmux_conf="$GENIE_HOME/tmux.conf"
728
728
  local scripts_dir="$GENIE_HOME/scripts"
729
729
  local tmux_scripts_src=""
730
730
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260408.1",
3
+ "version": "4.260408.2",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260408.1",
3
+ "version": "4.260408.2",
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.260408.1",
3
+ "version": "4.260408.2",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
@@ -94,16 +94,19 @@ set -g visual-activity off
94
94
  # ============================================================================
95
95
 
96
96
  # Reload config
97
- bind r source-file ~/.tmux.conf \; display-message "Config reloaded"
97
+ bind r source-file ~/.genie/tmux.conf \; display-message "Config reloaded"
98
98
 
99
99
  # Ctrl+T — new tab in current session
100
100
  bind -n C-t new-window -c "#{?session_path,#{session_path},#{pane_current_path}}"
101
101
 
102
102
  # Vi copy mode bindings — pipe to osc52-copy.sh for clipboard over SSH
103
103
  bind -T copy-mode-vi v send-keys -X begin-selection
104
- bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "~/.genie/osc52-copy.sh"
105
- bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "~/.genie/osc52-copy.sh"
106
- bind -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "~/.genie/osc52-copy.sh"
104
+ bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "~/.genie/scripts/osc52-copy.sh"
105
+ bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "~/.genie/scripts/osc52-copy.sh"
106
+
107
+ # Mouse selection: drag to select, release copies to clipboard + exits copy-mode
108
+ bind -T root MouseDrag1Pane if-shell -F "#{mouse_any_flag}" "send-keys -M" "copy-mode -M"
109
+ bind -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "~/.genie/scripts/osc52-copy.sh"
107
110
 
108
111
  # Session switching — Alt+[ / Alt+] cycle sessions (works in all terminals)
109
112
  bind -n M-] switch-client -n
@@ -24,9 +24,12 @@ setw -g mode-keys vi
24
24
 
25
25
  # Vi copy mode — pipe to osc52-copy.sh for clipboard over SSH
26
26
  bind -T copy-mode-vi v send-keys -X begin-selection
27
- bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "~/.genie/osc52-copy.sh"
28
- bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "~/.genie/osc52-copy.sh"
29
- bind -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "~/.genie/osc52-copy.sh"
27
+ bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "~/.genie/scripts/osc52-copy.sh"
28
+ bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "~/.genie/scripts/osc52-copy.sh"
29
+
30
+ # Mouse selection: drag to select, release copies to clipboard + exits copy-mode
31
+ bind -T root MouseDrag1Pane if-shell -F "#{mouse_any_flag}" "send-keys -M" "copy-mode -M"
32
+ bind -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "~/.genie/scripts/osc52-copy.sh"
30
33
 
31
34
  # NO status bars, NO pane-border shell probes — TUI renders its own UI
32
35
  set -g status off
@@ -440,9 +440,9 @@ function syncTmuxConf(tmuxScriptsSrc: string): void {
440
440
  }
441
441
  }
442
442
 
443
- // Install osc52-copy.sh → ~/.genie/osc52-copy.sh (clipboard helper for nested tmux)
443
+ // Install osc52-copy.sh → ~/.genie/scripts/osc52-copy.sh (clipboard helper for nested tmux)
444
444
  const osc52Src = join(tmuxScriptsSrc, 'osc52-copy.sh');
445
- const osc52Dest = join(GENIE_HOME, 'osc52-copy.sh');
445
+ const osc52Dest = join(GENIE_HOME, 'scripts', 'osc52-copy.sh');
446
446
  if (existsSync(osc52Src)) {
447
447
  try {
448
448
  copyFileSync(osc52Src, osc52Dest);
@@ -12,12 +12,31 @@ const GENIE_TMUX_SOCKET = process.env.GENIE_TMUX_SOCKET || 'genie';
12
12
 
13
13
  /**
14
14
  * Resolve the genie tmux config path.
15
- * Priority: ~/.genie/tmux.conf → shipped scripts/tmux/genie.tmux.conf → /dev/null
15
+ * Priority: ~/.genie/tmux.conf → npm package scripts/tmux/genie.tmux.conf → /dev/null
16
+ *
17
+ * Note: __dirname is unreliable in single-file bundles (dist/genie.js) since
18
+ * scripts/tmux/ doesn't exist next to the bundle. The npm global install path
19
+ * is used as a reliable fallback for the shipped config template.
16
20
  */
17
21
  function resolveGenieTmuxConf(): string {
18
22
  const home = homedir();
19
23
  const genieHome = process.env.GENIE_HOME ?? join(home, '.genie');
20
- const candidates = [join(genieHome, 'tmux.conf'), join(__dirname, '..', '..', 'scripts', 'tmux', 'genie.tmux.conf')];
24
+ const candidates = [
25
+ join(genieHome, 'tmux.conf'),
26
+ join(__dirname, '..', '..', 'scripts', 'tmux', 'genie.tmux.conf'),
27
+ join(
28
+ home,
29
+ '.bun',
30
+ 'install',
31
+ 'global',
32
+ 'node_modules',
33
+ '@automagik',
34
+ 'genie',
35
+ 'scripts',
36
+ 'tmux',
37
+ 'genie.tmux.conf',
38
+ ),
39
+ ];
21
40
  return candidates.find((p) => existsSync(p)) ?? '/dev/null';
22
41
  }
23
42
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from 'bun:test';
2
- import { sanitizeWindowName } from './claude-code.js';
2
+ import { buildOmniSpawnParams, sanitizeWindowName } from './claude-code.js';
3
3
 
4
4
  describe('sanitizeWindowName', () => {
5
5
  test('different JIDs produce different window names', () => {
@@ -46,3 +46,70 @@ describe('sanitizeWindowName', () => {
46
46
  expect(names.size).toBe(100);
47
47
  });
48
48
  });
49
+
50
+ describe('buildOmniSpawnParams', () => {
51
+ const fakeEntry = {
52
+ name: 'simone',
53
+ dir: '/home/genie/agents/simone',
54
+ promptMode: 'append' as const,
55
+ model: 'opus',
56
+ color: 'pink',
57
+ registeredAt: '2026-01-01T00:00:00Z',
58
+ };
59
+
60
+ test('returns provider claude by default', () => {
61
+ const params = buildOmniSpawnParams('simone', 'chat123', fakeEntry, {});
62
+ expect(params.provider).toBe('claude');
63
+ });
64
+
65
+ test('sets team and role to agentName', () => {
66
+ const params = buildOmniSpawnParams('simone', 'chat123', fakeEntry, {});
67
+ expect(params.team).toBe('simone');
68
+ expect(params.role).toBe('simone');
69
+ });
70
+
71
+ test('includes systemPromptFile pointing to AGENTS.md', () => {
72
+ const params = buildOmniSpawnParams('simone', 'chat123', fakeEntry, {});
73
+ expect(params.systemPromptFile).toBe('/home/genie/agents/simone/AGENTS.md');
74
+ });
75
+
76
+ test('injects turn-based prompt as systemPrompt', () => {
77
+ const params = buildOmniSpawnParams('simone', 'chat123', fakeEntry, {
78
+ OMNI_INSTANCE: 'inst-1',
79
+ OMNI_SENDER_NAME: 'Stefani',
80
+ });
81
+ expect(params.systemPrompt).toContain('WhatsApp Turn-Based Conversation');
82
+ expect(params.systemPrompt).toContain('Stefani');
83
+ expect(params.systemPrompt).toContain('inst-1');
84
+ expect(params.systemPrompt).toContain('chat123');
85
+ });
86
+
87
+ test('enables nativeTeam with agent name and color', () => {
88
+ const params = buildOmniSpawnParams('simone', 'chat123', fakeEntry, {});
89
+ expect(params.nativeTeam?.enabled).toBe(true);
90
+ expect(params.nativeTeam?.agentName).toBe('simone');
91
+ expect(params.nativeTeam?.color).toBe('pink');
92
+ });
93
+
94
+ test('passes model from directory entry', () => {
95
+ const params = buildOmniSpawnParams('simone', 'chat123', fakeEntry, {});
96
+ expect(params.model).toBe('opus');
97
+ });
98
+
99
+ test('passes initialMessage as initialPrompt', () => {
100
+ const params = buildOmniSpawnParams('simone', 'chat123', fakeEntry, {}, 'Hello!');
101
+ expect(params.initialPrompt).toBe('Hello!');
102
+ });
103
+
104
+ test('generates a sessionId', () => {
105
+ const params = buildOmniSpawnParams('simone', 'chat123', fakeEntry, {});
106
+ expect(params.sessionId).toBeDefined();
107
+ expect(params.sessionId).toMatch(/^[0-9a-f-]{36}$/);
108
+ });
109
+
110
+ test('uses entry.provider when set', () => {
111
+ const entryWithProvider = { ...fakeEntry, provider: 'codex' };
112
+ const params = buildOmniSpawnParams('simone', 'chat123', entryWithProvider, {});
113
+ expect(params.provider).toBe('codex');
114
+ });
115
+ });
@@ -11,11 +11,15 @@ import { mkdirSync, writeFileSync } from 'node:fs';
11
11
  import { homedir } from 'node:os';
12
12
  import { join } from 'node:path';
13
13
  import * as directory from '../../lib/agent-directory.js';
14
+ import type { DirectoryEntry } from '../../lib/agent-directory.js';
14
15
  import * as agents from '../../lib/agent-registry.js';
15
16
  import * as registry from '../../lib/executor-registry.js';
17
+ import { buildLaunchCommand } from '../../lib/provider-adapters.js';
18
+ import type { SpawnParams } from '../../lib/provider-adapters.js';
16
19
  import { shellQuote } from '../../lib/team-lead-command.js';
17
20
  import { ensureTeamWindow, executeTmux, isPaneAlive, isPaneProcessRunning, killWindow } from '../../lib/tmux.js';
18
21
  import type { IExecutor, OmniMessage, OmniSession, SafePgCallFn } from '../executor.js';
22
+ import { buildTurnBasedPrompt } from './turn-based-prompt.js';
19
23
 
20
24
  interface TmuxSessionState {
21
25
  executorId: string | null;
@@ -27,6 +31,38 @@ export function sanitizeWindowName(chatId: string): string {
27
31
  return `${prefix}-${hash}` || 'chat';
28
32
  }
29
33
 
34
+ /**
35
+ * Build SpawnParams from omni bridge context so we can delegate to buildLaunchCommand().
36
+ */
37
+ export function buildOmniSpawnParams(
38
+ agentName: string,
39
+ chatId: string,
40
+ entry: DirectoryEntry,
41
+ env: Record<string, string>,
42
+ initialMessage?: string,
43
+ ): SpawnParams {
44
+ const instanceId = env.OMNI_INSTANCE ?? '';
45
+ const senderName = env.OMNI_SENDER_NAME ?? 'whatsapp-user';
46
+ const turnPrompt = buildTurnBasedPrompt(senderName, instanceId, chatId);
47
+
48
+ return {
49
+ provider: (entry.provider as SpawnParams['provider']) ?? 'claude',
50
+ team: agentName,
51
+ role: agentName,
52
+ sessionId: randomUUID(),
53
+ model: entry.model,
54
+ promptMode: entry.promptMode,
55
+ systemPromptFile: join(entry.dir, 'AGENTS.md'),
56
+ systemPrompt: turnPrompt,
57
+ initialPrompt: initialMessage,
58
+ nativeTeam: {
59
+ enabled: true,
60
+ agentName,
61
+ color: (entry.color as 'blue' | undefined) ?? undefined,
62
+ },
63
+ };
64
+ }
65
+
30
66
  export class ClaudeCodeOmniExecutor implements IExecutor {
31
67
  private sessions = new Map<string, TmuxSessionState>();
32
68
  private safePgCall: SafePgCallFn | null = null;
@@ -54,26 +90,16 @@ export class ClaudeCodeOmniExecutor implements IExecutor {
54
90
  const { paneId, created } = await ensureTeamWindow(tmuxSession, windowName, entry.dir);
55
91
 
56
92
  if (created) {
57
- const envVars = { ...env, GENIE_OMNI_CHAT_ID: chatId, GENIE_OMNI_AGENT: agentName };
58
- const envPrefix = Object.entries(envVars)
93
+ const omniEnv: Record<string, string> = { ...env, GENIE_OMNI_CHAT_ID: chatId, GENIE_OMNI_AGENT: agentName };
94
+ const params = buildOmniSpawnParams(agentName, chatId, entry, omniEnv);
95
+ const launch = buildLaunchCommand(params);
96
+
97
+ // Merge omni-specific env vars with those produced by buildLaunchCommand
98
+ const allEnv = { ...omniEnv, ...launch.env };
99
+ const envPrefix = Object.entries(allEnv)
59
100
  .map(([k, v]) => `${k}=${shellQuote(v)}`)
60
101
  .join(' ');
61
- const systemPromptFile = join(entry.dir, 'AGENTS.md');
62
- const promptFlag = entry.promptMode === 'system' ? '--system-prompt-file' : '--append-system-prompt-file';
63
- const modelFlag = entry.model ? `--model ${shellQuote(entry.model)}` : '';
64
- const sessionId = randomUUID();
65
- const cmd = [
66
- envPrefix,
67
- 'claude',
68
- promptFlag,
69
- shellQuote(systemPromptFile),
70
- modelFlag,
71
- '--session-id',
72
- shellQuote(sessionId),
73
- '--dangerously-skip-permissions',
74
- ]
75
- .filter(Boolean)
76
- .join(' ');
102
+ const cmd = envPrefix ? `${envPrefix} ${launch.command}` : launch.command;
77
103
  await executeTmux(`send-keys -t '${paneId}' ${shellQuote(cmd)} Enter`);
78
104
  }
79
105
 
@@ -23,6 +23,7 @@ import { homedir } from 'node:os';
23
23
  import { join } from 'node:path';
24
24
  import type { Command } from 'commander';
25
25
  import { ensureTmux, tmuxBin } from '../lib/ensure-tmux.js';
26
+ import { genieTmuxCmd } from '../lib/tmux-wrapper.js';
26
27
 
27
28
  // ============================================================================
28
29
  // Paths
@@ -36,11 +37,6 @@ function servePidPath(): string {
36
37
  return join(genieHome(), 'serve.pid');
37
38
  }
38
39
 
39
- function genieTmuxConf(): string {
40
- const candidates = [join(genieHome(), 'tmux.conf')];
41
- return candidates.find((p) => existsSync(p)) ?? '/dev/null';
42
- }
43
-
44
40
  // TUI uses default tmux server (no separate socket or config)
45
41
 
46
42
  // ============================================================================
@@ -83,13 +79,8 @@ function isProcessAlive(pid: number): boolean {
83
79
  // tmux helpers
84
80
  // ============================================================================
85
81
 
86
- const GENIE_SOCKET = 'genie';
87
82
  const TUI_SESSION = 'genie-tui';
88
83
 
89
- function genieTmux(subcmd: string): string {
90
- return `${tmuxBin()} -L ${GENIE_SOCKET} -f ${genieTmuxConf()} ${subcmd}`;
91
- }
92
-
93
84
  /** TUI tmux config — minimal, no shell probes, no prefix key interference */
94
85
  function tuiTmuxConf(): string {
95
86
  const candidates = [join(genieHome(), 'tui-tmux.conf')];
@@ -104,7 +95,7 @@ function tuiTmux(subcmd: string): string {
104
95
  /** Check if a tmux server is running on a socket */
105
96
  function isGenieTmuxRunning(): boolean {
106
97
  try {
107
- execSync(genieTmux('list-sessions'), { stdio: 'ignore' });
98
+ execSync(genieTmuxCmd('list-sessions'), { stdio: 'ignore' });
108
99
  return true;
109
100
  } catch {
110
101
  return false;
@@ -284,7 +275,7 @@ function killTuiSession(): void {
284
275
  /** List sessions on the genie agent socket */
285
276
  function listAgentSessions(): string[] {
286
277
  try {
287
- const out = execSync(genieTmux("list-sessions -F '#{session_name}'"), { encoding: 'utf-8' });
278
+ const out = execSync(genieTmuxCmd("list-sessions -F '#{session_name}'"), { encoding: 'utf-8' });
288
279
  return out.trim().split('\n').filter(Boolean);
289
280
  } catch {
290
281
  return [];
@@ -464,9 +455,9 @@ async function startForeground(headless?: boolean): Promise<void> {
464
455
  if (!headless) {
465
456
  const sessions = listAgentSessions();
466
457
  if (sessions.length > 0) {
467
- console.log(` Agent server (-L ${GENIE_SOCKET}): ${sessions.length} sessions`);
458
+ console.log(` Agent server (-L genie): ${sessions.length} sessions`);
468
459
  } else {
469
- console.log(` Agent server (-L ${GENIE_SOCKET}): no sessions yet (created on first spawn)`);
460
+ console.log(' Agent server (-L genie): no sessions yet (created on first spawn)');
470
461
  }
471
462
  }
472
463
 
@@ -706,7 +697,7 @@ async function printPgserveStatus(): Promise<void> {
706
697
  function printTmuxStatus(): void {
707
698
  const agentRunning = isGenieTmuxRunning();
708
699
  const sessions = agentRunning ? listAgentSessions() : [];
709
- console.log(` tmux -L ${GENIE_SOCKET}: ${agentRunning ? `running (${sessions.length} sessions)` : 'stopped'}`);
700
+ console.log(` tmux -L genie: ${agentRunning ? `running (${sessions.length} sessions)` : 'stopped'}`);
710
701
  if (sessions.length > 0) {
711
702
  console.log(` ${sessions.join(', ')}`);
712
703
  }