@automagik/genie 4.260417.3 → 4.260417.4

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
@@ -363,7 +363,7 @@ Next steps:`),console.log(" 1. Reload tmux: tmux source ~/.tmux.conf"),console.
363
363
 
364
364
  ${content}`;if(params.extraArgs){let fileIdx=params.extraArgs.indexOf("--append-system-prompt-file");if(fileIdx!==-1&&params.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,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 saveConfig(teamName,config){await writeFile2(configPath(teamName),JSON.stringify(config,null,2))}async function countLeadSessionRefs(){let counts=new Map,teams=await listTeams();for(let team of teams){let leadSessionId=(await loadConfig(team))?.leadSessionId;if(!leadSessionId)continue;counts.set(leadSessionId,(counts.get(leadSessionId)??0)+1)}return counts}async function ensureNativeTeam(teamName,description,leadSessionId,leaderName){let dir=teamDir(teamName),inboxDir=inboxesDir(teamName);await mkdir3(dir,{recursive:!0}),await mkdir3(inboxDir,{recursive:!0}),ensureTeammateBypassPermissions();let existing=await loadConfig(teamName);if(existing)return existing;let sanitized=sanitizeTeamName(teamName),resolvedLeader=sanitizeTeamName(leaderName??teamName),config={name:sanitized,description,createdAt:Date.now(),leadAgentId:`${resolvedLeader}@${sanitized}`,leadSessionId,members:[]};return await saveConfig(teamName,config),config}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(`
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,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 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
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;let ct=entry.customTitle.toLowerCase();if(ct===needle||ct===`${needle}-${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 writeFile2(inbox,"[]")}async function unregisterNativeMember(teamName,agentName){let config=await loadConfig(teamName);if(!config)return;let sanitized=sanitizeTeamName(teamName),agentId=`${sanitizeTeamName(agentName)}@${sanitized}`,member=config.members.find((m)=>m.agentId===agentId);if(member)member.isActive=!1;await saveConfig(teamName,config)}async function writeNativeInbox(teamName,agentName,message){let path2=inboxPath(teamName,agentName);await mkdir3(inboxesDir(teamName),{recursive:!0}),await acquireLock(path2);try{let messages2=[];try{let content=await readFile3(path2,"utf-8");messages2=JSON.parse(content)}catch{}messages2.push(message),await writeFile2(path2,JSON.stringify(messages2,null,2))}finally{await releaseLock(path2)}}async function resolveNativeMemberName(teamName,genieWorkerId){let config=await loadConfig(teamName);if(!config||config.members.length===0)return null;let sanitizedId=sanitizeTeamName(genieWorkerId),sanitizedTeam=sanitizeTeamName(teamName),exactMatch=config.members.find((m)=>m.name===sanitizedId&&m.isActive);if(exactMatch)return exactMatch.name;let agentIdMatch=config.members.find((m)=>m.agentId===`${sanitizedId}@${sanitizedTeam}`&&m.isActive);if(agentIdMatch)return agentIdMatch.name;let teamPrefix=`${sanitizedTeam}-`;if(sanitizedId.startsWith(teamPrefix)){let stripped=sanitizedId.slice(teamPrefix.length),prefixMatch=config.members.find((m)=>m.name===stripped&&m.isActive);if(prefixMatch)return prefixMatch.name}let inactiveMatch=config.members.find((m)=>m.name===sanitizedId);if(inactiveMatch)return inactiveMatch.name;return null}async function assignColor(teamName){let config=await loadConfig(teamName);if(!config)return CLAUDE_TEAM_COLORS[0];let usedColors=new Set(config.members.map((m)=>m.color));for(let color of CLAUDE_TEAM_COLORS)if(!usedColors.has(color))return color;return CLAUDE_TEAM_COLORS[config.members.length%CLAUDE_TEAM_COLORS.length]}async function clearNativeInbox(teamName,agentName){let path2=inboxPath(teamName,agentName);await acquireLock(path2);try{await writeFile2(path2,"[]")}finally{await releaseLock(path2)}}async function deleteNativeTeam(teamName){let dir=teamDir(teamName);if(!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 readFile3(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 readFile3(inboxFile,"utf-8");messages2=JSON.parse(content)}catch{return null}if(!Array.isArray(messages2))return null;let unread=messages2.filter((m)=>m.read===!1);if(unread.length===0)return null;let workingDir=null;if(config){let leadMember=config.members.find((m)=>m.agentId===config?.leadAgentId||m.name===leaderInboxName);if(leadMember?.cwd)workingDir=leadMember.cwd}return{teamName:name,unreadCount:unread.length,workingDir,firstUnreadText:unread[0]?.text??null}}async function listTeamsWithUnreadInbox(){let base=teamsBaseDir(),teamDirs;try{teamDirs=await readdir2(base)}catch{return[]}let results=[];for(let name of teamDirs){let entry=await scanTeamInbox(base,name);if(entry)results.push(entry)}return results}function sanitizePath(p){return p.replace(/[^a-zA-Z0-9]/g,"-")}async function discoverClaudeSessionId(cwd){let envSessionId=process.env.CLAUDE_CODE_SESSION_ID;if(envSessionId)return envSessionId;let projectDir=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
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 readFile3(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 readFile3(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 writeFile2(inbox,"[]");let finalConfig=await loadConfig(teamName);if(!finalConfig)throw Error(`Failed to load config for team "${teamName}" after creation`);return{sessionId,config:finalConfig}}var LOCK_TIMEOUT_MS=5000,LOCK_POLL_MS=50;var init_claude_native_teams=__esm(()=>{init_claude_settings();init_provider_adapters()});var exports_team_lead_command={};__export(exports_team_lead_command,{shellQuote:()=>shellQuote,sessionExists:()=>sessionExists,ccProjectDirName:()=>ccProjectDirName,buildTeamLeadCommand:()=>buildTeamLeadCommand});import{readFileSync as 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?.continueName)parts.push(`--resume ${shellQuote(options.continueName)}`);else if(options?.sessionId)parts.push(`--session-id ${shellQuote(options.sessionId)}`);if(options?.systemPromptFile){let promptFlag=(options?.promptMode??loadGenieConfigSync().promptMode)==="system"?"--system-prompt-file":"--append-system-prompt-file";parts.push(`${promptFlag} ${shellQuote(options.systemPromptFile)}`)}return parts.join(" ")}function ccProjectDirName(dir){return dir.replace(/\//g,"-")}function fileHasSessionName(filePath,needle){try{let lines=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(`
@@ -519,7 +519,7 @@ ${bin} set-option -w pane-active-border-style "fg=$COLOR"
519
519
  AND ended_at IS NULL
520
520
  ORDER BY started_at DESC
521
521
  LIMIT 1
522
- `;return rows.length>0?rowToExecutor(rows[0]):null}async function relinkExecutorToAgent(executorId,agentId){await(await getConnection())`UPDATE agents SET current_executor_id = ${executorId} WHERE id = ${agentId}`}async function updateClaudeSessionId(executorId,sessionId){await(await getConnection())`UPDATE executors SET claude_session_id = ${sessionId} WHERE id = ${executorId}`}var init_executor_registry=__esm(()=>{init_audit();init_db()});var exports_team_manager={};__export(exports_team_manager,{validateBranchName:()=>validateBranchName,updateTeamConfig:()=>updateTeamConfig,unarchiveTeam:()=>unarchiveTeam,setTeamStatus:()=>setTeamStatus,resolveLeaderName:()=>resolveLeaderName,listTeams:()=>listTeams2,listMembers:()=>listMembers,killTeamMembers:()=>killTeamMembers,hireAgent:()=>hireAgent,getTeam:()=>getTeam,fireAgent:()=>fireAgent,disbandTeam:()=>disbandTeam,createTeam:()=>createTeam,archiveTeam:()=>archiveTeam});import{existsSync as existsSync18}from"fs";import{mkdir as mkdir4,rm as rm2,symlink}from"fs/promises";import{homedir as homedir17}from"os";import path2,{join as join20}from"path";var{$:$3}=globalThis.Bun;function parseMembers(raw){if(Array.isArray(raw))return raw;if(typeof raw==="string")try{let parsed=JSON.parse(raw);return Array.isArray(parsed)?parsed:[]}catch{return[]}return[]}function rowToTeamConfig(row){let config={name:row.name,repo:row.repo,baseBranch:row.base_branch,worktreePath:row.worktree_path,members:parseMembers(row.members),status:row.status,createdAt:row.created_at instanceof Date?row.created_at.toISOString():String(row.created_at)};if(row.leader)config.leader=row.leader;if(row.native_team_parent_session_id)config.nativeTeamParentSessionId=row.native_team_parent_session_id;if(row.native_teams_enabled)config.nativeTeamsEnabled=row.native_teams_enabled;if(row.tmux_session_name)config.tmuxSessionName=row.tmux_session_name;if(row.wish_slug)config.wishSlug=row.wish_slug;if(row.spawner)config.spawner=row.spawner;if(row.archived_at)config.archivedAt=row.archived_at instanceof Date?row.archived_at.toISOString():String(row.archived_at);if(row.parent_team)config.parentTeam=row.parent_team;let allow=parseAllowChildReachback(row.allow_child_reachback);if(allow.length>0)config.allowChildReachback=allow;return config}function parseAllowChildReachback(raw){if(Array.isArray(raw))return raw.filter((x)=>typeof x==="string");if(typeof raw==="string")try{let parsed=JSON.parse(raw);return Array.isArray(parsed)?parsed.filter((x)=>typeof x==="string"):[]}catch{return[]}return[]}function getGenieDir2(){return process.env.GENIE_HOME??join20(homedir17(),".genie")}function getWorktreeBase(repoPath){let base=loadGenieConfigSync().terminal?.worktreeBase;if(base&&base!==".worktrees"){if(path2.isAbsolute(base))return base;return join20(repoPath,base)}let projectName=path2.basename(repoPath);return join20(getGenieDir2(),"worktrees",projectName)}function validateBranchName(name){let errors3=[];if(/\s/.test(name))errors3.push("contains spaces");if(name.includes(".."))errors3.push('contains ".."');if(name.includes("~"))errors3.push('contains "~"');if(name.includes("^"))errors3.push('contains "^"');if(name.includes(":"))errors3.push('contains ":"');if(name.includes("?"))errors3.push('contains "?"');if(name.includes("*"))errors3.push('contains "*"');if(name.includes("["))errors3.push('contains "["');if(name.includes("\\"))errors3.push('contains "\\"');if(/[\x00-\x1f\x7f]/.test(name))errors3.push("contains control characters");if(name.endsWith(".lock"))errors3.push('ends with ".lock"');if(name.endsWith("/"))errors3.push('ends with "/"');if(name.endsWith("."))errors3.push('ends with "."');if(name.startsWith("-"))errors3.push('starts with "-"');if(errors3.length>0)throw Error(`Invalid team name '${name}': must be a valid git branch name (${errors3.join(", ")})`)}async function killWorkersByName(agentName,teamName){let matches=(await list()).filter((w)=>(w.role===agentName||w.id===agentName)&&(!teamName||w.team===teamName));for(let w of matches){if(w.currentExecutorId)try{await terminateExecutor(w.currentExecutorId),await setCurrentExecutor(w.id,null)}catch{}try{if(w.paneId&&w.paneId!=="inline"){let{execSync:execSync5}=__require("child_process"),{genieTmuxCmd:genieTmuxCmd2}=(init_tmux_wrapper(),__toCommonJS(exports_tmux_wrapper));execSync5(genieTmuxCmd2(`kill-pane -t ${w.paneId}`),{stdio:"ignore"})}}catch{}await unregister(w.id)}}async function postCloneInit(repoPath,worktreePath){let parentNodeModules=join20(repoPath,"node_modules"),worktreeNodeModules=join20(worktreePath,"node_modules");if(existsSync18(parentNodeModules)&&!existsSync18(worktreeNodeModules))try{await symlink(parentNodeModules,worktreeNodeModules,"dir")}catch{}let initScript=join20(repoPath,".genie","init.sh");if(existsSync18(initScript))try{await $3`bash ${initScript}`.cwd(worktreePath).quiet()}catch{}}async function ensureWorktree(repoPath,branchName,worktreePath,baseBranch){try{await $3`git -C ${repoPath} fetch origin ${baseBranch}`.quiet()}catch{}if(await mkdir4(path2.dirname(worktreePath),{recursive:!0}),existsSync18(worktreePath))return;let branchExists=!1;try{await $3`git -C ${repoPath} rev-parse --verify ${branchName}`.quiet(),branchExists=!0}catch{if(!branchExists)try{await $3`git -C ${repoPath} branch ${branchName} origin/${baseBranch}`.quiet()}catch{try{await $3`git -C ${repoPath} branch ${branchName} ${baseBranch}`.quiet()}catch{await $3`git -C ${repoPath} branch ${branchName}`.quiet()}}}await $3`git clone --shared --branch ${branchName} ${repoPath} ${worktreePath}`.quiet();try{let userName=(await $3`git -C ${repoPath} config user.name`.quiet()).text().trim(),userEmail=(await $3`git -C ${repoPath} config user.email`.quiet()).text().trim();if(userName)await $3`git -C ${worktreePath} config user.name ${userName}`.quiet();if(userEmail)await $3`git -C ${worktreePath} config user.email ${userEmail}`.quiet()}catch{}await postCloneInit(repoPath,worktreePath)}async function detectSpawnerParentTeam(newTeamName){let envTeam=process.env.GENIE_TEAM,spawnerName=process.env.GENIE_AGENT_NAME;if(!envTeam||!spawnerName)return null;if(spawnerName==="cli")return null;if(envTeam===newTeamName)return null;try{return await getTeam(envTeam)?envTeam:null}catch{return null}}async function createTeam(name,repo,baseBranch="dev"){validateBranchName(name);let repoPath=path2.resolve(repo),existing=await getTeam(name);if(existing)return existing;let worktreeBase=getWorktreeBase(repoPath),worktreePath=join20(worktreeBase,name);await ensureWorktree(repoPath,name,worktreePath,baseBranch);let now=new Date().toISOString(),config={name,repo:repoPath,baseBranch,worktreePath,members:[],status:"in_progress",createdAt:now},promoted=await detectSpawnerParentTeam(name);if(promoted)config.parentTeam=promoted;if(isInsideClaudeCode()){config.nativeTeamsEnabled=!0;try{let result2=await registerAsTeamLead(name);config.nativeTeamParentSessionId=result2.sessionId}catch{}}return await(await getConnection())`
522
+ `;return rows.length>0?rowToExecutor(rows[0]):null}async function relinkExecutorToAgent(executorId,agentId){await(await getConnection())`UPDATE agents SET current_executor_id = ${executorId} WHERE id = ${agentId}`}async function updateClaudeSessionId(executorId,sessionId){await(await getConnection())`UPDATE executors SET claude_session_id = ${sessionId} WHERE id = ${executorId}`}var init_executor_registry=__esm(()=>{init_audit();init_db()});var exports_team_manager={};__export(exports_team_manager,{validateBranchName:()=>validateBranchName,updateTeamConfig:()=>updateTeamConfig,unarchiveTeam:()=>unarchiveTeam,setTeamStatus:()=>setTeamStatus,resolveLeaderName:()=>resolveLeaderName,listTeams:()=>listTeams2,listMembers:()=>listMembers,killTeamMembers:()=>killTeamMembers,hireAgent:()=>hireAgent,getTeam:()=>getTeam,fireAgent:()=>fireAgent,ensureTeamRow:()=>ensureTeamRow,disbandTeam:()=>disbandTeam,createTeam:()=>createTeam,archiveTeam:()=>archiveTeam});import{existsSync as existsSync18}from"fs";import{mkdir as mkdir4,rm as rm2,symlink}from"fs/promises";import{homedir as homedir17}from"os";import path2,{join as join20}from"path";var{$:$3}=globalThis.Bun;function parseMembers(raw){if(Array.isArray(raw))return raw;if(typeof raw==="string")try{let parsed=JSON.parse(raw);return Array.isArray(parsed)?parsed:[]}catch{return[]}return[]}function rowToTeamConfig(row){let config={name:row.name,repo:row.repo,baseBranch:row.base_branch,worktreePath:row.worktree_path,members:parseMembers(row.members),status:row.status,createdAt:row.created_at instanceof Date?row.created_at.toISOString():String(row.created_at)};if(row.leader)config.leader=row.leader;if(row.native_team_parent_session_id)config.nativeTeamParentSessionId=row.native_team_parent_session_id;if(row.native_teams_enabled)config.nativeTeamsEnabled=row.native_teams_enabled;if(row.tmux_session_name)config.tmuxSessionName=row.tmux_session_name;if(row.wish_slug)config.wishSlug=row.wish_slug;if(row.spawner)config.spawner=row.spawner;if(row.archived_at)config.archivedAt=row.archived_at instanceof Date?row.archived_at.toISOString():String(row.archived_at);if(row.parent_team)config.parentTeam=row.parent_team;let allow=parseAllowChildReachback(row.allow_child_reachback);if(allow.length>0)config.allowChildReachback=allow;return config}function parseAllowChildReachback(raw){if(Array.isArray(raw))return raw.filter((x)=>typeof x==="string");if(typeof raw==="string")try{let parsed=JSON.parse(raw);return Array.isArray(parsed)?parsed.filter((x)=>typeof x==="string"):[]}catch{return[]}return[]}function getGenieDir2(){return process.env.GENIE_HOME??join20(homedir17(),".genie")}function getWorktreeBase(repoPath){let base=loadGenieConfigSync().terminal?.worktreeBase;if(base&&base!==".worktrees"){if(path2.isAbsolute(base))return base;return join20(repoPath,base)}let projectName=path2.basename(repoPath);return join20(getGenieDir2(),"worktrees",projectName)}function validateBranchName(name){let errors3=[];if(/\s/.test(name))errors3.push("contains spaces");if(name.includes(".."))errors3.push('contains ".."');if(name.includes("~"))errors3.push('contains "~"');if(name.includes("^"))errors3.push('contains "^"');if(name.includes(":"))errors3.push('contains ":"');if(name.includes("?"))errors3.push('contains "?"');if(name.includes("*"))errors3.push('contains "*"');if(name.includes("["))errors3.push('contains "["');if(name.includes("\\"))errors3.push('contains "\\"');if(/[\x00-\x1f\x7f]/.test(name))errors3.push("contains control characters");if(name.endsWith(".lock"))errors3.push('ends with ".lock"');if(name.endsWith("/"))errors3.push('ends with "/"');if(name.endsWith("."))errors3.push('ends with "."');if(name.startsWith("-"))errors3.push('starts with "-"');if(errors3.length>0)throw Error(`Invalid team name '${name}': must be a valid git branch name (${errors3.join(", ")})`)}async function killWorkersByName(agentName,teamName){let matches=(await list()).filter((w)=>(w.role===agentName||w.id===agentName)&&(!teamName||w.team===teamName));for(let w of matches){if(w.currentExecutorId)try{await terminateExecutor(w.currentExecutorId),await setCurrentExecutor(w.id,null)}catch{}try{if(w.paneId&&w.paneId!=="inline"){let{execSync:execSync5}=__require("child_process"),{genieTmuxCmd:genieTmuxCmd2}=(init_tmux_wrapper(),__toCommonJS(exports_tmux_wrapper));execSync5(genieTmuxCmd2(`kill-pane -t ${w.paneId}`),{stdio:"ignore"})}}catch{}await unregister(w.id)}}async function postCloneInit(repoPath,worktreePath){let parentNodeModules=join20(repoPath,"node_modules"),worktreeNodeModules=join20(worktreePath,"node_modules");if(existsSync18(parentNodeModules)&&!existsSync18(worktreeNodeModules))try{await symlink(parentNodeModules,worktreeNodeModules,"dir")}catch{}let initScript=join20(repoPath,".genie","init.sh");if(existsSync18(initScript))try{await $3`bash ${initScript}`.cwd(worktreePath).quiet()}catch{}}async function ensureWorktree(repoPath,branchName,worktreePath,baseBranch){try{await $3`git -C ${repoPath} fetch origin ${baseBranch}`.quiet()}catch{}if(await mkdir4(path2.dirname(worktreePath),{recursive:!0}),existsSync18(worktreePath))return;let branchExists=!1;try{await $3`git -C ${repoPath} rev-parse --verify ${branchName}`.quiet(),branchExists=!0}catch{if(!branchExists)try{await $3`git -C ${repoPath} branch ${branchName} origin/${baseBranch}`.quiet()}catch{try{await $3`git -C ${repoPath} branch ${branchName} ${baseBranch}`.quiet()}catch{await $3`git -C ${repoPath} branch ${branchName}`.quiet()}}}await $3`git clone --shared --branch ${branchName} ${repoPath} ${worktreePath}`.quiet();try{let userName=(await $3`git -C ${repoPath} config user.name`.quiet()).text().trim(),userEmail=(await $3`git -C ${repoPath} config user.email`.quiet()).text().trim();if(userName)await $3`git -C ${worktreePath} config user.name ${userName}`.quiet();if(userEmail)await $3`git -C ${worktreePath} config user.email ${userEmail}`.quiet()}catch{}await postCloneInit(repoPath,worktreePath)}async function detectSpawnerParentTeam(newTeamName){let envTeam=process.env.GENIE_TEAM,spawnerName=process.env.GENIE_AGENT_NAME;if(!envTeam||!spawnerName)return null;if(spawnerName==="cli")return null;if(envTeam===newTeamName)return null;try{return await getTeam(envTeam)?envTeam:null}catch{return null}}async function createTeam(name,repo,baseBranch="dev"){validateBranchName(name);let repoPath=path2.resolve(repo),existing=await getTeam(name);if(existing)return existing;let worktreeBase=getWorktreeBase(repoPath),worktreePath=join20(worktreeBase,name);await ensureWorktree(repoPath,name,worktreePath,baseBranch);let now=new Date().toISOString(),config={name,repo:repoPath,baseBranch,worktreePath,members:[],status:"in_progress",createdAt:now},promoted=await detectSpawnerParentTeam(name);if(promoted)config.parentTeam=promoted;if(isInsideClaudeCode()){config.nativeTeamsEnabled=!0;try{let result2=await registerAsTeamLead(name);config.nativeTeamParentSessionId=result2.sessionId}catch{}}return await(await getConnection())`
523
523
  INSERT INTO teams (
524
524
  name, repo, base_branch, worktree_path, leader,
525
525
  members, status, native_team_parent_session_id,
@@ -536,7 +536,17 @@ ${bin} set-option -w pane-active-border-style "fg=$COLOR"
536
536
  ${config.parentTeam??null},
537
537
  ${config.allowChildReachback?JSON.stringify(config.allowChildReachback):null}
538
538
  ) ON CONFLICT (name) DO NOTHING
539
- `,recordAuditEvent("team",name,"created",getActor(),{repo:repoPath,baseBranch}).catch(()=>{}),config}async function hireAgent(teamName,agentName){let config=await getTeam(teamName);if(!config)throw Error(`Team "${teamName}" not found.`);let added;if(agentName==="council")added=BUILTIN_COUNCIL_MEMBERS.map((m)=>m.name).filter((n)=>!config.members.includes(n)),config.members.push(...added);else{if(config.members.includes(agentName))return[];config.members.push(agentName),added=[agentName]}return await(await getConnection())`
539
+ `,recordAuditEvent("team",name,"created",getActor(),{repo:repoPath,baseBranch}).catch(()=>{}),config}async function ensureTeamRow(name,opts){try{validateBranchName(name)}catch{return null}let existing=await getTeam(name);if(existing)return existing;let repoPath=path2.resolve(opts?.repo??process.cwd()),now=new Date().toISOString(),config={name,repo:repoPath,baseBranch:"dev",worktreePath:repoPath,members:[],status:"in_progress",createdAt:now,nativeTeamsEnabled:!0};try{return await(await getConnection())`
540
+ INSERT INTO teams (
541
+ name, repo, base_branch, worktree_path, leader,
542
+ members, status, native_teams_enabled, created_at
543
+ ) VALUES (
544
+ ${config.name}, ${config.repo}, ${config.baseBranch},
545
+ ${config.worktreePath}, ${null},
546
+ ${JSON.stringify(config.members)}, ${config.status},
547
+ ${config.nativeTeamsEnabled??!1}, ${config.createdAt}
548
+ ) ON CONFLICT (name) DO NOTHING
549
+ `,recordAuditEvent("team",name,"backfilled",getActor(),{repo:repoPath,source:"native-team"}).catch(()=>{}),await getTeam(name)??config}catch{return null}}async function hireAgent(teamName,agentName){let config=await getTeam(teamName);if(!config)throw Error(`Team "${teamName}" not found.`);let added;if(agentName==="council")added=BUILTIN_COUNCIL_MEMBERS.map((m)=>m.name).filter((n)=>!config.members.includes(n)),config.members.push(...added);else{if(config.members.includes(agentName))return[];config.members.push(agentName),added=[agentName]}return await(await getConnection())`
540
550
  UPDATE teams SET members = ${JSON.stringify(config.members)}
541
551
  WHERE name = ${teamName}
542
552
  `,added}async function fireAgent(teamName,agentName){let config=await getTeam(teamName);if(!config)throw Error(`Team "${teamName}" not found.`);let idx=config.members.indexOf(agentName);if(idx===-1)return!1;config.members.splice(idx,1),await(await getConnection())`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260417.3",
3
+ "version": "4.260417.4",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260417.3",
3
+ "version": "4.260417.4",
4
4
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
5
5
  "author": {
6
6
  "name": "Namastex Labs"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260417.3",
3
+ "version": "4.260417.4",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",