@automagik/genie 4.260418.11 → 4.260418.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/genie.js
CHANGED
|
@@ -359,13 +359,13 @@ Next steps:`),console.log(" 1. Reload tmux: tmux source ~/.tmux.conf"),console.
|
|
|
359
359
|
COUNT(*) FILTER (WHERE entity_type = 'otel_tool')::int AS tool_calls,
|
|
360
360
|
COUNT(*) FILTER (WHERE entity_type = 'otel_api')::int AS api_requests
|
|
361
361
|
FROM audit_events
|
|
362
|
-
WHERE created_at >= $1::timestamptz`,[sinceTs]))[0]??{};return{agents_spawned:r.agents_spawned??0,tasks_moved:r.tasks_moved??0,total_cost:r.total_cost??0,error_count:r.error_count??0,total_events:r.total_events??0,tool_calls:r.tool_calls??0,api_requests:r.api_requests??0}}function generateTraceId(){return crypto.randomUUID()}var init_audit=__esm(()=>{init_db()});function isBlockingEvent(event){return BLOCKING_EVENTS.has(event)}var DISPATCHED_EVENTS,BLOCKING_EVENTS;var init_types3=__esm(()=>{DISPATCHED_EVENTS=["PreToolUse","PostToolUse","SessionStart","SessionEnd","TeammateIdle","TaskCompleted"],BLOCKING_EVENTS=new Set(["PreToolUse","UserPromptSubmit","TeammateIdle","TaskCompleted","PermissionRequest"])});var exports_inject={};__export(exports_inject,{isTeamHooked:()=>isTeamHooked,injectTeamHooks:()=>injectTeamHooks,buildDispatchCommand:()=>buildDispatchCommand});import{existsSync as existsSync14}from"fs";import{mkdir as mkdir2,readFile as
|
|
362
|
+
WHERE created_at >= $1::timestamptz`,[sinceTs]))[0]??{};return{agents_spawned:r.agents_spawned??0,tasks_moved:r.tasks_moved??0,total_cost:r.total_cost??0,error_count:r.error_count??0,total_events:r.total_events??0,tool_calls:r.tool_calls??0,api_requests:r.api_requests??0}}function generateTraceId(){return crypto.randomUUID()}var init_audit=__esm(()=>{init_db()});import{readFile as readFile2,unlink as unlink2,writeFile}from"fs/promises";function lockPath(filePath){return`${filePath}.lock`}function isPidAlive2(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 writeFile(lock,String(process.pid),{flag:"wx"});return}catch{try{let content=await readFile2(lock,"utf-8"),holderPid=Number.parseInt(content.trim(),10);if(!Number.isNaN(holderPid)&&!isPidAlive2(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(`[lockfile] Force-acquiring stale lock: ${lock}`),await writeFile(lock,String(process.pid))}async function releaseLock(path2){try{await unlink2(lockPath(path2))}catch{}}var LOCK_TIMEOUT_MS=5000,LOCK_POLL_MS=50;var init_lockfile=()=>{};function isBlockingEvent(event){return BLOCKING_EVENTS.has(event)}var DISPATCHED_EVENTS,BLOCKING_EVENTS;var init_types3=__esm(()=>{DISPATCHED_EVENTS=["PreToolUse","PostToolUse","SessionStart","SessionEnd","TeammateIdle","TaskCompleted"],BLOCKING_EVENTS=new Set(["PreToolUse","UserPromptSubmit","TeammateIdle","TaskCompleted","PermissionRequest"])});var exports_inject={};__export(exports_inject,{isTeamHooked:()=>isTeamHooked,injectTeamHooks:()=>injectTeamHooks,buildDispatchCommand:()=>buildDispatchCommand});import{existsSync as existsSync14}from"fs";import{mkdir as mkdir2,readFile as readFile3,writeFile as writeFile2}from"fs/promises";import{homedir as homedir14}from"os";import{join as join15}from"path";import{fileURLToPath}from"url";function escapeShellArg(arg){return`'${arg.replace(/'/g,"'\\''")}'`}function buildDispatchCommand(){let entrypoint=fileURLToPath(new URL("../genie.ts",import.meta.url));if(!existsSync14(entrypoint))return"genie hook dispatch";let bun=process.execPath||"bun";return`${escapeShellArg(bun)} run ${escapeShellArg(entrypoint)} hook dispatch`}function isGenieDispatchCommand(command){return typeof command==="string"&&/(?:^|\s)hook\s+dispatch(?:\s|$)/.test(command)}function claudeConfigDir(){return process.env.CLAUDE_CONFIG_DIR??join15(homedir14(),".claude")}function teamSettingsPath(teamName){let sanitized=teamName.replace(/[^a-zA-Z0-9]/g,"-").toLowerCase();return join15(claudeConfigDir(),"teams",sanitized,"settings.json")}function buildHooksConfig(){let hooks={},dispatchCommand=buildDispatchCommand();for(let event of DISPATCHED_EVENTS)hooks[event]=[{matcher:"*",hooks:[{type:"command",command:dispatchCommand,timeout:DISPATCH_TIMEOUT}]}];return hooks}async function injectIntoFile(settingsPath){let settings={};if(existsSync14(settingsPath))try{let content=await readFile3(settingsPath,"utf-8");settings=JSON.parse(content)}catch{}let hooksConfig=buildHooksConfig(),existingHooks=settings.hooks;if(existingHooks){if(DISPATCHED_EVENTS.every((event)=>{let existing=existingHooks[event],desiredCommand=hooksConfig[event][0].hooks[0].command;return existing?.some((m)=>m.hooks?.some((h)=>h.command===desiredCommand))}))return!1}let mergedHooks=existingHooks?{...existingHooks}:{};for(let event of DISPATCHED_EVENTS){let genieEntry=hooksConfig[event][0],existingEntries=(mergedHooks[event]??[]).map((matcher)=>({...matcher,hooks:matcher.hooks?.map((hook)=>isGenieDispatchCommand(hook.command)?{...hook,command:genieEntry.hooks[0].command,timeout:DISPATCH_TIMEOUT}:hook)}));if(!existingEntries.some((m)=>m.hooks?.some((h)=>isGenieDispatchCommand(h.command))))mergedHooks[event]=[...existingEntries,genieEntry];else mergedHooks[event]=existingEntries}settings.hooks=mergedHooks;let dir=join15(settingsPath,"..");return await mkdir2(dir,{recursive:!0}),await writeFile2(settingsPath,JSON.stringify(settings,null,2)),!0}async function injectTeamHooks(teamName){let path2=teamSettingsPath(teamName);return injectIntoFile(path2)}async function isTeamHooked(teamName){let path2=teamSettingsPath(teamName);if(!existsSync14(path2))return!1;try{let content=await readFile3(path2,"utf-8"),hooks=JSON.parse(content).hooks;if(!hooks)return!1;return DISPATCHED_EVENTS.every((event)=>{return hooks[event]?.some((m)=>m.hooks?.some((h)=>isGenieDispatchCommand(h.command)))})}catch{return!1}}var DISPATCH_TIMEOUT=15;var init_inject=__esm(()=>{init_types3()});var exports_provider_adapters={};__export(exports_provider_adapters,{validateSpawnParams:()=>validateSpawnParams,buildLaunchCommand:()=>buildLaunchCommand,buildCodexCommand:()=>buildCodexCommand,buildClaudeCommand:()=>buildClaudeCommand,CLAUDE_TEAM_COLORS:()=>CLAUDE_TEAM_COLORS});function validateSpawnParams(params){return spawnParamsSchema.parse(params)}function escapeShellArg2(arg){return`'${arg.replace(/'/g,"'\\''")}'`}function hasBinary(name){try{let BunExt=Bun;if(typeof BunExt.which==="function")return Boolean(BunExt.which(name));let{execSync:execSync5}=__require("child_process");return execSync5(`which ${name}`,{stdio:"ignore"}),!0}catch{return!1}}function resolveShellBinary(name){try{let{execFileSync}=__require("child_process"),shell=process.env.SHELL||"/bin/sh";return execFileSync(shell,["-lc",`command -v ${name}`],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim()||null}catch{return null}}function preflightCheck(provider){if(!hasBinary(provider))throw Error(`Provider binary "${provider}" not found on PATH. Install ${provider} or check your environment.`)}function appendNativeTeamFlags(parts,env,nt,params){env.CLAUDECODE="1",env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS="1";let agentName=nt.agentName??params.role??"worker";if(env.GENIE_AGENT_NAME=agentName,parts.push("--agent-id",escapeShellArg2(`${agentName}@${params.team}`)),parts.push("--agent-name",escapeShellArg2(agentName)),parts.push("--team-name",escapeShellArg2(params.team)),nt.color)parts.push("--agent-color",escapeShellArg2(nt.color));if(nt.parentSessionId)parts.push("--parent-session-id",escapeShellArg2(nt.parentSessionId));if(nt.agentType)parts.push("--agent-type",escapeShellArg2(nt.agentType));if(nt.planModeRequired)parts.push("--plan-mode-required");let effectivePermMode=nt.permissionMode??"bypassPermissions";parts.push("--permission-mode",escapeShellArg2(effectivePermMode))}function appendSystemPromptFlags(parts,params){if(params.systemPrompt){let{mkdirSync:mkdirSync8,writeFileSync:writeFileSync9,readFileSync:readFileSync10}=__require("fs"),{join:join16}=__require("path"),dir="/tmp/genie-prompts";mkdirSync8("/tmp/genie-prompts",{recursive:!0});let ts=Date.now().toString(36),promptFile=join16("/tmp/genie-prompts",`${params.role||"agent"}-${ts}.md`),content=params.systemPrompt;if(params.systemPromptFile)content=`${readFileSync10(params.systemPromptFile,"utf-8")}
|
|
363
363
|
|
|
364
364
|
${content}`;if(params.extraArgs){let fileIdx=params.extraArgs.indexOf("--append-system-prompt-file");if(fileIdx!==-1&¶ms.extraArgs[fileIdx+1])content=`${content}
|
|
365
365
|
|
|
366
|
-
${readFileSync10(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 settingsObj={};if(!params.skipHooks){let hookEntry={type:"command",command:buildDispatchCommand(),timeout:15};settingsObj.hooks={PreToolUse:[{matcher:"*",hooks:[hookEntry]}],PostToolUse:[{matcher:"*",hooks:[hookEntry]}],UserPromptSubmit:[{hooks:[hookEntry]}],Stop:[{hooks:[hookEntry]}]}}if(params.permissions){let perms={};if(params.permissions.allow?.length)perms.allow=params.permissions.allow;if(params.permissions.deny?.length)perms.deny=params.permissions.deny;if(Object.keys(perms).length>0)settingsObj.permissions=perms}if(Object.keys(settingsObj).length>0)parts.push("--settings",escapeShellArg2(JSON.stringify(settingsObj)));if(params.disallowedTools?.length)for(let tool of params.disallowedTools)parts.push("--disallowedTools",escapeShellArg2(tool));if(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,resolveOrMintLeadSessionId:()=>resolveOrMintLeadSessionId,resolveNativeMemberName:()=>resolveNativeMemberName,registerNativeMember:()=>registerNativeMember,registerAsTeamLead:()=>registerAsTeamLead,loadConfig:()=>loadConfig,listTeamsWithUnreadInbox:()=>listTeamsWithUnreadInbox,listTeams:()=>listTeams,isInsideClaudeCode:()=>isInsideClaudeCode,findTeamsContainingAgent:()=>findTeamsContainingAgent,ensureNativeTeamWithSessionId:()=>ensureNativeTeamWithSessionId,ensureNativeTeam:()=>ensureNativeTeam,discoverTeamName:()=>discoverTeamName,discoverClaudeParentSessionId:()=>discoverClaudeParentSessionId,deleteNativeTeam:()=>deleteNativeTeam,clearNativeInbox:()=>clearNativeInbox,assignColor:()=>assignColor});import{randomUUID as randomUUID2}from"crypto";import{existsSync as existsSync15}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 homedir15}from"os";import{join as join16}from"path";function claudeConfigDir2(){return process.env.CLAUDE_CONFIG_DIR??join16(homedir15(),".claude")}function teamsBaseDir(){return join16(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 join16(teamsBaseDir(),sanitizeTeamName(teamName))}function configPath(teamName){return join16(teamDir(teamName),"config.json")}function inboxesDir(teamName){return join16(teamDir(teamName),"inboxes")}function inboxPath(teamName,agentName){return join16(inboxesDir(teamName),`${sanitizeTeamName(agentName)}.json`)}function lockPath(filePath){return`${filePath}.lock`}function isPidAlive2(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)&&!isPidAlive2(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 findTeamsContainingAgent(agentName){let teams=await listTeams(),matches=[];for(let teamName of teams){let config=await loadConfig(teamName);if(!config)continue;if(config.members.some((m)=>m.name===agentName||m.agentType===agentName))matches.push(teamName)}return matches}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 await backfillTeamRow(sanitizeTeamName(teamName)),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),await backfillTeamRow(sanitized),config}async function backfillTeamRow(name){try{let{ensureTeamRow}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));await ensureTeamRow(name)}catch{}}function isHealthyLeadSessionId(id){if(typeof id!=="string"||id.length===0)return!1;return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)}async function ensureNativeTeamWithSessionId(teamName,description,sessionId,leaderName){let config=await ensureNativeTeam(teamName,description,sessionId,leaderName);if(config.leadSessionId===sessionId)return config;if(isHealthyLeadSessionId(config.leadSessionId))return config;return config.leadSessionId=sessionId,await saveConfig(teamName,config),config}async function resolveOrMintLeadSessionId(teamName,cwd){let priorId=await findNewestSessionIdForTeam(teamName,cwd);if(priorId)return{sessionId:priorId,shouldResume:!0};return{sessionId:randomUUID2(),shouldResume:!1}}async function findNewestSessionIdForTeam(teamName,cwd){let projectDir=join16(claudeConfigDir2(),"projects",sanitizePath(cwd)),entries;try{entries=await readdir2(projectDir)}catch{return null}let jsonls=entries.filter((e)=>e.endsWith(".jsonl"));if(jsonls.length===0)return null;let needle=sanitizeTeamName(teamName),best=null;for(let name of jsonls){let full=join16(projectDir,name);if(!await jsonlMatchesTitle(full,needle))continue;try{let s=await stat(full);if(!best||s.mtimeMs>best.mtime)best={name,mtime:s.mtimeMs}}catch{}}if(!best)return null;return best.name.replace(".jsonl","")}async function jsonlMatchesTitle(filePath,needle){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(`
|
|
367
|
-
`).slice(0,10)){let trimmed=line.trim();if(!trimmed||!trimmed.includes("custom-title"))continue;try{let entry=JSON.parse(trimmed);if(entry.type!=="custom-title"||typeof entry.customTitle!=="string")continue;if(entry.customTitle.toLowerCase()===needle)return!0}catch{}}}catch{return!1}finally{await handle?.close().catch(()=>{})}return!1}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(!existsSync15(inbox))await
|
|
368
|
-
`).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=join16(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=join16(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 base=teamsBaseDir(),sessionId=await discoverClaudeSessionId(cwd);if(sessionId)try{let teams=await readdir2(base);for(let name of teams){let cfgPath=join16(base,name,"config.json");try{let content=await
|
|
366
|
+
${readFileSync10(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 settingsObj={};if(!params.skipHooks){let hookEntry={type:"command",command:buildDispatchCommand(),timeout:15};settingsObj.hooks={PreToolUse:[{matcher:"*",hooks:[hookEntry]}],PostToolUse:[{matcher:"*",hooks:[hookEntry]}],UserPromptSubmit:[{hooks:[hookEntry]}],Stop:[{hooks:[hookEntry]}]}}if(params.permissions){let perms={};if(params.permissions.allow?.length)perms.allow=params.permissions.allow;if(params.permissions.deny?.length)perms.deny=params.permissions.deny;if(Object.keys(perms).length>0)settingsObj.permissions=perms}if(Object.keys(settingsObj).length>0)parts.push("--settings",escapeShellArg2(JSON.stringify(settingsObj)));if(params.disallowedTools?.length)for(let tool of params.disallowedTools)parts.push("--disallowedTools",escapeShellArg2(tool));if(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,resolveOrMintLeadSessionId:()=>resolveOrMintLeadSessionId,resolveNativeMemberName:()=>resolveNativeMemberName,registerNativeMember:()=>registerNativeMember,registerAsTeamLead:()=>registerAsTeamLead,loadConfig:()=>loadConfig,listTeamsWithUnreadInbox:()=>listTeamsWithUnreadInbox,listTeams:()=>listTeams,isInsideClaudeCode:()=>isInsideClaudeCode,findTeamsContainingAgent:()=>findTeamsContainingAgent,ensureNativeTeamWithSessionId:()=>ensureNativeTeamWithSessionId,ensureNativeTeam:()=>ensureNativeTeam,discoverTeamName:()=>discoverTeamName,discoverClaudeParentSessionId:()=>discoverClaudeParentSessionId,deleteNativeTeam:()=>deleteNativeTeam,clearNativeInbox:()=>clearNativeInbox,assignColor:()=>assignColor});import{randomUUID as randomUUID2}from"crypto";import{existsSync as existsSync15}from"fs";import{mkdir as mkdir3,open,readFile as readFile4,readdir as readdir2,rm,stat,writeFile as writeFile3}from"fs/promises";import{homedir as homedir15}from"os";import{join as join16}from"path";function claudeConfigDir2(){return process.env.CLAUDE_CONFIG_DIR??join16(homedir15(),".claude")}function teamsBaseDir(){return join16(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 join16(teamsBaseDir(),sanitizeTeamName(teamName))}function configPath(teamName){return join16(teamDir(teamName),"config.json")}function inboxesDir(teamName){return join16(teamDir(teamName),"inboxes")}function inboxPath(teamName,agentName){return join16(inboxesDir(teamName),`${sanitizeTeamName(agentName)}.json`)}async function loadConfig(teamName){try{let content=await readFile4(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 findTeamsContainingAgent(agentName){let teams=await listTeams(),matches=[];for(let teamName of teams){let config=await loadConfig(teamName);if(!config)continue;if(config.members.some((m)=>m.name===agentName||m.agentType===agentName))matches.push(teamName)}return matches}async function saveConfig(teamName,config){await writeFile3(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 await backfillTeamRow(sanitizeTeamName(teamName)),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),await backfillTeamRow(sanitized),config}async function backfillTeamRow(name){try{let{ensureTeamRow}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));await ensureTeamRow(name)}catch{}}function isHealthyLeadSessionId(id){if(typeof id!=="string"||id.length===0)return!1;return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)}async function ensureNativeTeamWithSessionId(teamName,description,sessionId,leaderName){let config=await ensureNativeTeam(teamName,description,sessionId,leaderName);if(config.leadSessionId===sessionId)return config;if(isHealthyLeadSessionId(config.leadSessionId))return config;return config.leadSessionId=sessionId,await saveConfig(teamName,config),config}async function resolveOrMintLeadSessionId(teamName,cwd){let priorId=await findNewestSessionIdForTeam(teamName,cwd);if(priorId)return{sessionId:priorId,shouldResume:!0};return{sessionId:randomUUID2(),shouldResume:!1}}async function findNewestSessionIdForTeam(teamName,cwd){let projectDir=join16(claudeConfigDir2(),"projects",sanitizePath(cwd)),entries;try{entries=await readdir2(projectDir)}catch{return null}let jsonls=entries.filter((e)=>e.endsWith(".jsonl"));if(jsonls.length===0)return null;let needle=sanitizeTeamName(teamName),best=null;for(let name of jsonls){let full=join16(projectDir,name);if(!await jsonlMatchesTitle(full,needle))continue;try{let s=await stat(full);if(!best||s.mtimeMs>best.mtime)best={name,mtime:s.mtimeMs}}catch{}}if(!best)return null;return best.name.replace(".jsonl","")}async function jsonlMatchesTitle(filePath,needle){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(`
|
|
367
|
+
`).slice(0,10)){let trimmed=line.trim();if(!trimmed||!trimmed.includes("custom-title"))continue;try{let entry=JSON.parse(trimmed);if(entry.type!=="custom-title"||typeof entry.customTitle!=="string")continue;if(entry.customTitle.toLowerCase()===needle)return!0}catch{}}}catch{return!1}finally{await handle?.close().catch(()=>{})}return!1}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(!existsSync15(inbox))await writeFile3(inbox,"[]")}async function unregisterNativeMember(teamName,agentName){let config=await loadConfig(teamName);if(!config)return;let sanitized=sanitizeTeamName(teamName),agentId=`${sanitizeTeamName(agentName)}@${sanitized}`,before=config.members.length;if(config.members=config.members.filter((m)=>m.agentId!==agentId),config.members.length===before)return;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 readFile4(path2,"utf-8");messages2=JSON.parse(content)}catch{}messages2.push(message),await writeFile3(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 writeFile3(path2,"[]")}finally{await releaseLock(path2)}}async function deleteNativeTeam(teamName){let dir=teamDir(teamName);if(!existsSync15(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 readFile4(join16(base,name,"config.json"),"utf-8");config=JSON.parse(cfgContent)}catch{}let leaderInboxName=extractLeaderInboxName(config,name),inboxFile=join16(base,name,"inboxes",`${leaderInboxName}.json`),messages2;try{let content=await readFile4(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=join16(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(join16(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(`
|
|
368
|
+
`).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=join16(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=join16(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 base=teamsBaseDir(),sessionId=await discoverClaudeSessionId(cwd);if(sessionId)try{let teams=await readdir2(base);for(let name of teams){let cfgPath=join16(base,name,"config.json");try{let content=await readFile4(cfgPath,"utf-8"),config=JSON.parse(content);if(config.leadSessionId===sessionId)return config.name}catch{}}}catch{}let tmuxSessionName=await currentTmuxSessionName();if(tmuxSessionName){let cfgPath=join16(base,tmuxSessionName,"config.json");try{let content=await readFile4(cfgPath,"utf-8");return JSON.parse(content).name}catch{}}return null}async function currentTmuxSessionName(){if(!process.env.TMUX)return null;try{let{getCurrentSessionName}=await Promise.resolve().then(() => (init_tmux(),exports_tmux));return await getCurrentSessionName()}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(!existsSync15(inbox))await writeFile3(inbox,"[]");let finalConfig=await loadConfig(teamName);if(!finalConfig)throw Error(`Failed to load config for team "${teamName}" after creation`);return{sessionId,config:finalConfig}}var init_claude_native_teams=__esm(()=>{init_claude_settings();init_lockfile();init_provider_adapters()});var exports_team_lead_command={};__export(exports_team_lead_command,{shellQuote:()=>shellQuote,sessionExists:()=>sessionExists,ccProjectDirName:()=>ccProjectDirName,buildTeamLeadCommand:()=>buildTeamLeadCommand});import{readFileSync as readFileSync10,readdirSync as readdirSync3}from"fs";import{basename,join as join17}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?.sessionId){let flag=options.resume?"--resume":"--session-id";parts.push(`${flag} ${shellQuote(options.sessionId)}`)}else if(options?.continueName)parts.push(`--resume ${shellQuote(options.continueName)}`);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=readFileSync10(filePath,"utf-8").split(`
|
|
369
369
|
`).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=join17(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=join17(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 existsSync16,mkdirSync as mkdirSync8}from"fs";import{homedir as homedir16}from"os";import{join as join18}from"path";import{promisify}from"util";function resolveGenieTmuxConf(){let home=homedir16(),genieHome2=process.env.GENIE_HOME??join18(home,".genie");return[join18(genieHome2,"tmux.conf"),join18(__dirname,"..","..","scripts","tmux","genie.tmux.conf"),join18(home,".bun","install","global","node_modules","@automagik","genie","scripts","tmux","genie.tmux.conf")].find((p)=>existsSync16(p))??"/dev/null"}function genieTmuxPrefix(){return["-L",GENIE_TMUX_SOCKET,"-f",resolveGenieTmuxConf()]}function genieTmuxCmd(subcommand){return`${tmuxBin()} ${genieTmuxPrefix().join(" ")} ${subcommand}`}function getLogDir(){let logDir=join18(homedir16(),".genie","logs","tmux");if(!existsSync16(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(`
|
|
370
370
|
`).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(`
|
|
371
371
|
`).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(`
|
|
@@ -1169,7 +1169,7 @@ Your role adapts based on workspace maturity:
|
|
|
1169
1169
|
</constraints>
|
|
1170
1170
|
`,GENIE_SOUL_TEMPLATE,GENIE_HEARTBEAT_TEMPLATE;var init_templates=__esm(()=>{init_defaults();GENIE_SOUL_TEMPLATE=["# Genie Specialist \u2014 Soul","","You are the genie workspace specialist. You guide users through the genie workflow and orchestrate agents.","","## The Genie Pipeline","","Every idea follows this pipeline:","","```","brainstorm \u2192 wish \u2192 work \u2192 review \u2192 ship","```","","1. **Brainstorm** \u2014 Explore the idea. Use `/brainstorm` to think through scope, tradeoffs, and approach.","2. **Wish** \u2014 Structure the idea into an actionable plan with acceptance criteria, execution groups, and validation commands. Use `/wish` to create one.","3. **Work** \u2014 Execute the wish. Use `/work` to dispatch engineers per execution group.","4. **Review** \u2014 Validate the work against wish criteria. Use `/review` to check compliance.","5. **Ship** \u2014 Merge, release, deploy. The pipeline ensures quality before shipping.","","## Genie Commands Reference","","### Agent Management","```bash","genie spawn <name> # Start an agent","genie kill <name> # Force kill an agent","genie stop <name> # Stop (preserves session)","genie resume [name] # Resume a suspended agent","genie ls # List agents with status","genie log [agent] # Unified observability feed","genie read <name> # Read terminal output","genie history <name> # Compressed session history","genie answer <name> <choice> # Answer a prompt for an agent","```","","### Agent Communication","```bash","genie agent send '<msg>' --to <name> # Direct message","genie agent send '<msg>' --broadcast # Team broadcast","genie agent inbox # View inbox","genie agent brief --team <name> # Cold-start summary","```","","### Team Orchestration","```bash","genie team create <name> --repo <path> --wish <slug> # Launch autonomous team","genie team hire <name> --team <team> # Add to team","genie team fire <name> --team <team> # Remove from team","genie team list # List teams","genie team disband <name> # Disband team","```","","### Task & Wish Management","```bash","genie task create --title 'x' # Create task","genie task list # List tasks","genie task status <slug> # Wish group status","genie task done <ref> # Mark done","genie task board # Planning board","```","","### Workspace","```bash","genie init # Initialize workspace","genie init agent <name> # Scaffold new agent","genie serve # Start infrastructure","genie doctor # Diagnostic checks","```","","## Concierge \u2192 Orchestrator Transition","","Detect workspace maturity and adapt:","","**Concierge mode** activates when:","- Workspace has 0-1 agents (just the default genie agent)","- No wishes exist yet","- User appears new to genie","","In concierge mode:","- Explain concepts with examples","- Suggest creating a first agent or brainstorming a first wish","- Walk through the pipeline step by step","","**Orchestrator mode** activates when:","- Workspace has 2+ agents","- Wishes exist with execution groups","- User gives direct commands","","In orchestrator mode:","- Route work to the right agents","- Monitor progress across teams","- Summarize status concisely","- Suggest next pipeline steps based on current state","","## Agent Analysis Capability","","When invoked in a workspace with existing agents (from genie or other systems), analyze their setup:","","### Analysis Process","1. List all directories under `agents/` (and any discovered via tree scan)","2. For each agent directory, check:"," - Has `AGENTS.md`? (identity file with frontmatter)"," - Has `SOUL.md`? (personality and knowledge)"," - Has `HEARTBEAT.md`? (autonomous checklist)"," - Has `.claude/settings.local.json`? (Claude Code config)"," - Frontmatter fields present vs. missing","3. Compare against genie conventions:"," - Missing files \u2192 propose creation with templates"," - Incomplete frontmatter \u2192 propose mini-wizard"," - Non-standard structure \u2192 explain conventions, offer migration","4. Present proposals as a checklist \u2014 never auto-modify",""].join(`
|
|
1171
1171
|
`),GENIE_HEARTBEAT_TEMPLATE=["# Heartbeat \u2014 Genie Specialist","","Run this checklist on every iteration. Exit early if nothing actionable.","","## Checklist","","### 1. Workspace State Check","Verify workspace health before doing anything else.","- Is `genie serve` running? If not, suggest starting it.","- Are there registered agents? List them with `genie ls`.","- Any agents in error/crashed state? Flag for user attention.","","### 2. Pending Agents Check","Look for agents waiting to be initialized.","- Check `.genie/pending-agents.json` for queued discoveries.","- If pending agents exist, notify the user and offer to initialize them.","- If new `AGENTS.md` files appeared outside `agents/`, flag for import.","","### 3. Wish Status Check","Review active work across the workspace.","- Check `genie task board` for in-progress wishes.","- For each active wish, check execution group progress.","- Flag blocked groups or stale tasks (no progress in 30+ minutes).","- Summarize: X wishes active, Y groups complete, Z blocked.","","### 4. Generate Suggestions","Based on workspace state, suggest the next most valuable action:",'- **Empty workspace** \u2192 "Start with /brainstorm to explore an idea"','- **Has brainstorm, no wish** \u2192 "Ready to structure this? Run /wish"','- **Has wish, no workers** \u2192 "Dispatch workers with /work"','- **Work complete** \u2192 "Time to review: /review"','- **Review passed** \u2192 "Ship it \u2014 merge the PR"','- **Agents from other systems** \u2192 "I can analyze your agents \u2014 want a compatibility report?"',"","### 5. Exit If Nothing Actionable","If workspace is healthy, no pending agents, no active wishes, and no suggestions \u2014 exit.","Don't create busywork. The user will invoke you when needed.",""].join(`
|
|
1172
|
-
`)});var exports_session={};__export(exports_session,{sessionCommand:()=>sessionCommand,sanitizeWindowName:()=>sanitizeWindowName,getAgentsFilePath:()=>getAgentsFilePath,buildClaudeCommand:()=>buildClaudeCommand2});import{spawnSync as spawnSync2}from"child_process";import{createHash as createHash3,randomUUID as randomUUID6}from"crypto";import{existsSync as existsSync24}from"fs";import{basename as basename5,join as join27}from"path";function shortPathHash(p){return createHash3("md5").update(p).digest("hex").slice(0,4)}function getAgentsFilePath(){let agentsPath=join27(process.cwd(),"AGENTS.md");if(existsSync24(agentsPath))return agentsPath;return null}async function resolveSessionLeaderName(teamName){try{let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));return await resolveLeaderName2(teamName)}catch{return teamName}}async function ensureNativeTeamForLeader(teamName,cwd,sessionId){let leaderName=await resolveSessionLeaderName(teamName);await ensureNativeTeamWithSessionId(teamName,`Genie team: ${teamName}`,sessionId,leaderName),await registerNativeMember(teamName,{agentName:basename5(cwd),agentType:leaderName,color:"blue",cwd})}function buildClaudeCommand2(teamName,systemPromptFile,continueName,leaderName,sessionId){return buildTeamLeadCommand(teamName,{systemPromptFile,continueName,leaderName,sessionId})}async function registerSessionInRegistry(sessionName,windowName,workspaceDir){try{let target=`${sessionName}:${windowName}`,paneId=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_id}'`)).trim(),now=new Date().toISOString(),sanitized=sanitizeTeamName(windowName),leaderName=await resolveSessionLeaderName(windowName),sanitizedLeader=sanitizeTeamName(leaderName);await register({id:`${sanitized}-${sanitizedLeader}`,paneId,session:sessionName,team:windowName,role:leaderName,worktree:null,startedAt:now,state:"working",lastStateChange:now,repoPath:workspaceDir,provider:"claude",transport:"tmux",nativeTeamEnabled:!0,nativeAgentId:`${sanitizedLeader}@${sanitized}`});let agentIdentity=await findOrCreateAgent(leaderName,sanitized,leaderName),pid=null;try{let pidStr=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_pid}'`)).trim(),parsed=Number.parseInt(pidStr,10);if(parsed>0)pid=parsed}catch{}await createAndLinkExecutor(agentIdentity.id,"claude","tmux",{pid,tmuxSession:sessionName,tmuxPaneId:paneId,tmuxWindow:windowName,state:"spawning",repoPath:workspaceDir})}catch{}}async function resolveWindowName(sessionName,cwd){let baseName=sanitizeWindowName(basename5(cwd));if(!await findWindowByName(sessionName,baseName))return baseName;if(await getWindowEnv(`${sessionName}:${baseName}`,"GENIE_CWD")===cwd)return baseName;return`${baseName}-${shortPathHash(cwd)}`}async function createSession2(sessionName,windowName,workspaceDir,systemPromptFile,leaderName){let{sessionId,shouldResume}=await resolveOrMintLeadSessionId(windowName,workspaceDir);if(await ensureNativeTeamForLeader(windowName,workspaceDir,sessionId),console.log(`Native team "${windowName}" ready at ~/.claude/teams/${sanitizeTeamName(windowName)}/`),console.log(`Creating session "${sessionName}"...`),!await createSession(sessionName))console.error(`Failed to create session "${sessionName}"`),process.exit(1);let firstWindow=(await listWindows(sessionName))[0];if(!firstWindow)console.error(`Failed to find initial window in session "${sessionName}"`),process.exit(1);await executeTmux2(`rename-window -t ${shellQuote(firstWindow.id)} ${shellQuote(windowName)}`),await executeTmux2(`set-window-option -t ${shellQuote(firstWindow.id)} automatic-rename off`),await setWindowEnv(`${sessionName}:${windowName}`,"GENIE_CWD",workspaceDir);let target=`${sessionName}:${windowName}`,cdCmd=`cd ${shellQuote(workspaceDir)}`;await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cdCmd)} Enter`);let agentName=basename5(workspaceDir),continueName=sanitizeTeamName(windowName),cmd=buildClaudeCommand2(windowName,systemPromptFile||void 0,shouldResume?continueName:void 0,leaderName,shouldResume?void 0:sessionId);await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cmd)} Enter`),console.log(`Started Claude Code as ${agentName} in ${workspaceDir}`);try{let sanitized=sanitizeTeamName(windowName),leaderName2=await resolveSessionLeaderName(windowName),agentIdentity=await findOrCreateAgent(leaderName2,sanitized,leaderName2);await terminateActiveExecutor(agentIdentity.id)}catch{}await registerSessionInRegistry(sessionName,windowName,workspaceDir)}async function launchWithContinueFallback(target,windowName,workspaceDir,systemPromptFile,leaderName,sessionId,shouldResume){let continueName=sanitizeTeamName(windowName),cmd=buildClaudeCommand2(windowName,systemPromptFile||void 0,shouldResume?continueName:void 0,leaderName,shouldResume?void 0:sessionId);if(await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cmd)} Enter`),shouldResume){await new Promise((r)=>setTimeout(r,3000));let afterCmd=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_current_command}'`)).trim();if(["bash","zsh","sh","fish"].includes(afterCmd)){console.log("Resume failed unexpectedly, starting fresh session...");let freshId=randomUUID6();await ensureNativeTeamForLeader(windowName,workspaceDir,freshId);let freshCmd=buildClaudeCommand2(windowName,systemPromptFile||void 0,void 0,leaderName,freshId);await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(freshCmd)} Enter`)}}}async function focusTeamWindow(sessionName,windowName,workingDir,systemPromptFile,leaderName){if((await ensureTeamWindow(sessionName,windowName,workingDir)).created){console.log(`Created team window "${windowName}"`),await setWindowEnv(`${sessionName}:${windowName}`,"GENIE_CWD",workingDir);let{sessionId,shouldResume}=await resolveOrMintLeadSessionId(windowName,workingDir);await ensureNativeTeamForLeader(windowName,workingDir,sessionId);let target=`${sessionName}:${windowName}`,cdCmd=`cd ${shellQuote(workingDir)}`;await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cdCmd)} Enter`),await launchWithContinueFallback(target,windowName,workingDir,systemPromptFile,leaderName,sessionId,shouldResume),console.log(`Started Claude Code as ${basename5(workingDir)}@${sanitizeTeamName(windowName)} in ${workingDir}`);try{let sanitized=sanitizeTeamName(windowName),leaderName2=await resolveSessionLeaderName(windowName),agentIdentity=await findOrCreateAgent(leaderName2,sanitized,leaderName2);await terminateActiveExecutor(agentIdentity.id)}catch{}await registerSessionInRegistry(sessionName,windowName,workingDir)}else{let target=`${sessionName}:${windowName}`,currentCmd=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_current_command}'`)).trim();if(["bash","zsh","sh","fish"].includes(currentCmd)){console.log(`Claude Code not running in "${windowName}", relaunching...`);let{sessionId,shouldResume}=await resolveOrMintLeadSessionId(windowName,workingDir);await ensureNativeTeamForLeader(windowName,workingDir,sessionId);let cdCmd=`cd ${shellQuote(workingDir)}`;await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cdCmd)} Enter`),await launchWithContinueFallback(target,windowName,workingDir,systemPromptFile,leaderName,sessionId,shouldResume);try{let sanitized=sanitizeTeamName(windowName),leaderName2=await resolveSessionLeaderName(windowName),agentIdentity=await findOrCreateAgent(leaderName2,sanitized,leaderName2);await terminateActiveExecutor(agentIdentity.id)}catch{}await registerSessionInRegistry(sessionName,windowName,workingDir)}}await executeTmux2(`select-window -t ${shellQuote(`${sessionName}:${windowName}`)}`),console.log(`Focused team window "${windowName}"`)}function sanitizeWindowName(name){return name.replace(/\./g,"-")}async function deriveWindowName(sessionName,workspaceDir,team){if(team)return sanitizeWindowName(team);if(await findSessionByName(sessionName))return sanitizeWindowName(await resolveWindowName(sessionName,workspaceDir));return sanitizeWindowName(basename5(workspaceDir))}async function handleReset(sessionName,windowName){let existing=await findSessionByName(sessionName);if(existing){let windows=await listWindows(existing.id);console.log(`Resetting session "${sessionName}"...`),await killSession(existing.id),await Promise.all(windows.map((w)=>deleteNativeTeam(w.name)))}else await deleteNativeTeam(windowName)}function attachToWindow(sessionName,windowName){console.log("Attaching...");let target=`${sessionName}:${windowName}`,cmd=process.env.TMUX?"switch-client":"attach",{genieTmuxPrefix:genieTmuxPrefix2}=(init_tmux_wrapper(),__toCommonJS(exports_tmux_wrapper)),{tmuxBin:tmuxBin2}=(init_ensure_tmux(),__toCommonJS(exports_ensure_tmux));spawnSync2(tmuxBin2(),[...genieTmuxPrefix2(),cmd,"-t",target],{stdio:"inherit"})}async function reconcileLeaderConfigs(){try{let{readdirSync:readdirSync7,readFileSync:readFileSync16,writeFileSync:writeFileSync13}=await import("fs"),{join:join28}=await import("path"),{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),teamsDir=join28(process.env.HOME??"/root",".claude","teams"),teams=readdirSync7(teamsDir);for(let team of teams)try{let configPath2=join28(teamsDir,team,"config.json"),raw=readFileSync16(configPath2,"utf-8"),config=JSON.parse(raw);if(config.leadAgentId?.startsWith("team-lead@")){let actualLeader=await resolveLeaderName2(team),sanitized=sanitizeTeamName(team);config.leadAgentId=`${sanitizeTeamName(actualLeader)}@${sanitized}`,writeFileSync13(configPath2,JSON.stringify(config,null,2)),console.log(`[reconcile] Updated leadAgentId for team "${team}": ${config.leadAgentId}`)}}catch{}}catch{}}async function launchInsideTmux(windowName,workspaceDir,systemPromptFile,leaderName){let{sessionId,shouldResume}=await resolveOrMintLeadSessionId(windowName,workspaceDir);if(shouldResume){let continueName=sanitizeTeamName(windowName);await ensureNativeTeamForLeader(windowName,workspaceDir,sessionId);let cmd=buildClaudeCommand2(windowName,systemPromptFile||void 0,continueName,leaderName,void 0),{execSync:execSyncCmd}=__require("child_process");execSyncCmd(cmd,{stdio:"inherit",cwd:workspaceDir})}else{let suffix=Date.now().toString(36).slice(-4),currentWindowName=`${windowName}-${suffix}`;await executeTmux2(`rename-window ${shellQuote(currentWindowName)}`),await ensureNativeTeamForLeader(currentWindowName,workspaceDir,sessionId);let cmd=buildClaudeCommand2(currentWindowName,systemPromptFile||void 0,void 0,leaderName,sessionId),{execSync:execSyncCmd}=__require("child_process");execSyncCmd(cmd,{stdio:"inherit",cwd:workspaceDir})}}async function sessionCommand(options={}){await reconcileStaleSpawns(),await reconcileLeaderConfigs();let workspaceDir=options.dir??process.cwd(),sessionName=options.name??sanitizeWindowName(basename5(workspaceDir));try{let windowName=await deriveWindowName(sessionName,workspaceDir,options.team),leaderName=await resolveSessionLeaderName(windowName);if(options.reset)await handleReset(sessionName,windowName);let session=await findSessionByName(sessionName),systemPromptFile=getAgentsFilePath();if(!systemPromptFile)if(await esm_default4({message:"No agent found in this directory. Scaffold one?",default:!0}))scaffoldAgentFiles(workspaceDir),systemPromptFile=join27(workspaceDir,"AGENTS.md"),console.log("Created SOUL.md, HEARTBEAT.md, and AGENTS.md");else console.error("AGENTS.md required. Run `genie` again to scaffold."),process.exit(1);if(!session)await createSession2(sessionName,windowName,workspaceDir,systemPromptFile,leaderName),attachToWindow(sessionName,windowName);else if(process.env.TMUX)await launchInsideTmux(windowName,workspaceDir,systemPromptFile,leaderName);else console.log(`Session "${sessionName}" already exists`),await focusTeamWindow(sessionName,windowName,workspaceDir,systemPromptFile,leaderName),attachToWindow(sessionName,windowName)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}var init_session=__esm(()=>{init_esm14();init_agent_registry();init_agent_registry();init_claude_native_teams();init_executor_registry();init_team_lead_command();init_tmux();init_templates()});var exports_team_auto_spawn={};__export(exports_team_auto_spawn,{isTeamActive:()=>isTeamActive,isAgentAlive:()=>isAgentAlive,ensureTeamLead:()=>ensureTeamLead});import{existsSync as existsSync25}from"fs";import{join as join28}from"path";function getSystemPromptFile(workingDir){let agentsPath=join28(workingDir,"AGENTS.md");if(existsSync25(agentsPath))return agentsPath;return null}async function ensureSession(teamName){let current=await getCurrentSessionName();if(current)return current;let{getTeam:getTeam2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),teamConfig=await getTeam2(teamName);if(teamConfig?.tmuxSessionName){if(await findSessionByName(teamConfig.tmuxSessionName))return teamConfig.tmuxSessionName}let sessionName=sanitizeTeamName(teamName);try{await createSession(sessionName)}catch(error2){if(!(error2 instanceof Error?error2.message:String(error2)).includes("duplicate session"))throw error2}return sessionName}async function isTeamActive(teamName){if(!await loadConfig(teamName))return!1;let sessionName=await getCurrentSessionName()??sanitizeTeamName(teamName);if(!await findSessionByName(sessionName))return!1;try{let windows=await listWindows(sessionName),sanitized=sanitizeTeamName(teamName);return windows.some((w)=>w.name===sanitized||w.name===teamName)}catch{return!1}}async function isAgentAlive(agentName){try{let{list:list2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),match=(await list2()).find((a)=>a.id===agentName||a.role===agentName);if(!match?.paneId)return!1;return resolveWorkerLivenessByTransport(match)}catch{return!1}}async function ensureTeamLead(teamName,workingDir){let currentSession=await getCurrentSessionName()??sanitizeTeamName(teamName);if(await isTeamActive(teamName))return{created:!1,session:currentSession,window:sanitizeWindowName(teamName)};let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),leaderName=await resolveLeaderName2(teamName),{sessionId,shouldResume}=await resolveOrMintLeadSessionId(teamName,workingDir);await ensureNativeTeamWithSessionId(teamName,`Genie team: ${teamName}`,sessionId,leaderName),await registerNativeMember(teamName,{agentName:leaderName,agentType:"general-purpose",color:"blue",cwd:workingDir});let session=await ensureSession(teamName),windowName=sanitizeWindowName(teamName),teamWindow=await ensureTeamWindow(session,windowName,workingDir);if(teamWindow.created){let systemPromptFile=getSystemPromptFile(workingDir),target=`${session}:${windowName}`,cdCmd=`cd ${shellQuote(workingDir)}`;await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cdCmd)} Enter`);let cmd=buildTeamLeadCommand(teamName,{systemPromptFile:systemPromptFile??void 0,leaderName,sessionId,resume:shouldResume});await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cmd)} Enter`),await recordTeamLeadExecutor({teamName,leaderName,session,windowName,windowId:teamWindow.windowId,paneId:teamWindow.paneId,sessionId,workingDir}).catch(()=>{})}return{created:teamWindow.created,session,window:windowName}}async function recordTeamLeadExecutor(opts){let sanitizedTeam=sanitizeTeamName(opts.teamName),agentIdentity=await findOrCreateAgent(opts.leaderName,sanitizedTeam,opts.leaderName);await terminateActiveExecutor(agentIdentity.id);let pid=null;try{let target=`${opts.session}:${opts.windowName}`,pidStr=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_pid}'`)).trim(),parsed=Number.parseInt(pidStr,10);if(parsed>0)pid=parsed}catch{}await createAndLinkExecutor(agentIdentity.id,"claude","tmux",{pid,tmuxSession:opts.session,tmuxPaneId:opts.paneId,tmuxWindow:opts.windowName,tmuxWindowId:opts.windowId??null,claudeSessionId:opts.sessionId,state:"spawning",repoPath:opts.workingDir})}var init_team_auto_spawn=__esm(()=>{init_session();init_agent_registry();init_claude_native_teams();init_executor_registry();init_team_lead_command();init_tmux()});var exports_inbox_watcher={};__export(exports_inbox_watcher,{stopInboxWatcher:()=>stopInboxWatcher,startInboxWatcher:()=>startInboxWatcher,resetSpawnFailures:()=>resetSpawnFailures,getInboxPollIntervalMs:()=>getInboxPollIntervalMs,checkInboxes:()=>checkInboxes});function getInboxPollIntervalMs(){let env=process.env.GENIE_INBOX_POLL_MS;if(env!==void 0){if(env==="")return INBOX_POLL_INTERVAL_MS;let parsed=Number(env);if(!Number.isNaN(parsed)&&parsed>=0)return parsed}return INBOX_POLL_INTERVAL_MS}function resetSpawnFailures(){spawnFailures.clear()}function resolveSessionKeyFromMessage(teamName,firstUnreadText){if(!firstUnreadText)return teamName;let header=parseRoutingHeader(firstUnreadText);return header?resolveSessionKey(teamName,header):teamName}async function checkInboxes(deps=defaultDeps){if(getInboxPollIntervalMs()===0)return[];let teamsWithUnread=await deps.listTeamsWithUnreadInbox(),spawned=[];for(let{teamName,workingDir,firstUnreadText}of teamsWithUnread){let sessionKey2=resolveSessionKeyFromMessage(teamName,firstUnreadText),failures=spawnFailures.get(sessionKey2)??0;if(failures>=MAX_SPAWN_FAILURES){deps.warn(`[inbox-watcher] Skipping "${sessionKey2}" \u2014 ${failures} consecutive spawn failures`);continue}if(await deps.isTeamActive(teamName))continue;if(!workingDir){deps.warn(`[inbox-watcher] Cannot spawn team-lead for "${teamName}" \u2014 no workingDir in config`);continue}try{await deps.ensureTeamLead(teamName,workingDir),spawnFailures.set(sessionKey2,0),spawned.push(teamName)}catch(err){let newCount=failures+1;spawnFailures.set(sessionKey2,newCount);let message=err instanceof Error?err.message:String(err);deps.warn(`[inbox-watcher] Failed to spawn team-lead for "${teamName}" (attempt ${newCount}/${MAX_SPAWN_FAILURES}): ${message}`)}}return spawned}function startInboxWatcher(deps=defaultDeps){return setInterval(()=>{checkInboxes(deps).catch((err)=>{let message=err instanceof Error?err.message:String(err);deps.warn(`[inbox-watcher] Poll error: ${message}`)})},getInboxPollIntervalMs())}function stopInboxWatcher(handle){clearInterval(handle)}var defaultDeps,INBOX_POLL_INTERVAL_MS=30000,MAX_SPAWN_FAILURES=3,spawnFailures;var init_inbox_watcher=__esm(()=>{init_claude_native_teams();init_routing_header();init_team_auto_spawn();defaultDeps={listTeamsWithUnreadInbox,isTeamActive:(teamName)=>isTeamActive(teamName),isAgentAlive:(agentName)=>isAgentAlive(agentName),ensureTeamLead:(teamName,workingDir)=>ensureTeamLead(teamName,workingDir),warn:(msg)=>console.warn(msg)};spawnFailures=new Map});var exports_msg={};__export(exports_msg,{suggestRelayLeader:()=>suggestRelayLeader,resolveSenderTeams:()=>resolveSenderTeams,registerSendInboxCommands:()=>registerSendInboxCommands,detectSenderIdentity:()=>detectSenderIdentity,checkSendScope:()=>checkSendScope});import{readFile as readFile4}from"fs/promises";import{homedir as homedir19}from"os";import{join as join29}from"path";async function getRegistry(){if(!_registry)_registry=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));return _registry}async function getTaskService(){if(!_taskService)_taskService=await Promise.resolve().then(() => (init_task_service(),exports_task_service));return _taskService}async function getTeamManager(){if(!_teamManager)_teamManager=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));return _teamManager}async function getMailbox(){if(!_mailbox)_mailbox=await Promise.resolve().then(() => (init_mailbox(),exports_mailbox));return _mailbox}async function detectSenderIdentity(teamName){let envName=process.env.GENIE_AGENT_NAME;if(envName)return envName;let paneId=process.env.TMUX_PANE;if(!paneId)return"cli";let registry=await getRegistry(),worker=typeof registry.findByPane==="function"?await registry.findByPane(paneId):null;if(worker)return worker.role??worker.id;let resolvedTeam=teamName??process.env.GENIE_TEAM;if(resolvedTeam){let memberName=await findMemberByPane(resolvedTeam,paneId);if(memberName)return memberName}return"cli"}async function findMemberByPane(teamName,paneId){let configDir=process.env.CLAUDE_CONFIG_DIR??join29(homedir19(),".claude"),sanitized=teamName.replace(/[^a-zA-Z0-9]/g,"-").toLowerCase(),cfgPath=join29(configDir,"teams",sanitized,"config.json");try{let raw=await readFile4(cfgPath,"utf-8");return(JSON.parse(raw).members??[]).find((m)=>m.tmuxPaneId===paneId)?.name??null}catch{return null}}async function resolveLeaderAlias(recipient,teamContext){if(recipient!=="team-lead")return recipient;let teamName=teamContext??process.env.GENIE_TEAM;if(teamName)return(await getTeamManager()).resolveLeaderName(teamName);return recipient}async function checkSendScope(_repoPath,sender,recipient){if(sender==="cli")return null;let teams=await(await getTeamManager()).listTeams(),senderTeams=resolveSenderTeams(teams,sender);if(senderTeams.length===0)return null;for(let team of senderTeams)if(isRecipientInTeam(team,recipient))return null;let teamNames=senderTeams.map((t)=>t.name).join(", ");return`Scope violation: "${recipient}" is not in sender's team(s): ${teamNames}`}function childReachbackAllowed(child,parent){if(parent.allowChildReachback?.some((prefix)=>child.name.startsWith(prefix)))return!0;return DEFAULT_REACHBACK_PREFIXES.some((prefix)=>child.name.startsWith(prefix))}function walkParentChain(teams,start,visited,out){let current=start,depth=0;while(current.parentTeam&&depth<PARENT_CHAIN_MAX_DEPTH){if(visited.has(current.parentTeam))return;let parent=teams.find((t)=>t.name===current.parentTeam);if(!parent)return;if(!childReachbackAllowed(current,parent))return;out.push(parent),visited.add(parent.name),current=parent,depth++}}function resolveSenderTeams(teams,sender){let direct=teams.filter((t)=>t.members.includes(sender)),visited=new Set(direct.map((t)=>t.name)),result2=[...direct];for(let team of direct)walkParentChain(teams,team,visited,result2);if(teams.some((t)=>t.leader===sender)||sender==="team-lead"){let envTeam=process.env.GENIE_TEAM;if(envTeam){let leaderTeam=teams.find((t)=>t.name===envTeam);if(leaderTeam&&!visited.has(leaderTeam.name))result2.push(leaderTeam),visited.add(leaderTeam.name),walkParentChain(teams,leaderTeam,visited,result2)}}return result2}async function suggestRelayLeader(sender){if(sender==="cli")return null;let teams=await(await getTeamManager()).listTeams(),reachable=resolveSenderTeams(teams,sender);if(reachable.length===0)return null;let target=reachable[0];return{leader:target.leader??target.name,team:target.name}}function isRecipientInTeam(team,recipient){if(team.members.includes(recipient)||recipient===team.leader||recipient==="team-lead")return!0;if(recipient.startsWith(`${team.name}-`)){let roleOnly=recipient.slice(team.name.length+1);if(team.members.includes(roleOnly))return!0}return!1}async function findAgentTeam(_repoPath,agentName){let teams=await(await getTeamManager()).listTeams(),memberTeam=teams.find((t)=>t.members.includes(agentName));if(memberTeam)return memberTeam;if(agentName==="team-lead"||teams.some((t)=>t.leader===agentName)){let envTeam=process.env.GENIE_TEAM;if(envTeam)return teams.find((t)=>t.name===envTeam)??null;let leaderTeam=teams.find((t)=>t.leader===agentName);if(leaderTeam)return leaderTeam}return null}function localActor(name){return{actorType:"local",actorId:name}}async function resolveTeamName2(explicit,repoPath,from){if(explicit)return explicit;let name=(await findAgentTeam(repoPath,from))?.name??process.env.GENIE_TEAM;if(!name)console.error("Error: Could not auto-detect team. Use --team <name>."),process.exit(1);return name}async function handleInbox(agent,options){let ts3=await getTaskService(),resolvedAgent=agent??await detectSenderIdentity(),actor=localActor(resolvedAgent),conversations=await ts3.listConversations(actor);if(options.json){console.log(JSON.stringify(conversations,null,2));return}if(conversations.length===0){console.log(`No conversations for "${resolvedAgent}".`);return}console.log(""),console.log(`INBOX: ${resolvedAgent}`),console.log("\u2500".repeat(60));for(let conv of conversations)await printConversationSummary(ts3,conv)}async function printConversationSummary(ts3,conv){let messages2=await ts3.getMessages(conv.id,{limit:1}),lastMsg=messages2.length>0?messages2[messages2.length-1]:null,name=conv.name??conv.id,type2=conv.type==="dm"?"DM":"Group",linked=conv.linkedEntity?` [${conv.linkedEntity}:${conv.linkedEntityId}]`:"",preview=lastMsg?truncate2(lastMsg.body,50):"(no messages)",time=lastMsg?formatTime(lastMsg.createdAt):"";if(console.log(` ${padRight(name,30)} ${padRight(type2,6)}${linked}`),lastMsg)console.log(` ${time} ${lastMsg.senderId}: ${preview}`);console.log("")}async function handleChatThread(messageId,options){let ts3=await getTaskService(),from=options.from??await detectSenderIdentity(),actor=localActor(from),parentMsgId=Number(messageId),parentMsg=await ts3.getMessage(parentMsgId);if(!parentMsg)console.error(`Error: Message not found: ${messageId}`),process.exit(1);let conv=await ts3.findOrCreateConversation({type:"group",name:options.name??`Thread on message #${parentMsgId}`,parentMessageId:parentMsgId,createdBy:actor,members:[actor]});console.log(`Thread created: ${conv.id}`),console.log(` Parent message: #${parentMsgId} in ${parentMsg.conversationId}`),console.log(` Name: ${conv.name??"(unnamed)"}`)}function printConversationTable(conversations){console.log(` ${padRight("ID",20)} ${padRight("NAME",25)} ${padRight("TYPE",8)} ${padRight("LINKED",20)} UPDATED`),console.log(` ${"\u2500".repeat(80)}`);for(let c of conversations){let name=truncate2(c.name??"(unnamed)",23),linked=c.linkedEntity?`${c.linkedEntity}:${c.linkedEntityId}`:"-",updated=formatTime(c.updatedAt);console.log(` ${padRight(c.id,20)} ${padRight(name,25)} ${padRight(c.type,8)} ${padRight(linked,20)} ${updated}`)}console.log(`
|
|
1172
|
+
`)});var exports_session={};__export(exports_session,{sessionCommand:()=>sessionCommand,sanitizeWindowName:()=>sanitizeWindowName,getAgentsFilePath:()=>getAgentsFilePath,buildClaudeCommand:()=>buildClaudeCommand2});import{spawnSync as spawnSync2}from"child_process";import{createHash as createHash3,randomUUID as randomUUID6}from"crypto";import{existsSync as existsSync24}from"fs";import{basename as basename5,join as join27}from"path";function shortPathHash(p){return createHash3("md5").update(p).digest("hex").slice(0,4)}function getAgentsFilePath(){let agentsPath=join27(process.cwd(),"AGENTS.md");if(existsSync24(agentsPath))return agentsPath;return null}async function resolveSessionLeaderName(teamName){try{let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));return await resolveLeaderName2(teamName)}catch{return teamName}}async function ensureNativeTeamForLeader(teamName,cwd,sessionId){let leaderName=await resolveSessionLeaderName(teamName);await ensureNativeTeamWithSessionId(teamName,`Genie team: ${teamName}`,sessionId,leaderName),await registerNativeMember(teamName,{agentName:basename5(cwd),agentType:leaderName,color:"blue",cwd})}function buildClaudeCommand2(teamName,systemPromptFile,continueName,leaderName,sessionId){return buildTeamLeadCommand(teamName,{systemPromptFile,continueName,leaderName,sessionId})}async function registerSessionInRegistry(sessionName,windowName,workspaceDir){try{let target=`${sessionName}:${windowName}`,paneId=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_id}'`)).trim(),now=new Date().toISOString(),sanitized=sanitizeTeamName(windowName),leaderName=await resolveSessionLeaderName(windowName),sanitizedLeader=sanitizeTeamName(leaderName);await register({id:`${sanitized}-${sanitizedLeader}`,paneId,session:sessionName,team:windowName,role:leaderName,worktree:null,startedAt:now,state:"working",lastStateChange:now,repoPath:workspaceDir,provider:"claude",transport:"tmux",nativeTeamEnabled:!0,nativeAgentId:`${sanitizedLeader}@${sanitized}`});let agentIdentity=await findOrCreateAgent(leaderName,sanitized,leaderName),pid=null;try{let pidStr=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_pid}'`)).trim(),parsed=Number.parseInt(pidStr,10);if(parsed>0)pid=parsed}catch{}await createAndLinkExecutor(agentIdentity.id,"claude","tmux",{pid,tmuxSession:sessionName,tmuxPaneId:paneId,tmuxWindow:windowName,state:"spawning",repoPath:workspaceDir})}catch{}}async function resolveWindowName(sessionName,cwd){let baseName=sanitizeWindowName(basename5(cwd));if(!await findWindowByName(sessionName,baseName))return baseName;if(await getWindowEnv(`${sessionName}:${baseName}`,"GENIE_CWD")===cwd)return baseName;return`${baseName}-${shortPathHash(cwd)}`}async function createSession2(sessionName,windowName,workspaceDir,systemPromptFile,leaderName){let{sessionId,shouldResume}=await resolveOrMintLeadSessionId(windowName,workspaceDir);if(await ensureNativeTeamForLeader(windowName,workspaceDir,sessionId),console.log(`Native team "${windowName}" ready at ~/.claude/teams/${sanitizeTeamName(windowName)}/`),console.log(`Creating session "${sessionName}"...`),!await createSession(sessionName))console.error(`Failed to create session "${sessionName}"`),process.exit(1);let firstWindow=(await listWindows(sessionName))[0];if(!firstWindow)console.error(`Failed to find initial window in session "${sessionName}"`),process.exit(1);await executeTmux2(`rename-window -t ${shellQuote(firstWindow.id)} ${shellQuote(windowName)}`),await executeTmux2(`set-window-option -t ${shellQuote(firstWindow.id)} automatic-rename off`),await setWindowEnv(`${sessionName}:${windowName}`,"GENIE_CWD",workspaceDir);let target=`${sessionName}:${windowName}`,cdCmd=`cd ${shellQuote(workspaceDir)}`;await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cdCmd)} Enter`);let agentName=basename5(workspaceDir),continueName=sanitizeTeamName(windowName),cmd=buildClaudeCommand2(windowName,systemPromptFile||void 0,shouldResume?continueName:void 0,leaderName,shouldResume?void 0:sessionId);await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cmd)} Enter`),console.log(`Started Claude Code as ${agentName} in ${workspaceDir}`);try{let sanitized=sanitizeTeamName(windowName),leaderName2=await resolveSessionLeaderName(windowName),agentIdentity=await findOrCreateAgent(leaderName2,sanitized,leaderName2);await terminateActiveExecutor(agentIdentity.id)}catch{}await registerSessionInRegistry(sessionName,windowName,workspaceDir)}async function launchWithContinueFallback(target,windowName,workspaceDir,systemPromptFile,leaderName,sessionId,shouldResume){let continueName=sanitizeTeamName(windowName),cmd=buildClaudeCommand2(windowName,systemPromptFile||void 0,shouldResume?continueName:void 0,leaderName,shouldResume?void 0:sessionId);if(await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cmd)} Enter`),shouldResume){await new Promise((r)=>setTimeout(r,3000));let afterCmd=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_current_command}'`)).trim();if(["bash","zsh","sh","fish"].includes(afterCmd)){console.log("Resume failed unexpectedly, starting fresh session...");let freshId=randomUUID6();await ensureNativeTeamForLeader(windowName,workspaceDir,freshId);let freshCmd=buildClaudeCommand2(windowName,systemPromptFile||void 0,void 0,leaderName,freshId);await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(freshCmd)} Enter`)}}}async function focusTeamWindow(sessionName,windowName,workingDir,systemPromptFile,leaderName){if((await ensureTeamWindow(sessionName,windowName,workingDir)).created){console.log(`Created team window "${windowName}"`),await setWindowEnv(`${sessionName}:${windowName}`,"GENIE_CWD",workingDir);let{sessionId,shouldResume}=await resolveOrMintLeadSessionId(windowName,workingDir);await ensureNativeTeamForLeader(windowName,workingDir,sessionId);let target=`${sessionName}:${windowName}`,cdCmd=`cd ${shellQuote(workingDir)}`;await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cdCmd)} Enter`),await launchWithContinueFallback(target,windowName,workingDir,systemPromptFile,leaderName,sessionId,shouldResume),console.log(`Started Claude Code as ${basename5(workingDir)}@${sanitizeTeamName(windowName)} in ${workingDir}`);try{let sanitized=sanitizeTeamName(windowName),leaderName2=await resolveSessionLeaderName(windowName),agentIdentity=await findOrCreateAgent(leaderName2,sanitized,leaderName2);await terminateActiveExecutor(agentIdentity.id)}catch{}await registerSessionInRegistry(sessionName,windowName,workingDir)}else{let target=`${sessionName}:${windowName}`,currentCmd=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_current_command}'`)).trim();if(["bash","zsh","sh","fish"].includes(currentCmd)){console.log(`Claude Code not running in "${windowName}", relaunching...`);let{sessionId,shouldResume}=await resolveOrMintLeadSessionId(windowName,workingDir);await ensureNativeTeamForLeader(windowName,workingDir,sessionId);let cdCmd=`cd ${shellQuote(workingDir)}`;await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cdCmd)} Enter`),await launchWithContinueFallback(target,windowName,workingDir,systemPromptFile,leaderName,sessionId,shouldResume);try{let sanitized=sanitizeTeamName(windowName),leaderName2=await resolveSessionLeaderName(windowName),agentIdentity=await findOrCreateAgent(leaderName2,sanitized,leaderName2);await terminateActiveExecutor(agentIdentity.id)}catch{}await registerSessionInRegistry(sessionName,windowName,workingDir)}}await executeTmux2(`select-window -t ${shellQuote(`${sessionName}:${windowName}`)}`),console.log(`Focused team window "${windowName}"`)}function sanitizeWindowName(name){return name.replace(/\./g,"-")}async function deriveWindowName(sessionName,workspaceDir,team){if(team)return sanitizeWindowName(team);if(await findSessionByName(sessionName))return sanitizeWindowName(await resolveWindowName(sessionName,workspaceDir));return sanitizeWindowName(basename5(workspaceDir))}async function handleReset(sessionName,windowName){let existing=await findSessionByName(sessionName);if(existing){let windows=await listWindows(existing.id);console.log(`Resetting session "${sessionName}"...`),await killSession(existing.id),await Promise.all(windows.map((w)=>deleteNativeTeam(w.name)))}else await deleteNativeTeam(windowName)}function attachToWindow(sessionName,windowName){console.log("Attaching...");let target=`${sessionName}:${windowName}`,cmd=process.env.TMUX?"switch-client":"attach",{genieTmuxPrefix:genieTmuxPrefix2}=(init_tmux_wrapper(),__toCommonJS(exports_tmux_wrapper)),{tmuxBin:tmuxBin2}=(init_ensure_tmux(),__toCommonJS(exports_ensure_tmux));spawnSync2(tmuxBin2(),[...genieTmuxPrefix2(),cmd,"-t",target],{stdio:"inherit"})}async function reconcileLeaderConfigs(){try{let{readdirSync:readdirSync7,readFileSync:readFileSync16,writeFileSync:writeFileSync13}=await import("fs"),{join:join28}=await import("path"),{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),teamsDir=join28(process.env.HOME??"/root",".claude","teams"),teams=readdirSync7(teamsDir);for(let team of teams)try{let configPath2=join28(teamsDir,team,"config.json"),raw=readFileSync16(configPath2,"utf-8"),config=JSON.parse(raw);if(config.leadAgentId?.startsWith("team-lead@")){let actualLeader=await resolveLeaderName2(team),sanitized=sanitizeTeamName(team);config.leadAgentId=`${sanitizeTeamName(actualLeader)}@${sanitized}`,writeFileSync13(configPath2,JSON.stringify(config,null,2)),console.log(`[reconcile] Updated leadAgentId for team "${team}": ${config.leadAgentId}`)}}catch{}}catch{}}async function launchInsideTmux(windowName,workspaceDir,systemPromptFile,leaderName){let{sessionId,shouldResume}=await resolveOrMintLeadSessionId(windowName,workspaceDir);if(shouldResume){let continueName=sanitizeTeamName(windowName);await ensureNativeTeamForLeader(windowName,workspaceDir,sessionId);let cmd=buildClaudeCommand2(windowName,systemPromptFile||void 0,continueName,leaderName,void 0),{execSync:execSyncCmd}=__require("child_process");execSyncCmd(cmd,{stdio:"inherit",cwd:workspaceDir})}else{let suffix=Date.now().toString(36).slice(-4),currentWindowName=`${windowName}-${suffix}`;await executeTmux2(`rename-window ${shellQuote(currentWindowName)}`),await ensureNativeTeamForLeader(currentWindowName,workspaceDir,sessionId);let cmd=buildClaudeCommand2(currentWindowName,systemPromptFile||void 0,void 0,leaderName,sessionId),{execSync:execSyncCmd}=__require("child_process");execSyncCmd(cmd,{stdio:"inherit",cwd:workspaceDir})}}async function sessionCommand(options={}){await reconcileStaleSpawns(),await reconcileLeaderConfigs();let workspaceDir=options.dir??process.cwd(),sessionName=options.name??sanitizeWindowName(basename5(workspaceDir));try{let windowName=await deriveWindowName(sessionName,workspaceDir,options.team),leaderName=await resolveSessionLeaderName(windowName);if(options.reset)await handleReset(sessionName,windowName);let session=await findSessionByName(sessionName),systemPromptFile=getAgentsFilePath();if(!systemPromptFile)if(await esm_default4({message:"No agent found in this directory. Scaffold one?",default:!0}))scaffoldAgentFiles(workspaceDir),systemPromptFile=join27(workspaceDir,"AGENTS.md"),console.log("Created SOUL.md, HEARTBEAT.md, and AGENTS.md");else console.error("AGENTS.md required. Run `genie` again to scaffold."),process.exit(1);if(!session)await createSession2(sessionName,windowName,workspaceDir,systemPromptFile,leaderName),attachToWindow(sessionName,windowName);else if(process.env.TMUX)await launchInsideTmux(windowName,workspaceDir,systemPromptFile,leaderName);else console.log(`Session "${sessionName}" already exists`),await focusTeamWindow(sessionName,windowName,workspaceDir,systemPromptFile,leaderName),attachToWindow(sessionName,windowName)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}var init_session=__esm(()=>{init_esm14();init_agent_registry();init_agent_registry();init_claude_native_teams();init_executor_registry();init_team_lead_command();init_tmux();init_templates()});var exports_team_auto_spawn={};__export(exports_team_auto_spawn,{isTeamActive:()=>isTeamActive,isAgentAlive:()=>isAgentAlive,ensureTeamLead:()=>ensureTeamLead});import{existsSync as existsSync25}from"fs";import{join as join28}from"path";function getSystemPromptFile(workingDir){let agentsPath=join28(workingDir,"AGENTS.md");if(existsSync25(agentsPath))return agentsPath;return null}async function ensureSession(teamName){let current=await getCurrentSessionName();if(current)return current;let{getTeam:getTeam2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),teamConfig=await getTeam2(teamName);if(teamConfig?.tmuxSessionName){if(await findSessionByName(teamConfig.tmuxSessionName))return teamConfig.tmuxSessionName}let sessionName=sanitizeTeamName(teamName);try{await createSession(sessionName)}catch(error2){if(!(error2 instanceof Error?error2.message:String(error2)).includes("duplicate session"))throw error2}return sessionName}async function isTeamActive(teamName){if(!await loadConfig(teamName))return!1;let sessionName=await getCurrentSessionName()??sanitizeTeamName(teamName);if(!await findSessionByName(sessionName))return!1;try{let windows=await listWindows(sessionName),sanitized=sanitizeTeamName(teamName);return windows.some((w)=>w.name===sanitized||w.name===teamName)}catch{return!1}}async function isAgentAlive(agentName){try{let{list:list2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),match=(await list2()).find((a)=>a.id===agentName||a.role===agentName);if(!match?.paneId)return!1;return resolveWorkerLivenessByTransport(match)}catch{return!1}}async function ensureTeamLead(teamName,workingDir){let currentSession=await getCurrentSessionName()??sanitizeTeamName(teamName);if(await isTeamActive(teamName))return{created:!1,session:currentSession,window:sanitizeWindowName(teamName)};let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),leaderName=await resolveLeaderName2(teamName),{sessionId,shouldResume}=await resolveOrMintLeadSessionId(teamName,workingDir);await ensureNativeTeamWithSessionId(teamName,`Genie team: ${teamName}`,sessionId,leaderName),await registerNativeMember(teamName,{agentName:leaderName,agentType:"general-purpose",color:"blue",cwd:workingDir});let session=await ensureSession(teamName),windowName=sanitizeWindowName(teamName),teamWindow=await ensureTeamWindow(session,windowName,workingDir);if(teamWindow.created){let systemPromptFile=getSystemPromptFile(workingDir),target=`${session}:${windowName}`,cdCmd=`cd ${shellQuote(workingDir)}`;await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cdCmd)} Enter`);let cmd=buildTeamLeadCommand(teamName,{systemPromptFile:systemPromptFile??void 0,leaderName,sessionId,resume:shouldResume});await executeTmux2(`send-keys -t ${shellQuote(target)} ${shellQuote(cmd)} Enter`),await recordTeamLeadExecutor({teamName,leaderName,session,windowName,windowId:teamWindow.windowId,paneId:teamWindow.paneId,sessionId,workingDir}).catch(()=>{})}return{created:teamWindow.created,session,window:windowName}}async function recordTeamLeadExecutor(opts){let sanitizedTeam=sanitizeTeamName(opts.teamName),agentIdentity=await findOrCreateAgent(opts.leaderName,sanitizedTeam,opts.leaderName);await terminateActiveExecutor(agentIdentity.id);let pid=null;try{let target=`${opts.session}:${opts.windowName}`,pidStr=(await executeTmux2(`display -t ${shellQuote(target)} -p '#{pane_pid}'`)).trim(),parsed=Number.parseInt(pidStr,10);if(parsed>0)pid=parsed}catch{}await createAndLinkExecutor(agentIdentity.id,"claude","tmux",{pid,tmuxSession:opts.session,tmuxPaneId:opts.paneId,tmuxWindow:opts.windowName,tmuxWindowId:opts.windowId??null,claudeSessionId:opts.sessionId,state:"spawning",repoPath:opts.workingDir})}var init_team_auto_spawn=__esm(()=>{init_session();init_agent_registry();init_claude_native_teams();init_executor_registry();init_team_lead_command();init_tmux()});var exports_inbox_watcher={};__export(exports_inbox_watcher,{stopInboxWatcher:()=>stopInboxWatcher,startInboxWatcher:()=>startInboxWatcher,resetSpawnFailures:()=>resetSpawnFailures,getInboxPollIntervalMs:()=>getInboxPollIntervalMs,checkInboxes:()=>checkInboxes});function getInboxPollIntervalMs(){let env=process.env.GENIE_INBOX_POLL_MS;if(env!==void 0){if(env==="")return INBOX_POLL_INTERVAL_MS;let parsed=Number(env);if(!Number.isNaN(parsed)&&parsed>=0)return parsed}return INBOX_POLL_INTERVAL_MS}function resetSpawnFailures(){spawnFailures.clear()}function resolveSessionKeyFromMessage(teamName,firstUnreadText){if(!firstUnreadText)return teamName;let header=parseRoutingHeader(firstUnreadText);return header?resolveSessionKey(teamName,header):teamName}async function checkInboxes(deps=defaultDeps){if(getInboxPollIntervalMs()===0)return[];let teamsWithUnread=await deps.listTeamsWithUnreadInbox(),spawned=[];for(let{teamName,workingDir,firstUnreadText}of teamsWithUnread){let sessionKey2=resolveSessionKeyFromMessage(teamName,firstUnreadText),failures=spawnFailures.get(sessionKey2)??0;if(failures>=MAX_SPAWN_FAILURES){deps.warn(`[inbox-watcher] Skipping "${sessionKey2}" \u2014 ${failures} consecutive spawn failures`);continue}if(await deps.isTeamActive(teamName))continue;if(!workingDir){deps.warn(`[inbox-watcher] Cannot spawn team-lead for "${teamName}" \u2014 no workingDir in config`);continue}try{await deps.ensureTeamLead(teamName,workingDir),spawnFailures.set(sessionKey2,0),spawned.push(teamName)}catch(err){let newCount=failures+1;spawnFailures.set(sessionKey2,newCount);let message=err instanceof Error?err.message:String(err);deps.warn(`[inbox-watcher] Failed to spawn team-lead for "${teamName}" (attempt ${newCount}/${MAX_SPAWN_FAILURES}): ${message}`)}}return spawned}function startInboxWatcher(deps=defaultDeps){return setInterval(()=>{checkInboxes(deps).catch((err)=>{let message=err instanceof Error?err.message:String(err);deps.warn(`[inbox-watcher] Poll error: ${message}`)})},getInboxPollIntervalMs())}function stopInboxWatcher(handle){clearInterval(handle)}var defaultDeps,INBOX_POLL_INTERVAL_MS=30000,MAX_SPAWN_FAILURES=3,spawnFailures;var init_inbox_watcher=__esm(()=>{init_claude_native_teams();init_routing_header();init_team_auto_spawn();defaultDeps={listTeamsWithUnreadInbox,isTeamActive:(teamName)=>isTeamActive(teamName),isAgentAlive:(agentName)=>isAgentAlive(agentName),ensureTeamLead:(teamName,workingDir)=>ensureTeamLead(teamName,workingDir),warn:(msg)=>console.warn(msg)};spawnFailures=new Map});var exports_msg={};__export(exports_msg,{suggestRelayLeader:()=>suggestRelayLeader,resolveSenderTeams:()=>resolveSenderTeams,registerSendInboxCommands:()=>registerSendInboxCommands,detectSenderIdentity:()=>detectSenderIdentity,checkSendScope:()=>checkSendScope});import{readFile as readFile5}from"fs/promises";import{homedir as homedir19}from"os";import{join as join29}from"path";async function getRegistry(){if(!_registry)_registry=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));return _registry}async function getTaskService(){if(!_taskService)_taskService=await Promise.resolve().then(() => (init_task_service(),exports_task_service));return _taskService}async function getTeamManager(){if(!_teamManager)_teamManager=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));return _teamManager}async function getMailbox(){if(!_mailbox)_mailbox=await Promise.resolve().then(() => (init_mailbox(),exports_mailbox));return _mailbox}async function detectSenderIdentity(teamName){let envName=process.env.GENIE_AGENT_NAME;if(envName)return envName;let paneId=process.env.TMUX_PANE;if(!paneId)return"cli";let registry=await getRegistry(),worker=typeof registry.findByPane==="function"?await registry.findByPane(paneId):null;if(worker)return worker.role??worker.id;let resolvedTeam=teamName??process.env.GENIE_TEAM;if(resolvedTeam){let memberName=await findMemberByPane(resolvedTeam,paneId);if(memberName)return memberName}return"cli"}async function findMemberByPane(teamName,paneId){let configDir=process.env.CLAUDE_CONFIG_DIR??join29(homedir19(),".claude"),sanitized=teamName.replace(/[^a-zA-Z0-9]/g,"-").toLowerCase(),cfgPath=join29(configDir,"teams",sanitized,"config.json");try{let raw=await readFile5(cfgPath,"utf-8");return(JSON.parse(raw).members??[]).find((m)=>m.tmuxPaneId===paneId)?.name??null}catch{return null}}async function resolveLeaderAlias(recipient,teamContext){if(recipient!=="team-lead")return recipient;let teamName=teamContext??process.env.GENIE_TEAM;if(teamName)return(await getTeamManager()).resolveLeaderName(teamName);return recipient}async function checkSendScope(_repoPath,sender,recipient){if(sender==="cli")return null;let teams=await(await getTeamManager()).listTeams(),senderTeams=resolveSenderTeams(teams,sender);if(senderTeams.length===0)return null;for(let team of senderTeams)if(isRecipientInTeam(team,recipient))return null;let teamNames=senderTeams.map((t)=>t.name).join(", ");return`Scope violation: "${recipient}" is not in sender's team(s): ${teamNames}`}function childReachbackAllowed(child,parent){if(parent.allowChildReachback?.some((prefix)=>child.name.startsWith(prefix)))return!0;return DEFAULT_REACHBACK_PREFIXES.some((prefix)=>child.name.startsWith(prefix))}function walkParentChain(teams,start,visited,out){let current=start,depth=0;while(current.parentTeam&&depth<PARENT_CHAIN_MAX_DEPTH){if(visited.has(current.parentTeam))return;let parent=teams.find((t)=>t.name===current.parentTeam);if(!parent)return;if(!childReachbackAllowed(current,parent))return;out.push(parent),visited.add(parent.name),current=parent,depth++}}function resolveSenderTeams(teams,sender){let direct=teams.filter((t)=>t.members.includes(sender)),visited=new Set(direct.map((t)=>t.name)),result2=[...direct];for(let team of direct)walkParentChain(teams,team,visited,result2);if(teams.some((t)=>t.leader===sender)||sender==="team-lead"){let envTeam=process.env.GENIE_TEAM;if(envTeam){let leaderTeam=teams.find((t)=>t.name===envTeam);if(leaderTeam&&!visited.has(leaderTeam.name))result2.push(leaderTeam),visited.add(leaderTeam.name),walkParentChain(teams,leaderTeam,visited,result2)}}return result2}async function suggestRelayLeader(sender){if(sender==="cli")return null;let teams=await(await getTeamManager()).listTeams(),reachable=resolveSenderTeams(teams,sender);if(reachable.length===0)return null;let target=reachable[0];return{leader:target.leader??target.name,team:target.name}}function isRecipientInTeam(team,recipient){if(team.members.includes(recipient)||recipient===team.leader||recipient==="team-lead")return!0;if(recipient.startsWith(`${team.name}-`)){let roleOnly=recipient.slice(team.name.length+1);if(team.members.includes(roleOnly))return!0}return!1}async function findAgentTeam(_repoPath,agentName){let teams=await(await getTeamManager()).listTeams(),memberTeam=teams.find((t)=>t.members.includes(agentName));if(memberTeam)return memberTeam;if(agentName==="team-lead"||teams.some((t)=>t.leader===agentName)){let envTeam=process.env.GENIE_TEAM;if(envTeam)return teams.find((t)=>t.name===envTeam)??null;let leaderTeam=teams.find((t)=>t.leader===agentName);if(leaderTeam)return leaderTeam}return null}function localActor(name){return{actorType:"local",actorId:name}}async function resolveTeamName2(explicit,repoPath,from){if(explicit)return explicit;let name=(await findAgentTeam(repoPath,from))?.name??process.env.GENIE_TEAM;if(!name)console.error("Error: Could not auto-detect team. Use --team <name>."),process.exit(1);return name}async function handleInbox(agent,options){let ts3=await getTaskService(),resolvedAgent=agent??await detectSenderIdentity(),actor=localActor(resolvedAgent),conversations=await ts3.listConversations(actor);if(options.json){console.log(JSON.stringify(conversations,null,2));return}if(conversations.length===0){console.log(`No conversations for "${resolvedAgent}".`);return}console.log(""),console.log(`INBOX: ${resolvedAgent}`),console.log("\u2500".repeat(60));for(let conv of conversations)await printConversationSummary(ts3,conv)}async function printConversationSummary(ts3,conv){let messages2=await ts3.getMessages(conv.id,{limit:1}),lastMsg=messages2.length>0?messages2[messages2.length-1]:null,name=conv.name??conv.id,type2=conv.type==="dm"?"DM":"Group",linked=conv.linkedEntity?` [${conv.linkedEntity}:${conv.linkedEntityId}]`:"",preview=lastMsg?truncate2(lastMsg.body,50):"(no messages)",time=lastMsg?formatTime(lastMsg.createdAt):"";if(console.log(` ${padRight(name,30)} ${padRight(type2,6)}${linked}`),lastMsg)console.log(` ${time} ${lastMsg.senderId}: ${preview}`);console.log("")}async function handleChatThread(messageId,options){let ts3=await getTaskService(),from=options.from??await detectSenderIdentity(),actor=localActor(from),parentMsgId=Number(messageId),parentMsg=await ts3.getMessage(parentMsgId);if(!parentMsg)console.error(`Error: Message not found: ${messageId}`),process.exit(1);let conv=await ts3.findOrCreateConversation({type:"group",name:options.name??`Thread on message #${parentMsgId}`,parentMessageId:parentMsgId,createdBy:actor,members:[actor]});console.log(`Thread created: ${conv.id}`),console.log(` Parent message: #${parentMsgId} in ${parentMsg.conversationId}`),console.log(` Name: ${conv.name??"(unnamed)"}`)}function printConversationTable(conversations){console.log(` ${padRight("ID",20)} ${padRight("NAME",25)} ${padRight("TYPE",8)} ${padRight("LINKED",20)} UPDATED`),console.log(` ${"\u2500".repeat(80)}`);for(let c of conversations){let name=truncate2(c.name??"(unnamed)",23),linked=c.linkedEntity?`${c.linkedEntity}:${c.linkedEntityId}`:"-",updated=formatTime(c.updatedAt);console.log(` ${padRight(c.id,20)} ${padRight(name,25)} ${padRight(c.type,8)} ${padRight(linked,20)} ${updated}`)}console.log(`
|
|
1173
1173
|
${conversations.length} conversation${conversations.length===1?"":"s"}`)}async function handleChatList(options){let ts3=await getTaskService(),from=options.from??await detectSenderIdentity(),actor=localActor(from),conversations=await ts3.listConversations(actor);if(options.type)conversations=conversations.filter((c)=>c.type===options.type);if(options.linked)conversations=conversations.filter((c)=>c.linkedEntity===options.linked);if(options.json){console.log(JSON.stringify(conversations,null,2));return}if(conversations.length===0){console.log("No conversations found.");return}printConversationTable(conversations)}async function handleChatRead(conversationId,options){let ts3=await getTaskService(),conv=await ts3.getConversation(conversationId);if(!conv)console.error(`Error: Conversation not found: ${conversationId}`),process.exit(1);let messages2=await ts3.getMessages(conversationId,{since:options.since,limit:Number(options.limit)||50});if(options.json){console.log(JSON.stringify(messages2,null,2));return}let name=conv.name??conversationId;if(messages2.length===0){console.log(`No messages in "${name}".`);return}console.log(""),console.log(`CHAT: ${name}`),console.log("\u2500".repeat(60));for(let msg of messages2){let time=formatTime(msg.createdAt),reply=msg.replyToId?` (reply to #${msg.replyToId})`:"";console.log(` [${time}] ${msg.senderId}: ${msg.body}${reply}`)}console.log("")}async function discoverCurrentTeam(nativeTeams,from,explicitTeam){if(explicitTeam)return explicitTeam;let discovered=await nativeTeams.discoverTeamName().catch(()=>null);if(discovered)return discovered;return(await(await getRegistry()).list()).find((w)=>w.role===from||w.id===from||w.customName===from)?.team??null}async function deliverToTeam(nativeTeams,team,recipient,msg){let nativeName=await nativeTeams.resolveNativeMemberName(team,recipient);if(!nativeName)return!1;return await nativeTeams.writeNativeInbox(team,nativeName,msg),!0}async function bridgeToNativeInbox(from,recipient,body,explicitTeam){if(from===recipient)return!1;let nativeTeams=await Promise.resolve().then(() => (init_claude_native_teams(),exports_claude_native_teams)),nativeMsg={from,text:body,summary:body.length>50?`${body.substring(0,50)}...`:body,timestamp:new Date().toISOString(),color:"blue",read:!1},currentTeam=await discoverCurrentTeam(nativeTeams,from,explicitTeam);if(currentTeam&&await deliverToTeam(nativeTeams,currentTeam,recipient,nativeMsg))return!0;let allTeams=await nativeTeams.listTeams().catch(()=>[]);for(let team of allTeams){if(team===currentTeam)continue;if(await deliverToTeam(nativeTeams,team,recipient,nativeMsg))return!0}return console.warn(`[genie send] Native inbox bridge: could not find native team member for "${recipient}"`),!1}function quoteForShell(value){return`'${value.replace(/'/g,"'\\''")}'`}async function printBridgeSuggestion(sender,recipient,body,scopeError){let suggestion=await suggestRelayLeader(sender);if(console.error(`Scope violation: ${scopeError}`),suggestion)console.error(`Nearest reachable leader: ${suggestion.leader}@${suggestion.team}`),console.error("Relay manually via:"),console.error(` genie send ${quoteForShell(`[relay to ${recipient}] ${body}`)} --to ${suggestion.leader} --team ${suggestion.team}`);else console.error("No reachable leader found \u2014 sender is not bound to any team.")}async function handleSend(body,options){let ts3=await getTaskService(),mailbox=await getMailbox(),repoPath=process.cwd(),from=options.from??await detectSenderIdentity(options.team),to=await resolveLeaderAlias(options.to,options.team),scopeError=await checkSendScope(repoPath,from,to);if(scopeError){if(options.bridge){await printBridgeSuggestion(from,to,body,scopeError);return}console.error(`Error: ${scopeError}`),process.exit(1)}let senderActor=localActor(from),recipientActor=localActor(to),conv=await ts3.findOrCreateConversation({type:"dm",members:[senderActor,recipientActor],createdBy:senderActor});await ts3.addMember(conv.id,senderActor),await ts3.addMember(conv.id,recipientActor);let mailboxMessage=await mailbox.send(repoPath,from,to,body),msg=await ts3.sendMessage(conv.id,senderActor,body);try{let{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(repoPath,`genie.msg.${to}`,{kind:"message",agent:from,direction:"out",peer:to,text:body,data:{messageId:msg.id,conversationId:conv.id,from,to},source:"mailbox"})}catch{}if(await bridgeToNativeInbox(from,to,body,options.team).catch((err)=>{let reason=err instanceof Error?err.message:String(err);return console.warn(`[genie send] Native inbox bridge failed: ${reason}`),!1}))await mailbox.markDelivered(repoPath,to,mailboxMessage.id).catch(()=>{});console.log(`Message sent to "${to}".`),console.log(` ID: ${msg.id}`),console.log(` Conversation: ${conv.id}`)}function registerSendInboxCommands(program2){program2.command("send <body>").description("Send a direct message to an agent (PG-backed)").option("--to <agent>","Recipient agent name (default: team leader)","team-lead").option("--from <sender>","Sender ID (auto-detected from context)").option("--team <name>","Explicit team context for sender/recipient resolution").option("--bridge","On scope violation, print an advisory + relay command instead of failing (exit 0)").addHelpText("after",`
|
|
1174
1174
|
Examples:
|
|
1175
1175
|
genie send 'start task #3' --to engineer # Message a specific agent
|
|
@@ -1177,7 +1177,7 @@ Examples:
|
|
|
1177
1177
|
genie send 'deploy ready' --team my-feature # Message within team context
|
|
1178
1178
|
genie send 'hi felipe' --to felipe-3 --bridge # Scope violation \u2192 print relay hint instead of erroring`).action(async(body,options)=>{try{await handleSend(body,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),program2.command("broadcast <body>").description("Send a message to your team conversation (PG-backed)").option("--from <sender>","Sender ID (auto-detected from context)").option("--team <name>","Team name (auto-detected from context)").action(async(body,options)=>{try{let ts3=await getTaskService(),repoPath=process.cwd(),from=options.from??await detectSenderIdentity(),teamName=await resolveTeamName2(options.team,repoPath,from),senderActor=localActor(from),conv=await ts3.findOrCreateConversation({type:"group",name:`Team: ${teamName}`,linkedEntity:"team",linkedEntityId:teamName,createdBy:senderActor,members:[senderActor]});await ts3.addMember(conv.id,senderActor);let msg=await ts3.sendMessage(conv.id,senderActor,body);try{let{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(repoPath,"genie.msg.broadcast",{kind:"message",agent:from,direction:"out",peer:teamName,text:body,data:{messageId:msg.id,conversationId:conv.id,from,team:teamName},source:"mailbox"})}catch{}console.log(`Broadcast sent to team "${teamName}".`),console.log(` Message ID: ${msg.id}`),console.log(` Conversation: ${conv.id}`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}});let inbox2=program2.command("inbox").description("Inbox management \u2014 list messages or watch for new ones");inbox2.command("list [agent]",{isDefault:!0}).description("List conversations with recent messages (PG-backed)").option("--json","Output as JSON").action(async(agent,options)=>{try{await handleInbox(agent,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),inbox2.command("watch").description("Run inbox watcher in foreground (Ctrl+C to stop)").action(async()=>{let{checkInboxes:checkInboxes2,getInboxPollIntervalMs:getInboxPollIntervalMs2,startInboxWatcher:startInboxWatcher2,stopInboxWatcher:stopInboxWatcher2}=await Promise.resolve().then(() => (init_inbox_watcher(),exports_inbox_watcher)),pollMs=getInboxPollIntervalMs2();if(pollMs===0)console.log("Inbox watcher is disabled (GENIE_INBOX_POLL_MS=0)"),process.exit(0);console.log(`Inbox watcher starting (poll every ${pollMs/1000}s)`),console.log(`Press Ctrl+C to stop.
|
|
1179
1179
|
`);let initial=await checkInboxes2();if(initial.length>0)console.log(`[inbox-watcher] Spawned team-leads for: ${initial.join(", ")}`);let handle=startInboxWatcher2({listTeamsWithUnreadInbox:(await Promise.resolve().then(() => (init_claude_native_teams(),exports_claude_native_teams))).listTeamsWithUnreadInbox,isTeamActive:async(teamName)=>{let{isTeamActive:isTeamActive2}=await Promise.resolve().then(() => (init_team_auto_spawn(),exports_team_auto_spawn));return isTeamActive2(teamName)},isAgentAlive:async(agentName)=>{let{isAgentAlive:isAgentAlive2}=await Promise.resolve().then(() => (init_team_auto_spawn(),exports_team_auto_spawn));return isAgentAlive2(agentName)},ensureTeamLead:async(teamName,workingDir)=>{let{ensureTeamLead:ensureTeamLead2}=await Promise.resolve().then(() => (init_team_auto_spawn(),exports_team_auto_spawn)),result2=await ensureTeamLead2(teamName,workingDir);return console.log(`[inbox-watcher] Spawned team-lead for "${teamName}" in ${workingDir}`),result2},warn:(msg)=>console.log(msg)}),shutdown2=()=>{console.log(`
|
|
1180
|
-
Stopping inbox watcher...`),stopInboxWatcher2(handle),process.exit(0)};process.on("SIGINT",shutdown2),process.on("SIGTERM",shutdown2),await new Promise(()=>{})});let chat=program2.command("chat").description("Conversation management (PG-backed)");chat.command("send <conversationId> <message>").description("Send a message to a specific conversation").option("--reply-to <msgId>","Reply to a specific message ID").option("--from <sender>","Sender ID (auto-detected)").action(async(conversationId,message,options)=>{try{let ts3=await getTaskService(),from=options.from??await detectSenderIdentity(),actor=localActor(from),replyTo=options.replyTo?Number(options.replyTo):void 0,msg=await ts3.sendMessage(conversationId,actor,message,replyTo);console.log(`Message #${msg.id} sent to conversation ${conversationId}.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),chat.command("thread <messageId>").description("Create a threaded sub-conversation from a message").option("--name <name>","Thread name").option("--from <sender>","Sender ID (auto-detected)").action(async(messageId,options)=>{try{await handleChatThread(messageId,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),chat.command("list").description("List conversations with filters").option("--type <type>","Filter by type: dm, group").option("--linked <entity>","Filter by linked entity: task, team").option("--json","Output as JSON").option("--from <sender>","Actor ID (auto-detected)").action(async(options)=>{try{await handleChatList(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),chat.command("read <conversationId>").description("Read messages in a conversation").option("--since <timestamp>","Show messages since timestamp").option("--limit <n>","Limit number of messages","50").option("--json","Output as JSON").action(async(conversationId,options)=>{try{await handleChatRead(conversationId,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}var _registry,_taskService,_teamManager,_mailbox,PARENT_CHAIN_MAX_DEPTH=3,DEFAULT_REACHBACK_PREFIXES;var init_msg=__esm(()=>{init_term_format();DEFAULT_REACHBACK_PREFIXES=["council-"]});function buildLayoutCommand(windowTarget,mode="mosaic"){return`select-layout -t '${windowTarget}' ${mode==="vertical"?"even-horizontal":"tiled"}`}function resolveLayoutMode(layoutFlag){if(layoutFlag==="vertical")return"vertical";return"mosaic"}var exports_claude_logs={};__export(exports_claude_logs,{projectPathToHash:()=>projectPathToHash,parseLogEntry:()=>parseLogEntry,listSessions:()=>listSessions2,findClaudeProjectDir:()=>findClaudeProjectDir,findActiveSession:()=>findActiveSession,claudeTranscriptProvider:()=>claudeTranscriptProvider,claudeEntryToTranscript:()=>claudeEntryToTranscript});import{access,readFile as
|
|
1180
|
+
Stopping inbox watcher...`),stopInboxWatcher2(handle),process.exit(0)};process.on("SIGINT",shutdown2),process.on("SIGTERM",shutdown2),await new Promise(()=>{})});let chat=program2.command("chat").description("Conversation management (PG-backed)");chat.command("send <conversationId> <message>").description("Send a message to a specific conversation").option("--reply-to <msgId>","Reply to a specific message ID").option("--from <sender>","Sender ID (auto-detected)").action(async(conversationId,message,options)=>{try{let ts3=await getTaskService(),from=options.from??await detectSenderIdentity(),actor=localActor(from),replyTo=options.replyTo?Number(options.replyTo):void 0,msg=await ts3.sendMessage(conversationId,actor,message,replyTo);console.log(`Message #${msg.id} sent to conversation ${conversationId}.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),chat.command("thread <messageId>").description("Create a threaded sub-conversation from a message").option("--name <name>","Thread name").option("--from <sender>","Sender ID (auto-detected)").action(async(messageId,options)=>{try{await handleChatThread(messageId,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),chat.command("list").description("List conversations with filters").option("--type <type>","Filter by type: dm, group").option("--linked <entity>","Filter by linked entity: task, team").option("--json","Output as JSON").option("--from <sender>","Actor ID (auto-detected)").action(async(options)=>{try{await handleChatList(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),chat.command("read <conversationId>").description("Read messages in a conversation").option("--since <timestamp>","Show messages since timestamp").option("--limit <n>","Limit number of messages","50").option("--json","Output as JSON").action(async(conversationId,options)=>{try{await handleChatRead(conversationId,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}var _registry,_taskService,_teamManager,_mailbox,PARENT_CHAIN_MAX_DEPTH=3,DEFAULT_REACHBACK_PREFIXES;var init_msg=__esm(()=>{init_term_format();DEFAULT_REACHBACK_PREFIXES=["council-"]});function buildLayoutCommand(windowTarget,mode="mosaic"){return`select-layout -t '${windowTarget}' ${mode==="vertical"?"even-horizontal":"tiled"}`}function resolveLayoutMode(layoutFlag){if(layoutFlag==="vertical")return"vertical";return"mosaic"}var exports_claude_logs={};__export(exports_claude_logs,{projectPathToHash:()=>projectPathToHash,parseLogEntry:()=>parseLogEntry,listSessions:()=>listSessions2,findClaudeProjectDir:()=>findClaudeProjectDir,findActiveSession:()=>findActiveSession,claudeTranscriptProvider:()=>claudeTranscriptProvider,claudeEntryToTranscript:()=>claudeEntryToTranscript});import{access,readFile as readFile6,readdir as readdir3,stat as stat2}from"fs/promises";import{join as join30}from"path";function projectPathToHash(projectPath){let normalized=projectPath.replace(/\/+$/,"");if(!normalized)normalized="/";return normalized.replace(/[/.]/g,"-")}function getClaudeDir(){return join30(process.env.HOME||"",".claude")}function getProjectsDir(claudeDir){return join30(claudeDir||getClaudeDir(),"projects")}async function findClaudeProjectDir(projectPath,claudeDir){let projectsDir=getProjectsDir(claudeDir),expectedHash=projectPathToHash(projectPath);try{await access(projectsDir);let projectDir=join30(projectsDir,expectedHash);return await access(projectDir),projectDir}catch{return null}}async function listSessions2(projectDir){let indexPath=join30(projectDir,"sessions-index.json");try{let content=await readFile6(indexPath,"utf-8");return JSON.parse(content).entries}catch{return await scanForSessions(projectDir)}}async function scanForSessions(projectDir){let sessions=[];try{let entries=await readdir3(projectDir);for(let entry of entries)if(entry.endsWith(".jsonl")&&!entry.startsWith(".")){let filePath=join30(projectDir,entry),stats=await stat2(filePath),sessionId=entry.replace(".jsonl","");sessions.push({sessionId,fullPath:filePath,fileMtime:stats.mtimeMs,messageCount:0,created:stats.birthtime.toISOString(),modified:stats.mtime.toISOString(),projectPath:"",isSidechain:!1})}}catch{}return sessions}async function findActiveSession(projectDir){let sessions=await listSessions2(projectDir);if(sessions.length===0)return null;return sessions.sort((a,b2)=>{let timeA=new Date(a.modified).getTime();return new Date(b2.modified).getTime()-timeA}),sessions[0]}async function findSessionById(projectDir,sessionId){return(await listSessions2(projectDir)).find((session)=>session.sessionId===sessionId)??null}function extractToolCalls(content){let toolCalls=[];for(let item of content)if(item.type==="tool_use")toolCalls.push({id:item.id||"",name:item.name||"",input:item.input||{}});return toolCalls.length>0?toolCalls:void 0}function populateAssistantFields(entry,raw){if(raw.type!=="assistant")return;if(Array.isArray(raw.message.content))entry.toolCalls=extractToolCalls(raw.message.content);if(raw.message.model)entry.model=raw.message.model;if(raw.message.usage)entry.usage=raw.message.usage}function parseLogEntry(line){if(!line||!line.trim())return null;try{let raw=JSON.parse(line);if(!raw.type)return null;let entry={type:raw.type,sessionId:raw.sessionId||"",uuid:raw.uuid||"",parentUuid:raw.parentUuid||null,timestamp:raw.timestamp||"",cwd:raw.cwd||"",gitBranch:raw.gitBranch,version:raw.version,raw};if(raw.message)entry.message={role:raw.message.role,content:raw.message.content},populateAssistantFields(entry,raw);if(raw.data)entry.data=raw.data;return entry}catch{return null}}async function readLogFile(logPath){let entries=[];try{let lines=(await readFile6(logPath,"utf-8")).split(`
|
|
1181
1181
|
`);for(let line of lines){let entry=parseLogEntry(line);if(entry)entries.push(entry)}}catch{}return entries}async function findLogsForWorkspace(workspacePath,claudeDir){let projectDir=await findClaudeProjectDir(workspacePath,claudeDir);if(!projectDir)return null;let session=await findActiveSession(projectDir);if(!session)return null;return{projectDir,session}}async function getLogsForPane(paneWorkdir,claudeDir){let result2=await findLogsForWorkspace(paneWorkdir,claudeDir);if(!result2)return null;return{logPath:result2.session.fullPath,session:result2.session,projectDir:result2.projectDir}}function extractText(content){if(typeof content==="string")return content;if(!Array.isArray(content))return"";let parts=[];for(let item of content)if(typeof item==="string")parts.push(item);else if(item&&typeof item==="object"&&"text"in item)parts.push(String(item.text));return parts.join(" ")}function convertAssistant(entry,base){let results=[],text=entry.message?extractText(entry.message.content):"";if(text)results.push({...base,role:"assistant",timestamp:entry.timestamp,text,usage:entry.usage?{input:entry.usage.input_tokens,output:entry.usage.output_tokens}:void 0});if(entry.toolCalls)for(let tc of entry.toolCalls)results.push({...base,role:"tool_call",timestamp:entry.timestamp,text:`${tc.name}: ${JSON.stringify(tc.input).slice(0,200)}`,toolCall:{id:tc.id,name:tc.name,input:tc.input}});return results}function claudeEntryToTranscript(entry){let base={provider:"claude",model:entry.model,raw:entry.raw};if(entry.type==="user"&&entry.message){let text=extractText(entry.message.content);return text?[{...base,role:"user",timestamp:entry.timestamp,text}]:[]}if(entry.type==="assistant")return convertAssistant(entry,base);if(entry.type==="system"||entry.type==="progress"){let text=entry.message?extractText(entry.message.content):"";return text?[{...base,role:"system",timestamp:entry.timestamp,text}]:[]}return[]}var claudeTranscriptProvider;var init_claude_logs=__esm(()=>{claudeTranscriptProvider={async discoverLogPath(worker){let workspacePath=worker.worktree||worker.repoPath,projectDir=await findClaudeProjectDir(workspacePath);if(!projectDir)return null;if(worker.claudeSessionId){let directPath=join30(projectDir,`${worker.claudeSessionId}.jsonl`);try{return await access(directPath),directPath}catch{let session=await findSessionById(projectDir,worker.claudeSessionId);if(session?.fullPath)return session.fullPath}}return(await getLogsForPane(workspacePath))?.logPath??null},async readEntries(logPath){return(await readLogFile(logPath)).flatMap(claudeEntryToTranscript)}}});class AppPtyProvider{name="app-pty";transport="process";buildSpawnCommand(ctx){let params=spawnContextToParams(ctx),launch=buildClaudeCommand(params);return{...launch,env:{...launch.env,GENIE_APP_PTY:"true"}}}async extractSession(executor){let sessionId=executor.claudeSessionId;if(!sessionId)return null;let{access:access2,readdir:readdir4}=await import("fs/promises"),{join:join31}=await import("path"),claudeDir=join31(process.env.HOME||"",".claude","projects");try{await access2(claudeDir)}catch{return{sessionId}}let jsonlName=`${sessionId}.jsonl`;if(executor.repoPath||executor.worktree){let{projectPathToHash:projectPathToHash2}=await Promise.resolve().then(() => (init_claude_logs(),exports_claude_logs)),path3=executor.repoPath??executor.worktree,hash=projectPathToHash2(path3),candidate=join31(claudeDir,hash,jsonlName);try{return await access2(candidate),{sessionId,logPath:candidate}}catch{}}try{let dirs=await readdir4(claudeDir);for(let dir of dirs){let candidate=join31(claudeDir,dir,jsonlName);try{return await access2(candidate),{sessionId,logPath:candidate}}catch{}}}catch{}return{sessionId}}async detectState(executor){if(!executor.pid)return"terminated";try{process.kill(executor.pid,0);let metadata=executor.metadata;if(metadata.appPtyState&&typeof metadata.appPtyState==="string")return metadata.appPtyState;return"running"}catch{return"terminated"}}async terminate(executor){if(!executor.pid)return;try{process.kill(executor.pid,"SIGTERM"),await new Promise((r)=>setTimeout(r,500))}catch{}try{process.kill(executor.pid,0),process.kill(executor.pid,"SIGKILL")}catch{}}canResume(){return!0}buildResumeCommand(ctx){let params=resumeContextToParams(ctx),launch=buildClaudeCommand(params);return{...launch,env:{...launch.env,GENIE_APP_PTY:"true"}}}async deliverMessage(executorId,message){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),rows=await(await getConnection2())`
|
|
1182
1182
|
SELECT a.custom_name, a.team
|
|
1183
1183
|
FROM executors e
|
|
@@ -1304,7 +1304,7 @@ Stopping inbox watcher...`),stopInboxWatcher2(handle),process.exit(0)};process.o
|
|
|
1304
1304
|
`,await sql`
|
|
1305
1305
|
DELETE FROM task_actors WHERE task_id = ANY(${ids}) AND role = 'assignee'
|
|
1306
1306
|
`,ids.length}var GroupStatusSchema,GroupStateSchema,WishStateSchema,WishStateMismatchError;var init_wish_state=__esm(()=>{init_zod();init_db();GroupStatusSchema=exports_external.enum(["blocked","ready","in_progress","done"]),GroupStateSchema=exports_external.object({status:GroupStatusSchema,assignee:exports_external.string().optional(),dependsOn:exports_external.array(exports_external.string()).default([]),startedAt:exports_external.string().optional(),completedAt:exports_external.string().optional()}),WishStateSchema=exports_external.object({wish:exports_external.string(),groups:exports_external.record(exports_external.string(),GroupStateSchema),createdAt:exports_external.string(),updatedAt:exports_external.string()});WishStateMismatchError=class WishStateMismatchError extends Error{slug;added;removed;changed;constructor(slug,added,removed,changed){let lines=[`Wish "${slug}" group structure has changed since state was created.`];if(added.length>0)lines.push(` + added: ${added.join(", ")}`);if(removed.length>0)lines.push(` - removed: ${removed.join(", ")}`);if(changed.length>0)lines.push(` ~ changed deps: ${changed.join(", ")}`);lines.push(""),lines.push(`Run \`genie reset ${slug}\` to recreate state from the current WISH.md.`);super(lines.join(`
|
|
1307
|
-
`));this.name="WishStateMismatchError",this.slug=slug,this.added=added,this.removed=removed,this.changed=changed}}});var exports_protocol_router_spawn={};__export(exports_protocol_router_spawn,{spawnWorkerFromTemplate:()=>spawnWorkerFromTemplate,injectResumeContext:()=>injectResumeContext,_deps:()=>_deps2});import{exec as exec2}from"child_process";import{readFile as
|
|
1307
|
+
`));this.name="WishStateMismatchError",this.slug=slug,this.added=added,this.removed=removed,this.changed=changed}}});var exports_protocol_router_spawn={};__export(exports_protocol_router_spawn,{spawnWorkerFromTemplate:()=>spawnWorkerFromTemplate,injectResumeContext:()=>injectResumeContext,_deps:()=>_deps2});import{exec as exec2}from"child_process";import{readFile as readFile7}from"fs/promises";import{join as join31}from"path";import{promisify as promisify2}from"util";async function resolveParentSession(_repoPath,team){let teamConfig=await getTeam(team);if(teamConfig?.nativeTeamParentSessionId)return teamConfig.nativeTeamParentSessionId;return await discoverClaudeParentSessionId()??`genie-${team}`}function buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId){let isClaude=template.provider==="claude"||template.provider==="claude-sdk",sessionName=template.role?`${template.team}-${template.role}`:void 0,newSessionId=isClaude&&!resumeSessionId?crypto.randomUUID():void 0,params={provider:template.provider,team:template.team,role:template.role,skill:template.skill,extraArgs:template.extraArgs,sessionId:newSessionId,resume:isClaude?resumeSessionId:void 0,name:sessionName};if(isClaude)params.nativeTeam={enabled:!0,parentSessionId,color:spawnColor,agentType:template.role??"general-purpose",agentName:template.role};return params}function buildFullCommand(launch){if(launch.env&&Object.keys(launch.env).length>0)return`env ${Object.entries(launch.env).map(([k,v])=>`${k}=${v}`).join(" ")} ${launch.command}`;return launch.command}async function generateWorkerId(team,role){let base=role?`${team}-${role}`:team;return(await list()).some((w)=>w.id===base)?`${base}-${crypto.randomUUID().slice(0,8)}`:base}async function terminateActiveExecutorIfRunning(agentId){let currentExec=await getCurrentExecutor(agentId);if(!currentExec||currentExec.state==="terminated"||currentExec.state==="done")return;let providerImpl=getProvider(currentExec.provider);if(providerImpl)try{await providerImpl.terminate(currentExec)}catch{}await terminateActiveExecutor(agentId)}async function capturePanePid(paneId){try{let{stdout:pidOut}=await execAsync(genieTmuxCmd(`display -t '${paneId}' -p '#{pane_pid}'`)),parsed=Number.parseInt(pidOut.trim(),10);return parsed>0?parsed:null}catch{return null}}function resolveExecutorTransport(provider){if(provider==="codex")return"api";if(provider==="claude-sdk")return"process";return"tmux"}async function createExecutorForAutoSpawn(template,paneId,session,repoPath,effectiveSessionId,spawnColor,teamWindow){let agentName=template.role??"worker",agentIdentity=await findOrCreateAgent(agentName,template.team,template.role);await(await getConnection()).begin(async(tx)=>{await tx`SELECT pg_advisory_xact_lock(hashtext(${agentIdentity.id}))`,await terminateActiveExecutorIfRunning(agentIdentity.id);let pid=await capturePanePid(paneId),executor=await createExecutor(agentIdentity.id,template.provider,resolveExecutorTransport(template.provider),{pid,tmuxSession:session,tmuxPaneId:paneId,tmuxWindow:teamWindow?.windowName??null,tmuxWindowId:teamWindow?.windowId??null,claudeSessionId:effectiveSessionId??null,state:"spawning",repoPath,paneColor:spawnColor??null});await setCurrentExecutor(agentIdentity.id,executor.id)})}async function spawnPaneInSession(session,team,repoPath,fullCommand){let teamWindow=null;try{teamWindow=await ensureTeamWindow(session,team,repoPath)}catch{}let splitTarget=teamWindow?`-t '${teamWindow.windowId}'`:"",escapedCmd=fullCommand.replace(/'/g,"'\\''"),{stdout}=await execAsync(genieTmuxCmd(`split-window -d ${splitTarget} -P -F '#{pane_id}' '${escapedCmd}'`)),paneId=stdout.trim(),layoutTarget=`${session}:${teamWindow?.windowName??""}`;if(!teamWindow){let wins=await listWindows(session);layoutTarget=wins[0]?wins[0].id:`${session}:`}try{await execAsync(genieTmuxCmd(buildLayoutCommand(layoutTarget,resolveLayoutMode())))}catch{}return{paneId,teamWindow}}async function registerNativeTeamMember(team,agentName,template,paneId,repoPath,spawnColor,resumeSessionId){let now=new Date().toISOString();await registerNativeMember(team,{agentName,agentType:template.role??"general-purpose",color:spawnColor??"blue",tmuxPaneId:paneId,cwd:repoPath});let leaderInboxTarget;try{let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));leaderInboxTarget=await resolveLeaderName2(team)}catch{leaderInboxTarget=team}try{await _deps2.writeNativeInbox(team,leaderInboxTarget,{from:agentName,text:`Worker ${agentName} (${template.provider}) auto-spawned${resumeSessionId?" with --resume":""}. Ready for tasks.`,summary:`${agentName} auto-spawned`,timestamp:now,color:spawnColor??"blue",read:!1})}catch(err){let msg=err instanceof Error?err.message:String(err);console.warn(`[protocol-router] Native inbox write failed for team="${team}" target="${leaderInboxTarget}": ${msg}`)}}async function tryAutoBrain(workerId,repoPath){try{let brain=await import("@khal-os/brain");if(brain.autoBrain)await brain.autoBrain({agentId:workerId,workdir:repoPath})}catch{}}async function spawnWorkerFromTemplate(template,resumeSessionId){let repoPath=template.cwd??process.cwd(),team=template.team,parentSessionId=await resolveParentSession(repoPath,team);await ensureNativeTeam(team,`Genie team: ${team}`,parentSessionId);let spawnColor=await assignColor(team),params=buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId),launch=buildLaunchCommand(validateSpawnParams(params)),fullCommand=buildFullCommand(launch),workerId=await generateWorkerId(team,template.role),session=(await getTeam(team))?.tmuxSessionName??await resolveRepoSession(repoPath)??await getCurrentSessionName()??team,{paneId,teamWindow}=await spawnPaneInSession(session,team,repoPath,fullCommand),now=new Date().toISOString(),agentName=template.role??"worker",isClaude=template.provider==="claude"||template.provider==="claude-sdk",effectiveSessionId=resumeSessionId??params.sessionId,workerEntry={id:workerId,paneId,session,provider:template.provider,transport:"tmux",role:template.role,skill:template.skill,team,worktree:null,startedAt:now,state:"spawning",lastStateChange:now,repoPath,claudeSessionId:effectiveSessionId,nativeTeamEnabled:isClaude,nativeAgentId:`${agentName}@${team}`,nativeColor:spawnColor,parentSessionId,window:teamWindow?.windowName,windowName:teamWindow?.windowName,windowId:teamWindow?.windowId};await register(workerEntry);try{await createExecutorForAutoSpawn(template,paneId,session,repoPath,effectiveSessionId,spawnColor,teamWindow)}catch{}if(isClaude)await registerNativeTeamMember(team,agentName,template,paneId,repoPath,spawnColor,resumeSessionId);if(spawnColor)await applyPaneColor(paneId,spawnColor,teamWindow?.windowId);return await injectResumeContext(repoPath,workerId,agentName,team),await tryAutoBrain(workerId,repoPath),{worker:workerEntry,paneId,workerId}}function extractGroupSection(content,groupName){let escaped=groupName.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),pattern=new RegExp(`^### Group ${escaped}:`,"m"),match=content.match(pattern);if(!match||match.index===void 0)return null;let start=match.index,nextBoundary=content.slice(start).slice(1).search(/^### Group \d|^---$/m),end=nextBoundary!==-1?start+1+nextBoundary:content.length;return content.slice(start,end).trim()}async function getRecentGitLog(repoPath,count=3){try{let{stdout}=await execAsync(`git -C '${repoPath}' log --oneline -${count} 2>/dev/null`);return stdout.trim()}catch{return""}}async function getGitStatus(repoPath){try{let{stdout}=await execAsync(`git -C '${repoPath}' status --short 2>/dev/null`);return stdout.trim()}catch{return""}}async function injectResumeContext(repoPath,workerId,agentName,_team){try{let match=await _deps2.findAnyGroupByAssignee(workerId,repoPath)??await _deps2.findAnyGroupByAssignee(agentName,repoPath);if(!match)return;let{slug,groupName,group}=match,wishPath=join31(repoPath,".genie","wishes",slug,"WISH.md"),groupSection="";try{let wishContent=await readFile7(wishPath,"utf-8");groupSection=extractGroupSection(wishContent,groupName)??""}catch{}let gitLog=await getRecentGitLog(repoPath),gitStatus=await getGitStatus(repoPath),resumePrompt=[`RESUME CONTEXT: You were working on wish "${slug}", group "${groupName}".`,`Status: ${group.status}. Started at: ${group.startedAt??"unknown"}.`,`Wish file: .genie/wishes/${slug}/WISH.md`,"",groupSection?`Group section:
|
|
1308
1308
|
${groupSection}`:"","",gitLog?`Last git log:
|
|
1309
1309
|
${gitLog}`:"","",gitStatus?`Uncommitted changes:
|
|
1310
1310
|
${gitStatus}`:"","","Pick up where you left off. Read the wish file for full context."].filter(Boolean).join(`
|
|
@@ -1617,8 +1617,8 @@ Use a different --role name for a second worker, e.g.: --role ${role}-2`),proces
|
|
|
1617
1617
|
`).length===0)return slice}return uuid}async function resolveSpawnIdentity(name,team,uuidFactory=()=>crypto.randomUUID(),isAliveFn=(agent)=>resolveWorkerLivenessByTransport(agent)){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),rows=await(await getConnection2())`
|
|
1618
1618
|
SELECT id, pane_id, team FROM agents WHERE id = ${name} LIMIT 1
|
|
1619
1619
|
`;if(rows.length===0)return{kind:"canonical",workerId:name,sessionUuid:uuidFactory()};let existing=rows[0];if(existing.team!==null&&existing.team!==team){let sessionUuid2=uuidFactory(),shortId2=await pickParallelShortId(name,team,sessionUuid2);return{kind:"parallel",workerId:`${name}-${shortId2}`,sessionUuid:sessionUuid2,canonicalId:name}}if(!(existing.pane_id?await isAliveFn({id:existing.id,paneId:existing.pane_id}):!1))return{kind:"canonical",workerId:name,sessionUuid:uuidFactory()};let sessionUuid=uuidFactory(),shortId=await pickParallelShortId(name,team,sessionUuid);return{kind:"parallel",workerId:`${name}-${shortId}`,sessionUuid,canonicalId:name}}async function resolveTeamAndResume(effectiveRole,options,agent){let teamWasExplicit=Boolean(options.team),team=await resolveTeamName3({explicitTeam:options.team,entryTeam:agent.entry?.team});if(!team){let candidates=await findTeamsContainingAgent(effectiveRole);if(candidates.length===1)team=candidates[0];else if(candidates.length>1)return console.error(`Error: agent "${effectiveRole}" is a member of multiple teams (${candidates.join(", ")}). Pass --team <name> to disambiguate.`),process.exit(1)}if(!team){if(await get2(effectiveRole))team=sanitizeTeamName(effectiveRole)}if(!team)return console.error(`Error: --team is required for agent "${effectiveRole}" (or set GENIE_TEAM, run inside a genie session, or register the agent in a team config).`),process.exit(1);let deadResumable=await findDeadResumable(team,effectiveRole);if(deadResumable)return console.log(`Resuming existing session for "${effectiveRole}" (session: ${deadResumable.claudeSessionId?.slice(0,8)}...)`),await resumeAgent(deadResumable),{team,teamWasExplicit,resumed:deadResumable.id};return{team,teamWasExplicit}}async function handleWorkerSpawn(name,options){let effectiveRole=options.role??name,agent=await resolveAgentForSpawn(name,options),{team,teamWasExplicit,resumed}=await resolveTeamAndResume(effectiveRole,options,agent);if(resumed)return resumed;let explicitRole=options.role!==void 0&&options.role!==name,identity=null;if(!explicitRole)identity=await resolveSpawnIdentity(name,team),effectiveRole=identity.workerId;else await rejectDuplicateRole(team,effectiveRole);let teamConfig=await getTeam(team);if(teamConfig?.worktreePath&&!agent.entry?.dir)agent={...agent,repoPath:teamConfig.worktreePath};let{params,parentSessionId,spawnColor}=await buildSpawnParams2(effectiveRole,team,options,agent,identity?.sessionUuid);if(!params.name)params.name=`${params.team}-${effectiveRole}`;let validated=validateSpawnParams(params),launch=buildLaunchCommand(validated),layoutMode=resolveLayoutMode(options.layout),workerId=identity?.workerId??await generateWorkerId2(validated.team,effectiveRole),insideTmux=Boolean(process.env.TMUX||options.session),nt=validated.nativeTeam,now=new Date().toISOString(),agentName=nt?.agentName??effectiveRole,agentIdentity=await findOrCreateAgent(agentName,team,effectiveRole);await terminateActiveExecutorWithCleanup(agentIdentity.id);let executorId=crypto.randomUUID(),otelRelayActive=await maybeStartOtelRelay(nt,validated,insideTmux),fullCommand=prependEnvVars(launch.command,launch.env),ctx={workerId,validated,launch,layoutMode,fullCommand,agentName,spawnColor,parentSessionId,claudeSessionId:validated.sessionId,otelRelayActive,now,transport:insideTmux?"tmux":"inline",extraArgs:options.extraArgs,cwd:agent.repoPath,spawnIntoCurrentWindow:!teamWasExplicit&&insideTmux&&!options.session,sessionOverride:options.session,autoResume:options.autoResume,agentIdentityId:agentIdentity.id,executorId};return recordAuditEvent("worker",workerId,"spawn",getActor(),{name,team:validated.team,provider:validated.provider}).catch(()=>{}),await dispatchSpawn(ctx,validated,options,agent,insideTmux)}async function cleanupWorkerNativeTeam(w){if(!w.team||!w.nativeAgentId)return;let agentName=w.nativeAgentId.split("@")[0];await clearNativeInbox(w.team,agentName).catch(()=>{}),await unregisterNativeMember(w.team,agentName).catch(()=>{})}function killWorkerPane(w){try{let{execSync:execSync9}=__require("child_process"),currentPane=execSync9(genieTmuxCmd("display-message -p '#{pane_id}'"),{encoding:"utf-8"}).trim();if(w.paneId&&/^(%\d+|inline)$/.test(w.paneId)&&w.paneId!==currentPane)execSync9(genieTmuxCmd(`kill-pane -t ${w.paneId}`),{stdio:"ignore"});else if(w.paneId===currentPane)console.log(" (skipped pane kill \u2014 would kill current session)")}catch{}}function cleanupRelayFiles(id){try{let{join:join32}=__require("path"),{homedir:homedir20}=__require("os"),{unlinkSync:unlinkSync8}=__require("fs"),relayDir=join32(homedir20(),".genie","relay");for(let suffix of["-pane","-meta"])try{unlinkSync8(join32(relayDir,`${id}${suffix}`))}catch{}}catch{}}async function resolveWorkerByName(name){let exact=await get(name);if(exact)return exact;let workers=await list(),byRole=workers.filter((w)=>w.role===name);if(byRole.length===1)return byRole[0];if(byRole.length>1){console.error(`Multiple agents with role "${name}". Specify full ID:`);for(let w of byRole)console.error(` ${w.id} (team: ${w.team})`);process.exit(1)}let bySuffix=workers.filter((w)=>w.id.endsWith(`-${name}`));if(bySuffix.length===1)return bySuffix[0];if(bySuffix.length>1){console.error(`Multiple agents matching "${name}". Specify full ID:`);for(let w of bySuffix)console.error(` ${w.id}`);process.exit(1)}console.error(`Agent "${name}" not found.`),console.error(" Run `genie agent list` to see agents."),process.exit(1)}async function handleWorkerKill(name){let w=await resolveWorkerByName(name);killWorkerPane(w),cleanupRelayFiles(w.id),await cleanupWorkerNativeTeam(w),await unregister(w.id),console.log(`Agent "${w.id}" killed and unregistered (template preserved).`),recordAuditEvent("worker",w.id,"kill",getActor(),{name}).catch(()=>{})}async function handleWorkerStop(name){let w=await resolveWorkerByName(name);if(w.state==="suspended"){console.log(`Agent "${w.id}" is already stopped.`);return}let{suspendWorker:suspendWorker2}=await Promise.resolve().then(() => (init_idle_timeout(),exports_idle_timeout));if(await suspendWorker2(w.id)){if(console.log(`Agent "${w.id}" stopped.`),w.claudeSessionId)console.log(` Session preserved: ${w.claudeSessionId}`);console.log(` Send a message to auto-resume: genie send '...' --to ${w.id}`),recordAuditEvent("worker",w.id,"stop",getActor(),{name}).catch(()=>{})}else console.error(`Failed to stop agent "${w.id}".`),process.exit(1)}async function isResumeEligible(w){if(!w.claudeSessionId)return!1;if(w.state==="done")return!1;let paneAlive=await isPaneAlive(w.paneId);if((w.state==="suspended"||w.state==="error")&&!paneAlive)return!0;if(!paneAlive&&(w.state==="working"||w.state==="idle"||w.state==="spawning"))return!0;return!1}async function resumeAllAgents(opts={}){let workers=await list(),toResume=[];for(let w of workers)if(await isResumeEligible(w))toResume.push(w);if(toResume.length===0){console.log("No eligible agents to resume.");return}console.log(`Resuming ${toResume.length} agent(s)...`);for(let w of toResume)try{await resumeAgent(w,opts)}catch(err){console.error(` Failed to resume "${w.id}": ${err instanceof Error?err.message:err}`)}}async function handleWorkerResume(name,options){let resumeOpts={resetAttempts:!options.noResetAttempts};if(options.all)return resumeAllAgents(resumeOpts);if(!name)console.error("Error: provide an agent name, or use --all to resume all eligible agents."),process.exit(1);let w=await resolveWorkerByName(name);if(!w.claudeSessionId)console.error(`Error: Agent "${w.id}" has no Claude session ID \u2014 cannot resume.`),console.error(" Only agents spawned with the Claude provider have resumable sessions."),process.exit(1);if(await isPaneAlive(w.paneId)){console.log(`Agent "${w.id}" is already running (pane ${w.paneId} is alive).`);return}await resumeAgent(w,resumeOpts)}async function buildResumeParams(agent,template,resumeSessionId){let agentName=agent.role??agent.id,provider=template?.provider??agent.provider??"claude",team=template?.team??agent.team??await discoverTeamName();if(!team)throw Error(`Cannot resume agent "${agent.id}": no team context (template, agent record, env, or session). Pass --team or set GENIE_TEAM, or run inside a registered tmux session.`);let systemPromptFile,promptMode,dirEntry=await get2(agentName);if(dirEntry?.dir)systemPromptFile=loadIdentity(dirEntry)??void 0,promptMode=dirEntry.promptMode;return{provider,team,role:agentName,skill:template?.skill??agent.skill,extraArgs:template?.extraArgs,resume:resumeSessionId,name:`${team}-${agentName}`,model:dirEntry?.model,systemPromptFile,promptMode}}function formatGroupStatus(name,group,allGroups){let detail=group.status;if(group.completedAt)detail+=` (completed at ${group.completedAt})`;else if(group.startedAt)detail+=` (started at ${group.startedAt})`;if(group.status==="blocked"&&group.dependsOn.length>0){let pending=group.dependsOn.filter((dep)=>allGroups[dep]?.status!=="done");if(pending.length>0)detail+=` (depends on ${pending.join(", ")})`}return`Group ${name}: ${detail}`}async function buildResumeContext(agent){if((agent.role==="team-lead"||agent.team&&agent.role===await resolveTeamLeaderName(agent.team))&&agent.wishSlug)try{let state=await(await Promise.resolve().then(() => (init_wish_state(),exports_wish_state))).getState(agent.wishSlug,agent.repoPath);if(state){let groupLines=Object.entries(state.groups).map(([name,group])=>formatGroupStatus(name,group,state.groups));return["You were resumed after a crash. Here's where you left off:",`Wish: ${state.wish}`,"",...groupLines,"",`Continue from where you left off. Run \`genie status ${state.wish}\` to verify, then dispatch the next wave.`].join(`
|
|
1620
|
-
`)}}catch{}if(agent.team)return"You were resumed. Check your team's current state with `genie status`.";return}async function buildFullResumeParams(agent,template){if(!agent.claudeSessionId)throw new MissingResumeSessionError(agent.id);let params=await buildResumeParams(agent,template,agent.claudeSessionId),resumeContext=await buildResumeContext(agent);if(resumeContext)params.initialPrompt=resumeContext;if(agent.nativeTeamEnabled){let nativeResult=await resolveNativeTeam(params.team,agent.repoPath,{provider:params.provider,role:params.role,color:agent.nativeColor});if(nativeResult.nativeTeam)params.nativeTeam=nativeResult.nativeTeam}return params}async function createResumeExecutor(agent,params,paneId,teamWindow,cwd,spawnColor){let resumeAgentName=agent.role??agent.id,resumeTeam=agent.team??params.team,agentIdentity=await findOrCreateAgent(resumeAgentName,resumeTeam,agent.role);await terminateActiveExecutorWithCleanup(agentIdentity.id);let pid=await capturePanePid2(paneId);await createAndLinkExecutor2(agentIdentity.id,params.provider,resolveExecutorTransport2(params.provider,"tmux"),{pid,tmuxSession:params.team,tmuxPaneId:paneId,tmuxWindow:teamWindow?.windowName??null,tmuxWindowId:teamWindow?.windowId??null,claudeSessionId:agent.claudeSessionId??null,state:"spawning",repoPath:cwd,paneColor:spawnColor})}async function resumeAgent(agent,opts={}){let resetAttempts=opts.resetAttempts!==!1,template=(await listTemplates()).find((t)=>t.id===(agent.role??agent.id));if(resetAttempts)await update(agent.id,{resumeAttempts:0});let params=await buildFullResumeParams(agent,template),validated=validateSpawnParams(params),launch=buildLaunchCommand(validated),fullCommand=prependEnvVars(launch.command,launch.env),now=new Date().toISOString();if(!process.env.TMUX)console.error("Error: resume requires tmux. Start a tmux session first."),process.exit(1);let ctx={workerId:agent.id,validated,launch,layoutMode:resolveLayoutMode(void 0),fullCommand,agentName:agent.role??agent.id,spawnColor:agent.nativeColor??"blue",parentSessionId:agent.parentSessionId??`genie-${params.team}`,claudeSessionId:agent.claudeSessionId,otelRelayActive:!1,now,transport:"tmux",extraArgs:template?.extraArgs,cwd:template?.cwd??agent.repoPath,spawnIntoCurrentWindow:!1,autoResume:agent.autoResume},teamWindow=await resolveSpawnTeamWindow(validated.team,ctx.cwd),paneId;try{paneId=createTmuxPane(ctx,teamWindow)}catch(err){console.error(`Failed to create tmux pane: ${err instanceof Error?err.message:"unknown error"}`),process.exit(1)}if(await createResumeExecutor(agent,validated,paneId,teamWindow,ctx.cwd,ctx.spawnColor),await applySpawnLayout(ctx,teamWindow),await update(agent.id,{paneId,state:"spawning",startedAt:now,lastStateChange:now,suspendedAt:void 0,windowName:teamWindow?.windowName,windowId:teamWindow?.windowId,window:teamWindow?.windowName}),await notifySpawnJoin(ctx,paneId),await injectResumeContext(ctx.cwd??agent.repoPath??process.cwd(),agent.id,agent.role??agent.id,params.team),ctx.spawnColor&&paneId!=="inline")await applyPaneColor(paneId,ctx.spawnColor,teamWindow?.windowId);if(recordAuditEvent("worker",agent.id,"resumed",getActor(),{claudeSessionId:agent.claudeSessionId,team:agent.team}).catch(()=>{}),console.log(`Agent "${agent.id}" resumed.`),console.log(` Session: ${agent.claudeSessionId}`),console.log(` Pane: ${paneId}`),teamWindow)console.log(` Window: ${teamWindow.windowName} (${teamWindow.windowId})`)}async function resolveWorkerLiveness(w){if(/^%\d+$/.test(w.paneId))return{alive:await isPaneAlive(w.paneId),state:w.state};let execState=await getLiveExecutorState(w.id);return{alive:execState!==null,state:execState??w.state}}async function buildWorkerStatusMap(workers){let statusMap=new Map;for(let w of workers){let name=w.role||w.id,{alive,state}=await resolveWorkerLiveness(w);if(alive)statusMap.set(name,{state,team:w.team||"-"});else if(w.state==="suspended"||w.state==="error"){let attempts=w.resumeAttempts??0,max=w.maxResumeAttempts??3,autoStr=w.autoResume===!1?"off":"on";statusMap.set(name,{state:`${w.state} (${attempts}/${max} resumes, auto-resume: ${autoStr})`,team:w.team||"-",resumeAttempts:attempts,maxResumeAttempts:max,autoResume:w.autoResume!==!1})}}return statusMap}async function resolveAgentNamesBySource(source){let executorRegistry=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),agentRegistry=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),executors=await executorRegistry.listExecutors(void 0,source),agentIds=new Set(executors.map((e)=>e.agentId)),agents=await agentRegistry.listAgents({});return new Set(agents.filter((a)=>agentIds.has(a.id)).map((a)=>a.customName??a.role??a.id))}async function handleLsCommand(options){let dirEntries=await ls(),workers=await list(),statusMap=await buildWorkerStatusMap(workers),sourceAgentNames=options.source?await resolveAgentNamesBySource(options.source):void 0,entries=[];for(let entry of dirEntries){let running=statusMap.get(entry.name);entries.push({name:entry.name,dir:entry.dir||"-",status:running?running.state:"offline",team:running?.team||"-",model:entry.model||"-",resumeAttempts:running?.resumeAttempts,maxResumeAttempts:running?.maxResumeAttempts,autoResume:running?.autoResume}),statusMap.delete(entry.name)}for(let[name,info]of statusMap)entries.push({name,dir:"(built-in)",status:info.state,team:info.team,model:"-",resumeAttempts:info.resumeAttempts,maxResumeAttempts:info.maxResumeAttempts,autoResume:info.autoResume});if(sourceAgentNames)entries=entries.filter((e)=>sourceAgentNames.has(e.name));if(options.json){console.log(JSON.stringify(entries,null,2));return}if(entries.length===0){console.log("No agents registered. Use `genie dir add <name> --dir <path>` to register one.");return}console.log(""),console.log(formatLsRow("NAME","DIR","STATUS","TEAM","MODEL")),console.log("-".repeat(106));for(let e of entries)console.log(formatLsRow(e.name,e.dir,e.status,e.team,e.model));console.log("")}function formatLsRow(name,dir,status,team,model){return`${name.padEnd(20).substring(0,20)}${dir.padEnd(30).substring(0,30)}${status.padEnd(44).substring(0,44)}${team.padEnd(12).substring(0,12)}${model}`}var UUID_REGEX;var init_agents=__esm(()=>{init_agent_directory();init_agent_registry();init_audit();init_builtin_agents();init_claude_native_teams();init_codex_config();init_defaults();init_ensure_tmux();init_executor_registry();init_otel_receiver();init_protocol_router_spawn();init_protocol_router();init_provider_adapters();init_registry();init_spawn_command();init_team_manager();init_tmux_wrapper();init_tmux();init_tmux();init_workspace();UUID_REGEX=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i});var exports_codex_logs={};__export(exports_codex_logs,{parseCodexLine:()=>parseCodexLine,extractCodexContent:()=>extractCodexContent,codexTranscriptProvider:()=>codexTranscriptProvider});import{access as access2,readFile as
|
|
1621
|
-
`),firstLine=nlIdx===-1?content:content.slice(0,nlIdx),entry=JSON.parse(firstLine);if(entry.type==="session_meta"&&entry.payload?.cwd)return{cwd:entry.payload.cwd}}catch{}return null}function parseEventMsg(payload,ts3,base){if(payload.type==="user_message"){let text=String(payload.message??"");return text?[{...base,role:"user",timestamp:ts3,text}]:[]}if(payload.type==="agent_message"){let text=String(payload.message??"");return text?[{...base,role:"assistant",timestamp:ts3,text}]:[]}return[]}function parseResponseMessage(payload,ts3,base){let role=payload.role,text=extractCodexContent(payload.content);if(!text)return[];if(role==="user")return[{...base,role:"user",timestamp:ts3,text}];if(role==="developer")return[{...base,role:"system",timestamp:ts3,text}];if(role==="assistant")return[{...base,role:"assistant",timestamp:ts3,text}];return[]}function parseFunctionCall(payload,ts3,base){let name=String(payload.name??payload.type),callId=String(payload.call_id??""),input={};try{input=typeof payload.arguments==="string"?JSON.parse(payload.arguments):{}}catch{input={raw:payload.arguments}}let cmdText=input.command?String(Array.isArray(input.command)?input.command.join(" "):input.command):name;return[{...base,role:"tool_call",timestamp:ts3,text:`${name}: ${cmdText.slice(0,200)}`,toolCall:{id:callId,name,input}}]}function parseWebSearch(payload,ts3,base){let action=payload.action,query2=String(action?.query??"web search");return[{...base,role:"tool_call",timestamp:ts3,text:`web_search: ${query2.slice(0,200)}`,toolCall:{id:"",name:"web_search",input:{query:query2}}}]}function parseResponseItem(payload,ts3,base){if(payload.type==="message")return parseResponseMessage(payload,ts3,base);if(payload.type==="function_call"||payload.type==="shell")return parseFunctionCall(payload,ts3,base);if(payload.type==="function_call_output"){let output=String(payload.output??"").slice(0,500);return[{...base,role:"tool_result",timestamp:ts3,text:output}]}if(payload.type==="web_search_call")return parseWebSearch(payload,ts3,base);return[]}function parseCodexLine(line){if(!line.trim())return[];let raw;try{raw=JSON.parse(line)}catch{return[]}if(!raw.type||!raw.timestamp)return[];let base={provider:"codex",raw};if(!raw.payload||typeof raw.payload!=="object")return[];if(raw.type==="event_msg")return parseEventMsg(raw.payload,raw.timestamp,base);if(raw.type==="response_item")return parseResponseItem(raw.payload,raw.timestamp,base);return[]}function extractCodexContent(content){if(typeof content==="string")return content;if(!Array.isArray(content))return"";let parts=[];for(let item of content)if(typeof item==="string")parts.push(item);else if(item&&typeof item==="object"){if("text"in item)parts.push(String(item.text));else if("input_text"in item)parts.push(String(item.input_text))}return parts.join(" ")}async function readEntries(logPath){let content;try{content=await
|
|
1620
|
+
`)}}catch{}if(agent.team)return"You were resumed. Check your team's current state with `genie status`.";return}async function buildFullResumeParams(agent,template){if(!agent.claudeSessionId)throw new MissingResumeSessionError(agent.id);let params=await buildResumeParams(agent,template,agent.claudeSessionId),resumeContext=await buildResumeContext(agent);if(resumeContext)params.initialPrompt=resumeContext;if(agent.nativeTeamEnabled){let nativeResult=await resolveNativeTeam(params.team,agent.repoPath,{provider:params.provider,role:params.role,color:agent.nativeColor});if(nativeResult.nativeTeam)params.nativeTeam=nativeResult.nativeTeam}return params}async function createResumeExecutor(agent,params,paneId,teamWindow,cwd,spawnColor){let resumeAgentName=agent.role??agent.id,resumeTeam=agent.team??params.team,agentIdentity=await findOrCreateAgent(resumeAgentName,resumeTeam,agent.role);await terminateActiveExecutorWithCleanup(agentIdentity.id);let pid=await capturePanePid2(paneId);await createAndLinkExecutor2(agentIdentity.id,params.provider,resolveExecutorTransport2(params.provider,"tmux"),{pid,tmuxSession:params.team,tmuxPaneId:paneId,tmuxWindow:teamWindow?.windowName??null,tmuxWindowId:teamWindow?.windowId??null,claudeSessionId:agent.claudeSessionId??null,state:"spawning",repoPath:cwd,paneColor:spawnColor})}async function resumeAgent(agent,opts={}){let resetAttempts=opts.resetAttempts!==!1,template=(await listTemplates()).find((t)=>t.id===(agent.role??agent.id));if(resetAttempts)await update(agent.id,{resumeAttempts:0});let params=await buildFullResumeParams(agent,template),validated=validateSpawnParams(params),launch=buildLaunchCommand(validated),fullCommand=prependEnvVars(launch.command,launch.env),now=new Date().toISOString();if(!process.env.TMUX)console.error("Error: resume requires tmux. Start a tmux session first."),process.exit(1);let ctx={workerId:agent.id,validated,launch,layoutMode:resolveLayoutMode(void 0),fullCommand,agentName:agent.role??agent.id,spawnColor:agent.nativeColor??"blue",parentSessionId:agent.parentSessionId??`genie-${params.team}`,claudeSessionId:agent.claudeSessionId,otelRelayActive:!1,now,transport:"tmux",extraArgs:template?.extraArgs,cwd:template?.cwd??agent.repoPath,spawnIntoCurrentWindow:!1,autoResume:agent.autoResume},teamWindow=await resolveSpawnTeamWindow(validated.team,ctx.cwd),paneId;try{paneId=createTmuxPane(ctx,teamWindow)}catch(err){console.error(`Failed to create tmux pane: ${err instanceof Error?err.message:"unknown error"}`),process.exit(1)}if(await createResumeExecutor(agent,validated,paneId,teamWindow,ctx.cwd,ctx.spawnColor),await applySpawnLayout(ctx,teamWindow),await update(agent.id,{paneId,state:"spawning",startedAt:now,lastStateChange:now,suspendedAt:void 0,windowName:teamWindow?.windowName,windowId:teamWindow?.windowId,window:teamWindow?.windowName}),await notifySpawnJoin(ctx,paneId),await injectResumeContext(ctx.cwd??agent.repoPath??process.cwd(),agent.id,agent.role??agent.id,params.team),ctx.spawnColor&&paneId!=="inline")await applyPaneColor(paneId,ctx.spawnColor,teamWindow?.windowId);if(recordAuditEvent("worker",agent.id,"resumed",getActor(),{claudeSessionId:agent.claudeSessionId,team:agent.team}).catch(()=>{}),console.log(`Agent "${agent.id}" resumed.`),console.log(` Session: ${agent.claudeSessionId}`),console.log(` Pane: ${paneId}`),teamWindow)console.log(` Window: ${teamWindow.windowName} (${teamWindow.windowId})`)}async function resolveWorkerLiveness(w){if(/^%\d+$/.test(w.paneId))return{alive:await isPaneAlive(w.paneId),state:w.state};let execState=await getLiveExecutorState(w.id);return{alive:execState!==null,state:execState??w.state}}async function buildWorkerStatusMap(workers){let statusMap=new Map;for(let w of workers){let name=w.role||w.id,{alive,state}=await resolveWorkerLiveness(w);if(alive)statusMap.set(name,{state,team:w.team||"-"});else if(w.state==="suspended"||w.state==="error"){let attempts=w.resumeAttempts??0,max=w.maxResumeAttempts??3,autoStr=w.autoResume===!1?"off":"on";statusMap.set(name,{state:`${w.state} (${attempts}/${max} resumes, auto-resume: ${autoStr})`,team:w.team||"-",resumeAttempts:attempts,maxResumeAttempts:max,autoResume:w.autoResume!==!1})}}return statusMap}async function resolveAgentNamesBySource(source){let executorRegistry=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),agentRegistry=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),executors=await executorRegistry.listExecutors(void 0,source),agentIds=new Set(executors.map((e)=>e.agentId)),agents=await agentRegistry.listAgents({});return new Set(agents.filter((a)=>agentIds.has(a.id)).map((a)=>a.customName??a.role??a.id))}async function handleLsCommand(options){let dirEntries=await ls(),workers=await list(),statusMap=await buildWorkerStatusMap(workers),sourceAgentNames=options.source?await resolveAgentNamesBySource(options.source):void 0,entries=[];for(let entry of dirEntries){let running=statusMap.get(entry.name);entries.push({name:entry.name,dir:entry.dir||"-",status:running?running.state:"offline",team:running?.team||"-",model:entry.model||"-",resumeAttempts:running?.resumeAttempts,maxResumeAttempts:running?.maxResumeAttempts,autoResume:running?.autoResume}),statusMap.delete(entry.name)}for(let[name,info]of statusMap)entries.push({name,dir:"(built-in)",status:info.state,team:info.team,model:"-",resumeAttempts:info.resumeAttempts,maxResumeAttempts:info.maxResumeAttempts,autoResume:info.autoResume});if(sourceAgentNames)entries=entries.filter((e)=>sourceAgentNames.has(e.name));if(options.json){console.log(JSON.stringify(entries,null,2));return}if(entries.length===0){console.log("No agents registered. Use `genie dir add <name> --dir <path>` to register one.");return}console.log(""),console.log(formatLsRow("NAME","DIR","STATUS","TEAM","MODEL")),console.log("-".repeat(106));for(let e of entries)console.log(formatLsRow(e.name,e.dir,e.status,e.team,e.model));console.log("")}function formatLsRow(name,dir,status,team,model){return`${name.padEnd(20).substring(0,20)}${dir.padEnd(30).substring(0,30)}${status.padEnd(44).substring(0,44)}${team.padEnd(12).substring(0,12)}${model}`}var UUID_REGEX;var init_agents=__esm(()=>{init_agent_directory();init_agent_registry();init_audit();init_builtin_agents();init_claude_native_teams();init_codex_config();init_defaults();init_ensure_tmux();init_executor_registry();init_otel_receiver();init_protocol_router_spawn();init_protocol_router();init_provider_adapters();init_registry();init_spawn_command();init_team_manager();init_tmux_wrapper();init_tmux();init_tmux();init_workspace();UUID_REGEX=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i});var exports_codex_logs={};__export(exports_codex_logs,{parseCodexLine:()=>parseCodexLine,extractCodexContent:()=>extractCodexContent,codexTranscriptProvider:()=>codexTranscriptProvider});import{access as access2,readFile as readFile8,readdir as readdir4}from"fs/promises";import{homedir as homedir20}from"os";import{join as join32}from"path";function getCodexDir(){return join32(homedir20(),".codex")}function getSessionsDir(){return join32(getCodexDir(),"sessions")}function getStateDbPath(){return join32(getCodexDir(),"state_5.sqlite")}async function discoverLogPath(worker){let cwd=worker.worktree||worker.repoPath,sqlitePath=await discoverViaSqlite(cwd);if(sqlitePath)try{return await access2(sqlitePath),sqlitePath}catch{}return discoverViaScan(cwd)}async function discoverViaSqlite(cwd){try{let{Database}=await import("bun:sqlite"),dbPath=getStateDbPath(),db=new Database(dbPath,{readonly:!0});try{return db.query("SELECT rollout_path FROM threads WHERE cwd = ? ORDER BY updated_at DESC LIMIT 1").get(cwd)?.rollout_path??null}finally{db.close()}}catch{return null}}async function listDirsDesc(parent,pattern){return(await readdir4(parent)).filter((d)=>pattern.test(d)).sort().reverse()}async function discoverViaScan(cwd){let sessionsDir=getSessionsDir();try{let years=await listDirsDesc(sessionsDir,/^\d{4}$/);for(let year of years.slice(0,2)){let result2=await scanYear(join32(sessionsDir,year),cwd);if(result2)return result2}}catch{}return null}async function scanYear(yearDir,cwd){let months=await listDirsDesc(yearDir,/^\d{2}$/);for(let month of months.slice(0,2)){let result2=await scanMonth(join32(yearDir,month),cwd);if(result2)return result2}return null}async function scanMonth(monthDir,cwd){let days=await listDirsDesc(monthDir,/^\d{2}$/);for(let day of days.slice(0,3)){let result2=await scanDay(join32(monthDir,day),cwd);if(result2)return result2}return null}async function scanDay(dayDir,cwd){let files=(await readdir4(dayDir)).filter((f)=>f.endsWith(".jsonl")).sort().reverse();for(let file of files.slice(0,5)){let filePath=join32(dayDir,file);if((await readSessionMeta(filePath))?.cwd===cwd)return filePath}return null}async function readSessionMeta(filePath){try{let content=await readFile8(filePath,"utf-8"),nlIdx=content.indexOf(`
|
|
1621
|
+
`),firstLine=nlIdx===-1?content:content.slice(0,nlIdx),entry=JSON.parse(firstLine);if(entry.type==="session_meta"&&entry.payload?.cwd)return{cwd:entry.payload.cwd}}catch{}return null}function parseEventMsg(payload,ts3,base){if(payload.type==="user_message"){let text=String(payload.message??"");return text?[{...base,role:"user",timestamp:ts3,text}]:[]}if(payload.type==="agent_message"){let text=String(payload.message??"");return text?[{...base,role:"assistant",timestamp:ts3,text}]:[]}return[]}function parseResponseMessage(payload,ts3,base){let role=payload.role,text=extractCodexContent(payload.content);if(!text)return[];if(role==="user")return[{...base,role:"user",timestamp:ts3,text}];if(role==="developer")return[{...base,role:"system",timestamp:ts3,text}];if(role==="assistant")return[{...base,role:"assistant",timestamp:ts3,text}];return[]}function parseFunctionCall(payload,ts3,base){let name=String(payload.name??payload.type),callId=String(payload.call_id??""),input={};try{input=typeof payload.arguments==="string"?JSON.parse(payload.arguments):{}}catch{input={raw:payload.arguments}}let cmdText=input.command?String(Array.isArray(input.command)?input.command.join(" "):input.command):name;return[{...base,role:"tool_call",timestamp:ts3,text:`${name}: ${cmdText.slice(0,200)}`,toolCall:{id:callId,name,input}}]}function parseWebSearch(payload,ts3,base){let action=payload.action,query2=String(action?.query??"web search");return[{...base,role:"tool_call",timestamp:ts3,text:`web_search: ${query2.slice(0,200)}`,toolCall:{id:"",name:"web_search",input:{query:query2}}}]}function parseResponseItem(payload,ts3,base){if(payload.type==="message")return parseResponseMessage(payload,ts3,base);if(payload.type==="function_call"||payload.type==="shell")return parseFunctionCall(payload,ts3,base);if(payload.type==="function_call_output"){let output=String(payload.output??"").slice(0,500);return[{...base,role:"tool_result",timestamp:ts3,text:output}]}if(payload.type==="web_search_call")return parseWebSearch(payload,ts3,base);return[]}function parseCodexLine(line){if(!line.trim())return[];let raw;try{raw=JSON.parse(line)}catch{return[]}if(!raw.type||!raw.timestamp)return[];let base={provider:"codex",raw};if(!raw.payload||typeof raw.payload!=="object")return[];if(raw.type==="event_msg")return parseEventMsg(raw.payload,raw.timestamp,base);if(raw.type==="response_item")return parseResponseItem(raw.payload,raw.timestamp,base);return[]}function extractCodexContent(content){if(typeof content==="string")return content;if(!Array.isArray(content))return"";let parts=[];for(let item of content)if(typeof item==="string")parts.push(item);else if(item&&typeof item==="object"){if("text"in item)parts.push(String(item.text));else if("input_text"in item)parts.push(String(item.input_text))}return parts.join(" ")}async function readEntries(logPath){let content;try{content=await readFile8(logPath,"utf-8")}catch{return[]}return content.split(`
|
|
1622
1622
|
`).flatMap(parseCodexLine)}var codexTranscriptProvider;var init_codex_logs=__esm(()=>{codexTranscriptProvider={discoverLogPath,readEntries}});var exports_transcript={};__export(exports_transcript,{readTranscript:()=>readTranscript,getProvider:()=>getProvider2,applyFilter:()=>applyFilter});function applyFilter(entries,filter){if(!filter)return entries;let result2=entries;if(filter.since){let sinceMs=new Date(filter.since).getTime();result2=result2.filter((e)=>new Date(e.timestamp).getTime()>=sinceMs)}if(filter.roles&&filter.roles.length>0){let roles=new Set(filter.roles);result2=result2.filter((e)=>roles.has(e.role))}if(filter.last&&filter.last>0)result2=result2.slice(-filter.last);return result2}async function getClaudeProvider(){if(!_claudeProvider)_claudeProvider=(await Promise.resolve().then(() => (init_claude_logs(),exports_claude_logs))).claudeTranscriptProvider;return _claudeProvider}async function getCodexProvider(){if(!_codexProvider)_codexProvider=(await Promise.resolve().then(() => (init_codex_logs(),exports_codex_logs))).codexTranscriptProvider;return _codexProvider}async function getProvider2(worker){if((worker.provider??"claude")==="codex")return getCodexProvider();return getClaudeProvider()}async function readTranscript(worker,filter){let provider=await getProvider2(worker),logPath=await provider.discoverLogPath(worker);if(!logPath)return[];let entries=await provider.readEntries(logPath);return applyFilter(entries,filter)}var _claudeProvider,_codexProvider;function isTmuxMarkerOrNoise(line){let trimmed=line.trim();if(trimmed.includes("TMUX_MCP_START")||trimmed.includes("TMUX_MCP_DONE_"))return!0;if(line.includes('echo "TMUX_MCP_START"')||line.includes('echo "TMUX_MCP_DONE_'))return!0;if(line.includes("-bash:")||line.includes("warning: setlocale:")||line.includes("cannot change locale"))return!0;if(trimmed==="or directory")return!0;return!1}function stripTmuxMarkers(content){let filtered=content.split(`
|
|
1623
1623
|
`).filter((line)=>!isTmuxMarkerOrNoise(line));while(filtered.length>0&&filtered[0].trim()==="")filtered.shift();while(filtered.length>0&&filtered[filtered.length-1].trim()==="")filtered.pop();return filtered.join(`
|
|
1624
1624
|
`)}async function resolveActivePaneId(sessionName,session){let windows=await listWindows(session.id);if(!windows||windows.length===0)throw Error(`No windows found in session "${sessionName}"`);let activeWindow=windows.find((w)=>w.active)||windows[0],panes=await listPanes(activeWindow.id);if(!panes||panes.length===0)throw Error(`No panes found in session "${sessionName}"`);return(panes.find((p)=>p.active)||panes[0]).id}function maybeReverse(content,reverse){return reverse?content.split(`
|
|
@@ -1713,7 +1713,7 @@ Stopped following`),process.exit(0)}),await new Promise(()=>{});return}let conte
|
|
|
1713
1713
|
processed_bytes = ${progress.processedBytes},
|
|
1714
1714
|
errors = ${progress.errors},
|
|
1715
1715
|
updated_at = now()
|
|
1716
|
-
`}async function shouldSkipBackfill(sql){try{let existing=await sql`SELECT status FROM session_sync WHERE id = 'backfill'`;if(existing.length>0&&existing[0].status==="complete")return!0}catch{}try{let[{count}]=await sql`SELECT count(*)::int as count FROM sessions`;if(count>0){let existing=await sql`SELECT status FROM session_sync WHERE id = 'backfill'`;if(existing.length===0||existing[0].status==="complete")return!0}}catch{return!0}return!1}async function yieldToLiveWork(){while(liveWorkPending)await sleep2(LIVE_YIELD_POLL_MS)}async function getFileStartOffset(sql,file){let existing=await sql`SELECT last_ingested_offset FROM sessions WHERE id = ${file.sessionId}`;if(existing.length>0)return existing[0].last_ingested_offset??0;return 0}async function processBackfillFile(sql,file,progress,workerMap){let offset=await getFileStartOffset(sql,file);if(offset>=file.fileSize){progress.processedFiles++,progress.processedBytes+=file.fileSize;return}let currentOffset=offset;while(currentOffset<file.fileSize){await yieldToLiveWork();let result2=await ingestFile(sql,file.sessionId,file.jsonlPath,file.projectPath,currentOffset,{chunkSize:CHUNK_SIZE,parentSessionId:file.parentSessionId,isSubagent:file.isSubagent,fileSize:file.fileSize,mtime:file.mtime,workerMap});if(result2.newOffset<=currentOffset)break;progress.processedBytes+=result2.newOffset-currentOffset,currentOffset=result2.newOffset}progress.processedFiles++}async function processAllFiles(sql,allFiles,progress,workerMap){for(let file of allFiles){if(!running)break;await yieldToLiveWork();try{await processBackfillFile(sql,file,progress,workerMap)}catch(err){progress.errors++;let message=err instanceof Error?err.message:String(err);console.error(`[backfill] error on ${file.jsonlPath}: ${message}`)}if(progress.processedFiles%50===0)await updateSyncState(sql,progress);await sleep2(SLEEP_BETWEEN_FILES_MS)}}function resolveBackfillStatus(progress){if(!running)progress.status="paused",console.log(`[backfill] paused: ${progress.processedFiles}/${progress.totalFiles} files (will resume on next daemon start)`);else if(progress.errors>0&&progress.errors>=progress.totalFiles)progress.status="failed",console.error(`[backfill] failed: ${progress.errors}/${progress.totalFiles} files errored \u2014 will retry on next daemon start`);else progress.status="complete",console.log(`[backfill] complete: ${progress.processedFiles}/${progress.totalFiles} files, ${progress.errors} errors`)}async function startBackfill(sql){if(running)return;if(await shouldSkipBackfill(sql))return;running=!0,console.log("[backfill] starting session backfill...");try{let allFiles=await discoverAllJsonlFiles();allFiles.sort((a,b2)=>b2.mtime-a.mtime);let totalBytes=allFiles.reduce((sum,f)=>sum+f.fileSize,0),progress={totalFiles:allFiles.length,processedFiles:0,totalBytes,processedBytes:0,errors:0,status:"running"};await updateSyncState(sql,progress),console.log(`[backfill] discovered ${allFiles.length} files (${(totalBytes/1024/1024).toFixed(1)} MB)`);let workerMap=await buildWorkerMap(sql);await processAllFiles(sql,allFiles,progress,workerMap),resolveBackfillStatus(progress),await updateSyncState(sql,progress)}catch(err){let message=err instanceof Error?err.message:String(err);console.error(`[backfill] fatal error: ${message}`)}finally{running=!1}}function stopBackfill(){running=!1}async function getBackfillStatus(sql){try{let rows=await sql`SELECT * FROM session_sync WHERE id = 'backfill'`;if(rows.length===0)return null;let row=rows[0];return{totalFiles:row.total_files,processedFiles:row.processed_files,totalBytes:row.total_bytes,processedBytes:row.processed_bytes,errors:row.errors,status:row.status}}catch{return null}}var CHUNK_SIZE=65536,SLEEP_BETWEEN_FILES_MS=100,LIVE_YIELD_POLL_MS=200,running=!1;var init_session_backfill=__esm(()=>{init_session_capture()});var exports_scheduler_daemon={};__export(exports_scheduler_daemon,{startDaemon:()=>startDaemon,runAgentRecoveryPass:()=>runAgentRecoveryPass,recoverOnStartup:()=>recoverOnStartup,reconcileOrphans:()=>reconcileOrphans,reconcileOrphanedRuns:()=>reconcileOrphanedRuns,reclaimExpiredLeases:()=>reclaimExpiredLeases,logToFile:()=>logToFile,fireTrigger:()=>fireTrigger,emitWorkerEvents:()=>emitWorkerEvents,collectMachineSnapshot:()=>collectMachineSnapshot,collectHeartbeats:()=>collectHeartbeats,claimDueTriggers:()=>claimDueTriggers,attemptAgentResume:()=>attemptAgentResume,_resetWorkerStatesForTesting:()=>_resetWorkerStatesForTesting});import{randomUUID as randomUUID8}from"crypto";import{appendFileSync as appendFileSync2,mkdirSync as mkdirSync10}from"fs";import{homedir as homedir23}from"os";import{join as join35}from"path";function getLogDir2(){return join35(process.env.GENIE_HOME??join35(homedir23(),".genie"),"logs")}function getLogFile(){return join35(getLogDir2(),"scheduler.log")}function logToFile(entry){let logDir=getLogDir2();mkdirSync10(logDir,{recursive:!0}),appendFileSync2(getLogFile(),`${JSON.stringify(entry)}
|
|
1716
|
+
`}async function shouldSkipBackfill(sql){try{let existing=await sql`SELECT status FROM session_sync WHERE id = 'backfill'`;if(existing.length>0&&existing[0].status==="complete")return!0}catch{}try{let[{count}]=await sql`SELECT count(*)::int as count FROM sessions`;if(count>0){let existing=await sql`SELECT status FROM session_sync WHERE id = 'backfill'`;if(existing.length===0||existing[0].status==="complete")return!0}}catch{return!0}return!1}async function yieldToLiveWork(){while(liveWorkPending)await sleep2(LIVE_YIELD_POLL_MS)}async function getFileStartOffset(sql,file){let existing=await sql`SELECT last_ingested_offset FROM sessions WHERE id = ${file.sessionId}`;if(existing.length>0)return existing[0].last_ingested_offset??0;return 0}async function processBackfillFile(sql,file,progress,workerMap){let offset=await getFileStartOffset(sql,file);if(offset>=file.fileSize){progress.processedFiles++,progress.processedBytes+=file.fileSize;return}let currentOffset=offset;while(currentOffset<file.fileSize){await yieldToLiveWork();let result2=await ingestFile(sql,file.sessionId,file.jsonlPath,file.projectPath,currentOffset,{chunkSize:CHUNK_SIZE,parentSessionId:file.parentSessionId,isSubagent:file.isSubagent,fileSize:file.fileSize,mtime:file.mtime,workerMap});if(result2.newOffset<=currentOffset)break;progress.processedBytes+=result2.newOffset-currentOffset,currentOffset=result2.newOffset}progress.processedFiles++}async function processAllFiles(sql,allFiles,progress,workerMap){for(let file of allFiles){if(!running)break;await yieldToLiveWork();try{await processBackfillFile(sql,file,progress,workerMap)}catch(err){progress.errors++;let message=err instanceof Error?err.message:String(err);console.error(`[backfill] error on ${file.jsonlPath}: ${message}`)}if(progress.processedFiles%50===0)await updateSyncState(sql,progress);await sleep2(SLEEP_BETWEEN_FILES_MS)}}function resolveBackfillStatus(progress){if(!running)progress.status="paused",console.log(`[backfill] paused: ${progress.processedFiles}/${progress.totalFiles} files (will resume on next daemon start)`);else if(progress.errors>0&&progress.errors>=progress.totalFiles)progress.status="failed",console.error(`[backfill] failed: ${progress.errors}/${progress.totalFiles} files errored \u2014 will retry on next daemon start`);else progress.status="complete",console.log(`[backfill] complete: ${progress.processedFiles}/${progress.totalFiles} files, ${progress.errors} errors`)}async function startBackfill(sql){if(running)return;if(await shouldSkipBackfill(sql))return;running=!0,console.log("[backfill] starting session backfill...");try{let allFiles=await discoverAllJsonlFiles();allFiles.sort((a,b2)=>b2.mtime-a.mtime);let totalBytes=allFiles.reduce((sum,f)=>sum+f.fileSize,0),progress={totalFiles:allFiles.length,processedFiles:0,totalBytes,processedBytes:0,errors:0,status:"running"};await updateSyncState(sql,progress),console.log(`[backfill] discovered ${allFiles.length} files (${(totalBytes/1024/1024).toFixed(1)} MB)`);let workerMap=await buildWorkerMap(sql);await processAllFiles(sql,allFiles,progress,workerMap),resolveBackfillStatus(progress),await updateSyncState(sql,progress)}catch(err){let message=err instanceof Error?err.message:String(err);console.error(`[backfill] fatal error: ${message}`)}finally{running=!1}}function stopBackfill(){running=!1}async function getBackfillStatus(sql){try{let rows=await sql`SELECT * FROM session_sync WHERE id = 'backfill'`;if(rows.length===0)return null;let row=rows[0];return{totalFiles:row.total_files,processedFiles:row.processed_files,totalBytes:row.total_bytes,processedBytes:row.processed_bytes,errors:row.errors,status:row.status}}catch{return null}}var CHUNK_SIZE=65536,SLEEP_BETWEEN_FILES_MS=100,LIVE_YIELD_POLL_MS=200,running=!1;var init_session_backfill=__esm(()=>{init_session_capture()});var exports_scheduler_daemon={};__export(exports_scheduler_daemon,{startDaemon:()=>startDaemon,runAgentRecoveryPass:()=>runAgentRecoveryPass,recoverOnStartup:()=>recoverOnStartup,reconcileOrphans:()=>reconcileOrphans,reconcileOrphanedRuns:()=>reconcileOrphanedRuns,reclaimExpiredLeases:()=>reclaimExpiredLeases,processMailboxRetryMessage:()=>processMailboxRetryMessage,logToFile:()=>logToFile,fireTrigger:()=>fireTrigger,emitWorkerEvents:()=>emitWorkerEvents,collectMachineSnapshot:()=>collectMachineSnapshot,collectHeartbeats:()=>collectHeartbeats,claimDueTriggers:()=>claimDueTriggers,attemptAgentResume:()=>attemptAgentResume,_resetWorkerStatesForTesting:()=>_resetWorkerStatesForTesting,MAX_DELIVERY_ATTEMPTS:()=>MAX_DELIVERY_ATTEMPTS,ESCALATION_RECIPIENT:()=>ESCALATION_RECIPIENT});import{randomUUID as randomUUID8}from"crypto";import{appendFileSync as appendFileSync2,mkdirSync as mkdirSync10}from"fs";import{homedir as homedir23}from"os";import{join as join35}from"path";function getLogDir2(){return join35(process.env.GENIE_HOME??join35(homedir23(),".genie"),"logs")}function getLogFile(){return join35(getLogDir2(),"scheduler.log")}function logToFile(entry){let logDir=getLogDir2();mkdirSync10(logDir,{recursive:!0}),appendFileSync2(getLogFile(),`${JSON.stringify(entry)}
|
|
1717
1717
|
`)}async function defaultSpawnCommand(command,env){return{pid:Bun.spawn(["sh","-c",command],{env:{...process.env,...env},stdio:["ignore","ignore","ignore"]}).pid}}function defaultJitter(maxMs){return Math.floor(Math.random()*maxMs)}function defaultSleep(ms){return new Promise((resolve5)=>setTimeout(resolve5,ms))}async function defaultIsPaneAlive(paneId){let{isPaneAlive:isPaneAlive2}=await Promise.resolve().then(() => (init_tmux(),exports_tmux));return isPaneAlive2(paneId)}async function defaultListWorkers(){let{list:list2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));return(await list2()).map((a)=>({id:a.id,paneId:a.paneId,repoPath:a.repoPath,state:a.state,team:a.team,wishSlug:a.wishSlug,groupNumber:a.groupNumber,autoResume:a.autoResume,resumeAttempts:a.resumeAttempts,maxResumeAttempts:a.maxResumeAttempts,lastResumeAttempt:a.lastResumeAttempt,claudeSessionId:a.claudeSessionId}))}async function defaultPublishEvent(subject,data,repoPath){let payload=data,{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(repoPath,subject,{timestamp:payload.timestamp,kind:payload.kind??"system",agent:payload.agent??"scheduler",team:payload.team,direction:payload.direction,peer:payload.peer,text:payload.text??subject,data:payload.data,source:payload.source??"registry"})}async function defaultCountTmuxSessions(){try{let{execSync:execSync9}=await import("child_process"),{genieTmuxCmd:genieTmuxCmd2}=await Promise.resolve().then(() => (init_tmux_wrapper(),exports_tmux_wrapper));return execSync9(`${genieTmuxCmd2("list-sessions")} 2>/dev/null`,{encoding:"utf-8"}).trim().split(`
|
|
1718
1718
|
`).filter(Boolean).length}catch{return 0}}async function defaultResumeAgent(agentId){try{let{execSync:execSync9}=await import("child_process");return execSync9(`genie agent resume ${agentId} --no-reset-attempts`,{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}),!0}catch{return!1}}async function defaultUpdateAgent(agentId,updates){let{update:update2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));await update2(agentId,updates)}function createDefaultDeps(){return{getConnection:async()=>{let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db));return getConnection2()},spawnCommand:defaultSpawnCommand,log:logToFile,generateId:randomUUID8,now:()=>new Date,sleep:defaultSleep,jitter:defaultJitter,isPaneAlive:defaultIsPaneAlive,listWorkers:defaultListWorkers,countTmuxSessions:defaultCountTmuxSessions,publishEvent:defaultPublishEvent,resumeAgent:defaultResumeAgent,updateAgent:defaultUpdateAgent}}function resolveConfig(overrides){let envMax=process.env.GENIE_MAX_CONCURRENT,maxConcurrent=envMax?Number.parseInt(envMax,10):5;return{maxConcurrent:overrides?.maxConcurrent??(Number.isNaN(maxConcurrent)?5:maxConcurrent),pollIntervalMs:overrides?.pollIntervalMs??30000,maxJitterMs:overrides?.maxJitterMs??30000,jitterThreshold:overrides?.jitterThreshold??3,heartbeatIntervalMs:overrides?.heartbeatIntervalMs??60000,orphanCheckIntervalMs:overrides?.orphanCheckIntervalMs??300000,deadHeartbeatThreshold:overrides?.deadHeartbeatThreshold??2,leaseRecoveryIntervalMs:overrides?.leaseRecoveryIntervalMs??60000}}async function claimDueTriggers(deps,config,daemonId){let sql=await deps.getConnection(),now=deps.now(),leaseUntil=new Date(now.getTime()+300000),runningCount=(await sql`
|
|
1719
1719
|
SELECT count(*)::int AS cnt FROM runs
|
|
@@ -1784,7 +1784,7 @@ Stopped following`),process.exit(0)}),await new Promise(()=>{});return}let conte
|
|
|
1784
1784
|
`,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`
|
|
1785
1785
|
INSERT INTO machine_snapshots (id, active_workers, active_teams, tmux_sessions, cpu_percent, memory_mb, created_at)
|
|
1786
1786
|
VALUES (${snapshotId}, ${activeWorkers}, ${teams.size}, ${tmuxSessions}, ${cpuPercent}, ${memoryMb}, ${now})
|
|
1787
|
-
`,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,agentResumeTimer=null,inboxWatcherHandle=null,captureFallbackTimer=null,eventRouterHandle=null,deliveryUnsub=null,deliveryRetryTimer=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(agentResumeTimer)clearInterval(agentResumeTimer),agentResumeTimer=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,deliveryRetryTimer)clearInterval(deliveryRetryTimer),deliveryRetryTimer=null;if(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 reconcileDeadPaneZombies(d){try{let{reconcileStaleSpawns:reconcileStaleSpawns2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));await reconcileStaleSpawns2()}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"warn",event:"reconcile_stale_spawns_error",error:message})}}function startAgentResumeTimer(d,cfg,dId){return setInterval(async()=>{if(!running2)return;try{await reconcileDeadPaneZombies(d),await runAgentRecoveryPass(d,dId,cfg)}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"agent_resume_timer_error",error:message})}},cfg.leaseRecoveryIntervalMs)}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),agentResumeTimer=startAgentResumeTimer(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{}let MAX_DELIVERY_ATTEMPTS=3;deliveryRetryTimer=setInterval(async()=>{try{let retryable=await getRetryable(MAX_DELIVERY_ATTEMPTS);for(let msg of retryable)try{let{deliverToPane:deliverToPane2}=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router));if(await deliverToPane2(msg.to,msg.id)){deps.log({timestamp:deps.now().toISOString(),level:"info",event:"mailbox_delivery_retried",messageId:msg.id,to:msg.to});continue}let rows=await(await deps.getConnection())`SELECT delivery_attempts, repo_path FROM mailbox WHERE id = ${msg.id} LIMIT 1`,attempts=rows[0]?.delivery_attempts??0;if(attempts>=MAX_DELIVERY_ATTEMPTS){await markEscalated(msg.id);let repoPath=rows[0]?.repo_path;if(repoPath){let{send:send2}=await Promise.resolve().then(() => (init_mailbox(),exports_mailbox));await send2(repoPath,"scheduler","team-lead",`[escalation] Message ${msg.id} from "${msg.from}" to "${msg.to}" failed delivery after ${MAX_DELIVERY_ATTEMPTS} attempts. Body: "${msg.body.slice(0,200)}"`)}deps.log({timestamp:deps.now().toISOString(),level:"warn",event:"mailbox_delivery_escalated",messageId:msg.id,to:msg.to,attempts})}}catch{}}catch(err){let message=err instanceof Error?err.message:String(err);deps.log({timestamp:deps.now().toISOString(),level:"error",event:"mailbox_retry_error",error:message})}},60000),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 RECOVERY_RETRY_DELAY_MS=60000,RESUME_COOLDOWN_MS=60000,DEFAULT_MAX_RESUME_ATTEMPTS=3,INACTIVE_WORKER_STATES,previousWorkerStates;var init_scheduler_daemon=__esm(()=>{init_cron();init_event_router();init_inbox_watcher();init_mailbox();init_run_spec();INACTIVE_WORKER_STATES=new Set(["done","error","suspended","spawning"]);previousWorkerStates=new Map});var exports_omni_approval_handler={};__export(exports_omni_approval_handler,{startOmniApprovalHandler:()=>startOmniApprovalHandler});class OmniApprovalHandler{nc=null;subs=[];sc=import_nats2.StringCodec();permissions;natsUrl;approveTokens;denyTokens;constructor(config){this.permissions=config.permissions,this.natsUrl=config.natsUrl??"localhost:4222",this.approveTokens=(config.permissions.approveTokens??DEFAULT_APPROVE_TOKENS).map((t)=>t.toLowerCase()),this.denyTokens=(config.permissions.denyTokens??DEFAULT_DENY_TOKENS).map((t)=>t.toLowerCase())}async start(){let{omniChat,omniInstance}=this.permissions;if(!omniChat||!omniInstance)return;this.nc=await import_nats2.connect({servers:this.natsUrl});let messageTopic=`omni.message.${omniInstance}.>`,msgSub=this.nc.subscribe(messageTopic);this.subs.push(msgSub),this.processMessages(msgSub);let eventSub=this.nc.subscribe("omni.event.>");this.subs.push(eventSub),this.processEvents(eventSub),handlerInstance=this,console.log(`[omni-approval] Listening for approval replies on ${messageTopic}`)}async stop(){for(let sub of this.subs)sub.unsubscribe();if(this.subs=[],this.nc)await this.nc.close(),this.nc=null;if(handlerInstance===this)handlerInstance=null}async processMessages(sub){for await(let msg of sub)try{let data=JSON.parse(this.sc.decode(msg.data));if((data.chatId??this.extractChatIdFromSubject(msg.subject))!==this.permissions.omniChat)continue;if(data.content)await this.handleTextReply(data.content,data.sender??"whatsapp-user")}catch{}}async processEvents(sub){for await(let msg of sub)try{let data=JSON.parse(this.sc.decode(msg.data));if(data.type!=="reaction")continue;if(data.chatId!==this.permissions.omniChat||data.instanceId!==this.permissions.omniInstance)continue;if(data.emoji&&data.messageId)await this.handleReaction(data.emoji,data.messageId,data.sender??"whatsapp-user")}catch{}}extractChatIdFromSubject(subject){let parts=subject.split(".");if(parts.length>=4)return parts.slice(3).join(".");return}async handleTextReply(content,sender){let normalized=content.trim().toLowerCase();if(!normalized)return!1;let decision=null;if(this.approveTokens.includes(normalized))decision="allow";else if(this.denyTokens.includes(normalized))decision="deny";if(!decision)return!1;let pending=await listPendingApprovals();if(pending.length===0)return!1;let oldest=pending[0],resolved=await resolveApproval(oldest.id,decision,sender);if(resolved)console.log(`[omni-approval] Resolved ${oldest.id} as ${decision} by ${sender} (text: "${normalized}")`);return resolved}async handleReaction(emoji,messageId,sender){let decision=null;if(DEFAULT_APPROVE_REACTIONS.includes(emoji))decision="allow";else if(DEFAULT_DENY_REACTIONS.includes(emoji))decision="deny";if(!decision)return!1;try{let sql=await getConnection(),[approval]=await sql`
|
|
1787
|
+
`,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()}async function processMailboxRetryMessage(deps,msg,overrides={}){let deliverFn=overrides.deliverFn??(async(toWorker,messageId)=>{let{deliverToPane:deliverToPane2}=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router));return deliverToPane2(toWorker,messageId)}),sendFn=overrides.sendFn??(async(repoPath2,from,to,body)=>{let{send:send2}=await Promise.resolve().then(() => (init_mailbox(),exports_mailbox));return send2(repoPath2,from,to,body)});if(await deliverFn(msg.to,msg.id)){deps.log({timestamp:deps.now().toISOString(),level:"info",event:"mailbox_delivery_retried",messageId:msg.id,to:msg.to});return}let rows=await(await deps.getConnection())`SELECT delivery_attempts, repo_path FROM mailbox WHERE id = ${msg.id} LIMIT 1`,attempts=rows[0]?.delivery_attempts??0;if(attempts<MAX_DELIVERY_ATTEMPTS)return;if(await markEscalated(msg.id),msg.from==="scheduler"){deps.log({timestamp:deps.now().toISOString(),level:"warn",event:"mailbox_delivery_escalation_dropped",reason:"already_escalated_by_scheduler",messageId:msg.id,to:msg.to,attempts});return}if(msg.body.startsWith("[escalation] ")){deps.log({timestamp:deps.now().toISOString(),level:"warn",event:"mailbox_delivery_escalation_dropped",reason:"body_prefix",messageId:msg.id,to:msg.to,attempts});return}if(msg.to===ESCALATION_RECIPIENT){deps.log({timestamp:deps.now().toISOString(),level:"warn",event:"mailbox_delivery_escalation_dropped",reason:"same_recipient",messageId:msg.id,to:msg.to,attempts});return}let repoPath=rows[0]?.repo_path;if(repoPath)await sendFn(repoPath,"scheduler",ESCALATION_RECIPIENT,`[escalation] Message ${msg.id} from "${msg.from}" to "${msg.to}" failed delivery after ${MAX_DELIVERY_ATTEMPTS} attempts. Body: "${msg.body.slice(0,200)}"`);deps.log({timestamp:deps.now().toISOString(),level:"warn",event:"mailbox_delivery_escalated",messageId:msg.id,to:msg.to,attempts})}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,agentResumeTimer=null,inboxWatcherHandle=null,captureFallbackTimer=null,eventRouterHandle=null,deliveryUnsub=null,deliveryRetryTimer=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(agentResumeTimer)clearInterval(agentResumeTimer),agentResumeTimer=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,deliveryRetryTimer)clearInterval(deliveryRetryTimer),deliveryRetryTimer=null;if(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 reconcileDeadPaneZombies(d){try{let{reconcileStaleSpawns:reconcileStaleSpawns2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));await reconcileStaleSpawns2()}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"warn",event:"reconcile_stale_spawns_error",error:message})}}function startAgentResumeTimer(d,cfg,dId){return setInterval(async()=>{if(!running2)return;try{await reconcileDeadPaneZombies(d),await runAgentRecoveryPass(d,dId,cfg)}catch(err){let message=err instanceof Error?err.message:String(err);d.log({timestamp:d.now().toISOString(),level:"error",event:"agent_resume_timer_error",error:message})}},cfg.leaseRecoveryIntervalMs)}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),agentResumeTimer=startAgentResumeTimer(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{}deliveryRetryTimer=setInterval(async()=>{try{let retryable=await getRetryable(MAX_DELIVERY_ATTEMPTS);for(let msg of retryable)try{await processMailboxRetryMessage(deps,msg)}catch{}}catch(err){let message=err instanceof Error?err.message:String(err);deps.log({timestamp:deps.now().toISOString(),level:"error",event:"mailbox_retry_error",error:message})}},60000),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 ESCALATION_RECIPIENT="team-lead",MAX_DELIVERY_ATTEMPTS=3,RECOVERY_RETRY_DELAY_MS=60000,RESUME_COOLDOWN_MS=60000,DEFAULT_MAX_RESUME_ATTEMPTS=3,INACTIVE_WORKER_STATES,previousWorkerStates;var init_scheduler_daemon=__esm(()=>{init_cron();init_event_router();init_inbox_watcher();init_mailbox();init_run_spec();INACTIVE_WORKER_STATES=new Set(["done","error","suspended","spawning"]);previousWorkerStates=new Map});var exports_omni_approval_handler={};__export(exports_omni_approval_handler,{startOmniApprovalHandler:()=>startOmniApprovalHandler});class OmniApprovalHandler{nc=null;subs=[];sc=import_nats2.StringCodec();permissions;natsUrl;approveTokens;denyTokens;constructor(config){this.permissions=config.permissions,this.natsUrl=config.natsUrl??"localhost:4222",this.approveTokens=(config.permissions.approveTokens??DEFAULT_APPROVE_TOKENS).map((t)=>t.toLowerCase()),this.denyTokens=(config.permissions.denyTokens??DEFAULT_DENY_TOKENS).map((t)=>t.toLowerCase())}async start(){let{omniChat,omniInstance}=this.permissions;if(!omniChat||!omniInstance)return;this.nc=await import_nats2.connect({servers:this.natsUrl});let messageTopic=`omni.message.${omniInstance}.>`,msgSub=this.nc.subscribe(messageTopic);this.subs.push(msgSub),this.processMessages(msgSub);let eventSub=this.nc.subscribe("omni.event.>");this.subs.push(eventSub),this.processEvents(eventSub),handlerInstance=this,console.log(`[omni-approval] Listening for approval replies on ${messageTopic}`)}async stop(){for(let sub of this.subs)sub.unsubscribe();if(this.subs=[],this.nc)await this.nc.close(),this.nc=null;if(handlerInstance===this)handlerInstance=null}async processMessages(sub){for await(let msg of sub)try{let data=JSON.parse(this.sc.decode(msg.data));if((data.chatId??this.extractChatIdFromSubject(msg.subject))!==this.permissions.omniChat)continue;if(data.content)await this.handleTextReply(data.content,data.sender??"whatsapp-user")}catch{}}async processEvents(sub){for await(let msg of sub)try{let data=JSON.parse(this.sc.decode(msg.data));if(data.type!=="reaction")continue;if(data.chatId!==this.permissions.omniChat||data.instanceId!==this.permissions.omniInstance)continue;if(data.emoji&&data.messageId)await this.handleReaction(data.emoji,data.messageId,data.sender??"whatsapp-user")}catch{}}extractChatIdFromSubject(subject){let parts=subject.split(".");if(parts.length>=4)return parts.slice(3).join(".");return}async handleTextReply(content,sender){let normalized=content.trim().toLowerCase();if(!normalized)return!1;let decision=null;if(this.approveTokens.includes(normalized))decision="allow";else if(this.denyTokens.includes(normalized))decision="deny";if(!decision)return!1;let pending=await listPendingApprovals();if(pending.length===0)return!1;let oldest=pending[0],resolved=await resolveApproval(oldest.id,decision,sender);if(resolved)console.log(`[omni-approval] Resolved ${oldest.id} as ${decision} by ${sender} (text: "${normalized}")`);return resolved}async handleReaction(emoji,messageId,sender){let decision=null;if(DEFAULT_APPROVE_REACTIONS.includes(emoji))decision="allow";else if(DEFAULT_DENY_REACTIONS.includes(emoji))decision="deny";if(!decision)return!1;try{let sql=await getConnection(),[approval]=await sql`
|
|
1788
1788
|
SELECT id FROM approvals
|
|
1789
1789
|
WHERE omni_message_id = ${messageId} AND decision = 'pending'
|
|
1790
1790
|
`;if(approval){let resolved2=await resolveApproval(approval.id,decision,sender);if(resolved2)console.log(`[omni-approval] Resolved ${approval.id} via reaction ${emoji} by ${sender}`);return resolved2}}catch{}let pending=await listPendingApprovals();if(pending.length===0)return!1;let resolved=await resolveApproval(pending[0].id,decision,sender);if(resolved)console.log(`[omni-approval] Resolved ${pending[0].id} via reaction ${emoji} by ${sender} (fallback)`);return resolved}}async function startOmniApprovalHandler(natsUrl){let ws=findWorkspace();if(!ws)return null;let config=getWorkspaceConfig(ws.root);if(!config.permissions?.omniChat||!config.permissions?.omniInstance)return null;let handler=new OmniApprovalHandler({natsUrl,permissions:config.permissions});return await handler.start(),handler}var import_nats2,DEFAULT_APPROVE_TOKENS,DEFAULT_DENY_TOKENS,DEFAULT_APPROVE_REACTIONS,DEFAULT_DENY_REACTIONS,handlerInstance=null;var init_omni_approval_handler=__esm(()=>{init_db();init_claude_sdk_remote_approval();init_workspace();import_nats2=__toESM(require_mod4(),1),DEFAULT_APPROVE_TOKENS=["y","yes","approve","sim"],DEFAULT_DENY_TOKENS=["n","no","deny","nao"],DEFAULT_APPROVE_REACTIONS=["\uD83D\uDC4D","\u2705","\uD83D\uDC4C"],DEFAULT_DENY_REACTIONS=["\uD83D\uDC4E","\u274C","\uD83D\uDEAB"]});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"])});class BridgeSessionStore{sql;constructor(sql){this.sql=sql}async create(opts){let[row]=await this.sql`
|
|
@@ -2293,7 +2293,7 @@ ${answer}
|
|
|
2293
2293
|
WHERE repo_path = ${repo}
|
|
2294
2294
|
AND wish_file LIKE ${pattern}
|
|
2295
2295
|
ORDER BY seq
|
|
2296
|
-
`).map((r)=>({id:r.id,seq:r.seq,title:r.title,stage:r.stage,repoPath:r.repo_path}))}var init_task_close_merged=__esm(()=>{init_wish_state()});var highlights_default="./highlights-ghv9g403.scm";var init_highlights=()=>{};var tree_sitter_javascript_default="./tree-sitter-javascript-nd0q4pe9.wasm";var init_tree_sitter_javascript=()=>{};var highlights_default2="./highlights-eq9cgrbb.scm";var init_highlights2=()=>{};var tree_sitter_typescript_default="./tree-sitter-typescript-zxjzwt75.wasm";var init_tree_sitter_typescript=()=>{};var highlights_default3="./highlights-r812a2qc.scm";var init_highlights3=()=>{};var tree_sitter_markdown_default="./tree-sitter-markdown-411r6y9b.wasm";var init_tree_sitter_markdown=()=>{};var injections_default="./injections-73j83es3.scm";var init_injections=()=>{};var highlights_default4="./highlights-x6tmsnaa.scm";var init_highlights4=()=>{};var tree_sitter_markdown_inline_default="./tree-sitter-markdown_inline-j5349f42.wasm";var init_tree_sitter_markdown_inline=()=>{};var highlights_default5="./highlights-hk7bwhj4.scm";var init_highlights5=()=>{};var tree_sitter_zig_default="./tree-sitter-zig-e78zbjpm.wasm";var init_tree_sitter_zig=()=>{};import{EventEmitter}from"events";import{Buffer as Buffer2}from"buffer";import{Buffer as Buffer3}from"buffer";import{EventEmitter as EventEmitter2}from"events";import{resolve as resolve12,dirname as dirname12}from"path";import{fileURLToPath as fileURLToPath2}from"url";import{resolve as resolve22,isAbsolute,parse as parse2}from"path";import{existsSync as existsSync45}from"fs";import{basename as basename11,join as join57}from"path";import os3 from"os";import path3 from"path";import{EventEmitter as EventEmitter3}from"events";import path22 from"path";import{readFile as readFile22,writeFile as writeFile22,mkdir as mkdir22}from"fs/promises";import*as path4 from"path";import{mkdir as mkdir9,readFile as readFile13,writeFile as writeFile6}from"fs/promises";import*as path32 from"path";import{readdir as readdir7}from"fs/promises";import{dlopen,toArrayBuffer as toArrayBuffer4,JSCallback,ptr as ptr4}from"bun:ffi";import{existsSync as existsSync210,writeFileSync as writeFileSync23}from"fs";import{EventEmitter as EventEmitter4}from"events";import{toArrayBuffer,ptr}from"bun:ffi";import{ptr as ptr2,toArrayBuffer as toArrayBuffer2}from"bun:ffi";import{ptr as ptr3,toArrayBuffer as toArrayBuffer3}from"bun:ffi";import{EventEmitter as EventEmitter5}from"events";import util3 from"util";import{EventEmitter as EventEmitter7}from"events";import{Console}from"console";import fs2 from"fs";import path5 from"path";import util22 from"util";import{Writable}from"stream";import{EventEmitter as EventEmitter6}from"events";import{EventEmitter as EventEmitter8}from"events";function __exportSetter2(name,newValue){this[name]=__returnValue2.bind(null,newValue)}function wrapAssembly(lib){function patch(prototype,name,fn){let original=prototype[name];prototype[name]=function(){for(var _len=arguments.length,args=Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];return fn.call(this,original,...args)}}for(let fnName of["setPosition","setMargin","setFlexBasis","setWidth","setHeight","setMinWidth","setMinHeight","setMaxWidth","setMaxHeight","setPadding","setGap"]){let methods={[Unit.Point]:lib.Node.prototype[fnName],[Unit.Percent]:lib.Node.prototype[`${fnName}Percent`],[Unit.Auto]:lib.Node.prototype[`${fnName}Auto`]};patch(lib.Node.prototype,fnName,function(original){for(var _len2=arguments.length,args=Array(_len2>1?_len2-1:0),_key2=1;_key2<_len2;_key2++)args[_key2-1]=arguments[_key2];let value=args.pop(),unit,asNumber;if(value==="auto")unit=Unit.Auto,asNumber=void 0;else if(typeof value==="object")unit=value.unit,asNumber=value.valueOf();else if(unit=typeof value==="string"&&value.endsWith("%")?Unit.Percent:Unit.Point,asNumber=parseFloat(value),value!==void 0&&!Number.isNaN(value)&&Number.isNaN(asNumber))throw Error(`Invalid value ${value} for ${fnName}`);if(!methods[unit])throw Error(`Failed to execute "${fnName}": Unsupported unit '${value}'`);if(asNumber!==void 0)return methods[unit].call(this,...args,asNumber);else return methods[unit].call(this,...args)})}function wrapMeasureFunction(measureFunction){return lib.MeasureCallback.implement({measure:function(){let{width,height:height2}=measureFunction(...arguments);return{width:width??NaN,height:height2??NaN}}})}patch(lib.Node.prototype,"setMeasureFunc",function(original,measureFunc){if(measureFunc)return original.call(this,wrapMeasureFunction(measureFunc));else return this.unsetMeasureFunc()});function wrapDirtiedFunc(dirtiedFunction){return lib.DirtiedCallback.implement({dirtied:dirtiedFunction})}return patch(lib.Node.prototype,"setDirtiedFunc",function(original,dirtiedFunc){original.call(this,wrapDirtiedFunc(dirtiedFunc))}),patch(lib.Config.prototype,"free",function(){lib.Config.destroy(this)}),patch(lib.Node,"create",(_,config)=>{return config?lib.Node.createWithConfig(config):lib.Node.createDefault()}),patch(lib.Node.prototype,"free",function(){lib.Node.destroy(this)}),patch(lib.Node.prototype,"freeRecursive",function(){for(let t=0,T=this.getChildCount();t<T;++t)this.getChild(0).freeRecursive();this.free()}),patch(lib.Node.prototype,"calculateLayout",function(original){let width=arguments.length>1&&arguments[1]!==void 0?arguments[1]:NaN,height2=arguments.length>2&&arguments[2]!==void 0?arguments[2]:NaN,direction=arguments.length>3&&arguments[3]!==void 0?arguments[3]:Direction.LTR;return original.call(this,width,height2,direction)}),{Config:lib.Config,Node:lib.Node,...YGEnums_default}}function isValidBorderStyle(value){return typeof value==="string"&&VALID_BORDER_STYLES.includes(value)}function parseBorderStyle(value,fallback="single"){if(isValidBorderStyle(value))return value;if(value!==void 0&&value!==null)console.warn(`Invalid borderStyle "${value}", falling back to "${fallback}". Valid values are: ${VALID_BORDER_STYLES.join(", ")}`);return fallback}function getBorderFromSides(sides){let result2=[];if(sides.top)result2.push("top");if(sides.right)result2.push("right");if(sides.bottom)result2.push("bottom");if(sides.left)result2.push("left");return result2.length>0?result2:!1}function getBorderSides(border){return border===!0?{top:!0,right:!0,bottom:!0,left:!0}:Array.isArray(border)?{top:border.includes("top"),right:border.includes("right"),bottom:border.includes("bottom"),left:border.includes("left")}:{top:!1,right:!1,bottom:!1,left:!1}}function borderCharsToArray(chars){let array=new Uint32Array(11);return array[0]=chars.topLeft.codePointAt(0),array[1]=chars.topRight.codePointAt(0),array[2]=chars.bottomLeft.codePointAt(0),array[3]=chars.bottomRight.codePointAt(0),array[4]=chars.horizontal.codePointAt(0),array[5]=chars.vertical.codePointAt(0),array[6]=chars.topT.codePointAt(0),array[7]=chars.bottomT.codePointAt(0),array[8]=chars.leftT.codePointAt(0),array[9]=chars.rightT.codePointAt(0),array[10]=chars.cross.codePointAt(0),array}class KeyEvent{name;ctrl;meta;shift;option;sequence;number;raw;eventType;source;code;super;hyper;capsLock;numLock;baseCode;repeated;_defaultPrevented=!1;_propagationStopped=!1;constructor(key){this.name=key.name,this.ctrl=key.ctrl,this.meta=key.meta,this.shift=key.shift,this.option=key.option,this.sequence=key.sequence,this.number=key.number,this.raw=key.raw,this.eventType=key.eventType,this.source=key.source,this.code=key.code,this.super=key.super,this.hyper=key.hyper,this.capsLock=key.capsLock,this.numLock=key.numLock,this.baseCode=key.baseCode,this.repeated=key.repeated}get defaultPrevented(){return this._defaultPrevented}get propagationStopped(){return this._propagationStopped}preventDefault(){this._defaultPrevented=!0}stopPropagation(){this._propagationStopped=!0}}class PasteEvent{type="paste";bytes;metadata;_defaultPrevented=!1;_propagationStopped=!1;constructor(bytes,metadata){this.bytes=bytes,this.metadata=metadata}get defaultPrevented(){return this._defaultPrevented}get propagationStopped(){return this._propagationStopped}preventDefault(){this._defaultPrevented=!0}stopPropagation(){this._propagationStopped=!0}}class RGBA{buffer;constructor(buffer2){this.buffer=buffer2}static fromArray(array){return new RGBA(array)}static fromValues(r,g,b2,a=1){return new RGBA(new Float32Array([r,g,b2,a]))}static fromInts(r,g,b2,a=255){return new RGBA(new Float32Array([r/255,g/255,b2/255,a/255]))}static fromHex(hex){return hexToRgb(hex)}toInts(){return[Math.round(this.r*255),Math.round(this.g*255),Math.round(this.b*255),Math.round(this.a*255)]}get r(){return this.buffer[0]}set r(value){this.buffer[0]=value}get g(){return this.buffer[1]}set g(value){this.buffer[1]=value}get b(){return this.buffer[2]}set b(value){this.buffer[2]=value}get a(){return this.buffer[3]}set a(value){this.buffer[3]=value}map(fn){return[fn(this.r),fn(this.g),fn(this.b),fn(this.a)]}toString(){return`rgba(${this.r.toFixed(2)}, ${this.g.toFixed(2)}, ${this.b.toFixed(2)}, ${this.a.toFixed(2)})`}equals(other){if(!other)return!1;return this.r===other.r&&this.g===other.g&&this.b===other.b&&this.a===other.a}}function hexToRgb(hex){if(hex=hex.replace(/^#/,""),hex.length===3)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];else if(hex.length===4)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]+hex[3]+hex[3];if(!/^[0-9A-Fa-f]{6}$/.test(hex)&&!/^[0-9A-Fa-f]{8}$/.test(hex))return console.warn(`Invalid hex color: ${hex}, defaulting to magenta`),RGBA.fromValues(1,0,1,1);let r=parseInt(hex.substring(0,2),16)/255,g=parseInt(hex.substring(2,4),16)/255,b2=parseInt(hex.substring(4,6),16)/255,a=hex.length===8?parseInt(hex.substring(6,8),16)/255:1;return RGBA.fromValues(r,g,b2,a)}function rgbToHex(rgb){return"#"+(rgb.a===1?[rgb.r,rgb.g,rgb.b]:[rgb.r,rgb.g,rgb.b,rgb.a]).map((x)=>{let hex=Math.floor(Math.max(0,Math.min(1,x)*255)).toString(16);return hex.length===1?"0"+hex:hex}).join("")}function hsvToRgb(h,s2,v){let r=0,g=0,b2=0,i2=Math.floor(h/60)%6,f=h/60-Math.floor(h/60),p=v*(1-s2),q=v*(1-f*s2),t=v*(1-(1-f)*s2);switch(i2){case 0:r=v,g=t,b2=p;break;case 1:r=q,g=v,b2=p;break;case 2:r=p,g=v,b2=t;break;case 3:r=p,g=q,b2=v;break;case 4:r=t,g=p,b2=v;break;case 5:r=v,g=p,b2=q;break}return RGBA.fromValues(r,g,b2,1)}function parseColor(color2){if(typeof color2==="string"){let lowerColor=color2.toLowerCase();if(lowerColor==="transparent")return RGBA.fromValues(0,0,0,0);if(CSS_COLOR_NAMES[lowerColor])return hexToRgb(CSS_COLOR_NAMES[lowerColor]);return hexToRgb(color2)}return color2}function parseColorTags(text){let segments=[],currentIndex=0,colorTagRegex=/<c(\d+)>(.*?)<\/c\d+>/g,lastIndex=0,match;while((match=colorTagRegex.exec(text))!==null){if(match.index>lastIndex){let plainText=text.slice(lastIndex,match.index);if(plainText)segments.push({text:plainText,colorIndex:0})}let colorIndex=parseInt(match[1])-1,taggedText=match[2];segments.push({text:taggedText,colorIndex:Math.max(0,colorIndex)}),lastIndex=match.index+match[0].length}if(lastIndex<text.length){let remainingText=text.slice(lastIndex);if(remainingText)segments.push({text:remainingText,colorIndex:0})}return segments}function getParsedFont(fontKey){if(!parsedFonts[fontKey]){let fontDef=fonts[fontKey],parsedChars={};for(let[char,lines]of Object.entries(fontDef.chars))parsedChars[char]=lines.map((line)=>parseColorTags(line));parsedFonts[fontKey]={...fontDef,colors:fontDef.colors||1,chars:parsedChars}}return parsedFonts[fontKey]}function measureText({text,font="tiny"}){let fontDef=getParsedFont(font);if(!fontDef)return console.warn(`Font '${font}' not found`),{width:0,height:0};let currentX=0;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char];if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0]){let spaceWidth=0;for(let segment of spaceChar[0])spaceWidth+=segment.text.length;currentX+=spaceWidth}else currentX+=1;continue}let charWidth=0;if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size}return{width:currentX,height:fontDef.lines}}function getCharacterPositions(text,font="tiny"){let fontDef=getParsedFont(font);if(!fontDef)return[0];let positions=[0],currentX=0;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char],charWidth=0;if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0])for(let segment of spaceChar[0])charWidth+=segment.text.length;else charWidth=1}else if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size;positions.push(currentX)}return positions}function coordinateToCharacterIndex(x,text,font="tiny"){let positions=getCharacterPositions(text,font);if(x<0)return 0;for(let i2=0;i2<positions.length-1;i2++){let currentPos=positions[i2],nextPos=positions[i2+1];if(x>=currentPos&&x<nextPos){let charMidpoint=currentPos+(nextPos-currentPos)/2;return x<charMidpoint?i2:i2+1}}if(positions.length>0&&x>=positions[positions.length-1])return text.length;return 0}function renderFontToFrameBuffer(buffer2,{text,x=0,y=0,color:color2=[RGBA.fromInts(255,255,255,255)],backgroundColor=RGBA.fromInts(0,0,0,255),font="tiny"}){let{width,height:height2}=buffer2,fontDef=getParsedFont(font);if(!fontDef)return console.warn(`Font '${font}' not found`),{width:0,height:0};let colors8=Array.isArray(color2)?color2:[color2];if(y<0||y+fontDef.lines>height2)return{width:0,height:fontDef.lines};let currentX=x,startX=x;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char];if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0]){let spaceWidth=0;for(let segment of spaceChar[0])spaceWidth+=segment.text.length;currentX+=spaceWidth}else currentX+=1;continue}let charWidth=0;if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX>=width)break;if(currentX+charWidth<0){currentX+=charWidth+fontDef.letterspace_size;continue}for(let lineIdx=0;lineIdx<fontDef.lines&&lineIdx<charDef.length;lineIdx++){let segments=charDef[lineIdx],renderY=y+lineIdx;if(renderY>=0&&renderY<height2){let segmentX=currentX;for(let segment of segments){let segmentColor=colors8[segment.colorIndex]||colors8[0];for(let charIdx=0;charIdx<segment.text.length;charIdx++){let renderX=segmentX+charIdx;if(renderX>=0&&renderX<width){let fontChar=segment.text[charIdx];if(fontChar!==" ")buffer2.setCellWithAlphaBlending(renderX,renderY,fontChar,parseColor(segmentColor),parseColor(backgroundColor))}}segmentX+=segment.text.length}}}if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size}return{width:currentX-startX,height:fontDef.lines}}function getBaseAttributes(attr){return attr&ATTRIBUTE_BASE_MASK}function createTextAttributes({bold=!1,italic=!1,underline=!1,dim=!1,blink=!1,inverse=!1,hidden=!1,strikethrough=!1}={}){let attributes=TextAttributes.NONE;if(bold)attributes|=TextAttributes.BOLD;if(italic)attributes|=TextAttributes.ITALIC;if(underline)attributes|=TextAttributes.UNDERLINE;if(dim)attributes|=TextAttributes.DIM;if(blink)attributes|=TextAttributes.BLINK;if(inverse)attributes|=TextAttributes.INVERSE;if(hidden)attributes|=TextAttributes.HIDDEN;if(strikethrough)attributes|=TextAttributes.STRIKETHROUGH;return attributes}function attributesWithLink(baseAttributes,linkId){let base=baseAttributes&ATTRIBUTE_BASE_MASK2,linkBits=(linkId&LINK_ID_PAYLOAD_MASK)<<LINK_ID_SHIFT;return base|linkBits}function getLinkId(attributes){return attributes>>>LINK_ID_SHIFT&LINK_ID_PAYLOAD_MASK}function visualizeRenderableTree(renderable,maxDepth=10){function buildTreeLines(node,prefix="",parentPrefix="",isLastChild=!0,depth=0){if(depth>=maxDepth)return[`${prefix}${node.id} ... (max depth reached)`];let lines=[],children=node.getChildren();if(lines.push(`${prefix}${node.id}`),children.length>0){let lastChildIndex=children.length-1;children.forEach((child,index)=>{let childIsLast=index===lastChildIndex,connector=childIsLast?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",childPrefix=parentPrefix+(isLastChild?" ":"\u2502 "),childLines=buildTreeLines(child,childPrefix+connector,childPrefix,childIsLast,depth+1);lines.push(...childLines)})}return lines}let treeLines=buildTreeLines(renderable);console.log(`Renderable Tree:
|
|
2296
|
+
`).map((r)=>({id:r.id,seq:r.seq,title:r.title,stage:r.stage,repoPath:r.repo_path}))}var init_task_close_merged=__esm(()=>{init_wish_state()});var highlights_default="./highlights-ghv9g403.scm";var init_highlights=()=>{};var tree_sitter_javascript_default="./tree-sitter-javascript-nd0q4pe9.wasm";var init_tree_sitter_javascript=()=>{};var highlights_default2="./highlights-eq9cgrbb.scm";var init_highlights2=()=>{};var tree_sitter_typescript_default="./tree-sitter-typescript-zxjzwt75.wasm";var init_tree_sitter_typescript=()=>{};var highlights_default3="./highlights-r812a2qc.scm";var init_highlights3=()=>{};var tree_sitter_markdown_default="./tree-sitter-markdown-411r6y9b.wasm";var init_tree_sitter_markdown=()=>{};var injections_default="./injections-73j83es3.scm";var init_injections=()=>{};var highlights_default4="./highlights-x6tmsnaa.scm";var init_highlights4=()=>{};var tree_sitter_markdown_inline_default="./tree-sitter-markdown_inline-j5349f42.wasm";var init_tree_sitter_markdown_inline=()=>{};var highlights_default5="./highlights-hk7bwhj4.scm";var init_highlights5=()=>{};var tree_sitter_zig_default="./tree-sitter-zig-e78zbjpm.wasm";var init_tree_sitter_zig=()=>{};import{EventEmitter}from"events";import{Buffer as Buffer2}from"buffer";import{Buffer as Buffer3}from"buffer";import{EventEmitter as EventEmitter2}from"events";import{resolve as resolve12,dirname as dirname12}from"path";import{fileURLToPath as fileURLToPath2}from"url";import{resolve as resolve22,isAbsolute,parse as parse2}from"path";import{existsSync as existsSync45}from"fs";import{basename as basename11,join as join57}from"path";import os3 from"os";import path3 from"path";import{EventEmitter as EventEmitter3}from"events";import path22 from"path";import{readFile as readFile22,writeFile as writeFile22,mkdir as mkdir22}from"fs/promises";import*as path4 from"path";import{mkdir as mkdir9,readFile as readFile14,writeFile as writeFile7}from"fs/promises";import*as path32 from"path";import{readdir as readdir7}from"fs/promises";import{dlopen,toArrayBuffer as toArrayBuffer4,JSCallback,ptr as ptr4}from"bun:ffi";import{existsSync as existsSync210,writeFileSync as writeFileSync23}from"fs";import{EventEmitter as EventEmitter4}from"events";import{toArrayBuffer,ptr}from"bun:ffi";import{ptr as ptr2,toArrayBuffer as toArrayBuffer2}from"bun:ffi";import{ptr as ptr3,toArrayBuffer as toArrayBuffer3}from"bun:ffi";import{EventEmitter as EventEmitter5}from"events";import util3 from"util";import{EventEmitter as EventEmitter7}from"events";import{Console}from"console";import fs2 from"fs";import path5 from"path";import util22 from"util";import{Writable}from"stream";import{EventEmitter as EventEmitter6}from"events";import{EventEmitter as EventEmitter8}from"events";function __exportSetter2(name,newValue){this[name]=__returnValue2.bind(null,newValue)}function wrapAssembly(lib){function patch(prototype,name,fn){let original=prototype[name];prototype[name]=function(){for(var _len=arguments.length,args=Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];return fn.call(this,original,...args)}}for(let fnName of["setPosition","setMargin","setFlexBasis","setWidth","setHeight","setMinWidth","setMinHeight","setMaxWidth","setMaxHeight","setPadding","setGap"]){let methods={[Unit.Point]:lib.Node.prototype[fnName],[Unit.Percent]:lib.Node.prototype[`${fnName}Percent`],[Unit.Auto]:lib.Node.prototype[`${fnName}Auto`]};patch(lib.Node.prototype,fnName,function(original){for(var _len2=arguments.length,args=Array(_len2>1?_len2-1:0),_key2=1;_key2<_len2;_key2++)args[_key2-1]=arguments[_key2];let value=args.pop(),unit,asNumber;if(value==="auto")unit=Unit.Auto,asNumber=void 0;else if(typeof value==="object")unit=value.unit,asNumber=value.valueOf();else if(unit=typeof value==="string"&&value.endsWith("%")?Unit.Percent:Unit.Point,asNumber=parseFloat(value),value!==void 0&&!Number.isNaN(value)&&Number.isNaN(asNumber))throw Error(`Invalid value ${value} for ${fnName}`);if(!methods[unit])throw Error(`Failed to execute "${fnName}": Unsupported unit '${value}'`);if(asNumber!==void 0)return methods[unit].call(this,...args,asNumber);else return methods[unit].call(this,...args)})}function wrapMeasureFunction(measureFunction){return lib.MeasureCallback.implement({measure:function(){let{width,height:height2}=measureFunction(...arguments);return{width:width??NaN,height:height2??NaN}}})}patch(lib.Node.prototype,"setMeasureFunc",function(original,measureFunc){if(measureFunc)return original.call(this,wrapMeasureFunction(measureFunc));else return this.unsetMeasureFunc()});function wrapDirtiedFunc(dirtiedFunction){return lib.DirtiedCallback.implement({dirtied:dirtiedFunction})}return patch(lib.Node.prototype,"setDirtiedFunc",function(original,dirtiedFunc){original.call(this,wrapDirtiedFunc(dirtiedFunc))}),patch(lib.Config.prototype,"free",function(){lib.Config.destroy(this)}),patch(lib.Node,"create",(_,config)=>{return config?lib.Node.createWithConfig(config):lib.Node.createDefault()}),patch(lib.Node.prototype,"free",function(){lib.Node.destroy(this)}),patch(lib.Node.prototype,"freeRecursive",function(){for(let t=0,T=this.getChildCount();t<T;++t)this.getChild(0).freeRecursive();this.free()}),patch(lib.Node.prototype,"calculateLayout",function(original){let width=arguments.length>1&&arguments[1]!==void 0?arguments[1]:NaN,height2=arguments.length>2&&arguments[2]!==void 0?arguments[2]:NaN,direction=arguments.length>3&&arguments[3]!==void 0?arguments[3]:Direction.LTR;return original.call(this,width,height2,direction)}),{Config:lib.Config,Node:lib.Node,...YGEnums_default}}function isValidBorderStyle(value){return typeof value==="string"&&VALID_BORDER_STYLES.includes(value)}function parseBorderStyle(value,fallback="single"){if(isValidBorderStyle(value))return value;if(value!==void 0&&value!==null)console.warn(`Invalid borderStyle "${value}", falling back to "${fallback}". Valid values are: ${VALID_BORDER_STYLES.join(", ")}`);return fallback}function getBorderFromSides(sides){let result2=[];if(sides.top)result2.push("top");if(sides.right)result2.push("right");if(sides.bottom)result2.push("bottom");if(sides.left)result2.push("left");return result2.length>0?result2:!1}function getBorderSides(border){return border===!0?{top:!0,right:!0,bottom:!0,left:!0}:Array.isArray(border)?{top:border.includes("top"),right:border.includes("right"),bottom:border.includes("bottom"),left:border.includes("left")}:{top:!1,right:!1,bottom:!1,left:!1}}function borderCharsToArray(chars){let array=new Uint32Array(11);return array[0]=chars.topLeft.codePointAt(0),array[1]=chars.topRight.codePointAt(0),array[2]=chars.bottomLeft.codePointAt(0),array[3]=chars.bottomRight.codePointAt(0),array[4]=chars.horizontal.codePointAt(0),array[5]=chars.vertical.codePointAt(0),array[6]=chars.topT.codePointAt(0),array[7]=chars.bottomT.codePointAt(0),array[8]=chars.leftT.codePointAt(0),array[9]=chars.rightT.codePointAt(0),array[10]=chars.cross.codePointAt(0),array}class KeyEvent{name;ctrl;meta;shift;option;sequence;number;raw;eventType;source;code;super;hyper;capsLock;numLock;baseCode;repeated;_defaultPrevented=!1;_propagationStopped=!1;constructor(key){this.name=key.name,this.ctrl=key.ctrl,this.meta=key.meta,this.shift=key.shift,this.option=key.option,this.sequence=key.sequence,this.number=key.number,this.raw=key.raw,this.eventType=key.eventType,this.source=key.source,this.code=key.code,this.super=key.super,this.hyper=key.hyper,this.capsLock=key.capsLock,this.numLock=key.numLock,this.baseCode=key.baseCode,this.repeated=key.repeated}get defaultPrevented(){return this._defaultPrevented}get propagationStopped(){return this._propagationStopped}preventDefault(){this._defaultPrevented=!0}stopPropagation(){this._propagationStopped=!0}}class PasteEvent{type="paste";bytes;metadata;_defaultPrevented=!1;_propagationStopped=!1;constructor(bytes,metadata){this.bytes=bytes,this.metadata=metadata}get defaultPrevented(){return this._defaultPrevented}get propagationStopped(){return this._propagationStopped}preventDefault(){this._defaultPrevented=!0}stopPropagation(){this._propagationStopped=!0}}class RGBA{buffer;constructor(buffer2){this.buffer=buffer2}static fromArray(array){return new RGBA(array)}static fromValues(r,g,b2,a=1){return new RGBA(new Float32Array([r,g,b2,a]))}static fromInts(r,g,b2,a=255){return new RGBA(new Float32Array([r/255,g/255,b2/255,a/255]))}static fromHex(hex){return hexToRgb(hex)}toInts(){return[Math.round(this.r*255),Math.round(this.g*255),Math.round(this.b*255),Math.round(this.a*255)]}get r(){return this.buffer[0]}set r(value){this.buffer[0]=value}get g(){return this.buffer[1]}set g(value){this.buffer[1]=value}get b(){return this.buffer[2]}set b(value){this.buffer[2]=value}get a(){return this.buffer[3]}set a(value){this.buffer[3]=value}map(fn){return[fn(this.r),fn(this.g),fn(this.b),fn(this.a)]}toString(){return`rgba(${this.r.toFixed(2)}, ${this.g.toFixed(2)}, ${this.b.toFixed(2)}, ${this.a.toFixed(2)})`}equals(other){if(!other)return!1;return this.r===other.r&&this.g===other.g&&this.b===other.b&&this.a===other.a}}function hexToRgb(hex){if(hex=hex.replace(/^#/,""),hex.length===3)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];else if(hex.length===4)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]+hex[3]+hex[3];if(!/^[0-9A-Fa-f]{6}$/.test(hex)&&!/^[0-9A-Fa-f]{8}$/.test(hex))return console.warn(`Invalid hex color: ${hex}, defaulting to magenta`),RGBA.fromValues(1,0,1,1);let r=parseInt(hex.substring(0,2),16)/255,g=parseInt(hex.substring(2,4),16)/255,b2=parseInt(hex.substring(4,6),16)/255,a=hex.length===8?parseInt(hex.substring(6,8),16)/255:1;return RGBA.fromValues(r,g,b2,a)}function rgbToHex(rgb){return"#"+(rgb.a===1?[rgb.r,rgb.g,rgb.b]:[rgb.r,rgb.g,rgb.b,rgb.a]).map((x)=>{let hex=Math.floor(Math.max(0,Math.min(1,x)*255)).toString(16);return hex.length===1?"0"+hex:hex}).join("")}function hsvToRgb(h,s2,v){let r=0,g=0,b2=0,i2=Math.floor(h/60)%6,f=h/60-Math.floor(h/60),p=v*(1-s2),q=v*(1-f*s2),t=v*(1-(1-f)*s2);switch(i2){case 0:r=v,g=t,b2=p;break;case 1:r=q,g=v,b2=p;break;case 2:r=p,g=v,b2=t;break;case 3:r=p,g=q,b2=v;break;case 4:r=t,g=p,b2=v;break;case 5:r=v,g=p,b2=q;break}return RGBA.fromValues(r,g,b2,1)}function parseColor(color2){if(typeof color2==="string"){let lowerColor=color2.toLowerCase();if(lowerColor==="transparent")return RGBA.fromValues(0,0,0,0);if(CSS_COLOR_NAMES[lowerColor])return hexToRgb(CSS_COLOR_NAMES[lowerColor]);return hexToRgb(color2)}return color2}function parseColorTags(text){let segments=[],currentIndex=0,colorTagRegex=/<c(\d+)>(.*?)<\/c\d+>/g,lastIndex=0,match;while((match=colorTagRegex.exec(text))!==null){if(match.index>lastIndex){let plainText=text.slice(lastIndex,match.index);if(plainText)segments.push({text:plainText,colorIndex:0})}let colorIndex=parseInt(match[1])-1,taggedText=match[2];segments.push({text:taggedText,colorIndex:Math.max(0,colorIndex)}),lastIndex=match.index+match[0].length}if(lastIndex<text.length){let remainingText=text.slice(lastIndex);if(remainingText)segments.push({text:remainingText,colorIndex:0})}return segments}function getParsedFont(fontKey){if(!parsedFonts[fontKey]){let fontDef=fonts[fontKey],parsedChars={};for(let[char,lines]of Object.entries(fontDef.chars))parsedChars[char]=lines.map((line)=>parseColorTags(line));parsedFonts[fontKey]={...fontDef,colors:fontDef.colors||1,chars:parsedChars}}return parsedFonts[fontKey]}function measureText({text,font="tiny"}){let fontDef=getParsedFont(font);if(!fontDef)return console.warn(`Font '${font}' not found`),{width:0,height:0};let currentX=0;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char];if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0]){let spaceWidth=0;for(let segment of spaceChar[0])spaceWidth+=segment.text.length;currentX+=spaceWidth}else currentX+=1;continue}let charWidth=0;if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size}return{width:currentX,height:fontDef.lines}}function getCharacterPositions(text,font="tiny"){let fontDef=getParsedFont(font);if(!fontDef)return[0];let positions=[0],currentX=0;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char],charWidth=0;if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0])for(let segment of spaceChar[0])charWidth+=segment.text.length;else charWidth=1}else if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size;positions.push(currentX)}return positions}function coordinateToCharacterIndex(x,text,font="tiny"){let positions=getCharacterPositions(text,font);if(x<0)return 0;for(let i2=0;i2<positions.length-1;i2++){let currentPos=positions[i2],nextPos=positions[i2+1];if(x>=currentPos&&x<nextPos){let charMidpoint=currentPos+(nextPos-currentPos)/2;return x<charMidpoint?i2:i2+1}}if(positions.length>0&&x>=positions[positions.length-1])return text.length;return 0}function renderFontToFrameBuffer(buffer2,{text,x=0,y=0,color:color2=[RGBA.fromInts(255,255,255,255)],backgroundColor=RGBA.fromInts(0,0,0,255),font="tiny"}){let{width,height:height2}=buffer2,fontDef=getParsedFont(font);if(!fontDef)return console.warn(`Font '${font}' not found`),{width:0,height:0};let colors8=Array.isArray(color2)?color2:[color2];if(y<0||y+fontDef.lines>height2)return{width:0,height:fontDef.lines};let currentX=x,startX=x;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char];if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0]){let spaceWidth=0;for(let segment of spaceChar[0])spaceWidth+=segment.text.length;currentX+=spaceWidth}else currentX+=1;continue}let charWidth=0;if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX>=width)break;if(currentX+charWidth<0){currentX+=charWidth+fontDef.letterspace_size;continue}for(let lineIdx=0;lineIdx<fontDef.lines&&lineIdx<charDef.length;lineIdx++){let segments=charDef[lineIdx],renderY=y+lineIdx;if(renderY>=0&&renderY<height2){let segmentX=currentX;for(let segment of segments){let segmentColor=colors8[segment.colorIndex]||colors8[0];for(let charIdx=0;charIdx<segment.text.length;charIdx++){let renderX=segmentX+charIdx;if(renderX>=0&&renderX<width){let fontChar=segment.text[charIdx];if(fontChar!==" ")buffer2.setCellWithAlphaBlending(renderX,renderY,fontChar,parseColor(segmentColor),parseColor(backgroundColor))}}segmentX+=segment.text.length}}}if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size}return{width:currentX-startX,height:fontDef.lines}}function getBaseAttributes(attr){return attr&ATTRIBUTE_BASE_MASK}function createTextAttributes({bold=!1,italic=!1,underline=!1,dim=!1,blink=!1,inverse=!1,hidden=!1,strikethrough=!1}={}){let attributes=TextAttributes.NONE;if(bold)attributes|=TextAttributes.BOLD;if(italic)attributes|=TextAttributes.ITALIC;if(underline)attributes|=TextAttributes.UNDERLINE;if(dim)attributes|=TextAttributes.DIM;if(blink)attributes|=TextAttributes.BLINK;if(inverse)attributes|=TextAttributes.INVERSE;if(hidden)attributes|=TextAttributes.HIDDEN;if(strikethrough)attributes|=TextAttributes.STRIKETHROUGH;return attributes}function attributesWithLink(baseAttributes,linkId){let base=baseAttributes&ATTRIBUTE_BASE_MASK2,linkBits=(linkId&LINK_ID_PAYLOAD_MASK)<<LINK_ID_SHIFT;return base|linkBits}function getLinkId(attributes){return attributes>>>LINK_ID_SHIFT&LINK_ID_PAYLOAD_MASK}function visualizeRenderableTree(renderable,maxDepth=10){function buildTreeLines(node,prefix="",parentPrefix="",isLastChild=!0,depth=0){if(depth>=maxDepth)return[`${prefix}${node.id} ... (max depth reached)`];let lines=[],children=node.getChildren();if(lines.push(`${prefix}${node.id}`),children.length>0){let lastChildIndex=children.length-1;children.forEach((child,index)=>{let childIsLast=index===lastChildIndex,connector=childIsLast?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",childPrefix=parentPrefix+(isLastChild?" ":"\u2502 "),childLines=buildTreeLines(child,childPrefix+connector,childPrefix,childIsLast,depth+1);lines.push(...childLines)})}return lines}let treeLines=buildTreeLines(renderable);console.log(`Renderable Tree:
|
|
2297
2297
|
`+treeLines.join(`
|
|
2298
2298
|
`))}function isStyledText(obj){return obj&&obj[BrandedStyledText]}function stringToStyledText(content){return new StyledText([{__isChunk:!0,text:content}])}function applyStyle(input,style){if(typeof input==="object"&&"__isChunk"in input){let existingChunk=input,fg=style.fg?parseColor(style.fg):existingChunk.fg,bg=style.bg?parseColor(style.bg):existingChunk.bg,newAttrs=createTextAttributes(style),mergedAttrs=existingChunk.attributes?existingChunk.attributes|newAttrs:newAttrs;return{__isChunk:!0,text:existingChunk.text,fg,bg,attributes:mergedAttrs,link:existingChunk.link}}else{let plainTextStr=String(input),fg=style.fg?parseColor(style.fg):void 0,bg=style.bg?parseColor(style.bg):void 0,attributes=createTextAttributes(style);return{__isChunk:!0,text:plainTextStr,fg,bg,attributes}}}function t(strings,...values2){let chunks=[];for(let i2=0;i2<strings.length;i2++){let raw=strings[i2];if(raw)chunks.push({__isChunk:!0,text:raw,attributes:0});let val=values2[i2];if(typeof val==="object"&&"__isChunk"in val)chunks.push(val);else if(val!==void 0){let plainTextStr=String(val);chunks.push({__isChunk:!0,text:plainTextStr,attributes:0})}}return new StyledText(chunks)}function hastToTextChunks(node,syntaxStyle,parentStyles=[]){let chunks=[];if(node.type==="text"){let stylesToMerge=parentStyles.length>0?parentStyles:["default"],mergedStyle=syntaxStyle.mergeStyles(...stylesToMerge);chunks.push({__isChunk:!0,text:node.value,fg:mergedStyle.fg,bg:mergedStyle.bg,attributes:mergedStyle.attributes})}else if(node.type==="element"){let currentStyles=[...parentStyles];if(node.properties?.className){let classes=node.properties.className.split(" ");for(let cls of classes)currentStyles.push(cls)}for(let child of node.children)chunks.push(...hastToTextChunks(child,syntaxStyle,currentStyles))}return chunks}function hastToStyledText(hast,syntaxStyle){let chunks=hastToTextChunks(hast,syntaxStyle);return new StyledText(chunks)}class SystemClock{now(){if(!globalThis.performance||typeof globalThis.performance.now!=="function")throw Error("SystemClock requires globalThis.performance.now()");return globalThis.performance.now()}setTimeout(fn,delayMs){return globalThis.setTimeout(fn,delayMs)}clearTimeout(handle){globalThis.clearTimeout(handle)}setInterval(fn,delayMs){return globalThis.setInterval(fn,delayMs)}clearInterval(handle){globalThis.clearInterval(handle)}}function fromKittyMods(mod){return{shift:!!(mod&1),alt:!!(mod&2),ctrl:!!(mod&4),super:!!(mod&8),hyper:!!(mod&16),meta:!!(mod&32),capsLock:!!(mod&64),numLock:!!(mod&128)}}function parseKittySpecialKey(sequence){let match=/^\x1b\[(\d+);(\d+):(\d+)([A-Z~])$/.exec(sequence);if(!match)return null;let keyNumOrOne=match[1],modifierStr=match[2],eventTypeStr=match[3],terminator=match[4],keyName;if(terminator==="~")keyName=tildeKeyMap[keyNumOrOne];else{if(keyNumOrOne!=="1")return null;keyName=functionalKeyMap[terminator]}if(!keyName)return null;let key={name:keyName,ctrl:!1,meta:!1,shift:!1,option:!1,number:!1,sequence,raw:sequence,eventType:"press",source:"kitty",super:!1,hyper:!1,capsLock:!1,numLock:!1};if(modifierStr){let modifierMask=parseInt(modifierStr,10);if(!isNaN(modifierMask)&&modifierMask>1){let mods=fromKittyMods(modifierMask-1);key.shift=mods.shift,key.ctrl=mods.ctrl,key.meta=mods.alt||mods.meta,key.option=mods.alt,key.super=mods.super,key.hyper=mods.hyper,key.capsLock=mods.capsLock,key.numLock=mods.numLock}}if(eventTypeStr==="1"||!eventTypeStr)key.eventType="press";else if(eventTypeStr==="2")key.eventType="press",key.repeated=!0;else if(eventTypeStr==="3")key.eventType="release";return key}function parseKittyKeyboard(sequence){let specialResult=parseKittySpecialKey(sequence);if(specialResult)return specialResult;let match=/^\x1b\[([^\x1b]+)u$/.exec(sequence);if(!match)return null;let fields=match[1].split(";");if(fields.length<1)return null;let key={name:"",ctrl:!1,meta:!1,shift:!1,option:!1,number:!1,sequence,raw:sequence,eventType:"press",source:"kitty",super:!1,hyper:!1,capsLock:!1,numLock:!1},text="",field1=fields[0]?.split(":")||[],codepointStr=field1[0];if(!codepointStr)return null;let codepoint=parseInt(codepointStr,10);if(isNaN(codepoint))return null;let shiftedCodepoint,baseCodepoint;if(field1[1]){let shifted=parseInt(field1[1],10);if(!isNaN(shifted)&&shifted>0&&shifted<=1114111)shiftedCodepoint=shifted}if(field1[2]){let base=parseInt(field1[2],10);if(!isNaN(base)&&base>0&&base<=1114111)baseCodepoint=base}let knownKey=kittyKeyMap[codepoint];if(knownKey)key.name=knownKey,key.code=`[${codepoint}u`;else if(codepoint===0)key.name="";else if(codepoint>0&&codepoint<=1114111){let char=String.fromCodePoint(codepoint);if(key.name=char,baseCodepoint)key.baseCode=baseCodepoint}else return null;if(fields[1]){let field2=fields[1].split(":"),modifierStr=field2[0],eventTypeStr=field2[1];if(modifierStr){let modifierMask=parseInt(modifierStr,10);if(!isNaN(modifierMask)&&modifierMask>1){let mods=fromKittyMods(modifierMask-1);key.shift=mods.shift,key.ctrl=mods.ctrl,key.meta=mods.alt||mods.meta,key.option=mods.alt,key.super=mods.super,key.hyper=mods.hyper,key.capsLock=mods.capsLock,key.numLock=mods.numLock}}if(eventTypeStr==="1"||!eventTypeStr)key.eventType="press";else if(eventTypeStr==="2")key.eventType="press",key.repeated=!0;else if(eventTypeStr==="3")key.eventType="release";else key.eventType="press"}if(fields[2]){let codepoints=fields[2].split(":");for(let cpStr of codepoints){let cp3=parseInt(cpStr,10);if(!isNaN(cp3)&&cp3>0&&cp3<=1114111)text+=String.fromCodePoint(cp3)}}if(text===""){if(key.name.length>0&&!kittyKeyMap[codepoint])if(key.shift&&shiftedCodepoint)text=String.fromCodePoint(shiftedCodepoint);else if(key.shift&&key.name.length===1)text=key.name.toLocaleUpperCase();else text=key.name}if(key.name===" "&&key.shift&&!key.ctrl&&!key.meta)text=" ";if(text){if(codepoint===0)key.name=text;key.sequence=text}if(codepoint===0&&text==="")return null;return key}class LinearScrollAccel{tick(_now){return 1}reset(){}}class MacOSScrollAccel{opts;lastTickTime=0;velocityHistory=[];historySize=3;streakTimeout=150;minTickInterval=6;constructor(opts={}){this.opts=opts}tick(now=Date.now()){let A=this.opts.A??0.8,tau=this.opts.tau??3,maxMultiplier=this.opts.maxMultiplier??6,dt=this.lastTickTime?now-this.lastTickTime:1/0;if(dt===1/0||dt>this.streakTimeout)return this.lastTickTime=now,this.velocityHistory=[],1;if(dt<this.minTickInterval)return 1;if(this.lastTickTime=now,this.velocityHistory.push(dt),this.velocityHistory.length>this.historySize)this.velocityHistory.shift();let x=100/(this.velocityHistory.reduce((a,b2)=>a+b2,0)/this.velocityHistory.length)/tau,multiplier=1+A*(Math.exp(x)-1);return Math.min(multiplier,maxMultiplier)}reset(){this.lastTickTime=0,this.velocityHistory=[]}}function parseAlign(value){if(value==null)return Align.Auto;switch(value.toLowerCase()){case"auto":return Align.Auto;case"flex-start":return Align.FlexStart;case"center":return Align.Center;case"flex-end":return Align.FlexEnd;case"stretch":return Align.Stretch;case"baseline":return Align.Baseline;case"space-between":return Align.SpaceBetween;case"space-around":return Align.SpaceAround;case"space-evenly":return Align.SpaceEvenly;default:return Align.Auto}}function parseAlignItems(value){if(value==null)return Align.Stretch;switch(value.toLowerCase()){case"auto":return Align.Auto;case"flex-start":return Align.FlexStart;case"center":return Align.Center;case"flex-end":return Align.FlexEnd;case"stretch":return Align.Stretch;case"baseline":return Align.Baseline;case"space-between":return Align.SpaceBetween;case"space-around":return Align.SpaceAround;case"space-evenly":return Align.SpaceEvenly;default:return Align.Stretch}}function parseBoxSizing(value){if(value==null)return BoxSizing.BorderBox;switch(value.toLowerCase()){case"border-box":return BoxSizing.BorderBox;case"content-box":return BoxSizing.ContentBox;default:return BoxSizing.BorderBox}}function parseDimension(value){if(value==null)return Dimension.Width;switch(value.toLowerCase()){case"width":return Dimension.Width;case"height":return Dimension.Height;default:return Dimension.Width}}function parseDirection(value){if(value==null)return Direction.LTR;switch(value.toLowerCase()){case"inherit":return Direction.Inherit;case"ltr":return Direction.LTR;case"rtl":return Direction.RTL;default:return Direction.LTR}}function parseDisplay(value){if(value==null)return Display.Flex;switch(value.toLowerCase()){case"flex":return Display.Flex;case"none":return Display.None;case"contents":return Display.Contents;default:return Display.Flex}}function parseEdge(value){if(value==null)return Edge.All;switch(value.toLowerCase()){case"left":return Edge.Left;case"top":return Edge.Top;case"right":return Edge.Right;case"bottom":return Edge.Bottom;case"start":return Edge.Start;case"end":return Edge.End;case"horizontal":return Edge.Horizontal;case"vertical":return Edge.Vertical;case"all":return Edge.All;default:return Edge.All}}function parseFlexDirection(value){if(value==null)return FlexDirection.Column;switch(value.toLowerCase()){case"column":return FlexDirection.Column;case"column-reverse":return FlexDirection.ColumnReverse;case"row":return FlexDirection.Row;case"row-reverse":return FlexDirection.RowReverse;default:return FlexDirection.Column}}function parseGutter(value){if(value==null)return Gutter.All;switch(value.toLowerCase()){case"column":return Gutter.Column;case"row":return Gutter.Row;case"all":return Gutter.All;default:return Gutter.All}}function parseJustify(value){if(value==null)return Justify.FlexStart;switch(value.toLowerCase()){case"flex-start":return Justify.FlexStart;case"center":return Justify.Center;case"flex-end":return Justify.FlexEnd;case"space-between":return Justify.SpaceBetween;case"space-around":return Justify.SpaceAround;case"space-evenly":return Justify.SpaceEvenly;default:return Justify.FlexStart}}function parseLogLevel(value){if(value==null)return LogLevel.Info;switch(value.toLowerCase()){case"error":return LogLevel.Error;case"warn":return LogLevel.Warn;case"info":return LogLevel.Info;case"debug":return LogLevel.Debug;case"verbose":return LogLevel.Verbose;case"fatal":return LogLevel.Fatal;default:return LogLevel.Info}}function parseMeasureMode(value){if(value==null)return MeasureMode.Undefined;switch(value.toLowerCase()){case"undefined":return MeasureMode.Undefined;case"exactly":return MeasureMode.Exactly;case"at-most":return MeasureMode.AtMost;default:return MeasureMode.Undefined}}function parseOverflow(value){if(value==null)return Overflow.Visible;switch(value.toLowerCase()){case"visible":return Overflow.Visible;case"hidden":return Overflow.Hidden;case"scroll":return Overflow.Scroll;default:return Overflow.Visible}}function parsePositionType(value){if(value==null)return PositionType.Relative;switch(value.toLowerCase()){case"static":return PositionType.Static;case"relative":return PositionType.Relative;case"absolute":return PositionType.Absolute;default:return PositionType.Static}}function parseUnit(value){if(value==null)return Unit.Point;switch(value.toLowerCase()){case"undefined":return Unit.Undefined;case"point":return Unit.Point;case"percent":return Unit.Percent;case"auto":return Unit.Auto;default:return Unit.Point}}function parseWrap(value){if(value==null)return Wrap.NoWrap;switch(value.toLowerCase()){case"no-wrap":return Wrap.NoWrap;case"wrap":return Wrap.Wrap;case"wrap-reverse":return Wrap.WrapReverse;default:return Wrap.NoWrap}}class SelectionAnchor{renderable;relativeX;relativeY;constructor(renderable,absoluteX,absoluteY){this.renderable=renderable,this.relativeX=absoluteX-this.renderable.x,this.relativeY=absoluteY-this.renderable.y}get x(){return this.renderable.x+this.relativeX}get y(){return this.renderable.y+this.relativeY}}class Selection{_anchor;_focus;_selectedRenderables=[];_touchedRenderables=[];_isActive=!0;_isDragging=!0;_isStart=!1;constructor(anchorRenderable,anchor,focus){this._anchor=new SelectionAnchor(anchorRenderable,anchor.x,anchor.y),this._focus={...focus}}get isStart(){return this._isStart}set isStart(value){this._isStart=value}get anchor(){return{x:this._anchor.x,y:this._anchor.y}}get focus(){return{...this._focus}}set focus(value){this._focus={...value}}get isActive(){return this._isActive}set isActive(value){this._isActive=value}get isDragging(){return this._isDragging}set isDragging(value){this._isDragging=value}get bounds(){let minX=Math.min(this._anchor.x,this._focus.x),maxX=Math.max(this._anchor.x,this._focus.x),minY=Math.min(this._anchor.y,this._focus.y),maxY=Math.max(this._anchor.y,this._focus.y),width=maxX-minX+1,height2=maxY-minY+1;return{x:minX,y:minY,width,height:height2}}updateSelectedRenderables(selectedRenderables){this._selectedRenderables=selectedRenderables}get selectedRenderables(){return this._selectedRenderables}updateTouchedRenderables(touchedRenderables){this._touchedRenderables=touchedRenderables}get touchedRenderables(){return this._touchedRenderables}getSelectedText(){return this._selectedRenderables.sort((a,b2)=>{let aY=a.y,bY=b2.y;if(aY!==bY)return aY-bY;return a.x-b2.x}).filter((renderable)=>!renderable.isDestroyed).map((renderable)=>renderable.getSelectedText()).filter((text)=>text).join(`
|
|
2299
2299
|
`)}}function convertGlobalToLocalSelection(globalSelection,localX,localY){if(!globalSelection?.isActive)return null;return{anchorX:globalSelection.anchor.x-localX,anchorY:globalSelection.anchor.y-localY,focusX:globalSelection.focus.x-localX,focusY:globalSelection.focus.y-localY,isActive:!0}}class ASCIIFontSelectionHelper{getText;getFont;localSelection=null;constructor(getText,getFont){this.getText=getText,this.getFont=getFont}hasSelection(){return this.localSelection!==null}getSelection(){return this.localSelection}shouldStartSelection(localX,localY,width,height2){if(localX<0||localX>=width||localY<0||localY>=height2)return!1;let text=this.getText(),font=this.getFont(),charIndex=coordinateToCharacterIndex(localX,text,font);return charIndex>=0&&charIndex<=text.length}onLocalSelectionChanged(localSelection,width,height2){let previousSelection=this.localSelection;if(!localSelection?.isActive)return this.localSelection=null,previousSelection!==null;let text=this.getText(),font=this.getFont(),selStart={x:localSelection.anchorX,y:localSelection.anchorY},selEnd={x:localSelection.focusX,y:localSelection.focusY};if(height2-1<selStart.y||0>selEnd.y)return this.localSelection=null,previousSelection!==null;let startCharIndex=0,endCharIndex=text.length;if(selStart.y>height2-1)return this.localSelection=null,previousSelection!==null;else if(selStart.y>=0&&selStart.y<=height2-1){if(selStart.x>0)startCharIndex=coordinateToCharacterIndex(selStart.x,text,font)}if(selEnd.y<0)return this.localSelection=null,previousSelection!==null;else if(selEnd.y>=0&&selEnd.y<=height2-1)if(selEnd.x>=0)endCharIndex=coordinateToCharacterIndex(selEnd.x,text,font);else endCharIndex=0;if(startCharIndex<endCharIndex&&startCharIndex>=0&&endCharIndex<=text.length)this.localSelection={start:startCharIndex,end:endCharIndex};else this.localSelection=null;return previousSelection?.start!==this.localSelection?.start||previousSelection?.end!==this.localSelection?.end}}function singleton(key,factory){let bag=globalThis[singletonCacheSymbol]??={};if(!(key in bag))bag[key]=factory();return bag[key]}function destroySingleton(key){let bag=globalThis[singletonCacheSymbol];if(bag&&key in bag)delete bag[key]}function hasSingleton(key){let bag=globalThis[singletonCacheSymbol];return bag&&key in bag}function registerEnvVar(config){let existing=envRegistry[config.name];if(existing){if(existing.description!==config.description||existing.type!==config.type||existing.default!==config.default)throw Error(`Environment variable "${config.name}" is already registered with different configuration. Existing: ${JSON.stringify(existing)}, New: ${JSON.stringify(config)}`);return}envRegistry[config.name]=config}function normalizeBoolean(value){let lowerValue=value.toLowerCase();return["true","1","on","yes"].includes(lowerValue)}function parseEnvValue(config){let envValue=process.env[config.name];if(envValue===void 0&&config.default!==void 0)return config.default;if(envValue===void 0)throw Error(`Required environment variable ${config.name} is not set. ${config.description}`);switch(config.type){case"boolean":return typeof envValue==="boolean"?envValue:normalizeBoolean(envValue);case"number":let numValue=Number(envValue);if(isNaN(numValue))throw Error(`Environment variable ${config.name} must be a valid number, got: ${envValue}`);return numValue;case"string":default:return envValue}}class EnvStore{parsedValues=new Map;get(key){if(this.parsedValues.has(key))return this.parsedValues.get(key);if(!(key in envRegistry))throw Error(`Environment variable ${key} is not registered.`);try{let value=parseEnvValue(envRegistry[key]);return this.parsedValues.set(key,value),value}catch(error2){throw Error(`Failed to parse env var ${key}: ${error2 instanceof Error?error2.message:String(error2)}`)}}has(key){return key in envRegistry}clearCache(){this.parsedValues.clear()}}function clearEnvCache(){envStore.clearCache()}function generateEnvMarkdown(){let configs=Object.values(envRegistry);if(configs.length===0)return`# Environment Variables
|
|
@@ -2321,7 +2321,7 @@ No environment variables registered.
|
|
|
2321
2321
|
`}else output+=`\x1B[32mDefault:\x1B[0m \x1B[31mRequired\x1B[0m
|
|
2322
2322
|
`;output+=`
|
|
2323
2323
|
`}return output}class ByteQueue{buf;start=0;end=0;constructor(capacity=INITIAL_PENDING_CAPACITY){this.buf=new Uint8Array(capacity)}get length(){return this.end-this.start}get capacity(){return this.buf.length}view(){return this.buf.subarray(this.start,this.end)}take(){let chunk=this.view();return this.start=0,this.end=0,chunk}append(chunk){if(chunk.length===0)return;this.ensureCapacity(this.length+chunk.length),this.buf.set(chunk,this.end),this.end+=chunk.length}consume(count){if(count<=0)return;if(count>=this.length){this.start=0,this.end=0;return}if(this.start+=count,this.start>=this.buf.length/2)this.buf.copyWithin(0,this.start,this.end),this.end-=this.start,this.start=0}clear(){this.start=0,this.end=0}reset(capacity=INITIAL_PENDING_CAPACITY){this.buf=new Uint8Array(capacity),this.start=0,this.end=0}ensureCapacity(requiredLength){let currentLength=this.length;if(requiredLength<=this.buf.length){if(this.buf.length-this.end>=requiredLength-currentLength)return;if(this.buf.copyWithin(0,this.start,this.end),this.end=currentLength,this.start=0,requiredLength<=this.buf.length)return}let nextCapacity=this.buf.length;while(nextCapacity<requiredLength)nextCapacity*=2;let next=new Uint8Array(nextCapacity);next.set(this.view(),0),this.buf=next,this.start=0,this.end=currentLength}}function normalizePositiveOption(value,fallback){if(typeof value!=="number"||!Number.isFinite(value)||value<=0)return fallback;return Math.floor(value)}function utf8SequenceLength(first){if(first<128)return 1;if(first>=194&&first<=223)return 2;if(first>=224&&first<=239)return 3;if(first>=240&&first<=244)return 4;return 0}function bytesEqual(left,right){if(left.length!==right.length)return!1;for(let index=0;index<left.length;index+=1)if(left[index]!==right[index])return!1;return!0}function isMouseSgrSequence(sequence){if(sequence.length<7)return!1;if(sequence[0]!==ESC||sequence[1]!==91||sequence[2]!==60)return!1;let final=sequence[sequence.length-1];if(final!==77&&final!==109)return!1;let part=0,hasDigit=!1;for(let index=3;index<sequence.length-1;index+=1){let byte=sequence[index];if(byte>=48&&byte<=57){hasDigit=!0;continue}if(byte===59&&hasDigit&&part<2){part+=1,hasDigit=!1;continue}return!1}return part===2&&hasDigit}function isAsciiDigit(byte){return byte>=48&&byte<=57}function parsePositiveDecimalPrefix(sequence,start2,endExclusive){if(start2>=endExclusive)return null;let value=0,sawDigit=!1;for(let index=start2;index<endExclusive;index+=1){let byte=sequence[index];if(!isAsciiDigit(byte))return null;sawDigit=!0,value=value*10+(byte-48)}return sawDigit?value:null}function canStillBeKittyU(state){return state.semicolons>=1}function canStillBeKittySpecial(state){return state.semicolons===1&&state.segments>1}function canStillBeExplicitWidthCpr(state){return state.firstParamValue===1&&state.semicolons===1}function canStillBePixelResolution(state){return state.firstParamValue===4&&state.semicolons===2}function canDeferParametricCsi(state,context){return context.kittyKeyboardEnabled&&(canStillBeKittyU(state)||canStillBeKittySpecial(state))||context.explicitWidthCprActive&&canStillBeExplicitWidthCpr(state)||context.pixelResolutionQueryActive&&canStillBePixelResolution(state)}function canCompleteDeferredParametricCsi(state,byte,context){if(context.kittyKeyboardEnabled){if(state.hasDigit&&byte===117)return!0;if(state.hasDigit&&state.semicolons===1&&state.segments>1&&(byte===126||byte>=65&&byte<=90))return!0}if(context.explicitWidthCprActive&&state.hasDigit&&state.firstParamValue===1&&state.semicolons===1&&byte===82)return!0;if(context.pixelResolutionQueryActive&&state.hasDigit&&state.firstParamValue===4&&state.semicolons===2&&byte===116)return!0;return!1}function canDeferPrivateReplyCsi(context){return context.privateCapabilityRepliesActive}function canCompleteDeferredPrivateReplyCsi(state,byte,context){if(!context.privateCapabilityRepliesActive)return!1;if(state.sawDollar)return state.hasDigit&&byte===121;if(byte===99)return state.hasDigit||state.semicolons>0;return state.hasDigit&&byte===117}function concatBytes(left,right){if(left.length===0)return right;if(right.length===0)return left;let combined=new Uint8Array(left.length+right.length);return combined.set(left,0),combined.set(right,left.length),combined}function indexOfBytes(haystack,needle){if(needle.length===0)return 0;let limit=haystack.length-needle.length;for(let offset=0;offset<=limit;offset+=1){let matched=!0;for(let index=0;index<needle.length;index+=1)if(haystack[offset+index]!==needle[index]){matched=!1;break}if(matched)return offset}return-1}function decodeLatin1(bytes){return Buffer3.from(bytes.buffer,bytes.byteOffset,bytes.byteLength).toString("latin1")}function decodeUtf8(bytes){return KEY_DECODER.decode(bytes)}function createPasteCollector(){return{tail:EMPTY_BYTES,parts:[],totalLength:0}}function joinPasteBytes(parts,totalLength){if(totalLength===0)return EMPTY_BYTES;if(parts.length===1)return parts[0];let bytes=new Uint8Array(totalLength),offset=0;for(let part of parts)bytes.set(part,offset),offset+=part.length;return bytes}class StdinParser{pending=new ByteQueue(INITIAL_PENDING_CAPACITY);events=[];timeoutMs;maxPendingBytes;armTimeouts;onTimeoutFlush;useKittyKeyboard;mouseParser=new MouseParser;clock;protocolContext;timeoutId=null;destroyed=!1;pendingSinceMs=null;forceFlush=!1;justFlushedEsc=!1;state={tag:"ground"};cursor=0;unitStart=0;paste=null;constructor(options={}){this.timeoutMs=normalizePositiveOption(options.timeoutMs,DEFAULT_TIMEOUT_MS),this.maxPendingBytes=normalizePositiveOption(options.maxPendingBytes,DEFAULT_MAX_PENDING_BYTES),this.armTimeouts=options.armTimeouts??!0,this.onTimeoutFlush=options.onTimeoutFlush??null,this.useKittyKeyboard=options.useKittyKeyboard??!0,this.clock=options.clock??SYSTEM_CLOCK,this.protocolContext={...DEFAULT_PROTOCOL_CONTEXT,kittyKeyboardEnabled:options.protocolContext?.kittyKeyboardEnabled??!1,privateCapabilityRepliesActive:options.protocolContext?.privateCapabilityRepliesActive??!1,pixelResolutionQueryActive:options.protocolContext?.pixelResolutionQueryActive??!1,explicitWidthCprActive:options.protocolContext?.explicitWidthCprActive??!1}}get bufferCapacity(){return this.pending.capacity}updateProtocolContext(patch){this.ensureAlive(),this.protocolContext={...this.protocolContext,...patch},this.reconcileDeferredStateWithProtocolContext(),this.reconcileTimeoutState()}push(data){if(this.ensureAlive(),data.length===0){this.emitKeyOrResponse("unknown","");return}let remainder=data;while(remainder.length>0){if(this.paste){remainder=this.consumePasteBytes(remainder);continue}let immediatePasteStartIndex=this.state.tag==="ground"&&this.pending.length===0?indexOfBytes(remainder,BRACKETED_PASTE_START):-1,appendEnd=immediatePasteStartIndex===-1?remainder.length:immediatePasteStartIndex+BRACKETED_PASTE_START.length;if(this.pending.append(remainder.subarray(0,appendEnd)),remainder=remainder.subarray(appendEnd),this.scanPending(),this.paste&&this.pending.length>0){remainder=this.consumePasteBytes(this.takePendingBytes());continue}if(!this.paste&&this.pending.length>this.maxPendingBytes){if(this.flushPendingOverflow(),this.scanPending(),this.paste&&this.pending.length>0)remainder=this.consumePasteBytes(this.takePendingBytes())}}this.reconcileTimeoutState()}read(){if(this.ensureAlive(),this.events.length===0&&this.forceFlush)this.scanPending(),this.reconcileTimeoutState();return this.events.shift()??null}drain(onEvent){this.ensureAlive();while(!0){if(this.destroyed)return;let event=this.read();if(!event)return;onEvent(event)}}flushTimeout(nowMsValue=this.clock.now()){if(this.ensureAlive(),this.paste||this.pendingSinceMs===null||this.pending.length===0)return;if(nowMsValue<this.pendingSinceMs||nowMsValue-this.pendingSinceMs<this.timeoutMs)return;this.forceFlush=!0}reset(){if(this.destroyed)return;this.clearTimeout(),this.resetState()}resetMouseState(){this.ensureAlive(),this.mouseParser.reset()}destroy(){if(this.destroyed)return;this.clearTimeout(),this.destroyed=!0,this.resetState()}ensureAlive(){if(this.destroyed)throw Error("StdinParser has been destroyed")}scanPending(){while(!this.paste){let bytes=this.pending.view();if(this.state.tag==="ground"&&this.cursor>=bytes.length){this.pending.clear(),this.cursor=0,this.unitStart=0,this.pendingSinceMs=null,this.forceFlush=!1;return}let byte=this.cursor<bytes.length?bytes[this.cursor]:-1;switch(this.state.tag){case"ground":{if(this.unitStart=this.cursor,this.justFlushedEsc){if(byte===91){this.justFlushedEsc=!1,this.cursor+=1,this.state={tag:"esc_recovery"};continue}this.justFlushedEsc=!1}if(byte===ESC){this.cursor+=1,this.state={tag:"esc"};continue}if(byte<128){this.emitKeyOrResponse("unknown",decodeUtf8(bytes.subarray(this.cursor,this.cursor+1))),this.consumePrefix(this.cursor+1);continue}let expected=utf8SequenceLength(byte);if(expected===0){if(!this.forceFlush&&this.cursor+1===bytes.length){this.markPending();return}this.emitLegacyHighByte(byte),this.consumePrefix(this.cursor+1);continue}this.cursor+=1,this.state={tag:"utf8",expected,seen:1};continue}case"utf8":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}this.emitLegacyHighByte(bytes[this.unitStart]),this.state={tag:"ground"},this.consumePrefix(this.unitStart+1);continue}if((byte&192)!==128){this.emitLegacyHighByte(bytes[this.unitStart]),this.state={tag:"ground"},this.consumePrefix(this.unitStart+1);continue}let nextSeen=this.state.seen+1;if(this.cursor+=1,nextSeen<this.state.expected){this.state={tag:"utf8",expected:this.state.expected,seen:nextSeen};continue}this.emitKeyOrResponse("unknown",decodeUtf8(bytes.subarray(this.unitStart,this.cursor))),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}case"esc":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}let flushedLoneEsc=this.cursor===this.unitStart+1&&bytes[this.unitStart]===ESC;this.emitKeyOrResponse("unknown",decodeUtf8(bytes.subarray(this.unitStart,this.cursor))),this.justFlushedEsc=flushedLoneEsc,this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}switch(byte){case 91:this.cursor+=1,this.state={tag:"csi"};continue;case 79:this.cursor+=1,this.state={tag:"ss3"};continue;case 93:this.cursor+=1,this.state={tag:"osc",sawEsc:!1};continue;case 80:this.cursor+=1,this.state={tag:"dcs",sawEsc:!1};continue;case 95:this.cursor+=1,this.state={tag:"apc",sawEsc:!1};continue;case ESC:this.cursor+=1;continue;default:this.cursor+=1,this.emitKeyOrResponse("unknown",decodeUtf8(bytes.subarray(this.unitStart,this.cursor))),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}}case"ss3":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(byte===ESC){this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}this.cursor+=1,this.emitKeyOrResponse("unknown",decodeUtf8(bytes.subarray(this.unitStart,this.cursor))),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}case"esc_recovery":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}this.emitKeyOrResponse("unknown",decodeUtf8(bytes.subarray(this.unitStart,this.cursor))),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(byte===60){this.cursor+=1,this.state={tag:"esc_less_mouse"};continue}if(byte===77){this.cursor+=1,this.state={tag:"esc_less_x10_mouse"};continue}this.emitKeyOrResponse("unknown",decodeUtf8(bytes.subarray(this.unitStart,this.unitStart+1))),this.state={tag:"ground"},this.consumePrefix(this.unitStart+1);continue}case"csi":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(byte===ESC){this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(byte===77&&this.cursor===this.unitStart+2){let end=this.cursor+4;if(bytes.length<end){if(!this.forceFlush){this.markPending();return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,bytes.length)),this.state={tag:"ground"},this.consumePrefix(bytes.length);continue}this.emitMouse(bytes.subarray(this.unitStart,end),"x10"),this.state={tag:"ground"},this.consumePrefix(end);continue}if(byte===36){let candidateEnd=this.cursor+1,candidate=decodeUtf8(bytes.subarray(this.unitStart,candidateEnd));if(RXVT_DOLLAR_CSI_RE.test(candidate)){this.emitKeyOrResponse("csi",candidate),this.state={tag:"ground"},this.consumePrefix(candidateEnd);continue}if(!this.forceFlush&&candidateEnd>=bytes.length){this.markPending();return}}if(byte===60&&this.cursor===this.unitStart+2){this.cursor+=1,this.state={tag:"csi_sgr_mouse",part:0,hasDigit:!1};continue}if(byte===91&&this.cursor===this.unitStart+2){this.cursor+=1;continue}if(byte===63&&this.cursor===this.unitStart+2){this.cursor+=1,this.state={tag:"csi_private_reply",semicolons:0,hasDigit:!1,sawDollar:!1};continue}if(byte===59){let firstParamValue=parsePositiveDecimalPrefix(bytes,this.unitStart+2,this.cursor);if(firstParamValue!==null){this.cursor+=1,this.state={tag:"csi_parametric",semicolons:1,segments:1,hasDigit:!1,firstParamValue};continue}}if(byte>=64&&byte<=126){let end=this.cursor+1,rawBytes=bytes.subarray(this.unitStart,end);if(bytesEqual(rawBytes,BRACKETED_PASTE_START)){this.state={tag:"ground"},this.consumePrefix(end),this.paste=createPasteCollector();continue}if(isMouseSgrSequence(rawBytes)){this.emitMouse(rawBytes,"sgr"),this.state={tag:"ground"},this.consumePrefix(end);continue}this.emitKeyOrResponse("csi",decodeUtf8(rawBytes)),this.state={tag:"ground"},this.consumePrefix(end);continue}this.cursor+=1;continue}case"csi_sgr_mouse":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}this.state={tag:"csi_sgr_mouse_deferred",part:this.state.part,hasDigit:this.state.hasDigit},this.pendingSinceMs=null,this.forceFlush=!1;return}if(byte===ESC){this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(isAsciiDigit(byte)){this.cursor+=1,this.state={tag:"csi_sgr_mouse",part:this.state.part,hasDigit:!0};continue}if(byte===59&&this.state.hasDigit&&this.state.part<2){this.cursor+=1,this.state={tag:"csi_sgr_mouse",part:this.state.part+1,hasDigit:!1};continue}if(byte>=64&&byte<=126){let end=this.cursor+1,rawBytes=bytes.subarray(this.unitStart,end);if(isMouseSgrSequence(rawBytes))this.emitMouse(rawBytes,"sgr");else this.emitKeyOrResponse("csi",decodeUtf8(rawBytes));this.state={tag:"ground"},this.consumePrefix(end);continue}this.state={tag:"csi"};continue}case"csi_sgr_mouse_deferred":{if(this.cursor>=bytes.length){this.pendingSinceMs=null,this.forceFlush=!1;return}if(byte===ESC){this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(isAsciiDigit(byte)||byte===59||byte===77||byte===109){this.state={tag:"csi_sgr_mouse",part:this.state.part,hasDigit:this.state.hasDigit};continue}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}case"csi_parametric":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}if(canDeferParametricCsi(this.state,this.protocolContext)){this.state={tag:"csi_parametric_deferred",semicolons:this.state.semicolons,segments:this.state.segments,hasDigit:this.state.hasDigit,firstParamValue:this.state.firstParamValue},this.pendingSinceMs=null,this.forceFlush=!1;return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(byte===ESC){this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(isAsciiDigit(byte)){this.cursor+=1,this.state={tag:"csi_parametric",semicolons:this.state.semicolons,segments:this.state.segments,hasDigit:!0,firstParamValue:this.state.firstParamValue};continue}if(byte===58&&this.state.hasDigit&&this.state.segments<3){this.cursor+=1,this.state={tag:"csi_parametric",semicolons:this.state.semicolons,segments:this.state.segments+1,hasDigit:!1,firstParamValue:this.state.firstParamValue};continue}if(byte===59&&this.state.semicolons<2){this.cursor+=1,this.state={tag:"csi_parametric",semicolons:this.state.semicolons+1,segments:1,hasDigit:!1,firstParamValue:this.state.firstParamValue};continue}if(byte>=64&&byte<=126){let end=this.cursor+1;this.emitKeyOrResponse("csi",decodeUtf8(bytes.subarray(this.unitStart,end))),this.state={tag:"ground"},this.consumePrefix(end);continue}this.state={tag:"csi"};continue}case"csi_parametric_deferred":{if(this.cursor>=bytes.length){this.pendingSinceMs=null,this.forceFlush=!1;return}if(byte===ESC){this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(isAsciiDigit(byte)||byte===58||byte===59){this.state={tag:"csi_parametric",semicolons:this.state.semicolons,segments:this.state.segments,hasDigit:this.state.hasDigit,firstParamValue:this.state.firstParamValue};continue}if(canCompleteDeferredParametricCsi(this.state,byte,this.protocolContext)){this.state={tag:"csi_parametric",semicolons:this.state.semicolons,segments:this.state.segments,hasDigit:this.state.hasDigit,firstParamValue:this.state.firstParamValue};continue}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}case"csi_private_reply":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}if(canDeferPrivateReplyCsi(this.protocolContext)){this.state={tag:"csi_private_reply_deferred",semicolons:this.state.semicolons,hasDigit:this.state.hasDigit,sawDollar:this.state.sawDollar},this.pendingSinceMs=null,this.forceFlush=!1;return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(byte===ESC){this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(isAsciiDigit(byte)){this.cursor+=1,this.state={tag:"csi_private_reply",semicolons:this.state.semicolons,hasDigit:!0,sawDollar:this.state.sawDollar};continue}if(byte===59){this.cursor+=1,this.state={tag:"csi_private_reply",semicolons:this.state.semicolons+1,hasDigit:!1,sawDollar:!1};continue}if(byte===36&&this.state.hasDigit&&!this.state.sawDollar){this.cursor+=1,this.state={tag:"csi_private_reply",semicolons:this.state.semicolons,hasDigit:!0,sawDollar:!0};continue}if(byte>=64&&byte<=126){let end=this.cursor+1;this.emitKeyOrResponse("csi",decodeUtf8(bytes.subarray(this.unitStart,end))),this.state={tag:"ground"},this.consumePrefix(end);continue}this.state={tag:"csi"};continue}case"csi_private_reply_deferred":{if(this.cursor>=bytes.length){this.pendingSinceMs=null,this.forceFlush=!1;return}if(byte===ESC){this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(isAsciiDigit(byte)||byte===59||byte===36){this.state={tag:"csi_private_reply",semicolons:this.state.semicolons,hasDigit:this.state.hasDigit,sawDollar:this.state.sawDollar};continue}if(canCompleteDeferredPrivateReplyCsi(this.state,byte,this.protocolContext)){this.state={tag:"csi_private_reply",semicolons:this.state.semicolons,hasDigit:this.state.hasDigit,sawDollar:this.state.sawDollar};continue}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}case"osc":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(this.state.sawEsc){if(byte===92){let end=this.cursor+1;this.emitOpaqueResponse("osc",bytes.subarray(this.unitStart,end)),this.state={tag:"ground"},this.consumePrefix(end);continue}this.state={tag:"osc",sawEsc:!1};continue}if(byte===BEL){let end=this.cursor+1;this.emitOpaqueResponse("osc",bytes.subarray(this.unitStart,end)),this.state={tag:"ground"},this.consumePrefix(end);continue}if(byte===ESC){this.cursor+=1,this.state={tag:"osc",sawEsc:!0};continue}this.cursor+=1;continue}case"dcs":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(this.state.sawEsc){if(byte===92){let end=this.cursor+1;this.emitOpaqueResponse("dcs",bytes.subarray(this.unitStart,end)),this.state={tag:"ground"},this.consumePrefix(end);continue}this.state={tag:"dcs",sawEsc:!1};continue}if(byte===ESC){this.cursor+=1,this.state={tag:"dcs",sawEsc:!0};continue}this.cursor+=1;continue}case"apc":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(this.state.sawEsc){if(byte===92){let end=this.cursor+1;this.emitOpaqueResponse("apc",bytes.subarray(this.unitStart,end)),this.state={tag:"ground"},this.consumePrefix(end);continue}this.state={tag:"apc",sawEsc:!1};continue}if(byte===ESC){this.cursor+=1,this.state={tag:"apc",sawEsc:!0};continue}this.cursor+=1;continue}case"esc_less_mouse":{if(this.cursor>=bytes.length){if(!this.forceFlush){this.markPending();return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}if(byte>=48&&byte<=57||byte===59){this.cursor+=1;continue}if(byte===77||byte===109){let end=this.cursor+1;this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,end)),this.state={tag:"ground"},this.consumePrefix(end);continue}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);continue}case"esc_less_x10_mouse":{let end=this.unitStart+5;if(bytes.length<end){if(!this.forceFlush){this.markPending();return}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,bytes.length)),this.state={tag:"ground"},this.consumePrefix(bytes.length);continue}this.emitOpaqueResponse("unknown",bytes.subarray(this.unitStart,end)),this.state={tag:"ground"},this.consumePrefix(end);continue}}}}emitKeyOrResponse(protocol,raw){let parsed=parseKeypress(raw,{useKittyKeyboard:this.useKittyKeyboard});if(parsed){this.events.push({type:"key",raw:parsed.raw,key:parsed});return}this.events.push({type:"response",protocol,sequence:raw})}emitMouse(rawBytes,encoding){let event=this.mouseParser.parseMouseEvent(rawBytes);if(!event){this.emitOpaqueResponse("unknown",rawBytes);return}this.events.push({type:"mouse",raw:decodeLatin1(rawBytes),encoding,event})}emitLegacyHighByte(byte){let parsed=parseKeypress(Buffer3.from([byte]),{useKittyKeyboard:this.useKittyKeyboard});if(parsed){this.events.push({type:"key",raw:parsed.raw,key:parsed});return}this.events.push({type:"response",protocol:"unknown",sequence:String.fromCharCode(byte)})}emitOpaqueResponse(protocol,rawBytes){this.events.push({type:"response",protocol,sequence:decodeLatin1(rawBytes)})}consumePrefix(endExclusive){this.pending.consume(endExclusive),this.cursor=0,this.unitStart=0,this.pendingSinceMs=null,this.forceFlush=!1}takePendingBytes(){let buffered=this.pending.take();return this.cursor=0,this.unitStart=0,this.pendingSinceMs=null,this.forceFlush=!1,buffered}flushPendingOverflow(){if(this.pending.length===0)return;this.emitOpaqueResponse("unknown",this.pending.view()),this.pending.clear(),this.cursor=0,this.unitStart=0,this.pendingSinceMs=null,this.forceFlush=!1,this.state={tag:"ground"}}markPending(){this.pendingSinceMs=this.clock.now()}consumePasteBytes(chunk){let paste=this.paste,combined=concatBytes(paste.tail,chunk),endIndex=indexOfBytes(combined,BRACKETED_PASTE_END);if(endIndex!==-1)return this.pushPasteBytes(combined.subarray(0,endIndex)),this.events.push({type:"paste",bytes:joinPasteBytes(paste.parts,paste.totalLength)}),this.paste=null,combined.subarray(endIndex+BRACKETED_PASTE_END.length);let keep=Math.min(BRACKETED_PASTE_END.length-1,combined.length),stableLength=combined.length-keep;if(stableLength>0)this.pushPasteBytes(combined.subarray(0,stableLength));return paste.tail=Uint8Array.from(combined.subarray(stableLength)),EMPTY_BYTES}pushPasteBytes(bytes){if(bytes.length===0)return;this.paste.parts.push(Uint8Array.from(bytes)),this.paste.totalLength+=bytes.length}reconcileDeferredStateWithProtocolContext(){switch(this.state.tag){case"csi_parametric_deferred":if(!canDeferParametricCsi(this.state,this.protocolContext))this.emitOpaqueResponse("unknown",this.pending.view().subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);return;case"csi_private_reply_deferred":if(!canDeferPrivateReplyCsi(this.protocolContext))this.emitOpaqueResponse("unknown",this.pending.view().subarray(this.unitStart,this.cursor)),this.state={tag:"ground"},this.consumePrefix(this.cursor);return}}reconcileTimeoutState(){if(!this.armTimeouts)return;if(this.paste||this.pendingSinceMs===null||this.pending.length===0){this.clearTimeout();return}this.clearTimeout(),this.timeoutId=this.clock.setTimeout(()=>{if(this.timeoutId=null,this.destroyed)return;try{this.flushTimeout(this.clock.now()),this.onTimeoutFlush?.()}catch(error2){console.error("stdin parser timeout flush failed",error2)}},this.timeoutMs)}clearTimeout(){if(!this.timeoutId)return;this.clock.clearTimeout(this.timeoutId),this.timeoutId=null}resetState(){this.pending.reset(INITIAL_PENDING_CAPACITY),this.events.length=0,this.pendingSinceMs=null,this.forceFlush=!1,this.justFlushedEsc=!1,this.state={tag:"ground"},this.cursor=0,this.unitStart=0,this.paste=null,this.mouseParser.reset()}}function getSpecificity(group){return group.split(".").length}function shouldSuppressInInjection(group,meta){if(meta?.isInjection)return!1;return group==="markup.raw.block"}function treeSitterToTextChunks(content,highlights,syntaxStyle,options){let chunks=[],defaultStyle=syntaxStyle.getStyle("default"),concealEnabled=options?.enabled??!0,injectionContainerRanges=[],boundaries=[];for(let i2=0;i2<highlights.length;i2++){let[start2,end,,meta]=highlights[i2];if(start2===end)continue;if(meta?.containsInjection)injectionContainerRanges.push({start:start2,end});boundaries.push({offset:start2,type:"start",highlightIndex:i2}),boundaries.push({offset:end,type:"end",highlightIndex:i2})}boundaries.sort((a,b2)=>{if(a.offset!==b2.offset)return a.offset-b2.offset;if(a.type==="end"&&b2.type==="start")return-1;if(a.type==="start"&&b2.type==="end")return 1;return 0});let activeHighlights=new Set,currentOffset=0;for(let i2=0;i2<boundaries.length;i2++){let boundary=boundaries[i2];if(currentOffset<boundary.offset&&activeHighlights.size>0){let segmentText=content.slice(currentOffset,boundary.offset),activeGroups=[];for(let idx of activeHighlights){let[,,group,meta]=highlights[idx];activeGroups.push({group,meta,index:idx})}let concealHighlight=concealEnabled?activeGroups.find((h)=>h.meta?.conceal!==void 0||h.group==="conceal"||h.group.startsWith("conceal.")):void 0;if(concealHighlight){let replacementText="";if(concealHighlight.meta?.conceal!==void 0)replacementText=concealHighlight.meta.conceal;else if(concealHighlight.group==="conceal.with.space")replacementText=" ";if(replacementText)chunks.push({__isChunk:!0,text:replacementText,fg:defaultStyle?.fg,bg:defaultStyle?.bg,attributes:defaultStyle?createTextAttributes({bold:defaultStyle.bold,italic:defaultStyle.italic,underline:defaultStyle.underline,dim:defaultStyle.dim}):0})}else{let insideInjectionContainer=injectionContainerRanges.some((range)=>currentOffset>=range.start&¤tOffset<range.end),sortedGroups=activeGroups.filter((h)=>{if(insideInjectionContainer&&shouldSuppressInInjection(h.group,h.meta))return!1;return!0}).sort((a,b2)=>{let aSpec=getSpecificity(a.group),bSpec=getSpecificity(b2.group);if(aSpec!==bSpec)return aSpec-bSpec;return a.index-b2.index}),mergedStyle={};for(let{group}of sortedGroups){let styleForGroup=syntaxStyle.getStyle(group);if(!styleForGroup&&group.includes(".")){let baseName=group.split(".")[0];styleForGroup=syntaxStyle.getStyle(baseName)}if(styleForGroup){if(styleForGroup.fg!==void 0)mergedStyle.fg=styleForGroup.fg;if(styleForGroup.bg!==void 0)mergedStyle.bg=styleForGroup.bg;if(styleForGroup.bold!==void 0)mergedStyle.bold=styleForGroup.bold;if(styleForGroup.italic!==void 0)mergedStyle.italic=styleForGroup.italic;if(styleForGroup.underline!==void 0)mergedStyle.underline=styleForGroup.underline;if(styleForGroup.dim!==void 0)mergedStyle.dim=styleForGroup.dim}else if(group.includes(".")){let baseName=group.split(".")[0];if(env.OTUI_TS_STYLE_WARN)console.warn(`Syntax style not found for group "${group}" or base scope "${baseName}", using default style`)}else if(env.OTUI_TS_STYLE_WARN)console.warn(`Syntax style not found for group "${group}", using default style`)}let finalStyle=Object.keys(mergedStyle).length>0?mergedStyle:defaultStyle;chunks.push({__isChunk:!0,text:segmentText,fg:finalStyle?.fg,bg:finalStyle?.bg,attributes:finalStyle?createTextAttributes({bold:finalStyle.bold,italic:finalStyle.italic,underline:finalStyle.underline,dim:finalStyle.dim}):0})}}else if(currentOffset<boundary.offset){let text=content.slice(currentOffset,boundary.offset);chunks.push({__isChunk:!0,text,fg:defaultStyle?.fg,bg:defaultStyle?.bg,attributes:defaultStyle?createTextAttributes({bold:defaultStyle.bold,italic:defaultStyle.italic,underline:defaultStyle.underline,dim:defaultStyle.dim}):0})}if(boundary.type==="start")activeHighlights.add(boundary.highlightIndex);else if(activeHighlights.delete(boundary.highlightIndex),concealEnabled){let[,,group,meta]=highlights[boundary.highlightIndex];if(meta?.concealLines!==void 0){if(boundary.offset<content.length&&content[boundary.offset]===`
|
|
2324
|
-
`){currentOffset=boundary.offset+1;continue}}if(meta?.conceal!==void 0){if(meta.conceal===" "){if(boundary.offset<content.length&&content[boundary.offset]===" "){currentOffset=boundary.offset+1;continue}}else if(meta.conceal===""&&group==="conceal"&&!meta.isInjection){if(boundary.offset<content.length&&content[boundary.offset]===" "){currentOffset=boundary.offset+1;continue}}}}currentOffset=boundary.offset}if(currentOffset<content.length){let text=content.slice(currentOffset);chunks.push({__isChunk:!0,text,fg:defaultStyle?.fg,bg:defaultStyle?.bg,attributes:defaultStyle?createTextAttributes({bold:defaultStyle.bold,italic:defaultStyle.italic,underline:defaultStyle.underline,dim:defaultStyle.dim}):0})}return chunks}async function treeSitterToStyledText(content,filetype,syntaxStyle,client,options){let result2=await client.highlightOnce(content,filetype);if(result2.highlights&&result2.highlights.length>0){let chunks=treeSitterToTextChunks(content,result2.highlights,syntaxStyle,options?.conceal);return new StyledText(chunks)}else{let defaultStyle=syntaxStyle.mergeStyles("default"),chunks=[{__isChunk:!0,text:content,fg:defaultStyle.fg,bg:defaultStyle.bg,attributes:defaultStyle.attributes}];return new StyledText(chunks)}}class DebounceController{scopeId;constructor(scopeId){if(this.scopeId=scopeId,!TIMERS_MAP.has(this.scopeId))TIMERS_MAP.set(this.scopeId,new Map)}debounce(id,ms,fn){let scopeMap=TIMERS_MAP.get(this.scopeId);return new Promise((resolve14,reject)=>{if(scopeMap.has(id))clearTimeout(scopeMap.get(id));let timerId=setTimeout(()=>{try{resolve14(fn())}catch(error2){reject(error2)}scopeMap.delete(id)},ms);scopeMap.set(id,timerId)})}clearDebounce(id){let scopeMap=TIMERS_MAP.get(this.scopeId);if(scopeMap&&scopeMap.has(id))clearTimeout(scopeMap.get(id)),scopeMap.delete(id)}clear(){let scopeMap=TIMERS_MAP.get(this.scopeId);if(scopeMap)scopeMap.forEach((timerId)=>clearTimeout(timerId)),scopeMap.clear()}}function createDebounce(scopeId){return new DebounceController(scopeId)}function clearDebounceScope(scopeId){let scopeMap=TIMERS_MAP.get(scopeId);if(scopeMap)scopeMap.forEach((timerId)=>clearTimeout(timerId)),scopeMap.clear()}class ProcessQueue{processor;queue=[];processing=!1;autoProcess=!0;constructor(processor,autoProcess=!0){this.processor=processor,this.autoProcess=autoProcess}enqueue(item){if(this.queue.push(item),!this.processing&&this.autoProcess)this.processQueue()}processQueue(){if(this.queue.length===0)return;this.processing=!0,queueMicrotask(async()=>{if(this.queue.length===0){this.processing=!1;return}let item=this.queue.shift();try{await this.processor(item)}catch(error2){console.error("Error processing queue item:",error2)}if(this.queue.length>0)this.processQueue();else this.processing=!1})}clear(){this.queue=[]}isProcessing(){return this.processing}size(){return this.queue.length}}function getParsers(){if(!_cachedParsers)_cachedParsers=[{filetype:"javascript",aliases:["javascriptreact"],queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_javascript_default)},{filetype:"typescript",aliases:["typescriptreact"],queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default2)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_typescript_default)},{filetype:"markdown",queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default3)],injections:[resolve12(dirname12(fileURLToPath2(import.meta.url)),injections_default)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_markdown_default),injectionMapping:{nodeTypes:{inline:"markdown_inline",pipe_table_cell:"markdown_inline"},infoStringMap:{javascript:"javascript",js:"javascript",jsx:"javascriptreact",javascriptreact:"javascriptreact",typescript:"typescript",ts:"typescript",tsx:"typescriptreact",typescriptreact:"typescriptreact",markdown:"markdown",md:"markdown"}}},{filetype:"markdown_inline",queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default4)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_markdown_inline_default)},{filetype:"zig",queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default5)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_zig_default)}];return _cachedParsers}function isBunfsPath(path6){return path6.includes("$bunfs")||/^B:[\\/]~BUN/i.test(path6)}function getBunfsRootPath(){return process.platform==="win32"?"B:\\~BUN\\root":"/$bunfs/root"}function normalizeBunfsPath(fileName){return join57(getBunfsRootPath(),basename11(fileName))}function addDefaultParsers(parsers2){for(let parser of parsers2)DEFAULT_PARSERS=[...DEFAULT_PARSERS.filter((existingParser)=>existingParser.filetype!==parser.filetype),parser]}function isValidDirectoryName(name){if(!name||typeof name!=="string")return!1;if(name.trim().length===0)return!1;if(["CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"].includes(name.toUpperCase()))return!1;if(/[<>:"|?*/\\\x00-\x1f]/.test(name))return!1;if(name.endsWith(".")||name.endsWith(" "))return!1;if(name==="."||name==="..")return!1;return!0}function getDataPaths(){return singleton("data-paths-opentui",()=>new DataPathsManager)}function normalizeFiletypeToken(value){return value.trim().replace(/^\./,"").toLowerCase()||void 0}function getBasename(value){let normalizedValue=value.trim().replaceAll("\\","/");if(!normalizedValue)return;return path22.posix.basename(normalizedValue).toLowerCase()||void 0}function extToFiletype(extension){let normalizedExtension=normalizeFiletypeToken(extension);if(!normalizedExtension)return;return extensionToFiletype.get(normalizedExtension)}function pathToFiletype(path33){if(typeof path33!=="string")return;let basename22=getBasename(path33);if(!basename22)return;let basenameFiletype=basenameToFiletype.get(basename22);if(basenameFiletype)return basenameFiletype;let lastDot=basename22.lastIndexOf(".");if(lastDot===-1||lastDot===basename22.length-1)return;let extension=basename22.substring(lastDot+1);return extToFiletype(extension)}function infoStringToFiletype(infoString){if(typeof infoString!=="string")return;let token=infoString.trim().split(/\s+/,1)[0],directBasenameMatch=basenameToFiletype.get(token.toLowerCase());if(directBasenameMatch)return directBasenameMatch;let normalizedToken=normalizeFiletypeToken(token);if(!normalizedToken)return;return basenameToFiletype.get(normalizedToken)??pathToFiletype(normalizedToken)??extToFiletype(normalizedToken)??normalizedToken}class DownloadUtils{static hashUrl(url){let hash=0;for(let i2=0;i2<url.length;i2++){let char=url.charCodeAt(i2);hash=(hash<<5)-hash+char,hash=hash&hash}return Math.abs(hash).toString(16)}static async downloadOrLoad(source,cacheDir,cacheSubdir,fileExtension,useHashForCache=!0,filetype){if(source.startsWith("http://")||source.startsWith("https://")){let cacheFileName;if(useHashForCache){let hash=this.hashUrl(source);cacheFileName=filetype?`${filetype}-${hash}${fileExtension}`:`${hash}${fileExtension}`}else cacheFileName=path32.basename(source);let cacheFile=path32.join(cacheDir,cacheSubdir,cacheFileName);await mkdir9(path32.dirname(cacheFile),{recursive:!0});try{let cachedContent=await
|
|
2324
|
+
`){currentOffset=boundary.offset+1;continue}}if(meta?.conceal!==void 0){if(meta.conceal===" "){if(boundary.offset<content.length&&content[boundary.offset]===" "){currentOffset=boundary.offset+1;continue}}else if(meta.conceal===""&&group==="conceal"&&!meta.isInjection){if(boundary.offset<content.length&&content[boundary.offset]===" "){currentOffset=boundary.offset+1;continue}}}}currentOffset=boundary.offset}if(currentOffset<content.length){let text=content.slice(currentOffset);chunks.push({__isChunk:!0,text,fg:defaultStyle?.fg,bg:defaultStyle?.bg,attributes:defaultStyle?createTextAttributes({bold:defaultStyle.bold,italic:defaultStyle.italic,underline:defaultStyle.underline,dim:defaultStyle.dim}):0})}return chunks}async function treeSitterToStyledText(content,filetype,syntaxStyle,client,options){let result2=await client.highlightOnce(content,filetype);if(result2.highlights&&result2.highlights.length>0){let chunks=treeSitterToTextChunks(content,result2.highlights,syntaxStyle,options?.conceal);return new StyledText(chunks)}else{let defaultStyle=syntaxStyle.mergeStyles("default"),chunks=[{__isChunk:!0,text:content,fg:defaultStyle.fg,bg:defaultStyle.bg,attributes:defaultStyle.attributes}];return new StyledText(chunks)}}class DebounceController{scopeId;constructor(scopeId){if(this.scopeId=scopeId,!TIMERS_MAP.has(this.scopeId))TIMERS_MAP.set(this.scopeId,new Map)}debounce(id,ms,fn){let scopeMap=TIMERS_MAP.get(this.scopeId);return new Promise((resolve14,reject)=>{if(scopeMap.has(id))clearTimeout(scopeMap.get(id));let timerId=setTimeout(()=>{try{resolve14(fn())}catch(error2){reject(error2)}scopeMap.delete(id)},ms);scopeMap.set(id,timerId)})}clearDebounce(id){let scopeMap=TIMERS_MAP.get(this.scopeId);if(scopeMap&&scopeMap.has(id))clearTimeout(scopeMap.get(id)),scopeMap.delete(id)}clear(){let scopeMap=TIMERS_MAP.get(this.scopeId);if(scopeMap)scopeMap.forEach((timerId)=>clearTimeout(timerId)),scopeMap.clear()}}function createDebounce(scopeId){return new DebounceController(scopeId)}function clearDebounceScope(scopeId){let scopeMap=TIMERS_MAP.get(scopeId);if(scopeMap)scopeMap.forEach((timerId)=>clearTimeout(timerId)),scopeMap.clear()}class ProcessQueue{processor;queue=[];processing=!1;autoProcess=!0;constructor(processor,autoProcess=!0){this.processor=processor,this.autoProcess=autoProcess}enqueue(item){if(this.queue.push(item),!this.processing&&this.autoProcess)this.processQueue()}processQueue(){if(this.queue.length===0)return;this.processing=!0,queueMicrotask(async()=>{if(this.queue.length===0){this.processing=!1;return}let item=this.queue.shift();try{await this.processor(item)}catch(error2){console.error("Error processing queue item:",error2)}if(this.queue.length>0)this.processQueue();else this.processing=!1})}clear(){this.queue=[]}isProcessing(){return this.processing}size(){return this.queue.length}}function getParsers(){if(!_cachedParsers)_cachedParsers=[{filetype:"javascript",aliases:["javascriptreact"],queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_javascript_default)},{filetype:"typescript",aliases:["typescriptreact"],queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default2)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_typescript_default)},{filetype:"markdown",queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default3)],injections:[resolve12(dirname12(fileURLToPath2(import.meta.url)),injections_default)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_markdown_default),injectionMapping:{nodeTypes:{inline:"markdown_inline",pipe_table_cell:"markdown_inline"},infoStringMap:{javascript:"javascript",js:"javascript",jsx:"javascriptreact",javascriptreact:"javascriptreact",typescript:"typescript",ts:"typescript",tsx:"typescriptreact",typescriptreact:"typescriptreact",markdown:"markdown",md:"markdown"}}},{filetype:"markdown_inline",queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default4)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_markdown_inline_default)},{filetype:"zig",queries:{highlights:[resolve12(dirname12(fileURLToPath2(import.meta.url)),highlights_default5)]},wasm:resolve12(dirname12(fileURLToPath2(import.meta.url)),tree_sitter_zig_default)}];return _cachedParsers}function isBunfsPath(path6){return path6.includes("$bunfs")||/^B:[\\/]~BUN/i.test(path6)}function getBunfsRootPath(){return process.platform==="win32"?"B:\\~BUN\\root":"/$bunfs/root"}function normalizeBunfsPath(fileName){return join57(getBunfsRootPath(),basename11(fileName))}function addDefaultParsers(parsers2){for(let parser of parsers2)DEFAULT_PARSERS=[...DEFAULT_PARSERS.filter((existingParser)=>existingParser.filetype!==parser.filetype),parser]}function isValidDirectoryName(name){if(!name||typeof name!=="string")return!1;if(name.trim().length===0)return!1;if(["CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"].includes(name.toUpperCase()))return!1;if(/[<>:"|?*/\\\x00-\x1f]/.test(name))return!1;if(name.endsWith(".")||name.endsWith(" "))return!1;if(name==="."||name==="..")return!1;return!0}function getDataPaths(){return singleton("data-paths-opentui",()=>new DataPathsManager)}function normalizeFiletypeToken(value){return value.trim().replace(/^\./,"").toLowerCase()||void 0}function getBasename(value){let normalizedValue=value.trim().replaceAll("\\","/");if(!normalizedValue)return;return path22.posix.basename(normalizedValue).toLowerCase()||void 0}function extToFiletype(extension){let normalizedExtension=normalizeFiletypeToken(extension);if(!normalizedExtension)return;return extensionToFiletype.get(normalizedExtension)}function pathToFiletype(path33){if(typeof path33!=="string")return;let basename22=getBasename(path33);if(!basename22)return;let basenameFiletype=basenameToFiletype.get(basename22);if(basenameFiletype)return basenameFiletype;let lastDot=basename22.lastIndexOf(".");if(lastDot===-1||lastDot===basename22.length-1)return;let extension=basename22.substring(lastDot+1);return extToFiletype(extension)}function infoStringToFiletype(infoString){if(typeof infoString!=="string")return;let token=infoString.trim().split(/\s+/,1)[0],directBasenameMatch=basenameToFiletype.get(token.toLowerCase());if(directBasenameMatch)return directBasenameMatch;let normalizedToken=normalizeFiletypeToken(token);if(!normalizedToken)return;return basenameToFiletype.get(normalizedToken)??pathToFiletype(normalizedToken)??extToFiletype(normalizedToken)??normalizedToken}class DownloadUtils{static hashUrl(url){let hash=0;for(let i2=0;i2<url.length;i2++){let char=url.charCodeAt(i2);hash=(hash<<5)-hash+char,hash=hash&hash}return Math.abs(hash).toString(16)}static async downloadOrLoad(source,cacheDir,cacheSubdir,fileExtension,useHashForCache=!0,filetype){if(source.startsWith("http://")||source.startsWith("https://")){let cacheFileName;if(useHashForCache){let hash=this.hashUrl(source);cacheFileName=filetype?`${filetype}-${hash}${fileExtension}`:`${hash}${fileExtension}`}else cacheFileName=path32.basename(source);let cacheFile=path32.join(cacheDir,cacheSubdir,cacheFileName);await mkdir9(path32.dirname(cacheFile),{recursive:!0});try{let cachedContent=await readFile14(cacheFile);if(cachedContent.byteLength>0)return console.log(`Loaded from cache: ${cacheFile} (${source})`),{content:cachedContent,filePath:cacheFile}}catch(error2){}try{console.log(`Downloading from URL: ${source}`);let response=await fetch(source);if(!response.ok)return{error:`Failed to fetch from ${source}: ${response.statusText}`};let content=Buffer.from(await response.arrayBuffer());try{await writeFile7(cacheFile,Buffer.from(content)),console.log(`Cached: ${source}`)}catch(cacheError){console.warn(`Failed to cache: ${cacheError}`)}return{content,filePath:cacheFile}}catch(error2){return{error:`Error downloading from ${source}: ${error2}`}}}else try{return console.log(`Loading from local path: ${source}`),{content:await readFile14(source),filePath:source}}catch(error2){return{error:`Error loading from local path ${source}: ${error2}`}}}static async downloadToPath(source,targetPath){let isUrl2=source.startsWith("http://")||source.startsWith("https://");if(await mkdir9(path32.dirname(targetPath),{recursive:!0}),isUrl2)try{console.log(`Downloading from URL: ${source}`);let response=await fetch(source);if(!response.ok)return{error:`Failed to fetch from ${source}: ${response.statusText}`};let content=Buffer.from(await response.arrayBuffer());return await writeFile7(targetPath,Buffer.from(content)),console.log(`Downloaded: ${source} -> ${targetPath}`),{content,filePath:targetPath}}catch(error2){return{error:`Error downloading from ${source}: ${error2}`}}else try{console.log(`Copying from local path: ${source}`);let content=await readFile14(source);return await writeFile7(targetPath,Buffer.from(content)),{content,filePath:targetPath}}catch(error2){return{error:`Error copying from local path ${source}: ${error2}`}}}static async fetchHighlightQueries(sources,cacheDir,filetype){let queryPromises=sources.map((source)=>this.fetchHighlightQuery(source,cacheDir,filetype));return(await Promise.all(queryPromises)).filter((query2)=>query2.trim().length>0).join(`
|
|
2325
2325
|
`)}static async fetchHighlightQuery(source,cacheDir,filetype){let result2=await this.downloadOrLoad(source,cacheDir,"queries",".scm",!0,filetype);if(result2.error)return console.error(`Error fetching highlight query from ${source}:`,result2.error),"";if(result2.content)return new TextDecoder().decode(result2.content);return""}}function getDefaultOptions(){return{configPath:path4.resolve(__dirname2,"../parsers-config"),assetsDir:path4.resolve(__dirname2),outputPath:path4.resolve(__dirname2,"../default-parsers.ts")}}async function loadConfig2(configPath2){let ext=path4.extname(configPath2),resolvedConfigPath=configPath2;if(ext===""){let file=(await readdir7(path4.dirname(configPath2))).find((file2)=>file2.startsWith(path4.basename(configPath2))&&(file2.endsWith(".json")||file2.endsWith(".ts")||file2.endsWith(".js")));if(!file)throw Error(`No config file found for ${configPath2}`);resolvedConfigPath=path4.join(path4.dirname(configPath2),file),ext=path4.extname(resolvedConfigPath)}if(ext===".json"){let configContent=await readFile22(resolvedConfigPath,"utf-8");return JSON.parse(configContent)}else if(ext===".ts"||ext===".js"){let{default:configContent}=await import(resolvedConfigPath);return configContent}throw Error(`Unsupported config file extension: ${ext}`)}async function downloadLanguage(filetype,languageUrl,assetsDir,outputPath){let languageDir=path4.join(assetsDir,filetype),languageFilename=path4.basename(languageUrl),languagePath=path4.join(languageDir,languageFilename),result2=await DownloadUtils.downloadToPath(languageUrl,languagePath);if(result2.error)throw Error(`Failed to download language for ${filetype}: ${result2.error}`);return"./"+path4.relative(path4.dirname(outputPath),languagePath)}async function downloadAndCombineQueries(filetype,queryUrls,assetsDir,outputPath,queryType,configPath2){let queriesDir=path4.join(assetsDir,filetype),queryPath=path4.join(queriesDir,`${queryType}.scm`),queryContents=[];for(let i2=0;i2<queryUrls.length;i2++){let queryUrl=queryUrls[i2];if(queryUrl.startsWith("./")){console.log(` Using local query ${i2+1}/${queryUrls.length}: ${queryUrl}`);try{let localPath=path4.resolve(path4.dirname(configPath2),queryUrl),content=await readFile22(localPath,"utf-8");if(content.trim())queryContents.push(content),console.log(` \u2713 Loaded ${content.split(`
|
|
2326
2326
|
`).length} lines from local file`)}catch(error2){console.warn(`Failed to read local query from ${queryUrl}: ${error2}`);continue}}else{console.log(` Downloading query ${i2+1}/${queryUrls.length}: ${queryUrl}`);try{let response=await fetch(queryUrl);if(!response.ok){console.warn(`Failed to download query from ${queryUrl}: ${response.statusText}`);continue}let content=await response.text();if(content.trim())queryContents.push(`; Query from: ${queryUrl}
|
|
2327
2327
|
${content}`),console.log(` \u2713 Downloaded ${content.split(`
|
|
@@ -3304,18 +3304,18 @@ Done. ${results.length} migration${results.length===1?"":"s"} applied.`),await s
|
|
|
3304
3304
|
`,count=Number(rows[0].cnt);console.log(`Would delete ${count} event${count===1?"":"s"} older than ${options.olderThan}.`)}else{let result2=await sql`
|
|
3305
3305
|
DELETE FROM genie_runtime_events
|
|
3306
3306
|
WHERE created_at < now() - make_interval(secs => ${intervalSec})
|
|
3307
|
-
`,count=Number(result2.count);console.log(`Deleted ${count} event${count===1?"":"s"} older than ${options.olderThan}.`)}await shutdown()}catch(err){let message=err instanceof Error?err.message:String(err);console.error(`Prune failed: ${message}`),process.exit(1)}}function registerDbCommands(program2){let db=program2.command("db").description("Database management (pgserve)");db.command("status").description("Show pgserve health, port, data dir, and table counts").action(dbStatusCommand),db.command("migrate").description("Run pending database migrations").action(dbMigrateCommand),db.command("query <sql>").description("Execute arbitrary SQL and print results").action(dbQueryCommand),db.command("url").description("Print postgres connection URL for direct access").option("--quiet","Print URL only, no trailing newline (for scripts)").action((options)=>{let url=`postgres://postgres:postgres@127.0.0.1:${getActivePort()}/genie`;if(options.quiet)process.stdout.write(url);else console.log(url)}),db.command("prune-events").description("Prune old runtime events beyond retention period").option("--older-than <duration>","Delete events older than (e.g., 30d, 7d)","14d").option("--dry-run","Show count without deleting").action(dbPruneEventsCommand),db.command("backup").description("Dump database to .genie/snapshot.sql.gz").action(dbBackupCommand),db.command("restore [file]").description("Restore database from snapshot (default: .genie/snapshot.sql.gz)").option("-y, --yes","Skip confirmation prompt").action(dbRestoreCommand)}init_protocol_router();import{execSync as execSync14}from"child_process";import{existsSync as existsSync39}from"fs";import{mkdir as mkdir6,readFile as readFile9,writeFile as writeFile3}from"fs/promises";import{tmpdir as tmpdir3}from"os";import{join as join48}from"path";init_tmux();import{existsSync as existsSync34}from"fs";import{join as join43}from"path";var REPOS_BASE="/home/genie/workspace/repos";function parseWishRef(ref){let trimmed=ref.trim();if(!trimmed)throw Error("Wish reference cannot be empty");let slashIndex=trimmed.indexOf("/");if(slashIndex===-1)return{slug:trimmed};let namespace=trimmed.slice(0,slashIndex),slug=trimmed.slice(slashIndex+1);if(!namespace)throw Error(`Invalid wish reference "${ref}": namespace is empty`);if(!slug)throw Error(`Invalid wish reference "${ref}": slug is empty`);if(slug.includes("/"))throw Error(`Invalid wish reference "${ref}": slug cannot contain "/"`);return{namespace,slug}}async function resolveWish(ref){let parsed=parseWishRef(ref);if(!parsed.namespace){let cwdWishPath=join43(process.cwd(),".genie","wishes",parsed.slug,"WISH.md");if(existsSync34(cwdWishPath)){let repo2=process.cwd(),session2=await resolveRepoSession(repo2);return{repo:repo2,wishPath:cwdWishPath,session:session2,slug:parsed.slug}}throw Error(`Wish "${parsed.slug}" not found in current directory. Use namespace/slug format (e.g., genie/${parsed.slug}) to specify the repo.`)}let repo=join43(REPOS_BASE,parsed.namespace);if(!existsSync34(repo))throw Error(`Repository "${parsed.namespace}" not found at ${repo}. Available repos are in ${REPOS_BASE}.`);let wishPath=join43(repo,".genie","wishes",parsed.slug,"WISH.md");if(!existsSync34(wishPath))throw Error(`Wish "${parsed.slug}" not found in repo "${parsed.namespace}". Expected: ${wishPath}`);let session=await resolveRepoSession(repo);return{repo,wishPath,session,slug:parsed.slug}}init_wish_state();init_agents();init_interactivity();init_term_format();init_wish_state();import{execSync as execSync12}from"child_process";import{existsSync as existsSync35}from"fs";import{readFile as readFile8}from"fs/promises";import{dirname as dirname9,join as join44}from"path";async function loadExecutorInfo(){let[registryMod,executorMod,assignmentMod]=await Promise.all([Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),Promise.resolve().then(() => (init_assignment_registry(),exports_assignment_registry))]);return{registry:registryMod,executorRegistry:executorMod,assignmentRegistry:assignmentMod}}function normalizeGitPath2(path3){if(process.platform!=="darwin")return path3;if(!path3.startsWith("/private/"))return path3;let logicalPath=path3.slice(8);return existsSync35(logicalPath)?logicalPath:path3}function resolveWishPath(slug,cwd){let base=cwd??process.cwd(),cwdPath=join44(base,".genie","wishes",slug,"WISH.md");if(existsSync35(cwdPath))return cwdPath;try{let commonDir=execSync12("git rev-parse --path-format=absolute --git-common-dir",{encoding:"utf-8",cwd:base,stdio:["pipe","pipe","pipe"]}).trim(),repoRoot=normalizeGitPath2(dirname9(commonDir));if(repoRoot!==base){let repoPath=join44(repoRoot,".genie","wishes",slug,"WISH.md");if(existsSync35(repoPath))return repoPath}}catch{}return null}function parseRef(ref){let hashIdx=ref.indexOf("#");if(hashIdx===-1)throw Error(`Invalid reference "${ref}". Expected format: <slug>#<group>`);let slug=ref.slice(0,hashIdx),group=ref.slice(hashIdx+1);if(!slug||!group)throw Error(`Invalid reference "${ref}". Both slug and group are required.`);return{slug,group}}var STATUS_ICONS={blocked:"\uD83D\uDD12",ready:"\uD83D\uDFE2",in_progress:"\uD83D\uDD04",done:"\u2705"};async function detectWaveCompletion(slug,groupName,cwd){let wishPath=resolveWishPath(slug,cwd);if(!wishPath)return null;let content=await readFile8(wishPath,"utf-8"),targetWave=parseExecutionStrategy(content).find((w)=>w.groups.some((g)=>g.group===groupName));if(!targetWave)return null;let state=await getState(slug,cwd);if(!state)return null;let waveGroupNames=targetWave.groups.map((g)=>g.group);if(!waveGroupNames.every((g)=>state.groups[g]?.status==="done"))return null;return{waveName:targetWave.name,waveGroups:waveGroupNames}}async function ensureWorkPushed(slug,group){try{if(execSync12("git status --porcelain",{encoding:"utf-8"}).trim())console.log(" Committing dirty working tree..."),execSync12("git add -A",{encoding:"utf-8"}),execSync12(`git commit -m "wip: ${slug}#${group}"`,{encoding:"utf-8"}),console.log(` Committed as "wip: ${slug}#${group}"`)}catch{}try{if(execSync12("git log @{u}..HEAD --oneline",{encoding:"utf-8"}).trim())console.log(" Pushing unpushed commits..."),execSync12("git push",{encoding:"utf-8",timeout:30000}),console.log(" Push complete.")}catch{try{let branch=execSync12("git rev-parse --abbrev-ref HEAD",{encoding:"utf-8"}).trim();if(branch&&branch!=="HEAD")execSync12(`git push -u origin ${branch}`,{encoding:"utf-8",timeout:30000}),console.log(" Push complete (set upstream).")}catch{console.log(" \u26A0\uFE0F Push failed \u2014 manual push may be needed.")}}}async function autoCleanupTeam(){let teamName=process.env.GENIE_TEAM;if(!teamName)return;try{let teamManager=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),config=await teamManager.getTeam(teamName);if(!config)return;if(config.status==="done")return;console.log(` \uD83E\uDDF9 Auto-cleaning team "${teamName}"...`),await teamManager.setTeamStatus(teamName,"done"),await teamManager.killTeamMembers(teamName),console.log(` \u2705 Team "${teamName}" marked done, members killed.`)}catch{console.log(` \u26A0\uFE0F Auto-cleanup skipped \u2014 run \`genie team done ${teamName}\` manually.`)}}function autoKillPane(){let paneId=process.env.TMUX_PANE;if(paneId)setTimeout(()=>{try{let{genieTmuxCmd:genieTmuxCmd2}=(init_tmux_wrapper(),__toCommonJS(exports_tmux_wrapper));execSync12(genieTmuxCmd2(`kill-pane -t '${paneId}'`),{encoding:"utf-8"})}catch{process.exit(0)}},1000);else process.exit(0)}async function resolveNotificationTargets(){let teamName=process.env.GENIE_TEAM;if(!teamName)return{leader:"team-lead"};try{let teamManager=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),leader=await teamManager.resolveLeaderName(teamName),config=await teamManager.getTeam(teamName);return{leader,spawner:config?.spawner}}catch{return{leader:teamName}}}async function notifyWaveCompletion(waveResult,wishComplete){console.log(` \uD83C\uDF0A ${waveResult.waveName} complete! All groups done: ${waveResult.waveGroups.join(", ")}`);try{let protocolRouter=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router)),repoPath=process.cwd(),{leader,spawner}=await resolveNotificationTargets(),message=wishComplete?`WISH COMPLETE \u2014 all groups done: [${waveResult.waveGroups.join(", ")}]. Run \`genie team done\` to clean up.`:`${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(", ")}]. Run /review or advance to next wave.`,result2=await protocolRouter.sendMessage(repoPath,"cli",leader,message);if(result2&&typeof result2==="object"&&"delivered"in result2&&!result2.delivered)console.warn(` \u26A0\uFE0F Wave-complete notification to ${leader} may not have been delivered.`);else console.log(` Notified ${leader} of wave completion.`);if(spawner&&spawner!==leader&&spawner!=="cli")await protocolRouter.sendMessage(repoPath,"cli",spawner,message).catch(()=>{}),console.log(` Notified spawner (${spawner}) of wave completion.`)}catch{console.warn(" \u26A0\uFE0F Could not notify leader (messaging unavailable).")}}async function doneCommand(ref){try{let{slug,group}=parseRef(ref),result2=await completeGroup(slug,group);if(console.log(`\u2705 Group "${group}" marked as done in wish "${slug}"`),result2.completedAt)console.log(` Completed at: ${formatTimestamp(result2.completedAt)}`);let state=await getState(slug);if(state){let nowReady=Object.entries(state.groups).filter(([,g])=>g.status==="ready"&&g.dependsOn.includes(group)).map(([name])=>name);if(nowReady.length>0)console.log(` Unblocked: ${nowReady.join(", ")}`)}await ensureWorkPushed(slug,group);let wishComplete=await isWishComplete(slug),waveResult=await detectWaveCompletion(slug,group);if(waveResult)await notifyWaveCompletion(waveResult,wishComplete);if(wishComplete)console.log(" \uD83C\uDF89 Wish fully complete \u2014 all groups done."),await autoCleanupTeam();autoKillPane()}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function autoInitWishState(slug){let wishPath=resolveWishPath(slug);if(!wishPath)console.error(`\u274C No state found for wish "${slug}" and no WISH.md found in cwd or repo root`),console.error(` Create it first: genie wish <agent> ${slug}`),process.exit(1);let content=await readFile8(wishPath,"utf-8"),groups=parseWishGroups(content);if(groups.length===0)console.error(`\u274C No execution groups found in ${wishPath}`),process.exit(1);let state=await createState(slug,groups);return console.log(`\uD83D\uDCDD Auto-initialized state for wish "${slug}" (${groups.length} groups)`),state}async function printWishExecutors(slug){try{let{registry:registry2,executorRegistry,assignmentRegistry}=await loadExecutorInfo(),agents=await registry2.listAgents({team:process.env.GENIE_TEAM}),executorInfoLines=[];for(let agent of agents){if(!agent.currentExecutorId)continue;let executor=await executorRegistry.getExecutor(agent.currentExecutorId);if(!executor||executor.state==="terminated"||executor.state==="done")continue;let assignment=await assignmentRegistry.getActiveAssignment(executor.id),taskLabel=assignment?.wishSlug===slug?`Group ${assignment.groupNumber??"?"}`:assignment?.wishSlug??"-",name=agent.customName??agent.role??agent.id.slice(0,12);executorInfoLines.push(` Agent: ${padRight(name,16)} | Executor: ${executor.id.slice(0,12)} (${executor.provider}) | State: ${padRight(executor.state,10)} | Task: ${taskLabel}`)}if(executorInfoLines.length>0){console.log(`
|
|
3307
|
+
`,count=Number(result2.count);console.log(`Deleted ${count} event${count===1?"":"s"} older than ${options.olderThan}.`)}await shutdown()}catch(err){let message=err instanceof Error?err.message:String(err);console.error(`Prune failed: ${message}`),process.exit(1)}}function registerDbCommands(program2){let db=program2.command("db").description("Database management (pgserve)");db.command("status").description("Show pgserve health, port, data dir, and table counts").action(dbStatusCommand),db.command("migrate").description("Run pending database migrations").action(dbMigrateCommand),db.command("query <sql>").description("Execute arbitrary SQL and print results").action(dbQueryCommand),db.command("url").description("Print postgres connection URL for direct access").option("--quiet","Print URL only, no trailing newline (for scripts)").action((options)=>{let url=`postgres://postgres:postgres@127.0.0.1:${getActivePort()}/genie`;if(options.quiet)process.stdout.write(url);else console.log(url)}),db.command("prune-events").description("Prune old runtime events beyond retention period").option("--older-than <duration>","Delete events older than (e.g., 30d, 7d)","14d").option("--dry-run","Show count without deleting").action(dbPruneEventsCommand),db.command("backup").description("Dump database to .genie/snapshot.sql.gz").action(dbBackupCommand),db.command("restore [file]").description("Restore database from snapshot (default: .genie/snapshot.sql.gz)").option("-y, --yes","Skip confirmation prompt").action(dbRestoreCommand)}init_protocol_router();import{execSync as execSync14}from"child_process";import{existsSync as existsSync39}from"fs";import{mkdir as mkdir6,readFile as readFile10,writeFile as writeFile4}from"fs/promises";import{tmpdir as tmpdir3}from"os";import{join as join48}from"path";init_tmux();import{existsSync as existsSync34}from"fs";import{join as join43}from"path";var REPOS_BASE="/home/genie/workspace/repos";function parseWishRef(ref){let trimmed=ref.trim();if(!trimmed)throw Error("Wish reference cannot be empty");let slashIndex=trimmed.indexOf("/");if(slashIndex===-1)return{slug:trimmed};let namespace=trimmed.slice(0,slashIndex),slug=trimmed.slice(slashIndex+1);if(!namespace)throw Error(`Invalid wish reference "${ref}": namespace is empty`);if(!slug)throw Error(`Invalid wish reference "${ref}": slug is empty`);if(slug.includes("/"))throw Error(`Invalid wish reference "${ref}": slug cannot contain "/"`);return{namespace,slug}}async function resolveWish(ref){let parsed=parseWishRef(ref);if(!parsed.namespace){let cwdWishPath=join43(process.cwd(),".genie","wishes",parsed.slug,"WISH.md");if(existsSync34(cwdWishPath)){let repo2=process.cwd(),session2=await resolveRepoSession(repo2);return{repo:repo2,wishPath:cwdWishPath,session:session2,slug:parsed.slug}}throw Error(`Wish "${parsed.slug}" not found in current directory. Use namespace/slug format (e.g., genie/${parsed.slug}) to specify the repo.`)}let repo=join43(REPOS_BASE,parsed.namespace);if(!existsSync34(repo))throw Error(`Repository "${parsed.namespace}" not found at ${repo}. Available repos are in ${REPOS_BASE}.`);let wishPath=join43(repo,".genie","wishes",parsed.slug,"WISH.md");if(!existsSync34(wishPath))throw Error(`Wish "${parsed.slug}" not found in repo "${parsed.namespace}". Expected: ${wishPath}`);let session=await resolveRepoSession(repo);return{repo,wishPath,session,slug:parsed.slug}}init_wish_state();init_agents();init_interactivity();init_term_format();init_wish_state();import{execSync as execSync12}from"child_process";import{existsSync as existsSync35}from"fs";import{readFile as readFile9}from"fs/promises";import{dirname as dirname9,join as join44}from"path";async function loadExecutorInfo(){let[registryMod,executorMod,assignmentMod]=await Promise.all([Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),Promise.resolve().then(() => (init_assignment_registry(),exports_assignment_registry))]);return{registry:registryMod,executorRegistry:executorMod,assignmentRegistry:assignmentMod}}function normalizeGitPath2(path3){if(process.platform!=="darwin")return path3;if(!path3.startsWith("/private/"))return path3;let logicalPath=path3.slice(8);return existsSync35(logicalPath)?logicalPath:path3}function resolveWishPath(slug,cwd){let base=cwd??process.cwd(),cwdPath=join44(base,".genie","wishes",slug,"WISH.md");if(existsSync35(cwdPath))return cwdPath;try{let commonDir=execSync12("git rev-parse --path-format=absolute --git-common-dir",{encoding:"utf-8",cwd:base,stdio:["pipe","pipe","pipe"]}).trim(),repoRoot=normalizeGitPath2(dirname9(commonDir));if(repoRoot!==base){let repoPath=join44(repoRoot,".genie","wishes",slug,"WISH.md");if(existsSync35(repoPath))return repoPath}}catch{}return null}function parseRef(ref){let hashIdx=ref.indexOf("#");if(hashIdx===-1)throw Error(`Invalid reference "${ref}". Expected format: <slug>#<group>`);let slug=ref.slice(0,hashIdx),group=ref.slice(hashIdx+1);if(!slug||!group)throw Error(`Invalid reference "${ref}". Both slug and group are required.`);return{slug,group}}var STATUS_ICONS={blocked:"\uD83D\uDD12",ready:"\uD83D\uDFE2",in_progress:"\uD83D\uDD04",done:"\u2705"};async function detectWaveCompletion(slug,groupName,cwd){let wishPath=resolveWishPath(slug,cwd);if(!wishPath)return null;let content=await readFile9(wishPath,"utf-8"),targetWave=parseExecutionStrategy(content).find((w)=>w.groups.some((g)=>g.group===groupName));if(!targetWave)return null;let state=await getState(slug,cwd);if(!state)return null;let waveGroupNames=targetWave.groups.map((g)=>g.group);if(!waveGroupNames.every((g)=>state.groups[g]?.status==="done"))return null;return{waveName:targetWave.name,waveGroups:waveGroupNames}}async function ensureWorkPushed(slug,group){try{if(execSync12("git status --porcelain",{encoding:"utf-8"}).trim())console.log(" Committing dirty working tree..."),execSync12("git add -A",{encoding:"utf-8"}),execSync12(`git commit -m "wip: ${slug}#${group}"`,{encoding:"utf-8"}),console.log(` Committed as "wip: ${slug}#${group}"`)}catch{}try{if(execSync12("git log @{u}..HEAD --oneline",{encoding:"utf-8"}).trim())console.log(" Pushing unpushed commits..."),execSync12("git push",{encoding:"utf-8",timeout:30000}),console.log(" Push complete.")}catch{try{let branch=execSync12("git rev-parse --abbrev-ref HEAD",{encoding:"utf-8"}).trim();if(branch&&branch!=="HEAD")execSync12(`git push -u origin ${branch}`,{encoding:"utf-8",timeout:30000}),console.log(" Push complete (set upstream).")}catch{console.log(" \u26A0\uFE0F Push failed \u2014 manual push may be needed.")}}}async function autoCleanupTeam(){let teamName=process.env.GENIE_TEAM;if(!teamName)return;try{let teamManager=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),config=await teamManager.getTeam(teamName);if(!config)return;if(config.status==="done")return;console.log(` \uD83E\uDDF9 Auto-cleaning team "${teamName}"...`),await teamManager.setTeamStatus(teamName,"done"),await teamManager.killTeamMembers(teamName),console.log(` \u2705 Team "${teamName}" marked done, members killed.`)}catch{console.log(` \u26A0\uFE0F Auto-cleanup skipped \u2014 run \`genie team done ${teamName}\` manually.`)}}function autoKillPane(){let paneId=process.env.TMUX_PANE;if(paneId)setTimeout(()=>{try{let{genieTmuxCmd:genieTmuxCmd2}=(init_tmux_wrapper(),__toCommonJS(exports_tmux_wrapper));execSync12(genieTmuxCmd2(`kill-pane -t '${paneId}'`),{encoding:"utf-8"})}catch{process.exit(0)}},1000);else process.exit(0)}async function resolveNotificationTargets(){let teamName=process.env.GENIE_TEAM;if(!teamName)return{leader:"team-lead"};try{let teamManager=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),leader=await teamManager.resolveLeaderName(teamName),config=await teamManager.getTeam(teamName);return{leader,spawner:config?.spawner}}catch{return{leader:teamName}}}async function notifyWaveCompletion(waveResult,wishComplete){console.log(` \uD83C\uDF0A ${waveResult.waveName} complete! All groups done: ${waveResult.waveGroups.join(", ")}`);try{let protocolRouter=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router)),repoPath=process.cwd(),{leader,spawner}=await resolveNotificationTargets(),message=wishComplete?`WISH COMPLETE \u2014 all groups done: [${waveResult.waveGroups.join(", ")}]. Run \`genie team done\` to clean up.`:`${waveResult.waveName} complete. All groups done: [${waveResult.waveGroups.join(", ")}]. Run /review or advance to next wave.`,result2=await protocolRouter.sendMessage(repoPath,"cli",leader,message);if(result2&&typeof result2==="object"&&"delivered"in result2&&!result2.delivered)console.warn(` \u26A0\uFE0F Wave-complete notification to ${leader} may not have been delivered.`);else console.log(` Notified ${leader} of wave completion.`);if(spawner&&spawner!==leader&&spawner!=="cli")await protocolRouter.sendMessage(repoPath,"cli",spawner,message).catch(()=>{}),console.log(` Notified spawner (${spawner}) of wave completion.`)}catch{console.warn(" \u26A0\uFE0F Could not notify leader (messaging unavailable).")}}async function doneCommand(ref){try{let{slug,group}=parseRef(ref),result2=await completeGroup(slug,group);if(console.log(`\u2705 Group "${group}" marked as done in wish "${slug}"`),result2.completedAt)console.log(` Completed at: ${formatTimestamp(result2.completedAt)}`);let state=await getState(slug);if(state){let nowReady=Object.entries(state.groups).filter(([,g])=>g.status==="ready"&&g.dependsOn.includes(group)).map(([name])=>name);if(nowReady.length>0)console.log(` Unblocked: ${nowReady.join(", ")}`)}await ensureWorkPushed(slug,group);let wishComplete=await isWishComplete(slug),waveResult=await detectWaveCompletion(slug,group);if(waveResult)await notifyWaveCompletion(waveResult,wishComplete);if(wishComplete)console.log(" \uD83C\uDF89 Wish fully complete \u2014 all groups done."),await autoCleanupTeam();autoKillPane()}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}async function autoInitWishState(slug){let wishPath=resolveWishPath(slug);if(!wishPath)console.error(`\u274C No state found for wish "${slug}" and no WISH.md found in cwd or repo root`),console.error(` Create it first: genie wish <agent> ${slug}`),process.exit(1);let content=await readFile9(wishPath,"utf-8"),groups=parseWishGroups(content);if(groups.length===0)console.error(`\u274C No execution groups found in ${wishPath}`),process.exit(1);let state=await createState(slug,groups);return console.log(`\uD83D\uDCDD Auto-initialized state for wish "${slug}" (${groups.length} groups)`),state}async function printWishExecutors(slug){try{let{registry:registry2,executorRegistry,assignmentRegistry}=await loadExecutorInfo(),agents=await registry2.listAgents({team:process.env.GENIE_TEAM}),executorInfoLines=[];for(let agent of agents){if(!agent.currentExecutorId)continue;let executor=await executorRegistry.getExecutor(agent.currentExecutorId);if(!executor||executor.state==="terminated"||executor.state==="done")continue;let assignment=await assignmentRegistry.getActiveAssignment(executor.id),taskLabel=assignment?.wishSlug===slug?`Group ${assignment.groupNumber??"?"}`:assignment?.wishSlug??"-",name=agent.customName??agent.role??agent.id.slice(0,12);executorInfoLines.push(` Agent: ${padRight(name,16)} | Executor: ${executor.id.slice(0,12)} (${executor.provider}) | State: ${padRight(executor.state,10)} | Task: ${taskLabel}`)}if(executorInfoLines.length>0){console.log(`
|
|
3308
3308
|
Active Executors:`),console.log("\u2500".repeat(60));for(let line of executorInfoLines)console.log(line)}}catch{}}async function statusCommand(slug){try{let state=await getState(slug)??await autoInitWishState(slug);console.log(`
|
|
3309
|
-
Wish: ${state.wish}`),console.log("\u2500".repeat(60));let entries=Object.entries(state.groups),maxNameLen=Math.max(...entries.map(([name])=>name.length),5);console.log(` ${padRight("GROUP",maxNameLen)} STATUS ASSIGNEE STARTED COMPLETED`),console.log(` ${"\u2500".repeat(maxNameLen+62)}`);for(let[name,group]of entries){let icon=STATUS_ICONS[group.status]??"\u2753",status=padRight(`${icon} ${group.status}`,13),assignee=padRight(group.assignee??"-",13),started=padRight(formatTimestamp(group.startedAt)||"-",14),completed=formatTimestamp(group.completedAt)||"-";console.log(` ${padRight(name,maxNameLen)} ${status} ${assignee} ${started} ${completed}`)}let total=entries.length,done=entries.filter(([,g])=>g.status==="done").length,inProgress=entries.filter(([,g])=>g.status==="in_progress").length,ready=entries.filter(([,g])=>g.status==="ready").length,blocked=entries.filter(([,g])=>g.status==="blocked").length;console.log(""),console.log(` Progress: ${done}/${total} done | ${inProgress} in progress | ${ready} ready | ${blocked} blocked`),await printWishExecutors(slug),console.log("")}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}function registerStateCommands(program2){program2.command("done <ref>").description("Mark a wish group as done (format: <slug>#<group>)").action(async(ref)=>{await doneCommand(ref)}),program2.command("status <slug>").description("Show wish state overview for all groups").action(async(slug)=>{await statusCommand(slug)}),program2.command("reset <ref>").option("-y, --yes","Skip confirmation prompt (required in non-interactive mode)").description("Reset wish state. <slug>#<group> resets one in-progress group; bare <slug> wipes the wish and recreates from current WISH.md").action(async(ref,options)=>{try{if(ref.includes("#")){let{slug,group}=parseRef(ref),result2=await resetGroup(slug,group);if(console.log(`\uD83D\uDD04 Group "${group}" reset to ready in wish "${slug}"`),result2.status==="ready")console.log(" Status: ready (assignee cleared)");return}await resetWishCommand(ref,options?.yes??!1)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}})}async function resetWishCommand(slug,confirmed){let wishPath=resolveWishPath(slug);if(!wishPath)throw Error(`No WISH.md found for "${slug}" \u2014 searched cwd and repo root`);let content=await
|
|
3309
|
+
Wish: ${state.wish}`),console.log("\u2500".repeat(60));let entries=Object.entries(state.groups),maxNameLen=Math.max(...entries.map(([name])=>name.length),5);console.log(` ${padRight("GROUP",maxNameLen)} STATUS ASSIGNEE STARTED COMPLETED`),console.log(` ${"\u2500".repeat(maxNameLen+62)}`);for(let[name,group]of entries){let icon=STATUS_ICONS[group.status]??"\u2753",status=padRight(`${icon} ${group.status}`,13),assignee=padRight(group.assignee??"-",13),started=padRight(formatTimestamp(group.startedAt)||"-",14),completed=formatTimestamp(group.completedAt)||"-";console.log(` ${padRight(name,maxNameLen)} ${status} ${assignee} ${started} ${completed}`)}let total=entries.length,done=entries.filter(([,g])=>g.status==="done").length,inProgress=entries.filter(([,g])=>g.status==="in_progress").length,ready=entries.filter(([,g])=>g.status==="ready").length,blocked=entries.filter(([,g])=>g.status==="blocked").length;console.log(""),console.log(` Progress: ${done}/${total} done | ${inProgress} in progress | ${ready} ready | ${blocked} blocked`),await printWishExecutors(slug),console.log("")}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}}function registerStateCommands(program2){program2.command("done <ref>").description("Mark a wish group as done (format: <slug>#<group>)").action(async(ref)=>{await doneCommand(ref)}),program2.command("status <slug>").description("Show wish state overview for all groups").action(async(slug)=>{await statusCommand(slug)}),program2.command("reset <ref>").option("-y, --yes","Skip confirmation prompt (required in non-interactive mode)").description("Reset wish state. <slug>#<group> resets one in-progress group; bare <slug> wipes the wish and recreates from current WISH.md").action(async(ref,options)=>{try{if(ref.includes("#")){let{slug,group}=parseRef(ref),result2=await resetGroup(slug,group);if(console.log(`\uD83D\uDD04 Group "${group}" reset to ready in wish "${slug}"`),result2.status==="ready")console.log(" Status: ready (assignee cleared)");return}await resetWishCommand(ref,options?.yes??!1)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}})}async function resetWishCommand(slug,confirmed){let wishPath=resolveWishPath(slug);if(!wishPath)throw Error(`No WISH.md found for "${slug}" \u2014 searched cwd and repo root`);let content=await readFile9(wishPath,"utf-8"),groups=parseWishGroups(content);if(groups.length===0)throw Error(`No execution groups found in ${wishPath}`);let existing=await getState(slug);if(existing){let groupCount=Object.keys(existing.groups).length,inProgress=Object.values(existing.groups).filter((g)=>g.status==="in_progress").length,summary=`Wipe all state for "${slug}" (${groupCount} groups, ${inProgress} in-progress)?`;if(!isInteractive()){if(!confirmed)console.error(`\u274C ${summary}`),console.error(" Refusing to wipe in non-interactive mode. Pass --yes to confirm."),process.exit(2)}else{let{confirm:confirm2}=await Promise.resolve().then(() => (init_esm14(),exports_esm));if(!await confirm2({message:summary,default:!1})){console.log("Aborted.");return}}}if(existing)console.log(`\uD83D\uDDD1\uFE0F Replacing existing state for wish "${slug}"`);else console.log(`\u2139\uFE0F No existing state for wish "${slug}" \u2014 creating fresh`);let state=await createState(slug,groups);console.log(`\uD83D\uDCDD Recreated state from ${wishPath} (${groups.length} groups)`),console.log(""),console.log(`Wish: ${state.wish}`),console.log("\u2500".repeat(60));for(let[name,group]of Object.entries(state.groups)){let icon=STATUS_ICONS[group.status]??"\u2753";console.log(` ${name} ${icon} ${group.status}`)}}async function writeContextFile(content){let dir=join48(tmpdir3(),"genie-dispatch");await mkdir6(dir,{recursive:!0});let ts3=Date.now().toString(36),rand=Math.random().toString(36).slice(2,8),filePath=join48(dir,`ctx-${ts3}-${rand}.md`);return await writeFile4(filePath,content),filePath}function extractGroup(content,groupName){let pattern=new RegExp(`^### Group ${escapeRegExp(groupName)}:`,"m"),match=content.match(pattern);if(!match||match.index===void 0)return null;let start2=match.index,nextBoundary=content.slice(start2).slice(1).search(/^### Group \d|^---$/m),end=nextBoundary!==-1?start2+1+nextBoundary:content.length;return content.slice(start2,end).trim()}function extractWishContext(content){let execGroupsIdx=content.indexOf("## Execution Groups");if(execGroupsIdx!==-1)return content.slice(0,execGroupsIdx).trim();return content.slice(0,2000).trim()}var SLUG_PATTERN=/^[a-zA-Z0-9._-]+$/;function validateSlug(slug){if(!SLUG_PATTERN.test(slug))console.error(`\u274C Invalid slug: "${slug}"`),console.error(" Slugs must match [a-zA-Z0-9._-]+ (no slashes, dots-dots, or special characters)"),process.exit(1)}function escapeRegExp(str5){return str5.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function buildContextPrompt(opts){let parts=[`# Dispatch Context (${opts.command})`,"",`**Source file:** \`${opts.filePath}\``,"(Read the full document at the path above for complete context)",""];if(opts.wishContext)parts.push("## Wish Context","",opts.wishContext,"");if(parts.push("## Assigned Section","",opts.sectionContent,""),opts.enrichedContext)parts.push(opts.enrichedContext);if(opts.skill)parts.push("## Initial Command","",`Run \`/${opts.skill}\` to begin.`,"");return parts.join(`
|
|
3310
3310
|
`)}function getGitDiff(){try{let diff=execSync14("git diff HEAD",{encoding:"utf-8",maxBuffer:1048576}),staged=execSync14("git diff --cached",{encoding:"utf-8",maxBuffer:1048576}),combined=[diff,staged].filter(Boolean).join(`
|
|
3311
3311
|
`);if(combined.length>50000)return`${combined.slice(0,50000)}
|
|
3312
3312
|
|
|
3313
|
-
... (diff truncated at 50KB)`;return combined}catch{return""}}function parseWishGroups(content){let groups=[],groupPattern=/^### Group ([A-Za-z0-9]+):/gim,match=groupPattern.exec(content);while(match!==null){let name=match[1],start2=match.index,rest=content.slice(start2+match[0].length),nextGroupIdx=rest.search(/^### Group [A-Za-z0-9]+:/m),depsMatch=(nextGroupIdx!==-1?rest.slice(0,nextGroupIdx):rest).match(/\*\*depends-on:\*\*\s*(.+)/i),dependsOn=[];if(depsMatch){let depsNormalized=depsMatch[1].trim().replace(/\s*\([^)]*\)/g,"").trim();if(depsNormalized.toLowerCase()!=="none")dependsOn=depsNormalized.split(",").map((d)=>d.trim().replace(/^groups?\s*/i,"").trim()).filter(Boolean)}groups.push({name,dependsOn}),match=groupPattern.exec(content)}return groups}function parseExecutionStrategy(content){let strategyMatch=content.match(/^## Execution Strategy\s*$/m);if(!strategyMatch||strategyMatch.index===void 0)return buildFallbackWaves(content);let strategyStart=strategyMatch.index+strategyMatch[0].length,nextSectionMatch=content.slice(strategyStart).match(/^## /m),strategyEnd=nextSectionMatch?.index!==void 0?strategyStart+nextSectionMatch.index:content.length,strategyContent=content.slice(strategyStart,strategyEnd),waves=[],wavePattern=/^### (Wave \d+[^\n]*)/gm,waveMatch=wavePattern.exec(strategyContent);while(waveMatch!==null){let waveName=waveMatch[1].trim(),waveStart=waveMatch.index+waveMatch[0].length,nextWaveIdx=strategyContent.slice(waveStart).search(/^### /m),waveEnd=nextWaveIdx!==-1?waveStart+nextWaveIdx:strategyContent.length,waveContent=strategyContent.slice(waveStart,waveEnd),waveGroups=[],tableRowPattern=/^\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*[^|]*\s*\|$/gm,rowMatch=tableRowPattern.exec(waveContent);while(rowMatch!==null){let groupVal=rowMatch[1].trim(),agentVal=rowMatch[2].trim();if(groupVal!=="Group"&&!groupVal.startsWith("-"))waveGroups.push({group:groupVal,agent:agentVal});rowMatch=tableRowPattern.exec(waveContent)}if(waveGroups.length>0)waves.push({name:waveName,groups:waveGroups});waveMatch=wavePattern.exec(strategyContent)}if(waves.length===0)return buildFallbackWaves(content);return waves}function buildFallbackWaves(content){let groups=parseWishGroups(content);if(groups.length===0)return[];return[{name:"Wave 1 (sequential fallback)",groups:groups.map((g)=>({group:g.name,agent:"engineer"}))}]}async function resolveLeaderTarget(){let teamName=process.env.GENIE_TEAM;if(!teamName)return"team-lead";try{let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));return await resolveLeaderName2(teamName)}catch{return teamName}}function detectWorkMode(ref,agent){if(!agent){if(ref.includes("#"))throw Error("Manual dispatch requires an agent: genie work <slug>#<group> <agent>");return{mode:"auto",slug:ref}}if(ref.includes("#"))return{mode:"manual",ref,agent};if(agent.includes("#"))return{mode:"manual",ref:agent,agent:ref};throw Error('Invalid: ref must contain "#" \u2014 use "genie work <slug>" or "genie work <agent> <slug>#<group>"')}async function autoOrchestrateCommand(slug){let wishPath,actualSlug=slug;if(parseWishRef(slug).namespace){let resolved=await resolveWish(slug);wishPath=resolved.wishPath,actualSlug=resolved.slug;let{handleTeamCreate:handleTeamCreate2}=await Promise.resolve().then(() => (init_team(),exports_team));await handleTeamCreate2(actualSlug,{repo:resolved.repo,branch:"dev",wish:actualSlug,tmuxSession:resolved.session});return}if(validateSlug(slug),wishPath=join48(process.cwd(),".genie","wishes",slug,"WISH.md"),!existsSync39(wishPath))console.error(`\u274C Wish not found: ${wishPath}`),console.error(` Create it first: genie wish <agent> ${slug}`),process.exit(1);Promise.resolve().then(() => (init_wish_sync(),exports_wish_sync)).then((ws)=>ws.syncWishes(process.cwd())).catch(()=>{});let content=await
|
|
3314
|
-
\u2705 Agents dispatched for ${nextWave.name} (groups: ${groupList})`),console.log(` Monitor: genie status ${slug}`),console.log(" Logs: genie read <agent>")}async function brainstormCommand(agentName,slug){validateSlug(slug);let draftPath=join48(process.cwd(),".genie","brainstorms",slug,"DRAFT.md");if(!existsSync39(draftPath))console.error(`\u274C Draft not found: ${draftPath}`),console.error(` Create it first: mkdir -p .genie/brainstorms/${slug} && touch .genie/brainstorms/${slug}/DRAFT.md`),process.exit(1);let content=await
|
|
3313
|
+
... (diff truncated at 50KB)`;return combined}catch{return""}}function parseWishGroups(content){let groups=[],groupPattern=/^### Group ([A-Za-z0-9]+):/gim,match=groupPattern.exec(content);while(match!==null){let name=match[1],start2=match.index,rest=content.slice(start2+match[0].length),nextGroupIdx=rest.search(/^### Group [A-Za-z0-9]+:/m),depsMatch=(nextGroupIdx!==-1?rest.slice(0,nextGroupIdx):rest).match(/\*\*depends-on:\*\*\s*(.+)/i),dependsOn=[];if(depsMatch){let depsNormalized=depsMatch[1].trim().replace(/\s*\([^)]*\)/g,"").trim();if(depsNormalized.toLowerCase()!=="none")dependsOn=depsNormalized.split(",").map((d)=>d.trim().replace(/^groups?\s*/i,"").trim()).filter(Boolean)}groups.push({name,dependsOn}),match=groupPattern.exec(content)}return groups}function parseExecutionStrategy(content){let strategyMatch=content.match(/^## Execution Strategy\s*$/m);if(!strategyMatch||strategyMatch.index===void 0)return buildFallbackWaves(content);let strategyStart=strategyMatch.index+strategyMatch[0].length,nextSectionMatch=content.slice(strategyStart).match(/^## /m),strategyEnd=nextSectionMatch?.index!==void 0?strategyStart+nextSectionMatch.index:content.length,strategyContent=content.slice(strategyStart,strategyEnd),waves=[],wavePattern=/^### (Wave \d+[^\n]*)/gm,waveMatch=wavePattern.exec(strategyContent);while(waveMatch!==null){let waveName=waveMatch[1].trim(),waveStart=waveMatch.index+waveMatch[0].length,nextWaveIdx=strategyContent.slice(waveStart).search(/^### /m),waveEnd=nextWaveIdx!==-1?waveStart+nextWaveIdx:strategyContent.length,waveContent=strategyContent.slice(waveStart,waveEnd),waveGroups=[],tableRowPattern=/^\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*[^|]*\s*\|$/gm,rowMatch=tableRowPattern.exec(waveContent);while(rowMatch!==null){let groupVal=rowMatch[1].trim(),agentVal=rowMatch[2].trim();if(groupVal!=="Group"&&!groupVal.startsWith("-"))waveGroups.push({group:groupVal,agent:agentVal});rowMatch=tableRowPattern.exec(waveContent)}if(waveGroups.length>0)waves.push({name:waveName,groups:waveGroups});waveMatch=wavePattern.exec(strategyContent)}if(waves.length===0)return buildFallbackWaves(content);return waves}function buildFallbackWaves(content){let groups=parseWishGroups(content);if(groups.length===0)return[];return[{name:"Wave 1 (sequential fallback)",groups:groups.map((g)=>({group:g.name,agent:"engineer"}))}]}async function resolveLeaderTarget(){let teamName=process.env.GENIE_TEAM;if(!teamName)return"team-lead";try{let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));return await resolveLeaderName2(teamName)}catch{return teamName}}function detectWorkMode(ref,agent){if(!agent){if(ref.includes("#"))throw Error("Manual dispatch requires an agent: genie work <slug>#<group> <agent>");return{mode:"auto",slug:ref}}if(ref.includes("#"))return{mode:"manual",ref,agent};if(agent.includes("#"))return{mode:"manual",ref:agent,agent:ref};throw Error('Invalid: ref must contain "#" \u2014 use "genie work <slug>" or "genie work <agent> <slug>#<group>"')}async function autoOrchestrateCommand(slug){let wishPath,actualSlug=slug;if(parseWishRef(slug).namespace){let resolved=await resolveWish(slug);wishPath=resolved.wishPath,actualSlug=resolved.slug;let{handleTeamCreate:handleTeamCreate2}=await Promise.resolve().then(() => (init_team(),exports_team));await handleTeamCreate2(actualSlug,{repo:resolved.repo,branch:"dev",wish:actualSlug,tmuxSession:resolved.session});return}if(validateSlug(slug),wishPath=join48(process.cwd(),".genie","wishes",slug,"WISH.md"),!existsSync39(wishPath))console.error(`\u274C Wish not found: ${wishPath}`),console.error(` Create it first: genie wish <agent> ${slug}`),process.exit(1);Promise.resolve().then(() => (init_wish_sync(),exports_wish_sync)).then((ws)=>ws.syncWishes(process.cwd())).catch(()=>{});let content=await readFile10(wishPath,"utf-8"),groups=parseWishGroups(content),waves=parseExecutionStrategy(content);if(waves.length===0)console.error("\u274C No execution groups found in wish"),process.exit(1);let state=await getOrCreateState(slug,groups),nextWave=waves.find((wave)=>wave.groups.some((g)=>{let gs=state?.groups[g.group];return!gs||gs.status==="ready"}));if(!nextWave){console.log(`\u2705 All waves already dispatched for wish "${slug}"`);return}console.log(`\uD83D\uDE80 Dispatching ${nextWave.name} for wish "${slug}" \u2014 ${nextWave.groups.length} group(s)`),await Promise.all(nextWave.groups.map(({group,agent})=>{let ref=`${slug}#${group}`;return workDispatchCommand(agent,ref)}));let groupList=nextWave.groups.map((g)=>g.group).join(", ");console.log(`
|
|
3314
|
+
\u2705 Agents dispatched for ${nextWave.name} (groups: ${groupList})`),console.log(` Monitor: genie status ${slug}`),console.log(" Logs: genie read <agent>")}async function brainstormCommand(agentName,slug){validateSlug(slug);let draftPath=join48(process.cwd(),".genie","brainstorms",slug,"DRAFT.md");if(!existsSync39(draftPath))console.error(`\u274C Draft not found: ${draftPath}`),console.error(` Create it first: mkdir -p .genie/brainstorms/${slug} && touch .genie/brainstorms/${slug}/DRAFT.md`),process.exit(1);let content=await readFile10(draftPath,"utf-8"),context=buildContextPrompt({filePath:draftPath,sectionContent:content,command:"brainstorm",skill:"brainstorm"}),contextFile=await writeContextFile(context);console.log(`\uD83D\uDCDD Dispatching brainstorm to ${agentName} for "${slug}"`),console.log(` Draft: ${draftPath}`);let brainstormPrompt=`Brainstorm "${slug}". Your context is in the system prompt. Explore the idea, ask clarifying questions, and build toward a design.`;await handleWorkerSpawn(agentName,{provider:"claude",extraArgs:["--append-system-prompt-file",contextFile],initialPrompt:brainstormPrompt});let repoPath=process.cwd(),result2=await sendMessage2(repoPath,"cli",agentName,brainstormPrompt);if(!result2.delivered)console.warn(`\u26A0 Backup delivery to ${agentName} failed: ${result2.reason??"unknown"}`)}async function wishCommand(agentName,slug){validateSlug(slug);let designPath=join48(process.cwd(),".genie","brainstorms",slug,"DESIGN.md");if(!existsSync39(designPath))console.error(`\u274C Design not found: ${designPath}`),console.error(` Run brainstorm first: genie brainstorm <agent> ${slug}`),process.exit(1);let content=await readFile10(designPath,"utf-8"),context=buildContextPrompt({filePath:designPath,sectionContent:content,command:"wish",skill:"wish"}),contextFile=await writeContextFile(context);console.log(`\uD83D\uDCDD Dispatching wish to ${agentName} for "${slug}"`),console.log(` Design: ${designPath}`);let wishPrompt=`Create a wish from the design for "${slug}". Your context is in the system prompt. Write the WISH.md with execution groups, acceptance criteria, and validation commands.`;await handleWorkerSpawn(agentName,{provider:"claude",extraArgs:["--append-system-prompt-file",contextFile],initialPrompt:wishPrompt});let repoPath=process.cwd(),result2=await sendMessage2(repoPath,"cli",agentName,wishPrompt);if(!result2.delivered)console.warn(`\u26A0 Backup delivery to ${agentName} failed: ${result2.reason??"unknown"}`)}async function workDispatchCommand(agentName,ref){let{slug,group}=parseRef(ref);validateSlug(slug);let wishPath=join48(process.cwd(),".genie","wishes",slug,"WISH.md");if(!existsSync39(wishPath))console.error(`\u274C Wish not found: ${wishPath}`),console.error(` Create it first: genie wish <agent> ${slug}`),process.exit(1);let content=await readFile10(wishPath,"utf-8"),groupSection=extractGroup(content,group);if(!groupSection){console.error(`\u274C Group "${group}" not found in ${wishPath}`),console.error(" Available groups:");let groups2=content.match(/^### Group [A-Za-z0-9]+:.*$/gm);if(groups2)for(let g of groups2)console.error(` ${g}`);process.exit(1)}let groups=parseWishGroups(content);await getOrCreateState(slug,groups);try{await startGroup(slug,group,agentName),console.log(`\u2705 Group "${group}" set to in_progress (assigned to ${agentName})`)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`\u274C ${message}`),process.exit(1)}let wishContext=extractWishContext(content),enrichedContext;try{let{enrichContext:enrichContext2}=await Promise.resolve().then(() => (init_context_enrichment(),exports_context_enrichment));enrichedContext=enrichContext2({query:`${slug} ${group}: ${groupSection.slice(0,500)}`})||void 0}catch{}let context=buildContextPrompt({filePath:wishPath,sectionContent:groupSection,wishContext,command:`work ${ref}`,skill:"work",enrichedContext}),contextFile=await writeContextFile(context);console.log(`\uD83D\uDD27 Dispatching work to ${agentName} for "${ref}"`),console.log(` Wish: ${wishPath}`),console.log(` Group: ${group}`);let effectiveRole=`${agentName}-${group}`,leaderTarget=await resolveLeaderTarget(),workPrompt=`Execute Group ${group} of wish "${slug}". Your full context is in the system prompt. Read the wish at ${wishPath} if needed. Implement all deliverables, run validation, and report completion.
|
|
3315
3315
|
|
|
3316
3316
|
When done:
|
|
3317
3317
|
1. Run: genie done ${slug}#${group}
|
|
3318
|
-
2. Run: genie send 'Group ${group} complete. <summary>' --to ${leaderTarget}`;await handleWorkerSpawn(agentName,{provider:"claude",role:effectiveRole,extraArgs:["--append-system-prompt-file",contextFile],initialPrompt:workPrompt});let repoPath=process.cwd(),result2=await sendMessage2(repoPath,"cli",effectiveRole,workPrompt);if(!result2.delivered)console.warn(`\u26A0 Backup delivery to ${effectiveRole} failed: ${result2.reason??"unknown"}`)}async function reviewCommand(agentName,ref){let{slug,group}=parseRef(ref);validateSlug(slug);let wishPath=join48(process.cwd(),".genie","wishes",slug,"WISH.md");if(!existsSync39(wishPath))console.error(`\u274C Wish not found: ${wishPath}`),process.exit(1);let content=await
|
|
3318
|
+
2. Run: genie send 'Group ${group} complete. <summary>' --to ${leaderTarget}`;await handleWorkerSpawn(agentName,{provider:"claude",role:effectiveRole,extraArgs:["--append-system-prompt-file",contextFile],initialPrompt:workPrompt});let repoPath=process.cwd(),result2=await sendMessage2(repoPath,"cli",effectiveRole,workPrompt);if(!result2.delivered)console.warn(`\u26A0 Backup delivery to ${effectiveRole} failed: ${result2.reason??"unknown"}`)}async function reviewCommand(agentName,ref){let{slug,group}=parseRef(ref);validateSlug(slug);let wishPath=join48(process.cwd(),".genie","wishes",slug,"WISH.md");if(!existsSync39(wishPath))console.error(`\u274C Wish not found: ${wishPath}`),process.exit(1);let content=await readFile10(wishPath,"utf-8"),groupSection=extractGroup(content,group);if(!groupSection)console.error(`\u274C Group "${group}" not found in ${wishPath}`),process.exit(1);let diff=getGitDiff(),wishContext=extractWishContext(content),reviewContent=[groupSection,"","## Git Diff (changes to review)","",diff?`\`\`\`diff
|
|
3319
3319
|
${diff}
|
|
3320
3320
|
\`\`\``:"(no uncommitted changes found \u2014 review committed changes)"].join(`
|
|
3321
3321
|
`),enrichedReviewContext;try{let{enrichContext:enrichContext2}=await Promise.resolve().then(() => (init_context_enrichment(),exports_context_enrichment));enrichedReviewContext=enrichContext2({query:`review ${slug} ${group}: ${groupSection.slice(0,500)}`})||void 0}catch{}let context=buildContextPrompt({filePath:wishPath,sectionContent:reviewContent,wishContext,command:`review ${ref}`,skill:"review",enrichedContext:enrichedReviewContext}),contextFile=await writeContextFile(context);if(console.log(`\uD83D\uDD0D Dispatching review to ${agentName} for "${ref}"`),console.log(` Wish: ${wishPath}`),console.log(` Group: ${group}`),diff)console.log(` Diff: ${diff.split(`
|
|
@@ -3365,13 +3365,13 @@ Event Throughput:`),console.log(` Emitted: ${data.events_emitted}`),console.lo
|
|
|
3365
3365
|
${projects.length} project${projects.length===1?"":"s"}`)}function printProjectDetail(p,tasks){let byStatus={},byStage={};for(let t of tasks)byStatus[t.status]=(byStatus[t.status]??0)+1,byStage[t.stage]=(byStage[t.stage]??0)+1;if(console.log(`
|
|
3366
3366
|
Project: ${p.name}`),console.log("\u2500".repeat(50)),console.log(` ID: ${p.id}`),console.log(` Type: ${p.repoPath?"repo":"virtual"}`),p.repoPath)console.log(` Path: ${p.repoPath}`);if(p.description)console.log(` Desc: ${p.description}`);if(console.log(` Created: ${formatDate(p.createdAt)}`),console.log(` Tasks: ${tasks.length}`),tasks.length>0){console.log(`
|
|
3367
3367
|
By status:`);for(let[status,count]of Object.entries(byStatus).sort())console.log(` ${padRight(status,15)} ${count}`);console.log(`
|
|
3368
|
-
By stage:`);for(let[stage,count]of Object.entries(byStage).sort())console.log(` ${padRight(stage,15)} ${count}`)}}function registerProjectCommands(program2){let project=program2.command("project").description("Project management \u2014 named task boards");project.command("list").description("List all projects").option("--all","Include archived projects").option("--json","Output as JSON").action(async(options)=>{try{let ts3=await getTaskService5(),projects=await ts3.listProjectsFiltered(options.all);if(options.json){console.log(JSON.stringify(projects,null,2));return}if(projects.length===0){console.log("No projects found. Projects are auto-created when you run `genie task create`.");return}await printProjectList(ts3,projects)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("create <name>").description("Create a new project").option("--virtual","Create a virtual project (not tied to a repo)").option("--repo <path>","Repo path for the project").option("--description <text>","Project description").action(async(name,options)=>{try{let ts3=await getTaskService5(),repoPath=options.virtual?null:options.repo??null,p=await ts3.createProject({name,repoPath,description:options.description});if(console.log(`Created project "${p.name}"`),console.log(` ID: ${p.id}`),console.log(` Type: ${p.repoPath?"repo":"virtual"}`),p.repoPath)console.log(` Path: ${p.repoPath}`);if(p.description)console.log(` Desc: ${p.description}`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("show <name>").description("Show project detail with task stats").option("--json","Output as JSON").action(async(name,options)=>{try{let ts3=await getTaskService5(),p=await ts3.getProjectByName(name);if(!p){console.error(`Error: Project not found: ${name}`),process.exit(1);return}if(options.json){console.log(JSON.stringify(p,null,2));return}let tasks=await ts3.listTasks({projectName:name,allProjects:!0});printProjectDetail(p,tasks)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("archive <name>").description("Archive a project (cascades to boards and unfinished tasks)").action(async(name)=>{try{await(await getTaskService5()).archiveProject(name),console.log(`Archived project "${name}" and cascaded to boards + unfinished tasks.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("unarchive <name>").description("Restore an archived project and its boards (tasks stay as-is)").action(async(name)=>{try{await(await getTaskService5()).unarchiveProject(name),console.log(`Unarchived project "${name}" and restored boards.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("set-default <name>").description("Set default project for when outside any repo").action(async(name)=>{try{if(!await(await getTaskService5()).getProjectByName(name))console.error(`Error: Project not found: ${name}`),process.exit(1);let{loadGenieConfig:loadGenieConfig2,saveGenieConfig:saveGenieConfig2}=await Promise.resolve().then(() => (init_genie_config2(),exports_genie_config)),config=await loadGenieConfig2();config.defaultProject=name,await saveGenieConfig2(config),console.log(`Default project set to "${name}"`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_agent_registry();import{readFile as
|
|
3369
|
-
`),name="",currentSection="none",setup=[],actions=[],expect=[],parsers2={none:()=>{},setup:(line)=>{let step=parseSetupLine(line);if(step)setup.push(step)},actions:(line)=>{let step=parseActionLine(line);if(step)actions.push(step)},expect:(line)=>{let exp=parseExpectLine(line);if(exp)expect.push(exp)}};for(let line of lines){let trimmed=line.trim();if(trimmed.startsWith("# ")&&!trimmed.startsWith("## ")){let match=trimmed.match(/^#\s+(?:Test:\s*)?(.+)$/);if(match)name=match[1].trim();continue}let section=detectSection(trimmed);if(section!==null){currentSection=section;continue}if(!trimmed||trimmed.startsWith("//")||trimmed.startsWith("<!--"))continue;parsers2[currentSection](trimmed)}if(!name)name=filePath.replace(/.*\//,"").replace(/\.md$/,"");return{name,file:filePath,setup,actions,expect}}function parseSetupLine(line){let text=stripListPrefix(line);if(!text)return null;let spawnMatch=text.match(/^spawn\s+(\S+)(?:\s+\((.+)\))?$/i);if(spawnMatch)return{kind:"spawn",target:spawnMatch[1],options:spawnMatch[2]?parseOptions2(spawnMatch[2]):{}};let followMatch=text.match(/^(?:start\s+)?follow(?:\s+(?:on\s+)?(\S+))?(?:\s+\((.+)\))?$/i);if(followMatch)return{kind:"follow",target:followMatch[1]||"team",options:followMatch[2]?parseOptions2(followMatch[2]):{}};return null}function parseActionLine(line){let text=stripListPrefix(line);if(!text)return null;let sendMatch=text.match(/^send\s+"([^"]+)"\s+to\s+(\S+)/i);if(sendMatch)return{kind:"send",message:sendMatch[1],to:sendMatch[2]};let waitMatch=text.match(/^wait\s+(?:for\s+.+?\s+)?\(?(?:max\s+)?(\d+)s\)?/i);if(waitMatch)return{kind:"wait",seconds:Number.parseInt(waitMatch[1],10)};let runMatch=text.match(/^run\s+(.+)$/i);if(runMatch)return{kind:"run",command:runMatch[1]};return null}function detectSource(text){if(/\binbox\b/i.test(text))return"inbox";if(/\blog\b/i.test(text))return"log";if(/\boutput\b/i.test(text))return"output";return"log"}function extractMatchers(text){let matchers={},matcherRegex=/(\w+)\s*([~!]?=)\s*(?:"([^"]+)"|(\S+))/g;for(let match of text.matchAll(matcherRegex)){let op=match[2]==="~="?"~":"",value=(match[3]??match[4]).trim();matchers[match[1]]=`${op}${value}`}return matchers}function parseExpectLine(line){let text=line.replace(/^[-*]\s*\[[ x]\]\s*/i,"").trim();if(!text)return null;return{description:text,source:detectSource(text),matchers:extractMatchers(text)}}function stripListPrefix(line){return line.replace(/^[-*]\s+/,"").replace(/^\d+\.\s+/,"").trim()}function parseOptions2(text){let opts={};for(let pair of text.split(",")){let[key,...rest]=pair.split(":");if(key&&rest.length>0)opts[key.trim()]=rest.join(":").trim()}return opts}import{cp as cp2,mkdir as mkdir8,rm as rm4,writeFile as
|
|
3368
|
+
By stage:`);for(let[stage,count]of Object.entries(byStage).sort())console.log(` ${padRight(stage,15)} ${count}`)}}function registerProjectCommands(program2){let project=program2.command("project").description("Project management \u2014 named task boards");project.command("list").description("List all projects").option("--all","Include archived projects").option("--json","Output as JSON").action(async(options)=>{try{let ts3=await getTaskService5(),projects=await ts3.listProjectsFiltered(options.all);if(options.json){console.log(JSON.stringify(projects,null,2));return}if(projects.length===0){console.log("No projects found. Projects are auto-created when you run `genie task create`.");return}await printProjectList(ts3,projects)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("create <name>").description("Create a new project").option("--virtual","Create a virtual project (not tied to a repo)").option("--repo <path>","Repo path for the project").option("--description <text>","Project description").action(async(name,options)=>{try{let ts3=await getTaskService5(),repoPath=options.virtual?null:options.repo??null,p=await ts3.createProject({name,repoPath,description:options.description});if(console.log(`Created project "${p.name}"`),console.log(` ID: ${p.id}`),console.log(` Type: ${p.repoPath?"repo":"virtual"}`),p.repoPath)console.log(` Path: ${p.repoPath}`);if(p.description)console.log(` Desc: ${p.description}`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("show <name>").description("Show project detail with task stats").option("--json","Output as JSON").action(async(name,options)=>{try{let ts3=await getTaskService5(),p=await ts3.getProjectByName(name);if(!p){console.error(`Error: Project not found: ${name}`),process.exit(1);return}if(options.json){console.log(JSON.stringify(p,null,2));return}let tasks=await ts3.listTasks({projectName:name,allProjects:!0});printProjectDetail(p,tasks)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("archive <name>").description("Archive a project (cascades to boards and unfinished tasks)").action(async(name)=>{try{await(await getTaskService5()).archiveProject(name),console.log(`Archived project "${name}" and cascaded to boards + unfinished tasks.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("unarchive <name>").description("Restore an archived project and its boards (tasks stay as-is)").action(async(name)=>{try{await(await getTaskService5()).unarchiveProject(name),console.log(`Unarchived project "${name}" and restored boards.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),project.command("set-default <name>").description("Set default project for when outside any repo").action(async(name)=>{try{if(!await(await getTaskService5()).getProjectByName(name))console.error(`Error: Project not found: ${name}`),process.exit(1);let{loadGenieConfig:loadGenieConfig2,saveGenieConfig:saveGenieConfig2}=await Promise.resolve().then(() => (init_genie_config2(),exports_genie_config)),config=await loadGenieConfig2();config.defaultProject=name,await saveGenieConfig2(config),console.log(`Default project set to "${name}"`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_agent_registry();import{readFile as readFile13,stat as stat5}from"fs/promises";import{join as join56}from"path";import{readFile as readFile11}from"fs/promises";async function parseQaSpec(filePath){let content=await readFile11(filePath,"utf-8");return parseQaSpecContent(content,filePath)}var SECTION_MAP={setup:"setup",actions:"actions",expect:"expect"};function detectSection(line){for(let[keyword,section]of Object.entries(SECTION_MAP))if(new RegExp(`^##\\s+${keyword}`,"i").test(line))return section;if(line.startsWith("## "))return"none";return null}function parseQaSpecContent(content,filePath="<inline>"){let lines=content.split(`
|
|
3369
|
+
`),name="",currentSection="none",setup=[],actions=[],expect=[],parsers2={none:()=>{},setup:(line)=>{let step=parseSetupLine(line);if(step)setup.push(step)},actions:(line)=>{let step=parseActionLine(line);if(step)actions.push(step)},expect:(line)=>{let exp=parseExpectLine(line);if(exp)expect.push(exp)}};for(let line of lines){let trimmed=line.trim();if(trimmed.startsWith("# ")&&!trimmed.startsWith("## ")){let match=trimmed.match(/^#\s+(?:Test:\s*)?(.+)$/);if(match)name=match[1].trim();continue}let section=detectSection(trimmed);if(section!==null){currentSection=section;continue}if(!trimmed||trimmed.startsWith("//")||trimmed.startsWith("<!--"))continue;parsers2[currentSection](trimmed)}if(!name)name=filePath.replace(/.*\//,"").replace(/\.md$/,"");return{name,file:filePath,setup,actions,expect}}function parseSetupLine(line){let text=stripListPrefix(line);if(!text)return null;let spawnMatch=text.match(/^spawn\s+(\S+)(?:\s+\((.+)\))?$/i);if(spawnMatch)return{kind:"spawn",target:spawnMatch[1],options:spawnMatch[2]?parseOptions2(spawnMatch[2]):{}};let followMatch=text.match(/^(?:start\s+)?follow(?:\s+(?:on\s+)?(\S+))?(?:\s+\((.+)\))?$/i);if(followMatch)return{kind:"follow",target:followMatch[1]||"team",options:followMatch[2]?parseOptions2(followMatch[2]):{}};return null}function parseActionLine(line){let text=stripListPrefix(line);if(!text)return null;let sendMatch=text.match(/^send\s+"([^"]+)"\s+to\s+(\S+)/i);if(sendMatch)return{kind:"send",message:sendMatch[1],to:sendMatch[2]};let waitMatch=text.match(/^wait\s+(?:for\s+.+?\s+)?\(?(?:max\s+)?(\d+)s\)?/i);if(waitMatch)return{kind:"wait",seconds:Number.parseInt(waitMatch[1],10)};let runMatch=text.match(/^run\s+(.+)$/i);if(runMatch)return{kind:"run",command:runMatch[1]};return null}function detectSource(text){if(/\binbox\b/i.test(text))return"inbox";if(/\blog\b/i.test(text))return"log";if(/\boutput\b/i.test(text))return"output";return"log"}function extractMatchers(text){let matchers={},matcherRegex=/(\w+)\s*([~!]?=)\s*(?:"([^"]+)"|(\S+))/g;for(let match of text.matchAll(matcherRegex)){let op=match[2]==="~="?"~":"",value=(match[3]??match[4]).trim();matchers[match[1]]=`${op}${value}`}return matchers}function parseExpectLine(line){let text=line.replace(/^[-*]\s*\[[ x]\]\s*/i,"").trim();if(!text)return null;return{description:text,source:detectSource(text),matchers:extractMatchers(text)}}function stripListPrefix(line){return line.replace(/^[-*]\s+/,"").replace(/^\d+\.\s+/,"").trim()}function parseOptions2(text){let opts={};for(let pair of text.split(",")){let[key,...rest]=pair.split(":");if(key&&rest.length>0)opts[key.trim()]=rest.join(":").trim()}return opts}import{cp as cp2,mkdir as mkdir8,rm as rm4,writeFile as writeFile6}from"fs/promises";import{tmpdir as tmpdir4}from"os";import{dirname as dirname11,join as join55,resolve as resolve11}from"path";var{$:$4}=globalThis.Bun;import{createHash as createHash5}from"crypto";import{mkdir as mkdir7,readFile as readFile12,readdir as readdir6,stat as stat4,writeFile as writeFile5}from"fs/promises";import{homedir as homedir31}from"os";import{join as join54,relative as relative5,resolve as resolve10}from"path";function repoHash(repoPath){return createHash5("sha256").update(resolve10(repoPath)).digest("hex").slice(0,12)}function resultsDir(repoPath){let base=process.env.GENIE_HOME??join54(homedir31(),".genie");return join54(base,"qa",repoHash(repoPath))}function resultsPath(repoPath){return join54(resultsDir(repoPath),"results.json")}async function loadResults(repoPath){try{let raw=await readFile12(resultsPath(repoPath),"utf-8");return JSON.parse(raw)}catch{return{}}}async function saveResult(repoPath,specKey,report){let dir=resultsDir(repoPath);await mkdir7(dir,{recursive:!0});let results=await loadResults(repoPath),specHash=await hashSpecFile(report.file);results[specKey]={lastRun:new Date().toISOString(),result:report.result,durationMs:report.durationMs,specHash,expectations:report.expectations,error:report.error},await writeFile5(resultsPath(repoPath),JSON.stringify(results,null,2))}async function isStale(repoPath,specKey,specFilePath){let stored=(await loadResults(repoPath))[specKey];if(!stored)return!1;return await hashSpecFile(specFilePath)!==stored.specHash}async function listAllSpecs(specDir){let entries=[];return await walkSpecs(specDir,specDir,entries),entries.sort((a,b2)=>{if(a.domain!==b2.domain)return a.domain.localeCompare(b2.domain);return a.name.localeCompare(b2.name)})}async function hashSpecFile(filePath){try{let content=await readFile12(filePath,"utf-8");return createHash5("sha256").update(content).digest("hex").slice(0,12)}catch{return"unknown"}}async function walkSpecs(baseDir,dir,entries){let items=await readdir6(dir);for(let item of items){let fullPath=join54(dir,item);if((await stat4(fullPath)).isDirectory())await walkSpecs(baseDir,fullPath,entries);else if(item.endsWith(".md")){let rel=relative5(baseDir,fullPath),parts=rel.replace(/\.md$/,"").split("/"),domain=parts.length>1?parts.slice(0,-1).join("/"):"(root)",name=parts[parts.length-1];entries.push({key:rel.replace(/\.md$/,""),domain,name,filePath:fullPath})}}}function specKeyFromPath(specDir,filePath){return relative5(specDir,filePath).replace(/\.md$/,"")}function formatTimeAgo(isoDate){let ms=Date.now()-new Date(isoDate).getTime(),seconds=Math.floor(ms/1000);if(seconds<60)return`${seconds}s ago`;let minutes=Math.floor(seconds/60);if(minutes<60)return`${minutes}m ago`;let hours=Math.floor(minutes/60);if(hours<24)return`${hours}h ago`;return`${Math.floor(hours/24)}d ago`}init_runtime_events();init_team_manager();function emitNdjson(event){process.stdout.write(`${JSON.stringify(event)}
|
|
3370
3370
|
`)}async function publishQaEvent(repoPath,qaType,payload){let{specKey,domain,team,...rest}=payload;await publishSubjectEvent(repoPath,`genie.qa.${qaType}`,{kind:"qa",agent:"qa",team,text:`${qaType}: ${specKey}`,data:{qaType,specKey,domain,...rest},source:"hook"})}async function emitQaEvent(repoPath,qaType,payload,ndjson){if(await publishQaEvent(repoPath,qaType,payload),ndjson)emitNdjson(payload)}function parseStatusEntry(status,parts,index){if(status.startsWith("R")){let from=parts[index]??"",to=parts[index+1]??"";if(from&&to)return{op:{kind:"rename",from,to},nextIndex:index+2};return{op:null,nextIndex:index+2}}let path3=parts[index]??"";if(!path3)return{op:null,nextIndex:index+1};return{op:{kind:status.startsWith("D")?"delete":"copy",path:path3},nextIndex:index+1}}function parseNameStatusZ(output){if(!output)return[];let parts=output.split("\x00").filter(Boolean),ops=[],i2=0;while(i2<parts.length){let status=parts[i2++]??"";if(!status)break;let{op,nextIndex}=parseStatusEntry(status,parts,i2);if(i2=nextIndex,op)ops.push(op)}return ops}async function overlayDirtyWorkingTree(repoPath,worktreePath){let tracked=(await $4`git -C ${repoPath} diff --name-status --find-renames -z HEAD --`.quiet().nothrow().text()).trim(),untracked=(await $4`git -C ${repoPath} ls-files --others --exclude-standard -z`.quiet().nothrow().text()).trim(),ops=parseNameStatusZ(tracked);for(let path3 of untracked.split("\x00").filter(Boolean))ops.push({kind:"copy",path:path3});for(let op of ops){if(op.kind==="delete"){await rm4(join55(worktreePath,op.path),{recursive:!0,force:!0});continue}if(op.kind==="rename"){await rm4(join55(worktreePath,op.from),{recursive:!0,force:!0});let src2=join55(repoPath,op.to),dest2=join55(worktreePath,op.to);await mkdir8(dirname11(dest2),{recursive:!0}),await cp2(src2,dest2,{recursive:!0,force:!0});continue}let src=join55(repoPath,op.path),dest=join55(worktreePath,op.path);await mkdir8(dirname11(dest),{recursive:!0}),await cp2(src,dest,{recursive:!0,force:!0})}}async function runAllSpecs(specDir,options){let entries=await listAllSpecs(specDir);return runSpecEntries(entries,specDir,options)}async function runDomainSpecs(specDir,domain,options){let filtered=(await listAllSpecs(specDir)).filter((e)=>e.domain===domain);return runSpecEntries(filtered,specDir,options)}async function prepareTeams(entries,repoPath,ndjson){let prepared=[];console.error(`
|
|
3371
|
-
[qa] Creating ${entries.length} teams...`);for(let entry of entries){let teamName=`qa-${Date.now().toString(36)}-${entry.name.slice(0,8)}`;try{let spec=await parseQaSpec(entry.filePath),config=await createTeam(teamName,repoPath);await overlayDirtyWorkingTree(repoPath,config.worktreePath),await hireAgent(teamName,"qa");let prompt2=buildTeamLeadPrompt(spec,teamName,repoPath),promptFile=join55(tmpdir4(),`genie-qa-${teamName}.md`);await
|
|
3371
|
+
[qa] Creating ${entries.length} teams...`);for(let entry of entries){let teamName=`qa-${Date.now().toString(36)}-${entry.name.slice(0,8)}`;try{let spec=await parseQaSpec(entry.filePath),config=await createTeam(teamName,repoPath);await overlayDirtyWorkingTree(repoPath,config.worktreePath),await hireAgent(teamName,"qa");let prompt2=buildTeamLeadPrompt(spec,teamName,repoPath),promptFile=join55(tmpdir4(),`genie-qa-${teamName}.md`);await writeFile6(promptFile,prompt2),prepared.push({entry,spec,teamName,worktreePath:config.worktreePath,promptFile}),console.error(` \u2713 ${entry.name}`),await emitQaEvent(repoPath,"team-created",{type:"qa:team-created",specKey:entry.key,domain:entry.domain,team:teamName},ndjson)}catch(err){console.error(` \u2717 ${entry.name}: ${err instanceof Error?err.message:err}`)}await new Promise((r)=>setTimeout(r,200))}return prepared}async function emitSpecDone(repoPath,specKey,domain,team,report,ndjson){await emitQaEvent(repoPath,"spec-done",{type:"qa:spec-done",specKey,domain,team,result:report.result,durationMs:report.durationMs,expectations:report.expectations,error:report.error},ndjson)}async function runSpecEntries(entries,specDir,options){let repoPath=resolve11(options?.repoPath??process.cwd()),maxConcurrency=options?.parallel??5,timeoutMs=(options?.timeout??3600)*1000,ndjson=options?.ndjson??!1,GREEN="\x1B[32m",RED="\x1B[31m",RESET2="\x1B[0m",DIM2="\x1B[90m",prepared=await prepareTeams(entries,repoPath,ndjson);if(prepared.length===0)return[];let timelineSub=await followRuntimeEvents({repoPath,teamPrefix:"qa-"},(event)=>{if(!event?.timestamp||!event?.kind)return;let team=event.team??"";if(!team.startsWith("qa-"))return;let match=prepared.find((p)=>p.teamName===team),specKey=match?.entry.key??team,specName=match?.entry.name??team,domain=match?.entry.domain??"",time=new Date(event.timestamp).toLocaleTimeString("en-US",{hour12:!1}),text=(event.text??"").slice(0,100);if(console.error(` \x1B[90m${time}\x1B[0m \x1B[90m[${event.kind}]\x1B[0m ${specName} \x1B[90m${text}\x1B[0m`),ndjson)emitNdjson({type:"qa:event",specKey,domain,team,event:{timestamp:event.timestamp,kind:event.kind,agent:event.agent??"",text:event.text??""}})},{pollIntervalMs:250});console.error(`
|
|
3372
3372
|
[qa] Running ${prepared.length} specs (max ${maxConcurrency} parallel)
|
|
3373
3373
|
`);let reports=[],running2=0,nextIdx=0,startTime=Date.now();await new Promise((resolveAll)=>{let tryStartNext=()=>{while(running2<maxConcurrency&&nextIdx<prepared.length){let p=prepared[nextIdx++];running2++,emitQaEvent(repoPath,"spec-started",{type:"qa:spec-started",specKey:p.entry.key,domain:p.entry.domain,team:p.teamName},ndjson),runPreparedSpec(repoPath,p.spec,p.teamName,p.worktreePath,p.promptFile,timeoutMs).then(async(report)=>{let key=specKeyFromPath(specDir,p.entry.filePath);await saveResult(repoPath,key,report);let icon=report.result==="pass"?`${"\x1B[32m"}\u2713${"\x1B[0m"}`:`${"\x1B[31m"}\u2717${"\x1B[0m"}`,elapsed=((Date.now()-startTime)/1000).toFixed(0);console.error(` ${icon} ${p.entry.name} (${(report.durationMs/1000).toFixed(0)}s) [${elapsed}s total]`),await emitSpecDone(repoPath,p.entry.key,p.entry.domain,p.teamName,report,ndjson),reports.push(report)}).catch((err)=>{let errorReport=makeErrorReport(p.spec,Date.now(),String(err));reports.push(errorReport),console.error(` ${"\x1B[31m"}\u2717${"\x1B[0m"} ${p.entry.name}: ${err}`),emitSpecDone(repoPath,p.entry.key,p.entry.domain,p.teamName,errorReport,ndjson)}).finally(()=>{if(running2--,reports.length===prepared.length)resolveAll();else tryStartNext()})}};tryStartNext()}),await timelineSub.stop(),console.error(`
|
|
3374
|
-
[qa] Cleaning up ${prepared.length} teams...`);for(let p of prepared)try{await disbandTeam(p.teamName)}catch{}return reports}async function runSpec(spec,options){let repoPath=resolve11(options?.repoPath??process.cwd()),timeoutMs=(options?.timeout??3600)*1000,ndjson=options?.ndjson??!1,specKey=options?.specKey??spec.name,domain=specKey.includes("/")?specKey.split("/").slice(0,-1).join("/"):"(root)",teamName=`qa-${Date.now().toString(36)}-${spec.name.slice(0,8).replace(/\s+/g,"-")}`,start2=Date.now();try{let config=await createTeam(teamName,repoPath);await overlayDirtyWorkingTree(repoPath,config.worktreePath),await hireAgent(teamName,"qa"),await emitQaEvent(repoPath,"team-created",{type:"qa:team-created",specKey,domain,team:teamName},ndjson);let prompt2=buildTeamLeadPrompt(spec,teamName,repoPath),promptFile=join55(tmpdir4(),`genie-qa-${teamName}.md`);await
|
|
3374
|
+
[qa] Cleaning up ${prepared.length} teams...`);for(let p of prepared)try{await disbandTeam(p.teamName)}catch{}return reports}async function runSpec(spec,options){let repoPath=resolve11(options?.repoPath??process.cwd()),timeoutMs=(options?.timeout??3600)*1000,ndjson=options?.ndjson??!1,specKey=options?.specKey??spec.name,domain=specKey.includes("/")?specKey.split("/").slice(0,-1).join("/"):"(root)",teamName=`qa-${Date.now().toString(36)}-${spec.name.slice(0,8).replace(/\s+/g,"-")}`,start2=Date.now();try{let config=await createTeam(teamName,repoPath);await overlayDirtyWorkingTree(repoPath,config.worktreePath),await hireAgent(teamName,"qa"),await emitQaEvent(repoPath,"team-created",{type:"qa:team-created",specKey,domain,team:teamName},ndjson);let prompt2=buildTeamLeadPrompt(spec,teamName,repoPath),promptFile=join55(tmpdir4(),`genie-qa-${teamName}.md`);await writeFile6(promptFile,prompt2),await emitQaEvent(repoPath,"spec-started",{type:"qa:spec-started",specKey,domain,team:teamName},ndjson);let report=await runPreparedSpec(repoPath,spec,teamName,config.worktreePath,promptFile,timeoutMs);return await emitSpecDone(repoPath,specKey,domain,teamName,report,ndjson),await disbandTeam(teamName),report}catch(err){try{await disbandTeam(teamName)}catch{}let errorReport=makeErrorReport(spec,start2,String(err));return await emitSpecDone(repoPath,specKey,domain,teamName,errorReport,ndjson),errorReport}}async function runPreparedSpec(repoPath,spec,teamName,worktreePath,promptFile,timeoutMs){let start2=Date.now(),effectiveTimeoutMs=computeEffectiveTimeoutMs(spec,timeoutMs);try{let{handleWorkerSpawn:handleWorkerSpawn2}=await Promise.resolve().then(() => (init_agents(),exports_agents)),_paneId=await handleWorkerSpawn2("qa",{provider:"claude",team:teamName,session:teamName,cwd:worktreePath,role:"qa",extraArgs:["--append-system-prompt-file",promptFile],initialPrompt:`Execute the QA spec "${spec.name}" end-to-end right now. Do not stop after partial progress or a wait step. Continue until you validate the expectations, publish qa-report, and run team done. Your full instructions are in the system prompt.`});return console.error(` [qa] Spawned qa for "${spec.name}" in ${teamName}`),await waitForResult(spec,repoPath,teamName,effectiveTimeoutMs,start2)}catch(err){return makeErrorReport(spec,start2,String(err))}}function buildQaPromptArtifacts(teamName){let base=`genie-qa-${teamName}`;return{followFile:join55(tmpdir4(),`${base}-follow.ndjson`),followPidFile:join55(tmpdir4(),`${base}-follow.pid`),followSinceFile:join55(tmpdir4(),`${base}-follow.since`),snapshotFile:join55(tmpdir4(),`${base}-snapshot.ndjson`)}}function buildTeamLeadPrompt(spec,teamName,repoPath){let genieEntry=join55(repoPath,"src/genie.ts"),genieCmd=`bun run "${genieEntry}"`,{followFile,followPidFile,followSinceFile,snapshotFile}=buildQaPromptArtifacts(teamName),qaCheckCmd=buildQaCheckCommand(genieCmd,spec.file,teamName,followSinceFile),specSummary=formatSpecForPrompt(spec),setupInstructions=formatSetupInstructions(spec,teamName,genieCmd,followFile,followPidFile,followSinceFile),actionInstructions=formatActionInstructions(spec,teamName,genieCmd,qaCheckCmd),expectInstructions=spec.expect.map((e)=>`- ${e.description} (source: ${e.source}, matchers: ${JSON.stringify(e.matchers)})`).join(`
|
|
3375
3375
|
`);return`You are a QA team-lead for team "${teamName}". Your job is to execute the following QA spec and report PASS or FAIL.
|
|
3376
3376
|
|
|
3377
3377
|
## QA Spec: ${spec.name}
|
|
@@ -3445,7 +3445,7 @@ Team: ${teamName}
|
|
|
3445
3445
|
`}function formatSetupInstructions(spec,teamName,genieCmd,followFile,followPidFile,followSinceFile){return spec.setup.map((s2)=>{if(s2.kind==="spawn"){let provider=s2.options.provider||"claude";return`- Spawn agent: \`${genieCmd} spawn ${s2.target} --provider ${provider} --team ${teamName}\``}if(s2.kind==="follow")return`- Start detached runtime follow with this exact command (do not use Claude background tasks): \`date -u +"%Y-%m-%dT%H:%M:%SZ" > "${followSinceFile}" && nohup ${genieCmd} log --follow --team ${teamName} --ndjson > "${followFile}" 2>&1 < /dev/null & echo $! > "${followPidFile}" && sleep 2\``;return`- Unknown setup step: ${s2.kind}`}).join(`
|
|
3446
3446
|
`)}function buildQaCheckCommand(genieCmd,specFile,teamName,followSinceFile){return`${genieCmd} qa check "${specFile}" --team ${teamName} --since-file "${followSinceFile}"`}function formatActionInstructions(spec,teamName,genieCmd,qaCheckCmd){return spec.actions.map((a,i2)=>{let isFinalAction=i2===spec.actions.length-1;if(a.kind==="send")return`${i2+1}. Send message: \`${genieCmd} send '${a.message}' --to ${a.to} --team ${teamName}\``;if(a.kind==="wait"){if(isFinalAction)return`${i2+1}. Finalize in one command: \`sleep ${a.seconds??1} && ${qaCheckCmd}\``;return`${i2+1}. Wait ${a.seconds??1} seconds`}if(a.kind==="run")return`${i2+1}. Run command: \`${rewriteRunCommand(a.command??"",genieCmd)}\``;return`${i2+1}. Unknown action: ${a.kind}`}).join(`
|
|
3447
3447
|
`)}function rewriteRunCommand(command,genieCmd){if(!command)return command;return command.replace(/(^|[;&|()\s])genie(?=\s|$)/g,`$1${genieCmd}`)}function formatSpecForPrompt(spec){return["### Setup",...spec.setup.map(formatSetupStep),"### Actions",...spec.actions.map(formatActionStep),"### Expectations",...spec.expect.map((e)=>`- [ ] ${e.description}`)].join(`
|
|
3448
|
-
`)}function formatSetupStep(s2){if(s2.kind==="spawn"){let opts=Object.entries(s2.options).map(([k,v])=>`${k}: ${v}`).join(", ");return`- spawn ${s2.target}${opts?` (${opts})`:""}`}return`- follow ${s2.target}`}function formatActionStep(a){if(a.kind==="send")return`- send "${a.message}" to ${a.to}`;if(a.kind==="wait")return`- wait ${a.seconds??1}s`;if(a.kind==="run")return`- run ${a.command}`;return`- ${a.kind}`}async function waitForResult(spec,repoPath,teamName,timeoutMs,start2){let subject=`genie.qa.${teamName}.result`,remainingMs=Math.max(timeoutMs-(Date.now()-start2),1),event=await waitForRuntimeEvent({repoPath,subject,team:teamName},remainingMs);if(!event)return makeErrorReport(spec,start2,`Timeout after ${timeoutMs}ms waiting for team-lead report in PG event log`);return parseTeamLeadReport(spec,event.data??{},start2)}function parseTeamLeadReport(spec,payload,start2){try{let data=payload;return{name:spec.name,file:spec.file,result:data.result==="pass"?"pass":"fail",expectations:data.expectations??[],collectedEvents:data.collectedEvents??[],durationMs:Date.now()-start2}}catch(err){return makeErrorReport(spec,start2,`Failed to parse team-lead report: ${err}`)}}function makeErrorReport(spec,start2,error2){return{name:spec.name,file:spec.file,result:"error",expectations:[],collectedEvents:[],durationMs:Date.now()-start2,error:error2}}function computeEffectiveTimeoutMs(spec,requestedTimeoutMs){let totalWaitMs=spec.actions.reduce((sum,action)=>sum+(action.kind==="wait"?(action.seconds??0)*1000:0),0),setupSpawnCount=spec.setup.filter((step)=>step.kind==="spawn").length,orchestrationSlackMs=Math.max(30000,Math.min(90000,15000+Math.floor(totalWaitMs/2)+setupSpawnCount*15000));return requestedTimeoutMs+orchestrationSlackMs}function defaultSpecDir(repoPath){return join55(resolve11(repoPath??process.cwd()),".genie","qa")}init_runtime_events();async function qaCommand(target,options){let specDir=defaultSpecDir(),runnerOpts={timeout:options.timeout??3600,parallel:options.parallel??5,verbose:options.verbose??!1,repoPath:process.cwd(),ndjson:options.ndjson??!1},reports;if(target)reports=await resolveAndRun(specDir,target,runnerOpts);else reports=await runAllSpecs(specDir,runnerOpts);if(reports.length===0){console.error(`\x1B[33mNo QA specs found for "${target??"all"}"\x1B[0m`),process.exitCode=1;return}if(options.ndjson)for(let report of reports)console.log(JSON.stringify(report));else printRunResults(reports,options.verbose??!1);if(!reports.every((r)=>r.result==="pass"))process.exitCode=1}async function qaCheckCommand(specFile,options){let team=options.team??process.env.GENIE_TEAM;if(!team){console.error("Error: QA team not set. Use --team <name> or run inside a QA worker."),process.exitCode=1;return}let repoPath=process.cwd(),spec=await parseQaSpec(specFile),since=options.since??(options.sinceFile?await readSinceValue(options.sinceFile):void 0),teamAgents=(await list()).filter((agent)=>agent.team===team),events=await readTeamLog(teamAgents,repoPath,team,buildQaCheckLogFilter(since)),expectations=evaluateExpectations(spec.expect,events),collectedEvents=toCollectedEvents(events),result2=expectations.every((exp)=>exp.result==="pass")?"pass":"fail";await publishSubjectEvent(repoPath,`genie.qa.${team}.result`,{kind:"qa",agent:"qa",team,text:`QA result: ${result2}`,data:{result:result2,expectations,collectedEvents},source:"hook"}),console.log(`QA result published to PG event log as genie.qa.${team}.result`)}async function qaStatusCommand(options){let specDir=defaultSpecDir(),repoPath=process.cwd(),specs=await listAllSpecs(specDir),results=await loadResults(repoPath);if(specs.length===0){if(options?.json)console.log(JSON.stringify({specs:[],summary:{total:0,pass:0,fail:0,stale:0,never:0}}));else console.error("\x1B[33mNo QA specs found.\x1B[0m");return}if(options?.json)await printJsonStatus(specs,results,repoPath);else await printHumanStatus(specs,results,repoPath)}async function printJsonStatus(specs,results,repoPath){let jsonSpecs=[];for(let spec of specs){let stored=results[spec.key],stale=stored?await isStale(repoPath,spec.key,spec.filePath):!1,status=!stored?"never":stale?"stale":stored.result;jsonSpecs.push({key:spec.key,domain:spec.domain,name:spec.name,status,durationMs:stored?.durationMs??null,lastRun:stored?.lastRun??null,expectations:stored?.expectations??[],error:stored?.error??null})}let counts={total:specs.length,pass:jsonSpecs.filter((s2)=>s2.status==="pass").length,fail:jsonSpecs.filter((s2)=>s2.status==="fail"||s2.status==="error").length,stale:jsonSpecs.filter((s2)=>s2.status==="stale").length,never:jsonSpecs.filter((s2)=>s2.status==="never").length};console.log(JSON.stringify({specs:jsonSpecs,summary:counts}))}async function printHumanStatus(specs,results,repoPath){console.log(),console.log("\x1B[1m QA Status\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\u2500\u2500\u2500\u2500\x1B[0m"),console.log();let currentDomain="",counts={pass:0,fail:0,stale:0,never:0};for(let spec of specs){if(spec.domain!==currentDomain)currentDomain=spec.domain,console.log(` \x1B[1m${currentDomain}/\x1B[0m`);let stored=results[spec.key],stale=stored?await isStale(repoPath,spec.key,spec.filePath):!1;console.log(formatStatusLine(spec,stored,stale)),tallyResult(counts,stored,stale)}printStatusSummary(specs.length,counts)}function tallyResult(counts,stored,stale){if(!stored)counts.never++;else if(stale)counts.stale++;else if(stored.result==="pass")counts.pass++;else counts.fail++}function printStatusSummary(total,counts){console.log();let parts=[`${counts.pass}/${total} pass`,counts.fail?`${counts.fail} fail`:"",counts.stale?`${counts.stale} stale`:"",counts.never?`${counts.never} never`:""].filter(Boolean);console.log(` ${parts.join(" | ")}`),console.log()}async function readSinceValue(path3){try{return(await readFile12(path3,"utf-8")).trim()||void 0}catch{return}}function evaluateExpectations(expectations,events){return expectations.map((expectation)=>{let matched=events.filter((event)=>eventMatchesExpectationSource(event,expectation.source)).find((event)=>eventMatchesExpectation(event,expectation.matchers));if(matched)return{description:expectation.description,result:"pass",evidence:`${matched.kind} ${matched.agent}: ${matched.text.slice(0,120)}`};return{description:expectation.description,result:"fail",reason:"No matching event found in team log/transcript snapshot"}})}function eventMatchesExpectation(event,matchers){return Object.entries(matchers).every(([field,expected])=>matcherMatches(readEventField(event,field),expected,field))}function buildQaCheckLogFilter(since){if(since)return{since};return{last:200}}function eventMatchesExpectationSource(event,source){switch(source){case"inbox":return event.source==="mailbox"&&event.direction==="in";case"output":return event.source==="provider";default:return!0}}function readEventField(event,field){switch(field){case"timestamp":return event.timestamp;case"kind":return event.kind;case"agent":return event.agent;case"team":return event.team;case"direction":return event.direction;case"peer":return event.peer;case"text":return event.text;case"source":return event.source;default:break}return event.data?.[field]}function matcherMatches(actual,expected,field){if(actual==null)return!1;if(field==="kind"&&expected==="message")return["message","assistant","user"].includes(String(actual));let actualText=String(actual);if(expected.startsWith("~"))return actualText.includes(expected.slice(1));return actualText===expected}function toCollectedEvents(events){return events.slice(-50).map((event)=>({timestamp:event.timestamp,kind:event.kind,agent:event.agent,text:event.text}))}async function qaHistoryCommand(){let repoPath=process.cwd(),results=await loadResults(repoPath),entries=Object.entries(results).map(([key,r])=>({key,...r})).sort((a,b2)=>new Date(b2.lastRun).getTime()-new Date(a.lastRun).getTime());if(entries.length===0){console.error("\x1B[33mNo QA history found. Run `genie qa` first.\x1B[0m");return}console.log(),console.log("\x1B[1m QA History\x1B[0m"),console.log();let limit=20;for(let entry of entries.slice(0,limit)){let icon=resultIcon(entry.result),duration=entry.durationMs?`${(entry.durationMs/1000).toFixed(1)}s`:"-",ago=formatTimeAgo(entry.lastRun);console.log(` ${icon} ${entry.key.padEnd(35)} ${duration.padStart(6)} \x1B[2m${ago}\x1B[0m`)}if(entries.length>limit)console.log(` \x1B[2m... and ${entries.length-limit} more\x1B[0m`);console.log()}async function resolveAndRun(specDir,target,opts){let domainDir=join56(specDir,target);if(await isDirectory(domainDir))return runDomainSpecs(specDir,target,opts);let specPath=await resolveSpecPath(specDir,target);if(specPath){let spec=await parseQaSpec(specPath),key=specKeyFromPath(specDir,specPath),report=await runSpec(spec,{...opts,specKey:key}),repoPath=opts.repoPath??process.cwd();return await saveResult(repoPath,key,report),[report]}console.error(`\x1B[31mSpec or domain not found: ${target}\x1B[0m`),console.error("Available:");let allSpecs=await listAllSpecs(specDir),domains=[...new Set(allSpecs.map((s2)=>s2.domain))];for(let d of domains)console.error(` \x1B[1m${d}/\x1B[0m`);for(let s2 of allSpecs)console.error(` ${s2.key}`);return process.exitCode=1,[]}async function resolveSpecPath(specDir,name){let candidates=[join56(specDir,name),join56(specDir,`${name}.md`)];for(let path3 of candidates)if(await isFile(path3))return path3;return null}function resultIcon(result2){if(result2==="pass")return"\x1B[32m\u2705\x1B[0m";if(result2==="fail")return"\x1B[31m\u274C\x1B[0m";if(result2==="error")return"\x1B[33m\u26A0\uFE0F\x1B[0m";return"\uD83D\uDD18"}function formatStatusLine(spec,stored,stale){if(!stored)return` \uD83D\uDD18 ${spec.name.padEnd(25)} ${"-".padStart(6)} \x1B[2mnever\x1B[0m`;if(stale){let ago2=formatTimeAgo(stored.lastRun);return` \u26A0\uFE0F ${spec.name.padEnd(25)} ${"-".padStart(6)} \x1B[33m${ago2} (stale)\x1B[0m`}let icon=resultIcon(stored.result),duration=stored.durationMs?`${(stored.durationMs/1000).toFixed(0)}s`:"-",ago=formatTimeAgo(stored.lastRun);return` ${icon} ${spec.name.padEnd(25)} ${duration.padStart(6)} \x1B[2m${ago}\x1B[0m`}function printRunResults(reports,verbose){console.log(),console.log("\x1B[1m QA Results\x1B[0m"),console.log();for(let report of reports)printReport(report,verbose);let passed=reports.filter((r)=>r.result==="pass").length,failed=reports.filter((r)=>r.result==="fail").length,errors3=reports.filter((r)=>r.result==="error").length,total=reports.length,color2=failed+errors3>0?"\x1B[31m":"\x1B[32m",failedStr=failed?` \x1B[31m${failed} failed\x1B[0m`:"",errorsStr=errors3?` \x1B[33m${errors3} errors\x1B[0m`:"";console.log(` ${color2}${passed}/${total} passed\x1B[0m${failedStr}${errorsStr}`),console.log()}function printReport(report,verbose){let duration=`${(report.durationMs/1000).toFixed(1)}s`;if(console.log(` ${resultIcon(report.result)} ${report.name} \x1B[2m(${duration})\x1B[0m`),report.error)console.log(` \x1B[31mError: ${report.error}\x1B[0m`);for(let exp of report.expectations)printExpectation(exp);if(verbose&&report.collectedEvents.length>0){console.log(` \x1B[2m--- Collected events (${report.collectedEvents.length}) ---\x1B[0m`);for(let event of report.collectedEvents)console.log(` \x1B[2m ${event.timestamp} ${event.kind} ${event.agent}: ${event.text.slice(0,80)}\x1B[0m`)}console.log()}function printExpectation(exp){let icon=exp.result==="pass"?"\x1B[32m\u2713\x1B[0m":"\x1B[31m\u2717\x1B[0m";if(console.log(` ${icon} ${exp.description}`),exp.result==="pass"&&exp.evidence)console.log(` \x1B[2m${exp.evidence}\x1B[0m`);if(exp.result==="fail"&&exp.reason)console.log(` \x1B[31m${exp.reason}\x1B[0m`)}async function isDirectory(path3){try{return(await stat5(path3)).isDirectory()}catch{return!1}}async function isFile(path3){try{return(await stat5(path3)).isFile()}catch{return!1}}init_read();init_term_format();var _taskService6;async function getTaskService6(){if(!_taskService6)_taskService6=await Promise.resolve().then(() => (init_task_service(),exports_task_service));return _taskService6}function registerReleaseCommands(program2){let release=program2.command("release").description("Release management");release.command("create <name>").description("Create a release and assign tasks to it").requiredOption("--tasks <ids...>","Task IDs or #seqs to include").action(async(name,options)=>{try{let updated=await(await getTaskService6()).setRelease(options.tasks,name);console.log(`Release "${name}" created with ${updated} task${updated===1?"":"s"}.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),release.command("list").description("List all releases").option("--json","Output as JSON").action(async(options)=>{try{let releases=await(await getTaskService6()).listReleases();if(options.json){console.log(JSON.stringify(releases,null,2));return}if(releases.length===0){console.log("No releases found.");return}console.log(` ${padRight("RELEASE",30)} TASKS`),console.log(` ${"\u2500".repeat(40)}`);for(let r of releases)console.log(` ${padRight(r.releaseId,30)} ${r.count}`);console.log(`
|
|
3448
|
+
`)}function formatSetupStep(s2){if(s2.kind==="spawn"){let opts=Object.entries(s2.options).map(([k,v])=>`${k}: ${v}`).join(", ");return`- spawn ${s2.target}${opts?` (${opts})`:""}`}return`- follow ${s2.target}`}function formatActionStep(a){if(a.kind==="send")return`- send "${a.message}" to ${a.to}`;if(a.kind==="wait")return`- wait ${a.seconds??1}s`;if(a.kind==="run")return`- run ${a.command}`;return`- ${a.kind}`}async function waitForResult(spec,repoPath,teamName,timeoutMs,start2){let subject=`genie.qa.${teamName}.result`,remainingMs=Math.max(timeoutMs-(Date.now()-start2),1),event=await waitForRuntimeEvent({repoPath,subject,team:teamName},remainingMs);if(!event)return makeErrorReport(spec,start2,`Timeout after ${timeoutMs}ms waiting for team-lead report in PG event log`);return parseTeamLeadReport(spec,event.data??{},start2)}function parseTeamLeadReport(spec,payload,start2){try{let data=payload;return{name:spec.name,file:spec.file,result:data.result==="pass"?"pass":"fail",expectations:data.expectations??[],collectedEvents:data.collectedEvents??[],durationMs:Date.now()-start2}}catch(err){return makeErrorReport(spec,start2,`Failed to parse team-lead report: ${err}`)}}function makeErrorReport(spec,start2,error2){return{name:spec.name,file:spec.file,result:"error",expectations:[],collectedEvents:[],durationMs:Date.now()-start2,error:error2}}function computeEffectiveTimeoutMs(spec,requestedTimeoutMs){let totalWaitMs=spec.actions.reduce((sum,action)=>sum+(action.kind==="wait"?(action.seconds??0)*1000:0),0),setupSpawnCount=spec.setup.filter((step)=>step.kind==="spawn").length,orchestrationSlackMs=Math.max(30000,Math.min(90000,15000+Math.floor(totalWaitMs/2)+setupSpawnCount*15000));return requestedTimeoutMs+orchestrationSlackMs}function defaultSpecDir(repoPath){return join55(resolve11(repoPath??process.cwd()),".genie","qa")}init_runtime_events();async function qaCommand(target,options){let specDir=defaultSpecDir(),runnerOpts={timeout:options.timeout??3600,parallel:options.parallel??5,verbose:options.verbose??!1,repoPath:process.cwd(),ndjson:options.ndjson??!1},reports;if(target)reports=await resolveAndRun(specDir,target,runnerOpts);else reports=await runAllSpecs(specDir,runnerOpts);if(reports.length===0){console.error(`\x1B[33mNo QA specs found for "${target??"all"}"\x1B[0m`),process.exitCode=1;return}if(options.ndjson)for(let report of reports)console.log(JSON.stringify(report));else printRunResults(reports,options.verbose??!1);if(!reports.every((r)=>r.result==="pass"))process.exitCode=1}async function qaCheckCommand(specFile,options){let team=options.team??process.env.GENIE_TEAM;if(!team){console.error("Error: QA team not set. Use --team <name> or run inside a QA worker."),process.exitCode=1;return}let repoPath=process.cwd(),spec=await parseQaSpec(specFile),since=options.since??(options.sinceFile?await readSinceValue(options.sinceFile):void 0),teamAgents=(await list()).filter((agent)=>agent.team===team),events=await readTeamLog(teamAgents,repoPath,team,buildQaCheckLogFilter(since)),expectations=evaluateExpectations(spec.expect,events),collectedEvents=toCollectedEvents(events),result2=expectations.every((exp)=>exp.result==="pass")?"pass":"fail";await publishSubjectEvent(repoPath,`genie.qa.${team}.result`,{kind:"qa",agent:"qa",team,text:`QA result: ${result2}`,data:{result:result2,expectations,collectedEvents},source:"hook"}),console.log(`QA result published to PG event log as genie.qa.${team}.result`)}async function qaStatusCommand(options){let specDir=defaultSpecDir(),repoPath=process.cwd(),specs=await listAllSpecs(specDir),results=await loadResults(repoPath);if(specs.length===0){if(options?.json)console.log(JSON.stringify({specs:[],summary:{total:0,pass:0,fail:0,stale:0,never:0}}));else console.error("\x1B[33mNo QA specs found.\x1B[0m");return}if(options?.json)await printJsonStatus(specs,results,repoPath);else await printHumanStatus(specs,results,repoPath)}async function printJsonStatus(specs,results,repoPath){let jsonSpecs=[];for(let spec of specs){let stored=results[spec.key],stale=stored?await isStale(repoPath,spec.key,spec.filePath):!1,status=!stored?"never":stale?"stale":stored.result;jsonSpecs.push({key:spec.key,domain:spec.domain,name:spec.name,status,durationMs:stored?.durationMs??null,lastRun:stored?.lastRun??null,expectations:stored?.expectations??[],error:stored?.error??null})}let counts={total:specs.length,pass:jsonSpecs.filter((s2)=>s2.status==="pass").length,fail:jsonSpecs.filter((s2)=>s2.status==="fail"||s2.status==="error").length,stale:jsonSpecs.filter((s2)=>s2.status==="stale").length,never:jsonSpecs.filter((s2)=>s2.status==="never").length};console.log(JSON.stringify({specs:jsonSpecs,summary:counts}))}async function printHumanStatus(specs,results,repoPath){console.log(),console.log("\x1B[1m QA Status\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\u2500\u2500\u2500\u2500\x1B[0m"),console.log();let currentDomain="",counts={pass:0,fail:0,stale:0,never:0};for(let spec of specs){if(spec.domain!==currentDomain)currentDomain=spec.domain,console.log(` \x1B[1m${currentDomain}/\x1B[0m`);let stored=results[spec.key],stale=stored?await isStale(repoPath,spec.key,spec.filePath):!1;console.log(formatStatusLine(spec,stored,stale)),tallyResult(counts,stored,stale)}printStatusSummary(specs.length,counts)}function tallyResult(counts,stored,stale){if(!stored)counts.never++;else if(stale)counts.stale++;else if(stored.result==="pass")counts.pass++;else counts.fail++}function printStatusSummary(total,counts){console.log();let parts=[`${counts.pass}/${total} pass`,counts.fail?`${counts.fail} fail`:"",counts.stale?`${counts.stale} stale`:"",counts.never?`${counts.never} never`:""].filter(Boolean);console.log(` ${parts.join(" | ")}`),console.log()}async function readSinceValue(path3){try{return(await readFile13(path3,"utf-8")).trim()||void 0}catch{return}}function evaluateExpectations(expectations,events){return expectations.map((expectation)=>{let matched=events.filter((event)=>eventMatchesExpectationSource(event,expectation.source)).find((event)=>eventMatchesExpectation(event,expectation.matchers));if(matched)return{description:expectation.description,result:"pass",evidence:`${matched.kind} ${matched.agent}: ${matched.text.slice(0,120)}`};return{description:expectation.description,result:"fail",reason:"No matching event found in team log/transcript snapshot"}})}function eventMatchesExpectation(event,matchers){return Object.entries(matchers).every(([field,expected])=>matcherMatches(readEventField(event,field),expected,field))}function buildQaCheckLogFilter(since){if(since)return{since};return{last:200}}function eventMatchesExpectationSource(event,source){switch(source){case"inbox":return event.source==="mailbox"&&event.direction==="in";case"output":return event.source==="provider";default:return!0}}function readEventField(event,field){switch(field){case"timestamp":return event.timestamp;case"kind":return event.kind;case"agent":return event.agent;case"team":return event.team;case"direction":return event.direction;case"peer":return event.peer;case"text":return event.text;case"source":return event.source;default:break}return event.data?.[field]}function matcherMatches(actual,expected,field){if(actual==null)return!1;if(field==="kind"&&expected==="message")return["message","assistant","user"].includes(String(actual));let actualText=String(actual);if(expected.startsWith("~"))return actualText.includes(expected.slice(1));return actualText===expected}function toCollectedEvents(events){return events.slice(-50).map((event)=>({timestamp:event.timestamp,kind:event.kind,agent:event.agent,text:event.text}))}async function qaHistoryCommand(){let repoPath=process.cwd(),results=await loadResults(repoPath),entries=Object.entries(results).map(([key,r])=>({key,...r})).sort((a,b2)=>new Date(b2.lastRun).getTime()-new Date(a.lastRun).getTime());if(entries.length===0){console.error("\x1B[33mNo QA history found. Run `genie qa` first.\x1B[0m");return}console.log(),console.log("\x1B[1m QA History\x1B[0m"),console.log();let limit=20;for(let entry of entries.slice(0,limit)){let icon=resultIcon(entry.result),duration=entry.durationMs?`${(entry.durationMs/1000).toFixed(1)}s`:"-",ago=formatTimeAgo(entry.lastRun);console.log(` ${icon} ${entry.key.padEnd(35)} ${duration.padStart(6)} \x1B[2m${ago}\x1B[0m`)}if(entries.length>limit)console.log(` \x1B[2m... and ${entries.length-limit} more\x1B[0m`);console.log()}async function resolveAndRun(specDir,target,opts){let domainDir=join56(specDir,target);if(await isDirectory(domainDir))return runDomainSpecs(specDir,target,opts);let specPath=await resolveSpecPath(specDir,target);if(specPath){let spec=await parseQaSpec(specPath),key=specKeyFromPath(specDir,specPath),report=await runSpec(spec,{...opts,specKey:key}),repoPath=opts.repoPath??process.cwd();return await saveResult(repoPath,key,report),[report]}console.error(`\x1B[31mSpec or domain not found: ${target}\x1B[0m`),console.error("Available:");let allSpecs=await listAllSpecs(specDir),domains=[...new Set(allSpecs.map((s2)=>s2.domain))];for(let d of domains)console.error(` \x1B[1m${d}/\x1B[0m`);for(let s2 of allSpecs)console.error(` ${s2.key}`);return process.exitCode=1,[]}async function resolveSpecPath(specDir,name){let candidates=[join56(specDir,name),join56(specDir,`${name}.md`)];for(let path3 of candidates)if(await isFile(path3))return path3;return null}function resultIcon(result2){if(result2==="pass")return"\x1B[32m\u2705\x1B[0m";if(result2==="fail")return"\x1B[31m\u274C\x1B[0m";if(result2==="error")return"\x1B[33m\u26A0\uFE0F\x1B[0m";return"\uD83D\uDD18"}function formatStatusLine(spec,stored,stale){if(!stored)return` \uD83D\uDD18 ${spec.name.padEnd(25)} ${"-".padStart(6)} \x1B[2mnever\x1B[0m`;if(stale){let ago2=formatTimeAgo(stored.lastRun);return` \u26A0\uFE0F ${spec.name.padEnd(25)} ${"-".padStart(6)} \x1B[33m${ago2} (stale)\x1B[0m`}let icon=resultIcon(stored.result),duration=stored.durationMs?`${(stored.durationMs/1000).toFixed(0)}s`:"-",ago=formatTimeAgo(stored.lastRun);return` ${icon} ${spec.name.padEnd(25)} ${duration.padStart(6)} \x1B[2m${ago}\x1B[0m`}function printRunResults(reports,verbose){console.log(),console.log("\x1B[1m QA Results\x1B[0m"),console.log();for(let report of reports)printReport(report,verbose);let passed=reports.filter((r)=>r.result==="pass").length,failed=reports.filter((r)=>r.result==="fail").length,errors3=reports.filter((r)=>r.result==="error").length,total=reports.length,color2=failed+errors3>0?"\x1B[31m":"\x1B[32m",failedStr=failed?` \x1B[31m${failed} failed\x1B[0m`:"",errorsStr=errors3?` \x1B[33m${errors3} errors\x1B[0m`:"";console.log(` ${color2}${passed}/${total} passed\x1B[0m${failedStr}${errorsStr}`),console.log()}function printReport(report,verbose){let duration=`${(report.durationMs/1000).toFixed(1)}s`;if(console.log(` ${resultIcon(report.result)} ${report.name} \x1B[2m(${duration})\x1B[0m`),report.error)console.log(` \x1B[31mError: ${report.error}\x1B[0m`);for(let exp of report.expectations)printExpectation(exp);if(verbose&&report.collectedEvents.length>0){console.log(` \x1B[2m--- Collected events (${report.collectedEvents.length}) ---\x1B[0m`);for(let event of report.collectedEvents)console.log(` \x1B[2m ${event.timestamp} ${event.kind} ${event.agent}: ${event.text.slice(0,80)}\x1B[0m`)}console.log()}function printExpectation(exp){let icon=exp.result==="pass"?"\x1B[32m\u2713\x1B[0m":"\x1B[31m\u2717\x1B[0m";if(console.log(` ${icon} ${exp.description}`),exp.result==="pass"&&exp.evidence)console.log(` \x1B[2m${exp.evidence}\x1B[0m`);if(exp.result==="fail"&&exp.reason)console.log(` \x1B[31m${exp.reason}\x1B[0m`)}async function isDirectory(path3){try{return(await stat5(path3)).isDirectory()}catch{return!1}}async function isFile(path3){try{return(await stat5(path3)).isFile()}catch{return!1}}init_read();init_term_format();var _taskService6;async function getTaskService6(){if(!_taskService6)_taskService6=await Promise.resolve().then(() => (init_task_service(),exports_task_service));return _taskService6}function registerReleaseCommands(program2){let release=program2.command("release").description("Release management");release.command("create <name>").description("Create a release and assign tasks to it").requiredOption("--tasks <ids...>","Task IDs or #seqs to include").action(async(name,options)=>{try{let updated=await(await getTaskService6()).setRelease(options.tasks,name);console.log(`Release "${name}" created with ${updated} task${updated===1?"":"s"}.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),release.command("list").description("List all releases").option("--json","Output as JSON").action(async(options)=>{try{let releases=await(await getTaskService6()).listReleases();if(options.json){console.log(JSON.stringify(releases,null,2));return}if(releases.length===0){console.log("No releases found.");return}console.log(` ${padRight("RELEASE",30)} TASKS`),console.log(` ${"\u2500".repeat(40)}`);for(let r of releases)console.log(` ${padRight(r.releaseId,30)} ${r.count}`);console.log(`
|
|
3449
3449
|
${releases.length} release${releases.length===1?"":"s"}`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_cron();init_db();init_term_format();function formatTimestamp2(iso){return formatTimestamp(iso,{seconds:!0})}function parseAbsoluteTime(input){let date=new Date(input);if(Number.isNaN(date.getTime()))throw Error(`Invalid time: "${input}". Expected ISO 8601 format (e.g., 2026-03-21T09:00)`);return date}function isCronExpression(input){let parts=input.trim().split(/\s+/);return parts.length>=5&&parts.length<=6}function computeFirstDueAt(options){if(options.at){let dueAt=parseAbsoluteTime(options.at);if(dueAt.getTime()<=Date.now())throw Error(`Schedule time is in the past: ${options.at}`);return{dueAt,cronExpr:"@once",scheduleType:"once"}}if(options.after){let delayMs=parseDuration(options.after);return{dueAt:new Date(Date.now()+delayMs),cronExpr:"@once",scheduleType:"once"}}if(options.every){if(isCronExpression(options.every))return{dueAt:computeNextCronDue(options.every,{timezone:options.timezone}),cronExpr:options.every,scheduleType:"cron"};let intervalMs=parseDuration(options.every);return{dueAt:new Date(Date.now()+intervalMs),cronExpr:`@every ${options.every.trim()}`,scheduleType:"interval"}}throw Error("One of --at, --every, or --after is required")}function generateId(){return crypto.randomUUID()}function formatDuration2(ms){if(ms==null||ms<0)return"-";if(ms<1000)return`${ms}ms`;if(ms<60000)return`${(ms/1000).toFixed(1)}s`;if(ms<3600000)return`${(ms/60000).toFixed(1)}m`;return`${(ms/3600000).toFixed(1)}h`}function printTable2(headers,rows){let widths=headers.map((h,i2)=>{let colValues=rows.map((r)=>(r[i2]??"").length);return Math.max(h.length,...colValues)}),headerLine=headers.map((h,i2)=>padRight(h,widths[i2])).join(" ");console.log(headerLine),console.log(widths.map((w)=>"\u2500".repeat(w)).join("\u2500\u2500"));for(let row of rows){let line=row.map((val,i2)=>padRight(val??"",widths[i2])).join(" ");console.log(line)}console.log(`(${rows.length} row${rows.length===1?"":"s"})`)}async function scheduleCreateCommand(name,options){if(!options.command)console.error("Error: --command is required"),process.exit(1);if(!options.at&&!options.every&&!options.after)console.error("Error: one of --at, --every, or --after is required"),process.exit(1);try{let{dueAt,cronExpr,scheduleType}=computeFirstDueAt(options),sql=await getConnection();if((await sql`SELECT id FROM schedules WHERE name = ${name} AND status = 'active'`).length>0)console.error(`Error: schedule "${name}" already exists. Cancel it first or use a different name.`),process.exit(1);let scheduleId=generateId(),triggerId=generateId(),runSpec2=options.leaseTimeout?{lease_timeout_ms:parseDuration(options.leaseTimeout)}:{},metadata={type:scheduleType,original_spec:options.at??options.every??options.after,timezone:options.timezone??"UTC"};await sql.begin(async(tx)=>{await tx`
|
|
3450
3450
|
INSERT INTO schedules (id, name, cron_expression, timezone, command, run_spec, metadata, status)
|
|
3451
3451
|
VALUES (${scheduleId}, ${name}, ${cronExpr}, ${options.timezone??"UTC"}, ${options.command}, ${JSON.stringify(runSpec2)}, ${JSON.stringify(metadata)}, 'active')
|