@automagik/genie 4.260427.6 → 4.260427.8
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
|
@@ -144,7 +144,7 @@ ${prompt}`}function extractPromptPreamble(input){let match=input.match(PREAMBLE_
|
|
|
144
144
|
|
|
145
145
|
${content}`;if(params.extraArgs){let fileIdx=params.extraArgs.indexOf("--append-system-prompt-file");if(fileIdx!==-1&¶ms.extraArgs[fileIdx+1])content=`${content}
|
|
146
146
|
|
|
147
|
-
${readFileSync5(params.extraArgs[fileIdx+1],"utf-8")}`,params.extraArgs.splice(fileIdx,2)}writeFileSync6(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 appendTraceContext(parts,env,params){let ctx=getAmbient();if(params.initialPrompt){let prompt=ctx?injectPromptPreamble(params.initialPrompt,ctx):params.initialPrompt;parts.push(escapeShellArg2(prompt))}if(ctx)env[TRACE_ENV_VAR]=mintToken(ctx),env[TRACE_ID_ENV_VAR]=ctx.trace_id}function buildClaudeGenieEnv(params){let env={};if(env.GENIE_WORKER="1",params.role)env.GENIE_AGENT_NAME=params.role;if(params.team)env.GENIE_TEAM=params.team;if(params.executorId)env.GENIE_EXECUTOR_ID=params.executorId;if(params.agentId)env.GENIE_AGENT_ID=params.agentId;return env}function appendSessionFlags(parts,params){if(params.resume)parts.push("--resume",escapeShellArg2(params.resume));else if(params.sessionId)parts.push("--session-id",escapeShellArg2(params.sessionId));
|
|
147
|
+
${readFileSync5(params.extraArgs[fileIdx+1],"utf-8")}`,params.extraArgs.splice(fileIdx,2)}writeFileSync6(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 appendTraceContext(parts,env,params){let ctx=getAmbient();if(params.initialPrompt){let prompt=ctx?injectPromptPreamble(params.initialPrompt,ctx):params.initialPrompt;parts.push(escapeShellArg2(prompt))}if(ctx)env[TRACE_ENV_VAR]=mintToken(ctx),env[TRACE_ID_ENV_VAR]=ctx.trace_id}function buildClaudeGenieEnv(params){let env={};if(env.GENIE_WORKER="1",params.role)env.GENIE_AGENT_NAME=params.role;if(params.team)env.GENIE_TEAM=params.team;if(params.executorId)env.GENIE_EXECUTOR_ID=params.executorId;if(params.agentId)env.GENIE_AGENT_ID=params.agentId;return env}function appendSessionFlags(parts,params){if(params.resume)parts.push("--resume",escapeShellArg2(params.resume));else if(params.sessionId)parts.push("--session-id",escapeShellArg2(params.sessionId));let claudeAgentFlag=params.agentTemplate??params.role;if(claudeAgentFlag)parts.push("--agent",escapeShellArg2(claudeAgentFlag));if(params.model)parts.push("--model",escapeShellArg2(params.model));if(params.name)parts.push("--name",escapeShellArg2(params.name))}function buildSettingsObject(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}return settingsObj}function appendDisallowedAndExtraArgs(parts,params){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))}function buildClaudeCommand(params){preflightCheck("claude");let parts=[resolveShellBinary("claude")??"claude","--permission-mode",escapeShellArg2("auto")],env=buildClaudeGenieEnv(params);if(appendOtelEnv(env,params),params.nativeTeam?.enabled)appendNativeTeamFlags(parts,env,params.nativeTeam,params);appendSessionFlags(parts,params),appendSystemPromptFlags(parts,params);let settingsObj=buildSettingsObject(params);if(Object.keys(settingsObj).length>0)parts.push("--settings",escapeShellArg2(JSON.stringify(settingsObj)));return appendDisallowedAndExtraArgs(parts,params),appendTraceContext(parts,env,params),{command:parts.join(" "),provider:"claude",env:Object.keys(env).length>0?env:void 0,meta:{role:params.role,skill:params.skill}}}function buildCodexAutoPrompt(params){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.`);return promptParts.join(" ")}function buildCodexCommand(params){preflightCheck("codex");let parts=["codex"],env={};if(params.executorId)env.GENIE_EXECUTOR_ID=params.executorId;if(params.agentId)env.GENIE_AGENT_ID=params.agentId;if(params.role)env.GENIE_AGENT_NAME=params.role;if(params.team)env.GENIE_TEAM=params.team;if(parts.push("--yolo"),parts.push("--no-alt-screen"),params.extraArgs)for(let arg of params.extraArgs)parts.push(escapeShellArg2(arg));let prompt=params.initialPrompt??buildCodexAutoPrompt(params);return parts.push(escapeShellArg2(prompt)),{command:parts.join(" "),provider:"codex",env:Object.keys(env).length>0?env:void 0,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();init_trace_context();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(),agentTemplate:exports_external.string().optional(),skill:exports_external.string().optional(),agentId:exports_external.string().optional(),executorId:exports_external.string().uuid().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_audit={};__export(exports_audit,{recordAuditEvent:()=>recordAuditEvent,queryToolUsage:()=>queryToolUsage,queryTimeline:()=>queryTimeline,querySummary:()=>querySummary,queryErrorPatterns:()=>queryErrorPatterns,queryCostBreakdown:()=>queryCostBreakdown,queryAuditEvents:()=>queryAuditEvents,getActor:()=>getActor,generateTraceId:()=>generateTraceId,followAuditEvents:()=>followAuditEvents});async function recordAuditEvent(entityType,entityId,eventType,actor,details){try{if(!await isAvailable())return;let sql=await getConnection();await sql`
|
|
148
148
|
INSERT INTO audit_events (entity_type, entity_id, event_type, actor, details)
|
|
149
149
|
VALUES (${entityType}, ${entityId}, ${eventType}, ${actor??null}, ${sql.json(details??{})})
|
|
150
150
|
`}catch{}}function parseSince(since){let match=since.match(/^(\d+)([smhd])$/);if(!match)return since;let amount=Number.parseInt(match[1],10),unit=match[2],ms={s:1000,m:60000,h:3600000,d:86400000}[unit]??3600000;return new Date(Date.now()-amount*ms).toISOString()}async function queryAuditEvents(options={}){let sql=await getConnection(),conditions=[],values=[],paramIdx=1;if(options.type)conditions.push(`(event_type = $${paramIdx} OR entity_type = $${paramIdx})`),paramIdx++,values.push(options.type);if(options.entity)conditions.push(`(entity_type = $${paramIdx} OR entity_id = $${paramIdx})`),paramIdx++,values.push(options.entity);if(options.since)conditions.push(`created_at >= $${paramIdx++}::timestamptz`),values.push(parseSince(options.since));if(options.errorsOnly)conditions.push("event_type LIKE '%error%' OR (details::text LIKE '%error%')");let where=conditions.length>0?`WHERE ${conditions.join(" AND ")}`:"",limit=options.limit??50;return await sql.unsafe(`SELECT id, entity_type, entity_id, event_type, actor, details, created_at
|
|
@@ -857,7 +857,7 @@ ${errCtx.stack}`;d.reject(err)}else d.resolve(msg)}});return sub.requestSubject=
|
|
|
857
857
|
FROM session_sync
|
|
858
858
|
WHERE id = 'backfill'
|
|
859
859
|
LIMIT 1
|
|
860
|
-
`;if(rows.length===0)return{driftPct:null,detail:"no prior backfill row \u2014 first run will seed"};let row=rows[0];if(row.total_bytes<=0)return{driftPct:0,detail:"no JSONL bytes discovered yet"};let pct=Math.max(0,row.total_bytes-row.processed_bytes)/row.total_bytes*100,display=pct.toFixed(1);return{driftPct:pct,detail:`processed ${row.processed_bytes}/${row.total_bytes} bytes (drift ${display}%)`}}catch(err){return{driftPct:null,detail:`drift probe failed: ${err.message}`}}}async function defaultRunBackfillSync(){if(!await isAvailable())return{ranSync:!1,driftPct:null,detail:"pg unavailable \u2014 backfill skipped"};return(async()=>{try{let sql=await getConnection(),{startBackfill:startBackfill2}=await Promise.resolve().then(() => (init_session_backfill(),exports_session_backfill));await startBackfill2(sql)}catch{}})(),{ranSync:!0,driftPct:null,detail:"background convergence kicked \u2014 `genie doctor --fix` to wait"}}async function defaultRunBackfillBlocking(){if(!await isAvailable())return{ranSync:!1,driftPct:null,detail:"pg unavailable \u2014 backfill skipped"};let sql=await getConnection(),{startBackfill:startBackfill2}=await Promise.resolve().then(() => (init_session_backfill(),exports_session_backfill));await startBackfill2(sql);let after=await defaultMeasureBackfillDrift();return{ranSync:!0,driftPct:after.driftPct,detail:after.detail}}async function defaultListOrphanedZombies(){try{let{listExhaustedZombies:listExhaustedZombies2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));return await listExhaustedZombies2()}catch{return[]}}function safeIsDirectory(path2){try{return statSync3(path2).isDirectory()}catch{return!1}}function summarizeInboxes(inboxesDir2){let inboxFiles=[];try{inboxFiles=readdirSync6(inboxesDir2).filter((f)=>f.endsWith(".json"))}catch{return{newestMs:null,hasContent:!1}}let newestMs=null,hasContent=!1;for(let f of inboxFiles)try{let st=statSync3(join23(inboxesDir2,f));if(newestMs===null||st.mtimeMs>newestMs)newestMs=st.mtimeMs;if(st.size>2)hasContent=!0}catch{}return{newestMs,hasContent}}function classifyTeamDir2(name,base,now){let dir=join23(base,name);if(!safeIsDirectory(dir))return null;if(existsSync18(join23(dir,"config.json")))return null;let inboxesDir2=join23(dir,"inboxes");if(!safeIsDirectory(inboxesDir2))return null;let{newestMs,hasContent}=summarizeInboxes(inboxesDir2),orphan={teamName:name,path:dir,newestInboxMs:newestMs,hasContent},active=hasContent&&newestMs!==null&&now-newestMs<ORPHAN_FRESH_WINDOW_MS;return{orphan,active}}function defaultScanTeamConfigOrphans(){let base=teamsBaseDir3(),result2={active:[],stale:[]};if(!existsSync18(base))return result2;let now=Date.now();for(let name of readdirSync6(base)){if(name.startsWith(".")||name==="_archive")continue;let classified=classifyTeamDir2(name,base,now);if(!classified)continue;(classified.active?result2.active:result2.stale).push(classified.orphan)}return result2}function defaultArchiveStaleTeamConfigs(orphans){if(orphans.length===0)return[];let archiveRoot=join23(teamsBaseDir3(),"_archive");mkdirSync9(archiveRoot,{recursive:!0});let ts3=new Date().toISOString().replace(/[:.]/g,"-"),archived=[];for(let o of orphans){let dest=join23(archiveRoot,`${o.teamName}-${ts3}`);try{renameSync4(o.path,dest),archived.push(dest)}catch{}}return archived}async function defaultRecordAudit(eventType,name,details){await recordAuditEvent("command","serve_start",eventType,"serve",{precondition:name,...details})}async function checkPartition(health,autoFix,deps){if(health.partition_health==="ok"||health.partition_health==="warn")return{name:"partition",status:"ok",detail:health.next_rotation_at?`next rotation: ${health.next_rotation_at}`:void 0};if(health.partition_health==="unknown")return{name:"partition",status:"skipped",detail:"pg unavailable \u2014 skipping partition probe"};if(!autoFix)return{name:"partition",status:"refused",detail:"today's partition is missing or rotation is overdue",fixCommand:"genie doctor --observability # then re-run; or `genie serve start` (without --no-fix)"};let result2=await deps.runPartitionMaintenance();return{name:"partition",status:"fixed",detail:`created/present ${result2.createdOrPresent}, dropped ${result2.dropped}; next rotation ${result2.nextRotationAt??"unknown"}`}}async function checkWatchdog(health,autoFix,deps){if(health.watchdog==="ok")return{name:"watchdog",status:"ok"};if(!autoFix)return{name:"watchdog",status:"refused",detail:health.watchdog_detail??"watchdog units missing",fixCommand:"sudo bun run packages/watchdog/src/cli.ts install"};try{let result2=await deps.installWatchdog();return{name:"watchdog",status:"fixed",detail:`wrote ${result2.filesWritten.length}, skipped ${result2.filesSkipped.length}`}}catch(err){return{name:"watchdog",status:"refused",detail:`auto-install failed: ${err.message}`,fixCommand:"sudo bun run packages/watchdog/src/cli.ts install"}}}async function checkBackfill(autoFix,deps){let drift=await deps.measureBackfillDrift();if(drift.driftPct===null)return{name:"backfill",status:"skipped",detail:drift.detail};if(drift.driftPct<BACKFILL_DRIFT_THRESHOLD_PCT)return{name:"backfill",status:"ok",detail:drift.detail};if(!autoFix)return{name:"backfill",status:"refused",detail:drift.detail,fixCommand:`genie sessions sync # drift ${drift.driftPct.toFixed(1)}% > ${BACKFILL_DRIFT_THRESHOLD_PCT}%`};return{name:"backfill",status:"fixed",detail:(await deps.runBackfillSync()).detail}}async function checkDeadPaneZombies(deps){let orphans=await deps.listOrphanedZombies();if(orphans.length===0)return{name:"dead_pane_zombies",status:"ok"};return{name:"dead_pane_zombies",status:"refused",detail:`${orphans.length} exhausted zombie(s) past TTL; visible in \`genie status\``,fixCommand:"genie prune --zombies # archive eligible rows"}}function checkTeamConfigOrphans(autoFix,deps){let scan=deps.scanTeamConfigOrphans();if(scan.active.length===0&&scan.stale.length===0)return{name:"team_config_orphans",status:"ok"};if(!autoFix){let summary=`active=${scan.active.length} stale=${scan.stale.length}`,firstActive=scan.active[0]?.teamName;return{name:"team_config_orphans",status:"refused",detail:summary,fixCommand:firstActive?`genie team repair ${firstActive} # active orphan; stale dirs archive on auto-fix`:"genie serve start # auto-fix archives stale orphans"}}let archivedPaths=deps.archiveStaleTeamConfigs(scan.stale);if(scan.active.length>0)return{name:"team_config_orphans",status:"refused",detail:`archived ${archivedPaths.length} stale; ${scan.active.length} active orphan(s) need repair`,fixCommand:`genie team repair ${scan.active[0].teamName}`};return{name:"team_config_orphans",status:"fixed",detail:`archived ${archivedPaths.length} stale orphan(s)`}}function bindDefaults(deps){return{collectHealth:deps?.collectHealth??collectObservabilityHealth,runPartitionMaintenance:deps?.runPartitionMaintenance??defaultRunPartitionMaintenance,installWatchdog:deps?.installWatchdog??defaultInstallWatchdog,runBackfillSync:deps?.runBackfillSync??defaultRunBackfillSync,measureBackfillDrift:deps?.measureBackfillDrift??defaultMeasureBackfillDrift,listOrphanedZombies:deps?.listOrphanedZombies??defaultListOrphanedZombies,scanTeamConfigOrphans:deps?.scanTeamConfigOrphans??defaultScanTeamConfigOrphans,archiveStaleTeamConfigs:deps?.archiveStaleTeamConfigs??defaultArchiveStaleTeamConfigs,recordAudit:deps?.recordAudit??defaultRecordAudit,log:deps?.log??((line)=>console.log(line))}}async function ensureServeReady(opts){let deps=bindDefaults(opts.deps),health=await deps.collectHealth(),results=[];results.push(await checkPartition(health,opts.autoFix,deps)),results.push(await checkBackfill(opts.autoFix,deps)),results.push(await checkDeadPaneZombies(deps)),await emitAuditEvents(results,deps);let ok=results.every((r)=>r.status==="ok"||r.status==="fixed"||r.status==="skipped");return printReport(results,deps.log),{ok,results}}async function runDoctorMaintenance(opts={}){let deps=bindDefaults({runBackfillSync:defaultRunBackfillBlocking,log:opts.silent?()=>{}:void 0,...opts.deps}),health=await deps.collectHealth(),results=[];results.push(await checkPartition(health,!0,deps)),results.push(await checkWatchdog(health,!0,deps)),results.push(await checkBackfill(!0,deps)),results.push(await checkDeadPaneZombies(deps)),results.push(checkTeamConfigOrphans(!0,deps)),await emitAuditEvents(results,deps);let ok=results.every((r)=>r.status==="ok"||r.status==="fixed"||r.status==="skipped");return printReport(results,deps.log),{ok,results}}async function emitAuditEvents(results,deps){for(let result2 of results)if(result2.status==="fixed")await deps.recordAudit("serve.precondition.fixed",result2.name,{detail:result2.detail??null}).catch(()=>{});else if(result2.status==="refused")await deps.recordAudit("serve.precondition.refused",result2.name,{detail:result2.detail??null,fix_command:result2.fixCommand??null}).catch(()=>{})}function printReport(results,log){log(" Preconditions:");for(let r of results){let tag=statusTag(r.status),suffix=r.detail?` \u2014 ${r.detail}`:"";if(log(` ${tag} ${r.name}${suffix}`),r.status==="refused"&&r.fixCommand)log(` \u2192 ${r.fixCommand}`)}}function statusTag(status){switch(status){case"ok":return"[ok]";case"fixed":return"[fix]";case"refused":return"[!!]";case"skipped":return"[--]"}}var BACKFILL_DRIFT_THRESHOLD_PCT=5,ORPHAN_FRESH_WINDOW_MS=86400000;var init_ensure_ready=__esm(()=>{init_observability_health();init_audit();init_db()});var exports_doctor={};__export(exports_doctor,{runPostUpdateMaintenance:()=>runPostUpdateMaintenance,findBundledTmuxConfigDir:()=>findBundledTmuxConfigDir,doctorCommand:()=>doctorCommand,checkTmuxConfigs:()=>checkTmuxConfigs,checkLegacyAgentFrontmatter:()=>checkLegacyAgentFrontmatter,checkGenieAgentTemplate:()=>checkGenieAgentTemplate});import{copyFileSync as copyFileSync2,existsSync as existsSync19,mkdirSync as mkdirSync10,readFileSync as readFileSync12,readdirSync as readdirSync7,statSync as statSync4,unlinkSync as unlinkSync6,writeFileSync as writeFileSync7}from"fs";import{homedir as homedir19}from"os";import{dirname as dirname7,join as join24,resolve as resolve3}from"path";import{fileURLToPath as fileURLToPath3}from"url";var{$:$3}=globalThis.Bun;function printSectionHeader(title){console.log(),console.log(`\x1B[1m${title}:\x1B[0m`)}function printCheckResult(result2){let icon={pass:"\x1B[32m\u2713\x1B[0m",fail:"\x1B[31m\u2717\x1B[0m",warn:"\x1B[33m!\x1B[0m"}[result2.status],message=result2.message?` ${result2.message}`:"";if(console.log(` ${icon} ${result2.name}${message}`),result2.suggestion)console.log(` \x1B[2m${result2.suggestion}\x1B[0m`)}async function checkPrerequisites(){let results=[],tmuxCheck=await checkCommand("tmux");if(tmuxCheck.exists)results.push({name:"tmux",status:"pass",message:tmuxCheck.version||""});else results.push({name:"tmux",status:"fail",suggestion:"Install with: brew install tmux (or apt install tmux)"});let jqCheck=await checkCommand("jq");if(jqCheck.exists)results.push({name:"jq",status:"pass",message:jqCheck.version||""});else results.push({name:"jq",status:"fail",suggestion:"Install with: brew install jq (or apt install jq)"});let bunCheck=await checkCommand("bun");if(bunCheck.exists)results.push({name:"bun",status:"pass",message:bunCheck.version||""});else results.push({name:"bun",status:"fail",suggestion:"Install with: curl -fsSL https://bun.sh/install | bash"});let claudeCheck=await checkCommand("claude");if(claudeCheck.exists)results.push({name:"Claude Code",status:"pass",message:claudeCheck.version||""});else results.push({name:"Claude Code",status:"warn",suggestion:"Install with: npm install -g @anthropic-ai/claude-code"});return results}async function checkConfiguration(){let results=[];if(genieConfigExists())results.push({name:"Genie config exists",status:"pass",message:contractClaudePath(getGenieConfigPath())});else results.push({name:"Genie config exists",status:"warn",message:"not found",suggestion:"Run: genie setup"});if(isSetupComplete())results.push({name:"Setup complete",status:"pass"});else results.push({name:"Setup complete",status:"warn",message:"not completed",suggestion:"Run: genie setup"});let claudeSettingsPath=getClaudeSettingsPath();if(existsSync19(claudeSettingsPath))results.push({name:"Claude settings exists",status:"pass",message:contractClaudePath(claudeSettingsPath)});else results.push({name:"Claude settings exists",status:"warn",message:"not found",suggestion:"Claude Code creates this on first run"});return results}async function checkTmux(){let results=[];try{if((await $3`${tmuxBin()} -L genie list-sessions 2>/dev/null`.quiet()).exitCode===0)results.push({name:"Server running",status:"pass"});else return results.push({name:"Server running",status:"warn",message:"no sessions",suggestion:"Start with: tmux new-session -d -s genie"}),results}catch{return results.push({name:"Server running",status:"warn",message:"could not check"}),results}let sessionName=(await loadGenieConfig()).session.name;try{if((await $3`${tmuxBin()} -L genie has-session -t ${`=${sessionName}`} 2>/dev/null`.quiet()).exitCode===0)results.push({name:`Session '${sessionName}' exists`,status:"pass"});else results.push({name:`Session '${sessionName}' exists`,status:"warn",suggestion:`Start with: tmux new-session -d -s ${sessionName}`})}catch{results.push({name:`Session '${sessionName}' exists`,status:"warn",message:"could not check"})}return results}async function checkWorkerProfiles(){let results=[];if(!genieConfigExists())return results.push({name:"Worker profiles",status:"warn",message:"no genie config",suggestion:"Run: genie setup"}),results;let config=await loadGenieConfig(),profiles=config.workerProfiles;if(!profiles||Object.keys(profiles).length===0)return results.push({name:"Worker profiles",status:"pass",message:"none configured (using defaults)"}),results;let totalProfiles=Object.keys(profiles).length;results.push({name:"Profiles configured",status:"pass",message:`${totalProfiles} profile${totalProfiles===1?"":"s"}`});for(let name of Object.keys(profiles))results.push({name:`Profile '${name}'`,status:"pass",message:"claude (direct)"});if(config.defaultWorkerProfile)if(profiles[config.defaultWorkerProfile])results.push({name:"Default profile",status:"pass",message:config.defaultWorkerProfile});else results.push({name:"Default profile",status:"warn",message:`'${config.defaultWorkerProfile}' not found`,suggestion:"Run: genie profiles default <profile>"});return results}async function checkBridge(){let results=[];try{let{getBridgeStatus:getBridgeStatus2,removeBridgePidfile:removeBridgePidfile2}=await Promise.resolve().then(() => (init_bridge_status(),exports_bridge_status)),res=await getBridgeStatus2(void 0,{});if(res.state==="stopped")return results.push({name:"Bridge",status:"warn",message:"stopped (no pidfile)",suggestion:"Start the bridge with: genie serve"}),results;if(res.state==="stale"){if(res.pidfile)removeBridgePidfile2();return results.push({name:"Bridge",status:"fail",message:`stale: ${res.detail}`,suggestion:"Restart the bridge with: genie serve restart"}),results}let{pong,pidfile}=res;if(!pong||!pidfile)return results.push({name:"Bridge",status:"warn",message:"running state missing pong/pidfile metadata"}),results;let uptimeSec=Math.round(pong.uptimeMs/1000);results.push({name:"Bridge running",status:"pass",message:`running (pid ${pong.pid}, uptime ${uptimeSec}s)`}),results.push({name:"NATS ping",status:"pass",message:`pong in ${res.latencyMs??0}ms (${pidfile.natsUrl})`}),results.push({name:"Subjects",status:"pass",message:pong.subjects.join(", ")})}catch(err){let detail=err instanceof Error?err.message:String(err);results.push({name:"Bridge module",status:"warn",message:`could not probe bridge: ${detail}`})}return results}function checkGenieAgentTemplate(workspaceRoot){let root=workspaceRoot??findWorkspace()?.root;if(!root)return[];let genieDir=join24(root,"agents","genie");if(!existsSync19(genieDir))return[];let agentsMd=join24(genieDir,"AGENTS.md"),agentYaml=join24(genieDir,"agent.yaml"),issues=[];if(existsSync19(agentsMd))try{if(readFileSync12(agentsMd,"utf-8").includes(STALE_GENIE_AGENTS_MD_MARKER))issues.push("AGENTS.md uses generic placeholder template")}catch{}if(existsSync19(agentYaml))try{let text=readFileSync12(agentYaml,"utf-8");if(!STALE_GENIE_AGENT_YAML_MISSING_MODEL_REGEX.test(text))issues.push("agent.yaml missing model field (TUI falls back to gray)")}catch{}if(issues.length===0)return[{name:"agents/genie scaffold up to date",status:"pass"}];return[{name:"agents/genie stale scaffold",status:"warn",message:issues.join("; "),suggestion:"Run: genie doctor --fix (re-emits genie specialist templates, preserves user edits)"}]}function findBundledTmuxConfigDir(){try{let moduleDir=dirname7(fileURLToPath3(import.meta.url));for(let i2=0;i2<6;i2+=1){let candidate=resolve3(moduleDir,"../".repeat(i2+1),"scripts","tmux");if(existsSync19(join24(candidate,"tui-tmux.conf"))&&existsSync19(join24(candidate,"genie.tmux.conf")))return candidate}}catch{}return null}function checkTmuxConfigs(){if(!findBundledTmuxConfigDir())return[{name:"tmux configs",status:"pass",message:"bundled configs unavailable (skipped)"}];let home=join24(homedir19(),".genie"),stale=[],expectedSnippet="unbind -n MouseDown3Pane";for(let file of["tui-tmux.conf","tmux.conf"]){let installedPath=join24(home,file);if(!existsSync19(installedPath))continue;try{if(!readFileSync12(installedPath,"utf-8").includes(expectedSnippet))stale.push(file)}catch{stale.push(file)}}if(stale.length===0)return[{name:"~/.genie tmux configs up to date",status:"pass"}];return[{name:"~/.genie tmux configs stale",status:"warn",message:`missing right-click unbind in: ${stale.join(", ")}`,suggestion:"Run: genie doctor --fix (refreshes ~/.genie tmux configs from bundled scripts/tmux/)"}]}function isAgentDirectory(path2){try{return statSync4(path2).isDirectory()}catch{return!1}}function hasLegacyFrontmatter(agentDir){let yamlPath=join24(agentDir,"agent.yaml"),agentsMdPath=join24(agentDir,"AGENTS.md");if(!existsSync19(yamlPath)||!existsSync19(agentsMdPath))return!1;try{return readFileSync12(agentsMdPath,"utf-8").slice(0,4).startsWith("---")}catch{return!1}}function checkLegacyAgentFrontmatter(workspaceRoot){let results=[],root=workspaceRoot??findWorkspace()?.root;if(!root)return[];let agentsDir=join24(root,"agents");if(!existsSync19(agentsDir))return[];let entries;try{entries=readdirSync7(agentsDir)}catch{return[]}for(let name of entries){let agentDir=join24(agentsDir,name);if(!isAgentDirectory(agentDir))continue;if(!hasLegacyFrontmatter(agentDir))continue;results.push({name:`agents/${name}/AGENTS.md`,status:"warn",message:"legacy frontmatter detected (ignored by sync)",suggestion:`Move config into agents/${name}/agent.yaml \u2014 AGENTS.md is prompt-only post-migration.`})}if(results.length===0)results.push({name:"No legacy frontmatter in agents/*/AGENTS.md",status:"pass"});return results}function runCheckSection(label,results,counts){printSectionHeader(label);for(let result2 of results){if(printCheckResult(result2),result2.status==="fail")counts.errors=!0;if(result2.status==="warn")counts.warnings=!0}}async function doctorCommand(options){if(options?.fix){await doctorFix();return}if(options?.fixTeamOrphans){await runFixTeamOrphans({dryRun:Boolean(options.dryRun),json:Boolean(options.json)});return}if(options?.observability){await runObservabilityCheck(Boolean(options.json));return}console.log(),console.log("\x1B[1mGenie Doctor\x1B[0m"),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`);let counts={errors:!1,warnings:!1};if(runCheckSection("Prerequisites",await checkPrerequisites(),counts),runCheckSection("Installer Resolution",await collectInstallerResolution(),counts),runCheckSection("Configuration",await checkConfiguration(),counts),runCheckSection("Tmux",await checkTmux(),counts),runCheckSection("Tmux Configs",checkTmuxConfigs(),counts),runCheckSection("Worker Profiles",await checkWorkerProfiles(),counts),runCheckSection("Omni Bridge",await checkBridge(),counts),runCheckSection("Agent Config",checkLegacyAgentFrontmatter(),counts),runCheckSection("Genie Specialist",checkGenieAgentTemplate(),counts),printDoctorSummary(counts),counts.errors)process.exit(1)}function printObservabilityReport(report){if(console.log(),console.log("\x1B[1mObservability Health\x1B[0m"),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`),console.log(` partition_health: ${report.partition_health}`),console.log(` partition_count: ${report.partition_count}`),console.log(` next_rotation_at: ${report.next_rotation_at??"n/a"}`),console.log(` oldest_partition: ${report.oldest_partition??"n/a"}`),console.log(` newest_partition: ${report.newest_partition??"n/a"}`),console.log(` GENIE_WIDE_EMIT: ${report.wide_emit_flag}`),report.message)console.log(` note: ${report.message}`);console.log()}async function runObservabilityCheck(json2){let report=await collectObservabilityHealth();if(json2)console.log(JSON.stringify(report,null,2));else printObservabilityReport(report);if(report.partition_health==="fail")process.exit(1)}async function runFixTeamOrphans(opts){let{archiveOrphanTeamConfigs:archiveOrphanTeamConfigs2}=await Promise.resolve().then(() => (init_archive_orphan_team_configs(),exports_archive_orphan_team_configs)),decisions=archiveOrphanTeamConfigs2({dryRun:opts.dryRun});if(opts.json){console.log(JSON.stringify({dryRun:opts.dryRun,decisions},null,2));return}if(decisions.length===0){console.log(" no team config dirs found \u2014 nothing to do");return}for(let d of decisions){let tag=d.classification==="stale"?opts.dryRun?"WOULD ARCHIVE":"ARCHIVED":d.classification.toUpperCase(),tail=d.archivedTo?` \u2192 ${d.archivedTo}`:"";console.log(` [${tag}] ${d.team} \u2014 ${d.reason}${tail}`)}let stale=decisions.filter((d)=>d.classification==="stale").length,active=decisions.filter((d)=>d.classification==="active").length;console.log(`
|
|
860
|
+
`;if(rows.length===0)return{driftPct:null,detail:"no prior backfill row \u2014 first run will seed"};let row=rows[0];if(row.total_bytes<=0)return{driftPct:0,detail:"no JSONL bytes discovered yet"};let pct=Math.max(0,row.total_bytes-row.processed_bytes)/row.total_bytes*100,display=pct.toFixed(1);return{driftPct:pct,detail:`processed ${row.processed_bytes}/${row.total_bytes} bytes (drift ${display}%)`}}catch(err){return{driftPct:null,detail:`drift probe failed: ${err.message}`}}}async function defaultRunBackfillSync(){if(!await isAvailable())return{ranSync:!1,driftPct:null,detail:"pg unavailable \u2014 backfill skipped"};return(async()=>{try{let sql=await getConnection(),{startBackfill:startBackfill2}=await Promise.resolve().then(() => (init_session_backfill(),exports_session_backfill));await startBackfill2(sql)}catch{}})(),{ranSync:!0,driftPct:null,detail:"background convergence kicked \u2014 `genie doctor --fix` to wait"}}async function defaultRunBackfillBlocking(){if(!await isAvailable())return{ranSync:!1,driftPct:null,detail:"pg unavailable \u2014 backfill skipped"};let sql=await getConnection(),{startBackfill:startBackfill2}=await Promise.resolve().then(() => (init_session_backfill(),exports_session_backfill));await startBackfill2(sql);let after=await defaultMeasureBackfillDrift();return{ranSync:!0,driftPct:after.driftPct,detail:after.detail}}async function defaultListOrphanedZombies(){try{let{listExhaustedZombies:listExhaustedZombies2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));return await listExhaustedZombies2()}catch{return[]}}function safeIsDirectory(path2){try{return statSync3(path2).isDirectory()}catch{return!1}}function summarizeInboxes(inboxesDir2){let inboxFiles=[];try{inboxFiles=readdirSync6(inboxesDir2).filter((f)=>f.endsWith(".json"))}catch{return{newestMs:null,hasContent:!1}}let newestMs=null,hasContent=!1;for(let f of inboxFiles)try{let st=statSync3(join23(inboxesDir2,f));if(newestMs===null||st.mtimeMs>newestMs)newestMs=st.mtimeMs;if(st.size>2)hasContent=!0}catch{}return{newestMs,hasContent}}function classifyTeamDir2(name,base,now){let dir=join23(base,name);if(!safeIsDirectory(dir))return null;if(existsSync18(join23(dir,"config.json")))return null;let inboxesDir2=join23(dir,"inboxes");if(!safeIsDirectory(inboxesDir2))return null;let{newestMs,hasContent}=summarizeInboxes(inboxesDir2),orphan={teamName:name,path:dir,newestInboxMs:newestMs,hasContent},active=hasContent&&newestMs!==null&&now-newestMs<ORPHAN_FRESH_WINDOW_MS;return{orphan,active}}function defaultScanTeamConfigOrphans(){let base=teamsBaseDir3(),result2={active:[],stale:[]};if(!existsSync18(base))return result2;let now=Date.now();for(let name of readdirSync6(base)){if(name.startsWith(".")||name==="_archive")continue;let classified=classifyTeamDir2(name,base,now);if(!classified)continue;(classified.active?result2.active:result2.stale).push(classified.orphan)}return result2}function defaultArchiveStaleTeamConfigs(orphans){if(orphans.length===0)return[];let archiveRoot=join23(teamsBaseDir3(),"_archive");mkdirSync9(archiveRoot,{recursive:!0});let ts3=new Date().toISOString().replace(/[:.]/g,"-"),archived=[];for(let o of orphans){let dest=join23(archiveRoot,`${o.teamName}-${ts3}`);try{renameSync4(o.path,dest),archived.push(dest)}catch{}}return archived}async function defaultRecordAudit(eventType,name,details){await recordAuditEvent("command","serve_start",eventType,"serve",{precondition:name,...details})}async function checkPartition(health,autoFix,deps){if(health.partition_health==="ok"||health.partition_health==="warn")return{name:"partition",status:"ok",detail:health.next_rotation_at?`next rotation: ${health.next_rotation_at}`:void 0};if(health.partition_health==="unknown")return{name:"partition",status:"skipped",detail:"pg unavailable \u2014 skipping partition probe"};if(!autoFix)return{name:"partition",status:"refused",detail:"today's partition is missing or rotation is overdue",fixCommand:"genie doctor --observability # then re-run; or `genie serve start` (without --no-fix)"};let result2=await deps.runPartitionMaintenance();return{name:"partition",status:"fixed",detail:`created/present ${result2.createdOrPresent}, dropped ${result2.dropped}; next rotation ${result2.nextRotationAt??"unknown"}`}}async function checkWatchdog(health,autoFix,deps){if(health.watchdog==="ok")return{name:"watchdog",status:"ok"};if(!autoFix)return{name:"watchdog",status:"refused",detail:health.watchdog_detail??"watchdog units missing",fixCommand:"sudo bun run packages/watchdog/src/cli.ts install"};try{let result2=await deps.installWatchdog();return{name:"watchdog",status:"fixed",detail:`wrote ${result2.filesWritten.length}, skipped ${result2.filesSkipped.length}`}}catch(err){return{name:"watchdog",status:"refused",detail:`auto-install failed: ${err.message}`,fixCommand:"sudo bun run packages/watchdog/src/cli.ts install"}}}async function checkBackfill(autoFix,deps){let drift=await deps.measureBackfillDrift();if(drift.driftPct===null)return{name:"backfill",status:"skipped",detail:drift.detail};if(drift.driftPct<BACKFILL_DRIFT_THRESHOLD_PCT)return{name:"backfill",status:"ok",detail:drift.detail};if(!autoFix)return{name:"backfill",status:"refused",detail:drift.detail,fixCommand:`genie sessions sync # drift ${drift.driftPct.toFixed(1)}% > ${BACKFILL_DRIFT_THRESHOLD_PCT}%`};return{name:"backfill",status:"fixed",detail:(await deps.runBackfillSync()).detail}}async function checkDeadPaneZombies(deps){let orphans=await deps.listOrphanedZombies();if(orphans.length===0)return{name:"dead_pane_zombies",status:"ok"};return{name:"dead_pane_zombies",status:"refused",detail:`${orphans.length} exhausted zombie(s) past TTL; visible in \`genie status\``,fixCommand:"genie prune --zombies # archive eligible rows"}}function checkTeamConfigOrphans(autoFix,deps){let scan=deps.scanTeamConfigOrphans();if(scan.active.length===0&&scan.stale.length===0)return{name:"team_config_orphans",status:"ok"};if(!autoFix){let summary=`active=${scan.active.length} stale=${scan.stale.length}`,firstActive=scan.active[0]?.teamName;return{name:"team_config_orphans",status:"refused",detail:summary,fixCommand:firstActive?`genie team repair ${firstActive} # active orphan; stale dirs archive on auto-fix`:"genie serve start # auto-fix archives stale orphans"}}let archivedPaths=deps.archiveStaleTeamConfigs(scan.stale);if(scan.active.length>0)return{name:"team_config_orphans",status:"refused",detail:`archived ${archivedPaths.length} stale; ${scan.active.length} active orphan(s) need repair`,fixCommand:`genie team repair ${scan.active[0].teamName}`};return{name:"team_config_orphans",status:"fixed",detail:`archived ${archivedPaths.length} stale orphan(s)`}}function bindDefaults(deps){return{collectHealth:deps?.collectHealth??collectObservabilityHealth,runPartitionMaintenance:deps?.runPartitionMaintenance??defaultRunPartitionMaintenance,installWatchdog:deps?.installWatchdog??defaultInstallWatchdog,runBackfillSync:deps?.runBackfillSync??defaultRunBackfillSync,measureBackfillDrift:deps?.measureBackfillDrift??defaultMeasureBackfillDrift,listOrphanedZombies:deps?.listOrphanedZombies??defaultListOrphanedZombies,scanTeamConfigOrphans:deps?.scanTeamConfigOrphans??defaultScanTeamConfigOrphans,archiveStaleTeamConfigs:deps?.archiveStaleTeamConfigs??defaultArchiveStaleTeamConfigs,recordAudit:deps?.recordAudit??defaultRecordAudit,log:deps?.log??((line)=>console.log(line))}}async function ensureServeReady(opts){let deps=bindDefaults(opts.deps),health=await deps.collectHealth(),results=[];results.push(await checkPartition(health,opts.autoFix,deps)),results.push(await checkBackfill(opts.autoFix,deps)),results.push(await checkDeadPaneZombies(deps)),await emitAuditEvents(results,deps);let ok=results.every((r)=>r.status==="ok"||r.status==="fixed"||r.status==="skipped");return printReport(results,deps.log),{ok,results}}async function runDoctorMaintenance(opts={}){let deps=bindDefaults({runBackfillSync:defaultRunBackfillBlocking,log:opts.silent?()=>{}:void 0,...opts.deps}),health=await deps.collectHealth(),results=[];results.push(await checkPartition(health,!0,deps)),results.push(await checkWatchdog(health,!0,deps)),results.push(await checkBackfill(!0,deps)),results.push(await checkDeadPaneZombies(deps)),results.push(checkTeamConfigOrphans(!0,deps)),await emitAuditEvents(results,deps);let ok=results.every((r)=>r.status==="ok"||r.status==="fixed"||r.status==="skipped");return printReport(results,deps.log),{ok,results}}async function emitAuditEvents(results,deps){for(let result2 of results)if(result2.status==="fixed")await deps.recordAudit("serve.precondition.fixed",result2.name,{detail:result2.detail??null}).catch(()=>{});else if(result2.status==="refused")await deps.recordAudit("serve.precondition.refused",result2.name,{detail:result2.detail??null,fix_command:result2.fixCommand??null}).catch(()=>{})}function printReport(results,log){log(" Preconditions:");for(let r of results){let tag=statusTag(r.status),suffix=r.detail?` \u2014 ${r.detail}`:"";if(log(` ${tag} ${r.name}${suffix}`),r.status==="refused"&&r.fixCommand)log(` \u2192 ${r.fixCommand}`)}}function statusTag(status){switch(status){case"ok":return"[ok]";case"fixed":return"[fix]";case"refused":return"[!!]";case"skipped":return"[--]"}}var BACKFILL_DRIFT_THRESHOLD_PCT=5,ORPHAN_FRESH_WINDOW_MS=86400000;var init_ensure_ready=__esm(()=>{init_observability_health();init_audit();init_db()});var exports_doctor={};__export(exports_doctor,{runPostUpdateMaintenance:()=>runPostUpdateMaintenance,findBundledTmuxConfigDir:()=>findBundledTmuxConfigDir,doctorCommand:()=>doctorCommand,checkTmuxConfigs:()=>checkTmuxConfigs,checkLegacyAgentFrontmatter:()=>checkLegacyAgentFrontmatter,checkGenieAgentTemplate:()=>checkGenieAgentTemplate});import{copyFileSync as copyFileSync2,existsSync as existsSync19,mkdirSync as mkdirSync10,readFileSync as readFileSync12,readdirSync as readdirSync7,statSync as statSync4,unlinkSync as unlinkSync6,writeFileSync as writeFileSync7}from"fs";import{homedir as homedir19}from"os";import{dirname as dirname7,join as join24,resolve as resolve3}from"path";import{fileURLToPath as fileURLToPath3}from"url";var{$:$3}=globalThis.Bun;function printSectionHeader(title){console.log(),console.log(`\x1B[1m${title}:\x1B[0m`)}function printCheckResult(result2){let icon={pass:"\x1B[32m\u2713\x1B[0m",fail:"\x1B[31m\u2717\x1B[0m",warn:"\x1B[33m!\x1B[0m"}[result2.status],message=result2.message?` ${result2.message}`:"";if(console.log(` ${icon} ${result2.name}${message}`),result2.suggestion)console.log(` \x1B[2m${result2.suggestion}\x1B[0m`)}async function checkPrerequisites(){let results=[],tmuxCheck=await checkCommand("tmux");if(tmuxCheck.exists)results.push({name:"tmux",status:"pass",message:tmuxCheck.version||""});else results.push({name:"tmux",status:"fail",suggestion:"Install with: brew install tmux (or apt install tmux)"});let jqCheck=await checkCommand("jq");if(jqCheck.exists)results.push({name:"jq",status:"pass",message:jqCheck.version||""});else results.push({name:"jq",status:"fail",suggestion:"Install with: brew install jq (or apt install jq)"});let bunCheck=await checkCommand("bun");if(bunCheck.exists)results.push({name:"bun",status:"pass",message:bunCheck.version||""});else results.push({name:"bun",status:"fail",suggestion:"Install with: curl -fsSL https://bun.sh/install | bash"});let claudeCheck=await checkCommand("claude");if(claudeCheck.exists)results.push({name:"Claude Code",status:"pass",message:claudeCheck.version||""});else results.push({name:"Claude Code",status:"warn",suggestion:"Install with: npm install -g @anthropic-ai/claude-code"});let codexCheck=await checkCommand("codex");if(codexCheck.exists)results.push({name:"Codex CLI",status:"pass",message:codexCheck.version||""});else results.push({name:"Codex CLI",status:"warn",suggestion:"Install via OpenAI account; codex is optional unless using --provider codex"});let requiredForSpawn=["genie","bun","node","npm","git","claude","codex"];for(let bin of requiredForSpawn){let interactivePath=await resolveBinaryInteractive(bin),nonInteractivePath=await resolveBinaryNonInteractive(bin);if(interactivePath&&nonInteractivePath)results.push({name:`Non-interactive PATH: ${bin}`,status:"pass",message:nonInteractivePath});else if(interactivePath&&!nonInteractivePath)results.push({name:`Non-interactive PATH: ${bin}`,status:"warn",message:"interactive-only",suggestion:`Move PATH export from ~/.bashrc to ~/.profile so spawn-scripts can resolve ${bin}. (Or use a stable symlink in ~/.local/bin.)`});else if(!interactivePath&&bin!=="codex"){if(bin==="genie"||bin==="node"||bin==="npm"||bin==="git")results.push({name:`Non-interactive PATH: ${bin}`,status:"warn",message:"not in non-interactive PATH",suggestion:`Add ${bin} to ~/.profile (not just ~/.bashrc). Some flows (e.g. genie update) shell out to bare '${bin}' from non-interactive subprocesses.`})}}return results}async function resolveBinaryInteractive(bin){try{return(await $3`command -v ${bin}`.quiet().text()).trim()||null}catch{return null}}async function resolveBinaryNonInteractive(bin){try{return(await $3`sh -c "command -v ${bin}"`.quiet().text()).trim()||null}catch{return null}}async function checkConfiguration(){let results=[];if(genieConfigExists())results.push({name:"Genie config exists",status:"pass",message:contractClaudePath(getGenieConfigPath())});else results.push({name:"Genie config exists",status:"warn",message:"not found",suggestion:"Run: genie setup"});if(isSetupComplete())results.push({name:"Setup complete",status:"pass"});else results.push({name:"Setup complete",status:"warn",message:"not completed",suggestion:"Run: genie setup"});let claudeSettingsPath=getClaudeSettingsPath();if(existsSync19(claudeSettingsPath))results.push({name:"Claude settings exists",status:"pass",message:contractClaudePath(claudeSettingsPath)});else results.push({name:"Claude settings exists",status:"warn",message:"not found",suggestion:"Claude Code creates this on first run"});return results}async function checkTmux(){let results=[];try{if((await $3`${tmuxBin()} -L genie list-sessions 2>/dev/null`.quiet()).exitCode===0)results.push({name:"Server running",status:"pass"});else return results.push({name:"Server running",status:"warn",message:"no sessions",suggestion:"Start with: tmux new-session -d -s genie"}),results}catch{return results.push({name:"Server running",status:"warn",message:"could not check"}),results}let sessionName=(await loadGenieConfig()).session.name;try{if((await $3`${tmuxBin()} -L genie has-session -t ${`=${sessionName}`} 2>/dev/null`.quiet()).exitCode===0)results.push({name:`Session '${sessionName}' exists`,status:"pass"});else results.push({name:`Session '${sessionName}' exists`,status:"warn",suggestion:`Start with: tmux new-session -d -s ${sessionName}`})}catch{results.push({name:`Session '${sessionName}' exists`,status:"warn",message:"could not check"})}return results}async function checkWorkerProfiles(){let results=[];if(!genieConfigExists())return results.push({name:"Worker profiles",status:"warn",message:"no genie config",suggestion:"Run: genie setup"}),results;let config=await loadGenieConfig(),profiles=config.workerProfiles;if(!profiles||Object.keys(profiles).length===0)return results.push({name:"Worker profiles",status:"pass",message:"none configured (using defaults)"}),results;let totalProfiles=Object.keys(profiles).length;results.push({name:"Profiles configured",status:"pass",message:`${totalProfiles} profile${totalProfiles===1?"":"s"}`});for(let name of Object.keys(profiles))results.push({name:`Profile '${name}'`,status:"pass",message:"claude (direct)"});if(config.defaultWorkerProfile)if(profiles[config.defaultWorkerProfile])results.push({name:"Default profile",status:"pass",message:config.defaultWorkerProfile});else results.push({name:"Default profile",status:"warn",message:`'${config.defaultWorkerProfile}' not found`,suggestion:"Run: genie profiles default <profile>"});return results}async function checkBridge(){let results=[];try{let{getBridgeStatus:getBridgeStatus2,removeBridgePidfile:removeBridgePidfile2}=await Promise.resolve().then(() => (init_bridge_status(),exports_bridge_status)),res=await getBridgeStatus2(void 0,{});if(res.state==="stopped")return results.push({name:"Bridge",status:"warn",message:"stopped (no pidfile)",suggestion:"Start the bridge with: genie serve"}),results;if(res.state==="stale"){if(res.pidfile)removeBridgePidfile2();return results.push({name:"Bridge",status:"fail",message:`stale: ${res.detail}`,suggestion:"Restart the bridge with: genie serve restart"}),results}let{pong,pidfile}=res;if(!pong||!pidfile)return results.push({name:"Bridge",status:"warn",message:"running state missing pong/pidfile metadata"}),results;let uptimeSec=Math.round(pong.uptimeMs/1000);results.push({name:"Bridge running",status:"pass",message:`running (pid ${pong.pid}, uptime ${uptimeSec}s)`}),results.push({name:"NATS ping",status:"pass",message:`pong in ${res.latencyMs??0}ms (${pidfile.natsUrl})`}),results.push({name:"Subjects",status:"pass",message:pong.subjects.join(", ")})}catch(err){let detail=err instanceof Error?err.message:String(err);results.push({name:"Bridge module",status:"warn",message:`could not probe bridge: ${detail}`})}return results}function checkGenieAgentTemplate(workspaceRoot){let root=workspaceRoot??findWorkspace()?.root;if(!root)return[];let genieDir=join24(root,"agents","genie");if(!existsSync19(genieDir))return[];let agentsMd=join24(genieDir,"AGENTS.md"),agentYaml=join24(genieDir,"agent.yaml"),issues=[];if(existsSync19(agentsMd))try{if(readFileSync12(agentsMd,"utf-8").includes(STALE_GENIE_AGENTS_MD_MARKER))issues.push("AGENTS.md uses generic placeholder template")}catch{}if(existsSync19(agentYaml))try{let text=readFileSync12(agentYaml,"utf-8");if(!STALE_GENIE_AGENT_YAML_MISSING_MODEL_REGEX.test(text))issues.push("agent.yaml missing model field (TUI falls back to gray)")}catch{}if(issues.length===0)return[{name:"agents/genie scaffold up to date",status:"pass"}];return[{name:"agents/genie stale scaffold",status:"warn",message:issues.join("; "),suggestion:"Run: genie doctor --fix (re-emits genie specialist templates, preserves user edits)"}]}function findBundledTmuxConfigDir(){try{let moduleDir=dirname7(fileURLToPath3(import.meta.url));for(let i2=0;i2<6;i2+=1){let candidate=resolve3(moduleDir,"../".repeat(i2+1),"scripts","tmux");if(existsSync19(join24(candidate,"tui-tmux.conf"))&&existsSync19(join24(candidate,"genie.tmux.conf")))return candidate}}catch{}return null}function checkTmuxConfigs(){if(!findBundledTmuxConfigDir())return[{name:"tmux configs",status:"pass",message:"bundled configs unavailable (skipped)"}];let home=join24(homedir19(),".genie"),stale=[],expectedSnippet="unbind -n MouseDown3Pane";for(let file of["tui-tmux.conf","tmux.conf"]){let installedPath=join24(home,file);if(!existsSync19(installedPath))continue;try{if(!readFileSync12(installedPath,"utf-8").includes(expectedSnippet))stale.push(file)}catch{stale.push(file)}}if(stale.length===0)return[{name:"~/.genie tmux configs up to date",status:"pass"}];return[{name:"~/.genie tmux configs stale",status:"warn",message:`missing right-click unbind in: ${stale.join(", ")}`,suggestion:"Run: genie doctor --fix (refreshes ~/.genie tmux configs from bundled scripts/tmux/)"}]}function isAgentDirectory(path2){try{return statSync4(path2).isDirectory()}catch{return!1}}function hasLegacyFrontmatter(agentDir){let yamlPath=join24(agentDir,"agent.yaml"),agentsMdPath=join24(agentDir,"AGENTS.md");if(!existsSync19(yamlPath)||!existsSync19(agentsMdPath))return!1;try{return readFileSync12(agentsMdPath,"utf-8").slice(0,4).startsWith("---")}catch{return!1}}function checkLegacyAgentFrontmatter(workspaceRoot){let results=[],root=workspaceRoot??findWorkspace()?.root;if(!root)return[];let agentsDir=join24(root,"agents");if(!existsSync19(agentsDir))return[];let entries;try{entries=readdirSync7(agentsDir)}catch{return[]}for(let name of entries){let agentDir=join24(agentsDir,name);if(!isAgentDirectory(agentDir))continue;if(!hasLegacyFrontmatter(agentDir))continue;results.push({name:`agents/${name}/AGENTS.md`,status:"warn",message:"legacy frontmatter detected (ignored by sync)",suggestion:`Move config into agents/${name}/agent.yaml \u2014 AGENTS.md is prompt-only post-migration.`})}if(results.length===0)results.push({name:"No legacy frontmatter in agents/*/AGENTS.md",status:"pass"});return results}function runCheckSection(label,results,counts){printSectionHeader(label);for(let result2 of results){if(printCheckResult(result2),result2.status==="fail")counts.errors=!0;if(result2.status==="warn")counts.warnings=!0}}async function doctorCommand(options){if(options?.fix){await doctorFix();return}if(options?.fixTeamOrphans){await runFixTeamOrphans({dryRun:Boolean(options.dryRun),json:Boolean(options.json)});return}if(options?.observability){await runObservabilityCheck(Boolean(options.json));return}console.log(),console.log("\x1B[1mGenie Doctor\x1B[0m"),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`);let counts={errors:!1,warnings:!1};if(runCheckSection("Prerequisites",await checkPrerequisites(),counts),runCheckSection("Installer Resolution",await collectInstallerResolution(),counts),runCheckSection("Configuration",await checkConfiguration(),counts),runCheckSection("Tmux",await checkTmux(),counts),runCheckSection("Tmux Configs",checkTmuxConfigs(),counts),runCheckSection("Worker Profiles",await checkWorkerProfiles(),counts),runCheckSection("Omni Bridge",await checkBridge(),counts),runCheckSection("Agent Config",checkLegacyAgentFrontmatter(),counts),runCheckSection("Genie Specialist",checkGenieAgentTemplate(),counts),printDoctorSummary(counts),counts.errors)process.exit(1)}function printObservabilityReport(report){if(console.log(),console.log("\x1B[1mObservability Health\x1B[0m"),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`),console.log(` partition_health: ${report.partition_health}`),console.log(` partition_count: ${report.partition_count}`),console.log(` next_rotation_at: ${report.next_rotation_at??"n/a"}`),console.log(` oldest_partition: ${report.oldest_partition??"n/a"}`),console.log(` newest_partition: ${report.newest_partition??"n/a"}`),console.log(` GENIE_WIDE_EMIT: ${report.wide_emit_flag}`),report.message)console.log(` note: ${report.message}`);console.log()}async function runObservabilityCheck(json2){let report=await collectObservabilityHealth();if(json2)console.log(JSON.stringify(report,null,2));else printObservabilityReport(report);if(report.partition_health==="fail")process.exit(1)}async function runFixTeamOrphans(opts){let{archiveOrphanTeamConfigs:archiveOrphanTeamConfigs2}=await Promise.resolve().then(() => (init_archive_orphan_team_configs(),exports_archive_orphan_team_configs)),decisions=archiveOrphanTeamConfigs2({dryRun:opts.dryRun});if(opts.json){console.log(JSON.stringify({dryRun:opts.dryRun,decisions},null,2));return}if(decisions.length===0){console.log(" no team config dirs found \u2014 nothing to do");return}for(let d of decisions){let tag=d.classification==="stale"?opts.dryRun?"WOULD ARCHIVE":"ARCHIVED":d.classification.toUpperCase(),tail=d.archivedTo?` \u2192 ${d.archivedTo}`:"";console.log(` [${tag}] ${d.team} \u2014 ${d.reason}${tail}`)}let stale=decisions.filter((d)=>d.classification==="stale").length,active=decisions.filter((d)=>d.classification==="active").length;console.log(`
|
|
861
861
|
${decisions.length} dirs inspected, ${stale} archived, ${active} flagged active.`)}function printDoctorSummary(counts){if(console.log(),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`),counts.errors)console.log("\x1B[31mSome checks failed.\x1B[0m Run \x1B[36mgenie setup\x1B[0m to fix.");else if(counts.warnings)console.log("\x1B[33mSome warnings detected.\x1B[0m Everything should still work.");else console.log("\x1B[32mAll checks passed!\x1B[0m");console.log()}async function killStalePostgres(){console.log(" Killing stale postgres processes...");try{let{execSync:execSync4}=await import("child_process");execSync4('pkill -9 -f "postgres.*pgserve" 2>/dev/null || true',{stdio:"ignore",timeout:5000}),console.log(" \x1B[32m\u2713\x1B[0m Stale postgres processes killed")}catch{console.log(" \x1B[33m!\x1B[0m Could not kill stale postgres processes")}}async function cleanSharedMemory(){console.log(" Cleaning shared memory...");try{let{execSync:execSync4}=await import("child_process");if(process.platform==="darwin")execSync4("ipcs -m 2>/dev/null | awk '/^m/ {print $2}' | xargs -I{} ipcrm -m {} 2>/dev/null || true",{stdio:"ignore",timeout:5000});else execSync4("ipcs -m 2>/dev/null | awk '$6 == 0 {print $2}' | xargs -I{} ipcrm -m {} 2>/dev/null || true",{stdio:"ignore",timeout:5000});console.log(" \x1B[32m\u2713\x1B[0m Shared memory cleaned")}catch{console.log(" \x1B[32m\u2713\x1B[0m No stale shared memory")}}async function stopExistingDaemon(pidFile){try{let{readFileSync:readFileSync13}=await import("fs"),pid=Number.parseInt(readFileSync13(pidFile,"utf-8").trim(),10);if(!Number.isNaN(pid)&&pid>0)try{process.kill(pid,"SIGTERM"),console.log(` \x1B[32m\u2713\x1B[0m Stopped existing daemon (PID ${pid})`),await new Promise((r)=>setTimeout(r,1000))}catch{}}catch{}}function removeStaleFiles(genieHome3,pidFile){let portFile=join24(genieHome3,"pgserve.port"),postmasterPid=join24(genieHome3,"data","pgserve","postmaster.pid");for(let file of[portFile,pidFile,postmasterPid])if(existsSync19(file))try{unlinkSync6(file),console.log(` \x1B[32m\u2713\x1B[0m Removed ${file}`)}catch{console.log(` \x1B[33m!\x1B[0m Could not remove ${file}`)}}async function restartDaemon(){console.log(" Restarting daemon...");try{let{spawn:spawn2}=await import("child_process"),bunPath=process.execPath??"bun",genieBin=process.argv[1]??"genie";spawn2(bunPath,[genieBin,"daemon","start"],{detached:!0,stdio:"ignore"}).unref(),await new Promise((resolve4)=>setTimeout(resolve4,2000)),console.log(" \x1B[32m\u2713\x1B[0m Daemon restart initiated")}catch{console.log(" \x1B[33m!\x1B[0m Could not restart daemon \u2014 run: genie daemon start")}}function isAgentsMdUserEdited(target,fresh){if(!existsSync19(target))return!1;try{let current=readFileSync12(target,"utf-8");if(current.trim().length===0)return!1;if(current.includes(STALE_GENIE_AGENTS_MD_MARKER))return!1;return current!==fresh}catch{return!1}}function writeGenieTemplate(targetDir,name,content){let target=join24(targetDir,name),userEdited=name==="AGENTS.md"&&isAgentsMdUserEdited(target,content),writeTo=userEdited?`${target}.new`:target;try{writeFileSync7(writeTo,content);let marker=userEdited?`wrote ${name}.new (user edits preserved \u2014 merge manually)`:`wrote ${name}`;console.log(` \x1B[32m\u2713\x1B[0m ${marker}`)}catch(err){let detail=err instanceof Error?err.message:String(err);console.log(` \x1B[31m\u2717\x1B[0m ${name}: ${detail}`)}}function fixGenieAgentTemplate(workspaceRoot){let root=workspaceRoot??findWorkspace()?.root;if(!root){console.log(" \x1B[33m!\x1B[0m No workspace detected \u2014 skipping genie-template repair");return}let genieDir=join24(root,"agents","genie");if(!existsSync19(genieDir)){console.log(" \x1B[2m\xB7\x1B[0m No agents/genie directory \u2014 skipping");return}console.log(" Refreshing agents/genie scaffold..."),writeGenieTemplate(genieDir,"AGENTS.md",GENIE_AGENTS_TEMPLATE),writeGenieTemplate(genieDir,"SOUL.md",GENIE_SOUL_TEMPLATE),writeGenieTemplate(genieDir,"HEARTBEAT.md",GENIE_HEARTBEAT_TEMPLATE)}function ensureGenieHomeDir(home){if(existsSync19(home))return!0;try{return mkdirSync10(home,{recursive:!0}),!0}catch{return console.log(` \x1B[31m\u2717\x1B[0m Could not create ${home}`),!1}}function refreshTmuxConfFile(bundledDir,home,srcFile,dstFile){let src=join24(bundledDir,srcFile),dst=join24(home,dstFile);if(!existsSync19(src))return;if(existsSync19(dst))try{copyFileSync2(dst,`${dst}.bak`)}catch{}try{copyFileSync2(src,dst),console.log(` \x1B[32m\u2713\x1B[0m wrote ${dstFile} (previous saved as ${dstFile}.bak)`)}catch(err){let detail=err instanceof Error?err.message:String(err);console.log(` \x1B[31m\u2717\x1B[0m ${dstFile}: ${detail}`)}}function fixTmuxConfigs(){let bundledDir=findBundledTmuxConfigDir();if(!bundledDir){console.log(" \x1B[33m!\x1B[0m Bundled tmux configs not found \u2014 skipping");return}let home=join24(homedir19(),".genie");if(!ensureGenieHomeDir(home))return;console.log(" Refreshing ~/.genie tmux configs..."),refreshTmuxConfFile(bundledDir,home,"tui-tmux.conf","tui-tmux.conf"),refreshTmuxConfFile(bundledDir,home,"genie.tmux.conf","tmux.conf")}async function runMaintenancePreconditions(silent=!1){try{let{runDoctorMaintenance:runDoctorMaintenance2}=await Promise.resolve().then(() => (init_ensure_ready(),exports_ensure_ready));await runDoctorMaintenance2({silent})}catch(err){let msg=err instanceof Error?err.message:String(err);if(!silent)console.warn(` Maintenance preconditions skipped: ${msg}`)}}async function doctorFix(){console.log(`
|
|
862
862
|
\x1B[1mGenie Doctor \u2014 Auto Fix\x1B[0m`),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m
|
|
863
863
|
`),await killStalePostgres(),await cleanSharedMemory();let genieHome3=process.env.GENIE_HOME??join24(homedir19(),".genie"),pidFile=join24(genieHome3,"scheduler.pid");await stopExistingDaemon(pidFile),removeStaleFiles(genieHome3,pidFile),fixGenieAgentTemplate(),fixTmuxConfigs(),await runMaintenancePreconditions(),await restartDaemon(),console.log(`
|
|
@@ -1543,7 +1543,9 @@ Stopping inbox watcher...`),stopInboxWatcher2(handle),process.exit(0)};process.o
|
|
|
1543
1543
|
ORDER BY created_at ASC
|
|
1544
1544
|
`}async function pollApprovalState(approvalId,defaultAction){let sql=await getConnection(),[approval]=await sql`
|
|
1545
1545
|
SELECT decision, timeout_at FROM approvals WHERE id = ${approvalId}
|
|
1546
|
-
`;if(!approval)return"deny";if(approval.decision==="allow"||approval.decision==="deny")return approval.decision;if(new Date(approval.timeout_at)<=new Date)return await resolveApproval(approvalId,defaultAction,"timeout"),defaultAction;return null}async function waitForResolution(approvalId,timeoutAt,defaultAction){let sql=await getConnection();return new Promise((resolve6)=>{let resolved=!1,listener=null,pollTimer=null,finish=(decision)=>{if(resolved)return;if(resolved=!0,listener)listener.unlisten().catch(()=>{});if(pollTimer)clearInterval(pollTimer);resolve6(decision)},checkResolution=async()=>{if(resolved)return;try{let result2=await pollApprovalState(approvalId,defaultAction);if(result2!==null)finish(result2)}catch{finish("deny")}};sql.listen("genie_approval_resolved",(payload)=>{if(payload===approvalId)checkResolution()}).then((l)=>{if(listener=l,resolved)l.unlisten().catch(()=>{})}).catch(()=>{}),pollTimer=setInterval(checkResolution,5000);let msUntilTimeout=Math.max(0,timeoutAt.getTime()-Date.now()+1000);setTimeout(()=>{if(!resolved)resolveApproval(approvalId,defaultAction,"timeout").catch(()=>{}),finish(defaultAction)},msUntilTimeout).unref(),checkResolution()})}function createRemoteApprovalGate(config){let timeoutSec=config.permissions?.timeout??300,defaultAction=config.permissions?.defaultAction??"deny";return async(input)=>{let hookInput=input,toolName=hookInput.tool_name,toolInput=hookInput.tool_input??{},preview=JSON.stringify(toolInput).slice(0,500),timeoutAt=new Date(Date.now()+timeoutSec*1000),approvalId=await insertApproval(config.executorId,config.agentName,toolName,preview,timeoutAt);if(config.permissions?.omniChat&&config.permissions?.omniInstance)sendApprovalToOmni(approvalId,config.agentName,toolName,preview,config.permissions).catch(()=>{});return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:await waitForResolution(approvalId,timeoutAt,defaultAction)}}}}var recentOmniSends,BATCH_WINDOW_MS=1e4,BATCH_THRESHOLD=3;var init_claude_sdk_remote_approval=__esm(()=>{init_db();recentOmniSends=[]});var exports_claude_sdk={};__export(exports_claude_sdk,{translateSdkConfig:()=>translateSdkConfig,ClaudeSdkProvider:()=>ClaudeSdkProvider});import{readFileSync as readFileSync20}from"fs";import{query}from"@anthropic-ai/claude-agent-sdk";function translateSdkConfig(sdkConfig){let opts={};for(let key of SDK_TRUTHY_FIELDS)if(sdkConfig[key])opts[key]=sdkConfig[key];for(let key of SDK_NULLABLE_FIELDS)if(sdkConfig[key]!=null)opts[key]=sdkConfig[key];for(let key of SDK_CAST_FIELDS)if(sdkConfig[key])opts[key]=sdkConfig[key];return opts}function mergeHooks(...sources){let merged={};for(let src of sources){if(!src)continue;for(let[event,matchers]of Object.entries(src)){if(!matchers)continue;if(!merged[event])merged[event]=[...matchers];else merged[event].push(...matchers)}}return merged}class ClaudeSdkProvider{name="claude-sdk";transport="process";activeQueries=new Map;buildSpawnCommand(ctx){return{command:"claude-sdk-in-process",provider:"claude-sdk",meta:{role:ctx.role,skill:ctx.skill}}}runQuery(ctx,prompt2,permissionConfig,extraOptions,sdkConfig){ensureClaudeSettingsSafe();let abortController=new AbortController,tracker={abortController,done:!1};this.activeQueries.set(ctx.executorId,tracker);let permHooks;if(sdkConfig?.permissionMode==="remoteApproval"){let ws=findWorkspace(ctx.cwd),permissions=ws?getWorkspaceConfig(ws.root).permissions:void 0;permHooks={PreToolUse:[{matcher:"*",hooks:[createRemoteApprovalGate({executorId:ctx.executorId,agentName:ctx.agentId??ctx.role??"unknown",permissions})]}]}}else if(permissionConfig)permHooks={PreToolUse:[{matcher:"*",hooks:[createPermissionGate(permissionConfig)]}]};let translatedSdk=sdkConfig?translateSdkConfig(sdkConfig):void 0,mergedHooks=mergeHooks(permHooks,translatedSdk?.hooks,extraOptions?.hooks),hasHooks=Object.keys(mergedHooks).length>0,resolvedSystemPrompt=ctx.systemPrompt;if(!resolvedSystemPrompt&&ctx.systemPromptFile)try{resolvedSystemPrompt=readFileSync20(ctx.systemPromptFile,"utf-8")}catch{}let options={cwd:ctx.cwd,abortController,...ctx.model&&{model:ctx.model},...resolvedSystemPrompt&&{systemPrompt:resolvedSystemPrompt},includePartialMessages:!0,agentProgressSummaries:!0,...translatedSdk,...extraOptions,...hasHooks&&{hooks:mergedHooks},permissionMode:"auto",allowDangerouslySkipPermissions:!0},messages2=query({prompt:prompt2,options}),originalReturn=messages2.return.bind(messages2);messages2.return=async(value)=>{return tracker.done=!0,this.activeQueries.delete(ctx.executorId),originalReturn(value)};let self2=this,wrappedMessages=async function*(){try{for await(let msg of messages2)yield msg,routeSdkMessage(msg,ctx.executorId,ctx.agentId).catch(()=>{})}finally{tracker.done=!0,self2.activeQueries.delete(ctx.executorId)}}();return wrappedMessages.interrupt=messages2.interrupt.bind(messages2),wrappedMessages.setPermissionMode=messages2.setPermissionMode.bind(messages2),wrappedMessages.setModel=messages2.setModel.bind(messages2),wrappedMessages.return=messages2.return.bind(messages2),wrappedMessages.throw=messages2.throw.bind(messages2),{messages:wrappedMessages,abortController}}async extractSession(executor){let sessionId=executor.claudeSessionId;if(!sessionId)return null;return{sessionId}}async detectState(executor){let tracker=this.activeQueries.get(executor.id);if(!tracker)return"done";return tracker.done?"done":"running"}async terminate(executor){let tracker=this.activeQueries.get(executor.id);if(tracker)tracker.abortController.abort(),tracker.done=!0,this.activeQueries.delete(executor.id)}canResume(){return!1}}var SDK_TRUTHY_FIELDS,SDK_NULLABLE_FIELDS,SDK_CAST_FIELDS;var init_claude_sdk=__esm(()=>{init_claude_settings();init_workspace();init_claude_sdk_events();init_claude_sdk_permissions();init_claude_sdk_remote_approval();SDK_TRUTHY_FIELDS=["tools","allowedTools","disallowedTools","plugins","systemPrompt","betas","settingSources"],SDK_NULLABLE_FIELDS=["maxTurns","maxBudgetUsd","persistSession","enableFileCheckpointing","includePartialMessages","includeHookEvents","promptSuggestions","agentProgressSummaries"],SDK_CAST_FIELDS=["effort","thinking","agents","mcpServers","outputFormat","sandbox","settings"]});
|
|
1546
|
+
`;if(!approval)return"deny";if(approval.decision==="allow"||approval.decision==="deny")return approval.decision;if(new Date(approval.timeout_at)<=new Date)return await resolveApproval(approvalId,defaultAction,"timeout"),defaultAction;return null}async function waitForResolution(approvalId,timeoutAt,defaultAction){let sql=await getConnection();return new Promise((resolve6)=>{let resolved=!1,listener=null,pollTimer=null,finish=(decision)=>{if(resolved)return;if(resolved=!0,listener)listener.unlisten().catch(()=>{});if(pollTimer)clearInterval(pollTimer);resolve6(decision)},checkResolution=async()=>{if(resolved)return;try{let result2=await pollApprovalState(approvalId,defaultAction);if(result2!==null)finish(result2)}catch{finish("deny")}};sql.listen("genie_approval_resolved",(payload)=>{if(payload===approvalId)checkResolution()}).then((l)=>{if(listener=l,resolved)l.unlisten().catch(()=>{})}).catch(()=>{}),pollTimer=setInterval(checkResolution,5000);let msUntilTimeout=Math.max(0,timeoutAt.getTime()-Date.now()+1000);setTimeout(()=>{if(!resolved)resolveApproval(approvalId,defaultAction,"timeout").catch(()=>{}),finish(defaultAction)},msUntilTimeout).unref(),checkResolution()})}function createRemoteApprovalGate(config){let timeoutSec=config.permissions?.timeout??300,defaultAction=config.permissions?.defaultAction??"deny";return async(input)=>{let hookInput=input,toolName=hookInput.tool_name,toolInput=hookInput.tool_input??{},preview=JSON.stringify(toolInput).slice(0,500),timeoutAt=new Date(Date.now()+timeoutSec*1000),approvalId=await insertApproval(config.executorId,config.agentName,toolName,preview,timeoutAt);if(config.permissions?.omniChat&&config.permissions?.omniInstance)sendApprovalToOmni(approvalId,config.agentName,toolName,preview,config.permissions).catch(()=>{});return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:await waitForResolution(approvalId,timeoutAt,defaultAction)}}}}var recentOmniSends,BATCH_WINDOW_MS=1e4,BATCH_THRESHOLD=3;var init_claude_sdk_remote_approval=__esm(()=>{init_db();recentOmniSends=[]});var exports_claude_sdk={};__export(exports_claude_sdk,{translateSdkConfig:()=>translateSdkConfig,ClaudeSdkProvider:()=>ClaudeSdkProvider});import{readFileSync as readFileSync20}from"fs";import{query}from"@anthropic-ai/claude-agent-sdk";function translateSdkConfig(sdkConfig){let opts={};for(let key of SDK_TRUTHY_FIELDS)if(sdkConfig[key])opts[key]=sdkConfig[key];for(let key of SDK_NULLABLE_FIELDS)if(sdkConfig[key]!=null)opts[key]=sdkConfig[key];for(let key of SDK_CAST_FIELDS)if(sdkConfig[key])opts[key]=sdkConfig[key];return opts}function mergeHooks(...sources){let merged={};for(let src of sources){if(!src)continue;for(let[event,matchers]of Object.entries(src)){if(!matchers)continue;if(!merged[event])merged[event]=[...matchers];else merged[event].push(...matchers)}}return merged}class ClaudeSdkProvider{name="claude-sdk";transport="process";activeQueries=new Map;buildSpawnCommand(ctx){return{command:"claude-sdk-in-process",provider:"claude-sdk",meta:{role:ctx.role,skill:ctx.skill}}}runQuery(ctx,prompt2,permissionConfig,extraOptions,sdkConfig){ensureClaudeSettingsSafe();let abortController=new AbortController,tracker={abortController,done:!1};this.activeQueries.set(ctx.executorId,tracker);let permHooks;if(sdkConfig?.permissionMode==="remoteApproval"){let ws=findWorkspace(ctx.cwd),permissions=ws?getWorkspaceConfig(ws.root).permissions:void 0;permHooks={PreToolUse:[{matcher:"*",hooks:[createRemoteApprovalGate({executorId:ctx.executorId,agentName:ctx.agentId??ctx.role??"unknown",permissions})]}]}}else if(permissionConfig)permHooks={PreToolUse:[{matcher:"*",hooks:[createPermissionGate(permissionConfig)]}]};let translatedSdk=sdkConfig?translateSdkConfig(sdkConfig):void 0,mergedHooks=mergeHooks(permHooks,translatedSdk?.hooks,extraOptions?.hooks),hasHooks=Object.keys(mergedHooks).length>0,resolvedSystemPrompt=ctx.systemPrompt;if(!resolvedSystemPrompt&&ctx.systemPromptFile)try{resolvedSystemPrompt=readFileSync20(ctx.systemPromptFile,"utf-8")}catch{}let options={cwd:ctx.cwd,abortController,...ctx.model&&{model:ctx.model},...resolvedSystemPrompt&&{systemPrompt:resolvedSystemPrompt},includePartialMessages:!0,agentProgressSummaries:!0,...translatedSdk,...extraOptions,...hasHooks&&{hooks:mergedHooks},permissionMode:"auto",allowDangerouslySkipPermissions:!0},messages2=query({prompt:prompt2,options}),originalReturn=messages2.return.bind(messages2);messages2.return=async(value)=>{return tracker.done=!0,this.activeQueries.delete(ctx.executorId),originalReturn(value)};let self2=this,wrappedMessages=async function*(){try{for await(let msg of messages2)yield msg,routeSdkMessage(msg,ctx.executorId,ctx.agentId).catch(()=>{})}finally{tracker.done=!0,self2.activeQueries.delete(ctx.executorId)}}();return wrappedMessages.interrupt=messages2.interrupt.bind(messages2),wrappedMessages.setPermissionMode=messages2.setPermissionMode.bind(messages2),wrappedMessages.setModel=messages2.setModel.bind(messages2),wrappedMessages.return=messages2.return.bind(messages2),wrappedMessages.throw=messages2.throw.bind(messages2),{messages:wrappedMessages,abortController}}async extractSession(executor){let sessionId=executor.claudeSessionId;if(!sessionId)return null;return{sessionId}}async detectState(executor){let tracker=this.activeQueries.get(executor.id);if(!tracker)return"done";return tracker.done?"done":"running"}async terminate(executor){let tracker=this.activeQueries.get(executor.id);if(tracker)tracker.abortController.abort(),tracker.done=!0,this.activeQueries.delete(executor.id)}canResume(){return!1}}var SDK_TRUTHY_FIELDS,SDK_NULLABLE_FIELDS,SDK_CAST_FIELDS;var init_claude_sdk=__esm(()=>{init_claude_settings();init_workspace();init_claude_sdk_events();init_claude_sdk_permissions();init_claude_sdk_remote_approval();SDK_TRUTHY_FIELDS=["tools","allowedTools","disallowedTools","plugins","systemPrompt","betas","settingSources"],SDK_NULLABLE_FIELDS=["maxTurns","maxBudgetUsd","persistSession","enableFileCheckpointing","includePartialMessages","includeHookEvents","promptSuggestions","agentProgressSummaries"],SDK_CAST_FIELDS=["effort","thinking","agents","mcpServers","outputFormat","sandbox","settings"]});function detectCodexState(output){if(!output||!output.trim())return{type:"unknown",detail:"empty pane"};let tail=output.split(`
|
|
1547
|
+
`).filter((l)=>l.trim()).slice(-8).join(`
|
|
1548
|
+
`);if(/Press enter to confirm or esc to cancel/.test(tail))return{type:"permission",detail:"enter-or-esc gate"};if(/Would you like to run/.test(tail))return{type:"permission",detail:"run-confirmation"};if(/[\u25E6\u25D0\u25D1\u25D2\u25D3\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F]/.test(tail))return{type:"working",detail:"spinner glyph"};if(/esc to interrupt/.test(tail))return{type:"working",detail:"esc-to-interrupt affordance"};if(/^\s*[>\u203A]\s/m.test(tail))return{type:"idle",detail:"prompt detected"};return{type:"working",detail:"between-turns render"}}function mapCodexToExecutorState(state){switch(state){case"working":return"working";case"permission":return"permission";case"idle":return"idle";default:return"idle"}}class CodexProvider{name="codex";transport="api";buildSpawnCommand(ctx){let params=spawnContextToParams3(ctx);return buildCodexCommand(params)}async extractSession(_executor){return null}async detectState(executor){if(executor.state==="terminated"||executor.endedAt)return"terminated";let paneId=executor.tmuxPaneId;if(!paneId)return"working";try{let{capturePaneContent:capturePaneContent2}=await Promise.resolve().then(() => (init_tmux(),exports_tmux)),content=await capturePaneContent2(paneId,50);if(!content||!content.trim())return"working";let result2=detectCodexState(content);return mapCodexToExecutorState(result2.type)}catch{return"working"}}async terminate(executor){if(executor.pid)try{process.kill(executor.pid,"SIGTERM")}catch{}}canResume(){return!1}async deliverMessage(_executorId,_message){}}function spawnContextToParams3(ctx){return{provider:"codex",team:ctx.team,role:ctx.role,skill:ctx.skill,agentId:ctx.agentId,executorId:ctx.executorId,extraArgs:ctx.extraArgs}}var init_codex=__esm(()=>{init_provider_adapters()});function getProvider(name){return providers.get(name)}var providers,claude,codex,appPty,claudeSdk;var init_registry2=__esm(()=>{init_app_pty();init_claude_code();init_claude_sdk();init_codex();providers=new Map;claude=new ClaudeCodeProvider,codex=new CodexProvider,appPty=new AppPtyProvider,claudeSdk=new ClaudeSdkProvider;providers.set("claude",claude);providers.set("codex",codex);providers.set("app-pty",appPty);providers.set("claude-sdk",claudeSdk)});var exports_wish_state={};__export(exports_wish_state,{wipeState:()=>wipeState,startGroup:()=>startGroup,resolveRepoPath:()=>resolveRepoPath,resetInProgressGroups:()=>resetInProgressGroups,resetGroup:()=>resetGroup,isWishComplete:()=>isWishComplete,getState:()=>getState,getOrCreateState:()=>getOrCreateState,getGroupState:()=>getGroupState,findGroupByAssignee:()=>findGroupByAssignee,findAnyGroupByAssignee:()=>findAnyGroupByAssignee,createState:()=>createState,computeGroupsSignature:()=>computeGroupsSignature,completeGroup:()=>completeGroup,WishStateMismatchError:()=>WishStateMismatchError});import{execSync as execSync9}from"child_process";import{createHash as createHash5}from"crypto";import{existsSync as existsSync33}from"fs";import{dirname as dirname9}from"path";function computeGroupsSignature(groups){let canonical=groups.map((g)=>({name:g.name,dependsOn:[...g.dependsOn??[]].sort()})).sort((a,b2)=>a.name<b2.name?-1:a.name>b2.name?1:0);return createHash5("sha256").update(JSON.stringify(canonical)).digest("hex")}function diffGroups(prev,next){let prevMap=new Map(prev.map((g)=>[g.name,[...g.dependsOn??[]].sort()])),nextMap=new Map(next.map((g)=>[g.name,[...g.dependsOn??[]].sort()])),added=[],removed=[],changed=[];for(let[name,deps]of nextMap)if(!prevMap.has(name))added.push(name);else if(JSON.stringify(prevMap.get(name))!==JSON.stringify(deps))changed.push(name);for(let name of prevMap.keys())if(!nextMap.has(name))removed.push(name);return{added:added.sort(),removed:removed.sort(),changed:changed.sort()}}function normalizeGitPath(path3){if(process.platform!=="darwin")return path3;if(!path3.startsWith("/private/"))return path3;let logicalPath=path3.slice(8);return existsSync33(logicalPath)?logicalPath:path3}function resolveRepoPath(cwd){if(cwd)return cwd;try{let currentDir=process.cwd(),commonDir=execSync9("git rev-parse --path-format=absolute --git-common-dir",{encoding:"utf-8",stdio:["pipe","pipe","pipe"],env:{...process.env,GIT_CEILING_DIRECTORIES:dirname9(currentDir)}}).trim();return normalizeGitPath(dirname9(commonDir))}catch{return normalizeGitPath(process.cwd())}}function wishFilePath(slug){return`.genie/wishes/${slug}/WISH.md`}function toISO(v){if(v==null)return;if(v instanceof Date)return v.toISOString();return String(v)}async function findParent(sql,slug,repoPath){let wishFile=wishFilePath(slug),rows=await sql`
|
|
1547
1549
|
SELECT * FROM tasks
|
|
1548
1550
|
WHERE wish_file = ${wishFile} AND repo_path = ${repoPath} AND parent_id IS NULL
|
|
1549
1551
|
LIMIT 1
|
|
@@ -1666,7 +1668,7 @@ ${otherList}
|
|
|
1666
1668
|
`,await sql`
|
|
1667
1669
|
DELETE FROM task_actors WHERE task_id = ANY(${ids}) AND role = 'assignee'
|
|
1668
1670
|
`,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(`
|
|
1669
|
-
`));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 readFile9}from"fs/promises";import{join as join40}from"path";import{promisify as promisify2}from"util";async function resolveParentSession(_repoPath,team){let leaderName=await resolveLeaderName(team),sanitized=sanitizeTeamName(team),leaderAgent=await getAgentByName(leaderName,sanitized).catch(()=>null);if(leaderAgent){let decision=await shouldResume(leaderAgent.id).catch(()=>null);if(decision?.sessionId)return decision.sessionId}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 liveStates=new Set(["running","idle","working","permission","question"]);if(currentExec.tmuxPaneId&&liveStates.has(currentExec.state)&&await isPaneAlive(currentExec.tmuxPaneId)){recordAuditEvent("executor",currentExec.id,"tried_to_kill_live_executor",process.env.GENIE_AGENT_NAME??"cli",{agent_id:agentId,state:currentExec.state,pane_id:currentExec.tmuxPaneId,reason:"live_pane_guard"}).catch(()=>{});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),teamConfig=await getTeam(team),session=(Boolean(process.env.TMUX)?await getCurrentSessionName():null)??teamConfig?.tmuxSessionName??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,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(
|
|
1671
|
+
`));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 readFile9}from"fs/promises";import{join as join40}from"path";import{promisify as promisify2}from"util";async function resolveParentSession(_repoPath,team){let leaderName=await resolveLeaderName(team),sanitized=sanitizeTeamName(team),leaderAgent=await getAgentByName(leaderName,sanitized).catch(()=>null);if(leaderAgent){let decision=await shouldResume(leaderAgent.id).catch(()=>null);if(decision?.sessionId)return decision.sessionId}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 liveStates=new Set(["running","idle","working","permission","question"]);if(currentExec.tmuxPaneId&&liveStates.has(currentExec.state)&&await isPaneAlive(currentExec.tmuxPaneId)){recordAuditEvent("executor",currentExec.id,"tried_to_kill_live_executor",process.env.GENIE_AGENT_NAME??"cli",{agent_id:agentId,state:currentExec.state,pane_id:currentExec.tmuxPaneId,reason:"live_pane_guard"}).catch(()=>{});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),teamConfig=await getTeam(team),session=(Boolean(process.env.TMUX)?await getCurrentSessionName():null)??teamConfig?.tmuxSessionName??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,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(await registerNativeTeamMember(team,agentName,template,paneId,repoPath,spawnColor,resumeSessionId),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=join40(repoPath,".genie","wishes",slug,"WISH.md"),groupSection="";try{let wishContent=await readFile9(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:
|
|
1670
1672
|
${groupSection}`:"","",gitLog?`Last git log:
|
|
1671
1673
|
${gitLog}`:"","",gitStatus?`Uncommitted changes:
|
|
1672
1674
|
${gitStatus}`:"","","Pick up where you left off. Read the wish file for full context."].filter(Boolean).join(`
|
|
@@ -1974,11 +1976,11 @@ exec ${fullCommand}
|
|
|
1974
1976
|
Agent "${ctx.workerId}" SDK session ended.`),ctx.workerId}async function launchInlineSpawn(ctx){let nt=ctx.validated.nativeTeam,paneId="inline",workerRegistered=!1,workerEntry;try{if(ctx.agentIdentityId&&ctx.executorId)await createAndLinkExecutor2(ctx.agentIdentityId,ctx.validated.provider,resolveExecutorTransport2(ctx.validated.provider,"inline"),{id:ctx.executorId,claudeSessionId:ctx.claudeSessionId??null,state:"spawning",repoPath:ctx.cwd});if(workerEntry=await registerSpawnWorker(ctx,"inline"),workerRegistered=!0,await notifySpawnJoin(ctx,"inline"),console.log(`Agent "${ctx.workerId}" starting inline...`),console.log(` Provider: ${ctx.launch.provider} | Team: ${ctx.validated.team} | Role: ${ctx.validated.role??"-"}`),nt?.enabled)console.log(` Native: enabled | AgentID: ${workerEntry.nativeAgentId}`);console.log("")}catch(err){throw await rollbackSpawn(ctx,{workerRegistered}),err}let{spawnSync:spawnSync5}=__require("child_process"),envVars={...process.env,...ctx.launch.env??{}},result2=spawnSync5("sh",["-c",ctx.launch.command],{env:envVars,stdio:"inherit"});if(result2.error)throw await rollbackSpawn(ctx,{workerRegistered}),result2.error;if(ctx.agentIdentityId&&ctx.executorId)await updateExecutorState(ctx.executorId,"done").catch(()=>{});if(await unregister(ctx.workerId),nt?.enabled&&ctx.agentName)await clearNativeInbox(ctx.validated.team,ctx.agentName).catch(()=>{}),await unregisterNativeMember(ctx.validated.team,ctx.agentName).catch(()=>{});return console.log(`
|
|
1975
1977
|
Agent "${ctx.workerId}" session ended.`),process.exit(result2.status??0)}async function findDeadResumable(team,role){let prefiltered=(await list()).filter((w)=>w.role===role&&w.team===team&&w.provider==="claude"&&w.transport==="tmux"),candidate=null;for(let w of prefiltered)if((await getCurrentExecutor(w.id))?.claudeSessionId){candidate=w;break}if(!candidate)return null;return await isPaneAliveOrDead(candidate.paneId)?null:candidate}async function rejectDuplicateRole(team,role){let existing=await list();for(let w of existing)if(w.role===role&&w.team===team){let alive=await resolveWorkerLivenessByTransport(w);if(alive&&w.session&&/^%\d+$/.test(w.paneId)){if(await getPaneSession(w.paneId)!==w.session){await unregister(w.id);continue}console.error(`Error: Worker with role "${role}" already exists in team "${team}" (state: ${w.state}, pane: ${w.paneId})
|
|
1976
1978
|
Use a different --role name for a second worker, e.g.: --role ${role}-2`),process.exit(1)}if(alive)console.error(`Error: Worker with role "${role}" already exists in team "${team}" (state: ${w.state}, pane: ${w.paneId})
|
|
1977
|
-
Use a different --role name for a second worker, e.g.: --role ${role}-2`),process.exit(1);await unregister(w.id)}}async function getPaneSession(paneId){try{return(await executeTmux2(`display-message -t '${paneId}' -p '#{session_name}'`)).trim()||null}catch{return null}}async function resolveNativeTeam(team,_repoPath,options){let leaderName=await resolveLeaderName(team),sanitizedTeam=sanitizeTeamName(team),leaderAgent=await getAgentByName(leaderName,sanitizedTeam).catch(()=>null),parentSessionId;if(leaderAgent)parentSessionId=(await shouldResume(leaderAgent.id).catch(()=>null))?.sessionId;if(!parentSessionId)parentSessionId=await discoverClaudeParentSessionId()??`genie-${team}`;await ensureNativeTeam(team,`Genie team: ${team}`,parentSessionId);let spawnColor=options.color??await assignColor(team),nativeTeam;if(options.provider==="claude")nativeTeam={enabled:!0,parentSessionId,color:spawnColor,agentType:options.role??"general-purpose",planModeRequired:options.planMode,permissionMode:options.permissionMode,agentName:options.role};return{parentSessionId,spawnColor,nativeTeam}}async function resolveAgentForSpawn(name,options){let resolved=await resolve5(name);if(!resolved)console.error(`Error: Agent "${name}" not found in directory or built-ins.`),console.error(` Register with: genie dir add ${name} --dir <path>`),console.error(" Or use a built-in: engineer, reviewer, qa, fix, ..."),process.exit(1);let entry2=resolved.entry,identityPath=null;if(resolved.builtin)identityPath=resolveBuiltinAgentPath(name);else if(entry2.dir)identityPath=loadIdentity(entry2);let repoPath=resolveAgentWorkingDir(entry2,options.cwd),model=options.model;if(!model){let ctx=buildSpawnResolveContext(name,entry2);model=resolveField(entry2,"model",ctx)}return{entry:entry2,repoPath,identityPath,model}}function buildSpawnResolveContext(agentName,_entry){let ctx={};try{let ws=findWorkspace();if(ws){let wsConfig=getWorkspaceConfig(ws.root);ctx.workspaceDefaults=wsConfig.agents?.defaults}}catch{}if(agentName.includes("/")){let parentName=agentName.split("/")[0];try{let{readFileSync:readFileSync21,existsSync:existsSync34}=__require("fs"),{join:join41}=__require("path"),ws=findWorkspace();if(ws){let parentAgentsMd=join41(ws.root,"agents",parentName,"AGENTS.md");if(existsSync34(parentAgentsMd)){let{parseFrontmatter:parseFrontmatter3}=(init_frontmatter(),__toCommonJS(exports_frontmatter)),parentFm=parseFrontmatter3(readFileSync21(parentAgentsMd,"utf-8"));ctx.parent={name:parentName,fields:parentFm}}}}catch{}}return ctx}function resolveAgentWorkingDir(entry2,explicitCwd){if(explicitCwd)return explicitCwd;if(entry2.dir)return entry2.dir;let repo=entry2.repo;if(repo&&__require("fs").existsSync(repo))return repo;return process.cwd()}async function buildSpawnParams2(name,team,options,agent,preassignedSessionId){let resolvedProvider=options.provider??agent.entry.provider??"claude",params={provider:resolvedProvider,team,role:name,skill:options.skill,extraArgs:options.extraArgs,model:agent.model,systemPromptFile:agent.identityPath??void 0,promptMode:agent.entry.promptMode,initialPrompt:options.prompt??options.initialPrompt,newWindow:options.newWindow,windowTarget:options.window},{parentSessionId,spawnColor,nativeTeam}=await resolveNativeTeam(team,agent.repoPath,{...options,provider:resolvedProvider,role:name});if(nativeTeam)params.nativeTeam=nativeTeam;try{let{injectTeamHooks:injectTeamHooks2}=await Promise.resolve().then(() => (init_inject(),exports_inject));if(await injectTeamHooks2(team))console.log(` Hooks: injected genie hook dispatch into team "${team}"`)}catch(err){console.warn(`Warning: could not inject hooks for team "${team}": ${err instanceof Error?err.message:err}`)}if(params.provider==="claude")params.sessionId=preassignedSessionId??crypto.randomUUID();if(params.provider==="claude"){if(await startOtelReceiver())params.otelPort=getOtelPort(),params.otelLogPrompts=!0}return{params,parentSessionId,spawnColor}}async function maybeStartOtelRelay(nt,validated,insideTmux){if(!nt?.enabled&&validated.provider==="codex"&&insideTmux)return ensureCodexOtelConfig(),await ensureOtelRelay(validated.team);return!1}function buildSdkRuntimeExtra(options){let extra={};if(options.sdkMaxTurns!=null)extra.maxTurns=options.sdkMaxTurns;if(options.sdkMaxBudget!=null)extra.maxBudgetUsd=options.sdkMaxBudget;if(options.sdkEffort)extra.effort=options.sdkEffort;if(options.sdkResume)extra.resume=options.sdkResume;return Object.keys(extra).length>0?extra:void 0}async function dispatchSpawn(ctx,validated,options,agent,insideTmux){if(validated.provider==="claude-sdk"){let streamFormat=options.streamFormat??"text",streamOpts=options.stream||options.sdkStream?{stream:!0,streamFormat}:void 0;return await launchSdkSpawn(ctx,agent.entry.permissions,streamOpts,agent.entry.sdk,buildSdkRuntimeExtra(options))}if(insideTmux)return await launchTmuxSpawn(ctx);return await launchInlineSpawn(ctx)}async function resolveTeamName3(opts){if(opts.explicitTeam)return opts.explicitTeam;if(opts.entryTeam)return opts.entryTeam;let env=opts.env??process.env;if(env.GENIE_TEAM)return env.GENIE_TEAM;return await(opts.discover??discoverTeamName)()??null}async function pickParallelShortId(baseName,team,uuid){if(!UUID_REGEX.test(uuid))throw Error(`pickParallelShortId: expected a well-formed UUID (8-4-4-4-12 hex), got ${JSON.stringify(uuid)}`);let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2();for(let k=4;k<=uuid.length;k++){let slice=uuid.slice(0,k),id=`${baseName}-${slice}`;if((await sql`
|
|
1979
|
+
Use a different --role name for a second worker, e.g.: --role ${role}-2`),process.exit(1);await unregister(w.id)}}async function getPaneSession(paneId){try{return(await executeTmux2(`display-message -t '${paneId}' -p '#{session_name}'`)).trim()||null}catch{return null}}async function resolveNativeTeam(team,_repoPath,options){let leaderName=await resolveLeaderName(team),sanitizedTeam=sanitizeTeamName(team),leaderAgent=await getAgentByName(leaderName,sanitizedTeam).catch(()=>null),parentSessionId;if(leaderAgent)parentSessionId=(await shouldResume(leaderAgent.id).catch(()=>null))?.sessionId;if(!parentSessionId)parentSessionId=await discoverClaudeParentSessionId()??`genie-${team}`;await ensureNativeTeam(team,`Genie team: ${team}`,parentSessionId);let spawnColor=options.color??await assignColor(team),nativeTeam;if(options.provider==="claude")nativeTeam={enabled:!0,parentSessionId,color:spawnColor,agentType:options.role??"general-purpose",planModeRequired:options.planMode,permissionMode:options.permissionMode,agentName:options.role};return{parentSessionId,spawnColor,nativeTeam}}async function resolveAgentForSpawn(name,options){let resolved=await resolve5(name);if(!resolved)console.error(`Error: Agent "${name}" not found in directory or built-ins.`),console.error(` Register with: genie dir add ${name} --dir <path>`),console.error(" Or use a built-in: engineer, reviewer, qa, fix, ..."),process.exit(1);let entry2=resolved.entry,identityPath=null;if(resolved.builtin)identityPath=resolveBuiltinAgentPath(name);else if(entry2.dir)identityPath=loadIdentity(entry2);let repoPath=resolveAgentWorkingDir(entry2,options.cwd),model=options.model;if(!model){let ctx=buildSpawnResolveContext(name,entry2);model=resolveField(entry2,"model",ctx)}return{entry:entry2,repoPath,identityPath,model}}function buildSpawnResolveContext(agentName,_entry){let ctx={};try{let ws=findWorkspace();if(ws){let wsConfig=getWorkspaceConfig(ws.root);ctx.workspaceDefaults=wsConfig.agents?.defaults}}catch{}if(agentName.includes("/")){let parentName=agentName.split("/")[0];try{let{readFileSync:readFileSync21,existsSync:existsSync34}=__require("fs"),{join:join41}=__require("path"),ws=findWorkspace();if(ws){let parentAgentsMd=join41(ws.root,"agents",parentName,"AGENTS.md");if(existsSync34(parentAgentsMd)){let{parseFrontmatter:parseFrontmatter3}=(init_frontmatter(),__toCommonJS(exports_frontmatter)),parentFm=parseFrontmatter3(readFileSync21(parentAgentsMd,"utf-8"));ctx.parent={name:parentName,fields:parentFm}}}}catch{}}return ctx}function resolveAgentWorkingDir(entry2,explicitCwd){if(explicitCwd)return explicitCwd;if(entry2.dir)return entry2.dir;let repo=entry2.repo;if(repo&&__require("fs").existsSync(repo))return repo;return process.cwd()}async function buildSpawnParams2(name,team,options,agent,preassignedSessionId,agentTemplate){let resolvedProvider=options.provider??agent.entry.provider??"claude",params={provider:resolvedProvider,team,role:name,agentTemplate:agentTemplate??name,skill:options.skill,extraArgs:options.extraArgs,model:agent.model,systemPromptFile:agent.identityPath??void 0,promptMode:agent.entry.promptMode,initialPrompt:options.prompt??options.initialPrompt,newWindow:options.newWindow,windowTarget:options.window},{parentSessionId,spawnColor,nativeTeam}=await resolveNativeTeam(team,agent.repoPath,{...options,provider:resolvedProvider,role:name});if(nativeTeam)params.nativeTeam=nativeTeam;try{let{injectTeamHooks:injectTeamHooks2}=await Promise.resolve().then(() => (init_inject(),exports_inject));if(await injectTeamHooks2(team))console.log(` Hooks: injected genie hook dispatch into team "${team}"`)}catch(err){console.warn(`Warning: could not inject hooks for team "${team}": ${err instanceof Error?err.message:err}`)}if(params.provider==="claude")params.sessionId=preassignedSessionId??crypto.randomUUID();if(params.provider==="claude"){if(await startOtelReceiver())params.otelPort=getOtelPort(),params.otelLogPrompts=!0}return{params,parentSessionId,spawnColor}}async function maybeStartOtelRelay(nt,validated,insideTmux){if(!nt?.enabled&&validated.provider==="codex"&&insideTmux)return ensureCodexOtelConfig(),await ensureOtelRelay(validated.team);return!1}function buildSdkRuntimeExtra(options){let extra={};if(options.sdkMaxTurns!=null)extra.maxTurns=options.sdkMaxTurns;if(options.sdkMaxBudget!=null)extra.maxBudgetUsd=options.sdkMaxBudget;if(options.sdkEffort)extra.effort=options.sdkEffort;if(options.sdkResume)extra.resume=options.sdkResume;return Object.keys(extra).length>0?extra:void 0}async function dispatchSpawn(ctx,validated,options,agent,insideTmux){if(validated.provider==="claude-sdk"){let streamFormat=options.streamFormat??"text",streamOpts=options.stream||options.sdkStream?{stream:!0,streamFormat}:void 0;return await launchSdkSpawn(ctx,agent.entry.permissions,streamOpts,agent.entry.sdk,buildSdkRuntimeExtra(options))}if(insideTmux)return await launchTmuxSpawn(ctx);return await launchInlineSpawn(ctx)}async function resolveTeamName3(opts){if(opts.explicitTeam)return opts.explicitTeam;if(opts.entryTeam)return opts.entryTeam;let env=opts.env??process.env;if(env.GENIE_TEAM)return env.GENIE_TEAM;return await(opts.discover??discoverTeamName)()??null}async function pickParallelShortId(baseName,team,uuid){if(!UUID_REGEX.test(uuid))throw Error(`pickParallelShortId: expected a well-formed UUID (8-4-4-4-12 hex), got ${JSON.stringify(uuid)}`);let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2();for(let k=4;k<=uuid.length;k++){let slice=uuid.slice(0,k),id=`${baseName}-${slice}`;if((await sql`
|
|
1978
1980
|
SELECT id FROM agents WHERE id = ${id} LIMIT 1
|
|
1979
1981
|
`).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())`
|
|
1980
1982
|
SELECT id, pane_id, team FROM agents WHERE id = ${name} LIMIT 1
|
|
1981
|
-
`;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}}let alive=!1;if(existing.pane_id)try{alive=await isAliveFn({id:existing.id,paneId:existing.pane_id})}catch(err){let message=err instanceof Error?err.message:String(err);if(err instanceof TmuxUnreachableError||message.includes("no server running")||message.includes("server exited")||message.includes("error connecting"))alive=!1;else throw err}if(!alive)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){let sessionShort=(await getCurrentExecutor(deadResumable.id))?.claudeSessionId?.slice(0,8)??"unknown";return console.log(`Resuming existing session for "${effectiveRole}" (session: ${sessionShort}...)`),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 nt=params.nativeTeam,agentName=nt?.agentName??effectiveRole,agentIdentity=await findOrCreateAgent(agentName,team,effectiveRole);await terminateActiveExecutorWithCleanup(agentIdentity.id);let executorId=crypto.randomUUID();params.agentId=agentIdentity.id,params.executorId=executorId;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),now=new Date().toISOString(),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&&!process.env.GENIE_TEAM&&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:execSync10}=__require("child_process"),currentPane=execSync10(genieTmuxCmd("display-message -p '#{pane_id}'"),{encoding:"utf-8"}).trim();if(w.paneId&&/^(%\d+|inline)$/.test(w.paneId)&&w.paneId!==currentPane)execSync10(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:join41}=__require("path"),{homedir:homedir27}=__require("os"),{unlinkSync:unlinkSync9}=__require("fs"),relayDir=join41(homedir27(),".genie","relay");for(let suffix of["-pane","-meta"])try{unlinkSync9(join41(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}if(!w.currentExecutorId)console.error(`Cannot stop agent "${w.id}" \u2014 no active executor linked.`),console.error(" The agent may have already exited, or was spawned without"),console.error(" executor tracking (e.g. native Claude Code teammate)."),console.error(` To remove the agent row, use: genie kill ${w.id}`),process.exit(1);let{suspendWorker:suspendWorker2}=await Promise.resolve().then(() => (init_idle_timeout(),exports_idle_timeout));if(await suspendWorker2(w.currentExecutorId)){console.log(`Agent "${w.id}" stopped.`);let stoppedExecutor=await getExecutor(w.currentExecutorId);if(stoppedExecutor?.claudeSessionId)console.log(` Session preserved: ${stoppedExecutor.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}" \u2014 executor ${w.currentExecutorId} not found in executors table.`),console.error(" This indicates a stale current_executor_id FK. Try:"),console.error(` genie kill ${w.id} # force remove the agent row`),process.exit(1)}async function isResumeEligible(w){if(!(await getCurrentExecutor(w.id))?.claudeSessionId)return!1;if(w.state==="done")return!1;let paneAlive=await isPaneAliveOrDead(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),decision=await shouldResume(w.id);if(!decision.resume||!decision.sessionId){let errReason=decision.reason==="unknown_agent"?"no_executor":"no_session_id";throw new MissingResumeSessionError(w.id,void 0,errReason)}let _sessionId=decision.sessionId;if(await isPaneAliveOrDead(w.paneId)){console.log(`Agent "${w.id}" is already running (pane ${w.paneId} is alive).`);return}await resumeAgent(w,resumeOpts)}async function resolveAgentForRecover(name){let exact=await get(name);if(exact)return exact;let masterRow=await get(`dir:${name}`);if(masterRow)return masterRow;let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2(),byCustomName=await sql`SELECT * FROM agents WHERE custom_name = ${name} LIMIT 2`;if(byCustomName.length===1)return await get(byCustomName[0].id);if(byCustomName.length>1){let ids=byCustomName.map((r)=>r.id).join(", ");throw Error(`Multiple agents with custom_name "${name}": ${ids}. Pass the full id (e.g. dir:${name}).`)}let byRole=await sql`SELECT * FROM agents WHERE role = ${name} LIMIT 2`;if(byRole.length===1)return await get(byRole[0].id);if(byRole.length>1){let ids=byRole.map((r)=>r.id).join(", ");throw Error(`Multiple agents with role "${name}": ${ids}. Pass the full id.`)}throw new RecoverAgentNotFoundError(name)}async function recoverSurgery(agentId){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2(),flipped=await sql`
|
|
1983
|
+
`;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}}let alive=!1;if(existing.pane_id)try{alive=await isAliveFn({id:existing.id,paneId:existing.pane_id})}catch(err){let message=err instanceof Error?err.message:String(err);if(err instanceof TmuxUnreachableError||message.includes("no server running")||message.includes("server exited")||message.includes("error connecting"))alive=!1;else throw err}if(!alive)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){let sessionShort=(await getCurrentExecutor(deadResumable.id))?.claudeSessionId?.slice(0,8)??"unknown";return console.log(`Resuming existing session for "${effectiveRole}" (session: ${sessionShort}...)`),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,name);if(!params.name)params.name=`${params.team}-${effectiveRole}`;let nt=params.nativeTeam,agentName=nt?.agentName??effectiveRole,agentIdentity=await findOrCreateAgent(agentName,team,effectiveRole);await terminateActiveExecutorWithCleanup(agentIdentity.id);let executorId=crypto.randomUUID();params.agentId=agentIdentity.id,params.executorId=executorId;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),now=new Date().toISOString(),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&&!process.env.GENIE_TEAM&&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:execSync10}=__require("child_process"),currentPane=execSync10(genieTmuxCmd("display-message -p '#{pane_id}'"),{encoding:"utf-8"}).trim();if(w.paneId&&/^(%\d+|inline)$/.test(w.paneId)&&w.paneId!==currentPane)execSync10(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:join41}=__require("path"),{homedir:homedir27}=__require("os"),{unlinkSync:unlinkSync9}=__require("fs"),relayDir=join41(homedir27(),".genie","relay");for(let suffix of["-pane","-meta"])try{unlinkSync9(join41(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}if(!w.currentExecutorId)console.error(`Cannot stop agent "${w.id}" \u2014 no active executor linked.`),console.error(" The agent may have already exited, or was spawned without"),console.error(" executor tracking (e.g. native Claude Code teammate)."),console.error(` To remove the agent row, use: genie kill ${w.id}`),process.exit(1);let{suspendWorker:suspendWorker2}=await Promise.resolve().then(() => (init_idle_timeout(),exports_idle_timeout));if(await suspendWorker2(w.currentExecutorId)){console.log(`Agent "${w.id}" stopped.`);let stoppedExecutor=await getExecutor(w.currentExecutorId);if(stoppedExecutor?.claudeSessionId)console.log(` Session preserved: ${stoppedExecutor.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}" \u2014 executor ${w.currentExecutorId} not found in executors table.`),console.error(" This indicates a stale current_executor_id FK. Try:"),console.error(` genie kill ${w.id} # force remove the agent row`),process.exit(1)}async function isResumeEligible(w){if(!(await getCurrentExecutor(w.id))?.claudeSessionId)return!1;if(w.state==="done")return!1;let paneAlive=await isPaneAliveOrDead(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),decision=await shouldResume(w.id);if(!decision.resume||!decision.sessionId){let errReason=decision.reason==="unknown_agent"?"no_executor":"no_session_id";throw new MissingResumeSessionError(w.id,void 0,errReason)}let _sessionId=decision.sessionId;if(await isPaneAliveOrDead(w.paneId)){console.log(`Agent "${w.id}" is already running (pane ${w.paneId} is alive).`);return}await resumeAgent(w,resumeOpts)}async function resolveAgentForRecover(name){let exact=await get(name);if(exact)return exact;let masterRow=await get(`dir:${name}`);if(masterRow)return masterRow;let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2(),byCustomName=await sql`SELECT * FROM agents WHERE custom_name = ${name} LIMIT 2`;if(byCustomName.length===1)return await get(byCustomName[0].id);if(byCustomName.length>1){let ids=byCustomName.map((r)=>r.id).join(", ");throw Error(`Multiple agents with custom_name "${name}": ${ids}. Pass the full id (e.g. dir:${name}).`)}let byRole=await sql`SELECT * FROM agents WHERE role = ${name} LIMIT 2`;if(byRole.length===1)return await get(byRole[0].id);if(byRole.length>1){let ids=byRole.map((r)=>r.id).join(", ");throw Error(`Multiple agents with role "${name}": ${ids}. Pass the full id.`)}throw new RecoverAgentNotFoundError(name)}async function recoverSurgery(agentId){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2(),flipped=await sql`
|
|
1982
1984
|
UPDATE agents SET auto_resume = true
|
|
1983
1985
|
WHERE id = ${agentId} AND auto_resume = false
|
|
1984
1986
|
RETURNING id
|
|
@@ -3765,7 +3767,7 @@ Bus `);for(let i2=1;i2<parts.length;i2++){let usb2=parseLinuxUsb(parts[i2]);resu
|
|
|
3765
3767
|
`),service=util4.getValue(lines,"Service",":"),errorCode=util4.getValue(lines,"ConfigManagerErrorCode",":");if(util4.getValue(lines,"PNPClass",":").toLowerCase()==="bluetooth"&&errorCode==="0"&&service==="")result2.push(parseWindowsBluetooth(lines))});if(callback)callback(result2);resolve16(result2)});if(_freebsd||_netbsd||_openbsd||_sunos)resolve16(null)})})}exports.bluetoothDevices=bluetoothDevices});var require_lib5=__commonJS((exports)=>{var lib_version=require_package().version,util4=require_util3(),system=require_system(),osInfo=require_osinfo(),cpu=require_cpu(),memory=require_memory(),battery=require_battery(),graphics=require_graphics(),filesystem=require_filesystem(),network=require_network(),wifi=require_wifi(),processes=require_processes(),users=require_users(),internet=require_internet(),docker=require_docker(),vbox=require_virtualbox(),printer=require_printer(),usb=require_usb(),audio=require_audio(),bluetooth=require_bluetooth(),_platform=process.platform,_windows=_platform==="win32",_freebsd=_platform==="freebsd",_openbsd=_platform==="openbsd",_netbsd=_platform==="netbsd",_sunos=_platform==="sunos";if(_windows)util4.getCodepage(),util4.getPowershell();function version(){return lib_version}function getStaticData(callback){return new Promise((resolve16)=>{process.nextTick(()=>{let data={};data.version=version(),Promise.all([system.system(),system.bios(),system.baseboard(),system.chassis(),osInfo.osInfo(),osInfo.uuid(),osInfo.versions(),cpu.cpu(),cpu.cpuFlags(),graphics.graphics(),network.networkInterfaces(),memory.memLayout(),filesystem.diskLayout(),audio.audio(),bluetooth.bluetoothDevices(),usb.usb(),printer.printer()]).then((res)=>{if(data.system=res[0],data.bios=res[1],data.baseboard=res[2],data.chassis=res[3],data.os=res[4],data.uuid=res[5],data.versions=res[6],data.cpu=res[7],data.cpu.flags=res[8],data.graphics=res[9],data.net=res[10],data.memLayout=res[11],data.diskLayout=res[12],data.audio=res[13],data.bluetooth=res[14],data.usb=res[15],data.printer=res[16],callback)callback(data);resolve16(data)})})})}function getDynamicData(srv,iface,callback){if(util4.isFunction(iface))callback=iface,iface="";if(util4.isFunction(srv))callback=srv,srv="";return new Promise((resolve16)=>{process.nextTick(()=>{iface=iface||network.getDefaultNetworkInterface(),srv=srv||"";let functionProcessed=(()=>{let totalFunctions=15;if(_windows)totalFunctions=13;if(_freebsd||_openbsd||_netbsd)totalFunctions=11;if(_sunos)totalFunctions=6;return function(){if(--totalFunctions===0){if(callback)callback(data);resolve16(data)}}})(),data={};if(data.time=osInfo.time(),data.node=process.versions.node,data.v8=process.versions.v8,cpu.cpuCurrentSpeed().then((res)=>{data.cpuCurrentSpeed=res,functionProcessed()}),users.users().then((res)=>{data.users=res,functionProcessed()}),processes.processes().then((res)=>{data.processes=res,functionProcessed()}),cpu.currentLoad().then((res)=>{data.currentLoad=res,functionProcessed()}),!_sunos)cpu.cpuTemperature().then((res)=>{data.temp=res,functionProcessed()});if(!_openbsd&&!_freebsd&&!_netbsd&&!_sunos)network.networkStats(iface).then((res)=>{data.networkStats=res,functionProcessed()});if(!_sunos)network.networkConnections().then((res)=>{data.networkConnections=res,functionProcessed()});if(memory.mem().then((res)=>{data.mem=res,functionProcessed()}),!_sunos)battery().then((res)=>{data.battery=res,functionProcessed()});if(!_sunos)processes.services(srv).then((res)=>{data.services=res,functionProcessed()});if(!_sunos)filesystem.fsSize().then((res)=>{data.fsSize=res,functionProcessed()});if(!_windows&&!_openbsd&&!_freebsd&&!_netbsd&&!_sunos)filesystem.fsStats().then((res)=>{data.fsStats=res,functionProcessed()});if(!_windows&&!_openbsd&&!_freebsd&&!_netbsd&&!_sunos)filesystem.disksIO().then((res)=>{data.disksIO=res,functionProcessed()});if(!_openbsd&&!_freebsd&&!_netbsd&&!_sunos)wifi.wifiNetworks().then((res)=>{data.wifiNetworks=res,functionProcessed()});internet.inetLatency().then((res)=>{data.inetLatency=res,functionProcessed()})})})}function getAllData(srv,iface,callback){return new Promise((resolve16)=>{process.nextTick(()=>{let data={};if(iface&&util4.isFunction(iface)&&!callback)callback=iface,iface="";if(srv&&util4.isFunction(srv)&&!iface&&!callback)callback=srv,srv="",iface="";getStaticData().then((res)=>{data=res,getDynamicData(srv,iface).then((res2)=>{for(let key in res2)if({}.hasOwnProperty.call(res2,key))data[key]=res2[key];if(callback)callback(data);resolve16(data)})})})})}function get3(valueObject,callback){return new Promise((resolve16)=>{process.nextTick(()=>{let allPromises=Object.keys(valueObject).filter((func)=>({}).hasOwnProperty.call(exports,func)).map((func)=>{let params=valueObject[func].substring(valueObject[func].lastIndexOf("(")+1,valueObject[func].lastIndexOf(")")),funcWithoutParams=func.indexOf(")")>=0?func.split(")")[1].trim():func;if(funcWithoutParams=func.indexOf("|")>=0?func.split("|")[0].trim():funcWithoutParams,params)return exports[funcWithoutParams](params);else return exports[funcWithoutParams]("")});Promise.all(allPromises).then((data)=>{let result2={},i2=0;for(let key in valueObject)if({}.hasOwnProperty.call(valueObject,key)&&{}.hasOwnProperty.call(exports,key)&&data.length>i2){if(valueObject[key]==="*"||valueObject[key]==="all")result2[key]=data[i2];else{let keys=valueObject[key],filter="",filterParts=[];if(keys.indexOf(")")>=0)keys=keys.split(")")[1].trim();if(keys.indexOf("|")>=0)filter=keys.split("|")[1].trim(),filterParts=filter.split(":"),keys=keys.split("|")[0].trim();if(keys=keys.replace(/,/g," ").replace(/ +/g," ").split(" "),data[i2])if(Array.isArray(data[i2])){let partialArray=[];data[i2].forEach((element)=>{let partialRes={};if(keys.length===1&&(keys[0]==="*"||keys[0]==="all"))partialRes=element;else keys.forEach((k2)=>{if({}.hasOwnProperty.call(element,k2))partialRes[k2]=element[k2]});if(filter&&filterParts.length===2){if({}.hasOwnProperty.call(partialRes,filterParts[0].trim())){let val=partialRes[filterParts[0].trim()];if(typeof val==="number"){if(val===parseFloat(filterParts[1].trim()))partialArray.push(partialRes)}else if(typeof val==="string"){if(val.toLowerCase()===filterParts[1].trim().toLowerCase())partialArray.push(partialRes)}}}else partialArray.push(partialRes)}),result2[key]=partialArray}else{let partialRes={};keys.forEach((k2)=>{if({}.hasOwnProperty.call(data[i2],k2))partialRes[k2]=data[i2][k2]}),result2[key]=partialRes}else result2[key]={}}i2++}if(callback)callback(result2);resolve16(result2)})})})}function observe(valueObject,interval,callback){let _data=null;return setInterval(()=>{get3(valueObject).then((data)=>{if(JSON.stringify(_data)!==JSON.stringify(data))_data=Object.assign({},data),callback(data)})},interval)}exports.version=version;exports.system=system.system;exports.bios=system.bios;exports.baseboard=system.baseboard;exports.chassis=system.chassis;exports.time=osInfo.time;exports.osInfo=osInfo.osInfo;exports.versions=osInfo.versions;exports.shell=osInfo.shell;exports.uuid=osInfo.uuid;exports.cpu=cpu.cpu;exports.cpuFlags=cpu.cpuFlags;exports.cpuCache=cpu.cpuCache;exports.cpuCurrentSpeed=cpu.cpuCurrentSpeed;exports.cpuTemperature=cpu.cpuTemperature;exports.currentLoad=cpu.currentLoad;exports.fullLoad=cpu.fullLoad;exports.mem=memory.mem;exports.memLayout=memory.memLayout;exports.battery=battery;exports.graphics=graphics.graphics;exports.fsSize=filesystem.fsSize;exports.fsOpenFiles=filesystem.fsOpenFiles;exports.blockDevices=filesystem.blockDevices;exports.fsStats=filesystem.fsStats;exports.disksIO=filesystem.disksIO;exports.diskLayout=filesystem.diskLayout;exports.networkInterfaceDefault=network.networkInterfaceDefault;exports.networkGatewayDefault=network.networkGatewayDefault;exports.networkInterfaces=network.networkInterfaces;exports.networkStats=network.networkStats;exports.networkConnections=network.networkConnections;exports.wifiNetworks=wifi.wifiNetworks;exports.wifiInterfaces=wifi.wifiInterfaces;exports.wifiConnections=wifi.wifiConnections;exports.services=processes.services;exports.processes=processes.processes;exports.processLoad=processes.processLoad;exports.users=users.users;exports.inetChecksite=internet.inetChecksite;exports.inetLatency=internet.inetLatency;exports.dockerInfo=docker.dockerInfo;exports.dockerImages=docker.dockerImages;exports.dockerContainers=docker.dockerContainers;exports.dockerContainerStats=docker.dockerContainerStats;exports.dockerContainerProcesses=docker.dockerContainerProcesses;exports.dockerVolumes=docker.dockerVolumes;exports.dockerAll=docker.dockerAll;exports.vboxInfo=vbox.vboxInfo;exports.printer=printer.printer;exports.usb=usb.usb;exports.audio=audio.audio;exports.bluetoothDevices=bluetooth.bluetoothDevices;exports.getStaticData=getStaticData;exports.getDynamicData=getDynamicData;exports.getAllData=getAllData;exports.get=get3;exports.observe=observe;exports.powerShellStart=util4.powerShellStart;exports.powerShellRelease=util4.powerShellRelease});import os4 from"os";function toGB(bytes){return Math.round(bytes/1073741824*10)/10}function bar(percent,width){let p=Math.max(0,Math.min(100,percent)),filled=Math.round(p/100*width);return`[${"=".repeat(filled)}${"-".repeat(width-filled)}]`}function pickColor(percent){if(percent>90)return palette.error;if(percent>70)return palette.warning;return palette.accent}function SystemStatsView({stats:stats2}){if(!stats2)return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",paddingX:1,backgroundColor:palette.bgRaised,children:import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.accent,children:"genie"},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:` v${VERSION}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this);let{cpu,ram,swap,load:load3}=stats2,hotStr=cpu.hotCores.map((c)=>`#${c.id} ${c.load}%`).join(" ");return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",paddingX:1,backgroundColor:palette.bgRaised,children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.accent,children:"genie"},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:` v${VERSION}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:"CPU "},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:pickColor(cpu.combined),children:`${String(cpu.combined).padStart(3)}% ${bar(cpu.combined,8)}`},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:` ${cpu.coreCount}c`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:" hot "},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.warning,children:hotStr},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:"RAM "},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:pickColor(ram.percent),children:`${ram.usedGB}/${ram.totalGB}G ${bar(ram.percent,8)}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),swap.totalGB>0?import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:"SWP "},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:pickColor(swap.percent),children:`${swap.usedGB}/${swap.totalGB}G ${bar(swap.percent,8)}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this):null,import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:"Load "},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:pickColor(load3.percent),children:`${load3.percent}%`},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:` (${load3.busy}/${load3.total} busy)`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function SystemStats(){let[stats2,setStats]=import_react19.useState(null),mountedRef=import_react19.useRef(!0);return import_react19.useEffect(()=>{mountedRef.current=!0;async function refresh(){try{let[cpu,mem]=await Promise.all([import_systeminformation.default.currentLoad(),import_systeminformation.default.mem()]);if(!mountedRef.current)return;let coreCount=os4.cpus().length,avg1=os4.loadavg()[0],sorted=cpu.cpus.map((c,i2)=>({id:i2,load:Math.round(c.load)})).sort((a,b3)=>b3.load-a.load);setStats({cpu:{combined:Math.round(cpu.currentLoad),hotCores:sorted.slice(0,3),coreCount},ram:{usedGB:toGB(mem.active),totalGB:toGB(mem.total),percent:mem.total>0?Math.round(mem.active/mem.total*100):0},swap:{usedGB:toGB(mem.swapused),totalGB:toGB(mem.swaptotal),percent:mem.swaptotal>0?Math.round(mem.swapused/mem.swaptotal*100):0},load:{percent:coreCount>0?Math.round(avg1/coreCount*100):0,busy:Math.round(avg1*10)/10,total:coreCount}})}catch{}}refresh();let timer2=setInterval(refresh,3000);return()=>{mountedRef.current=!1,clearInterval(timer2)}},[]),import_jsx_dev_runtime2.jsxDEV(SystemStatsView,{stats:stats2},void 0,!1,void 0,this)}var import_react19,import_systeminformation;var init_SystemStats=__esm(()=>{init_version();init_theme2();init_jsx_dev_runtime();import_react19=__toESM(require_react_development(),1),import_systeminformation=__toESM(require_lib5(),1)});function validateName(name){if(name.length===0)return null;try{return validateBranchName(name),null}catch(err){return err instanceof Error?err.message:String(err)}}function TeamCreate({availableAgents,workspaceRoot,onConfirm,onCancel}){let[step,setStep]=import_react21.useState("name"),[teamName,setTeamName]=import_react21.useState(""),[selected,setSelected]=import_react21.useState(()=>new Set),[memberCursor,setMemberCursor]=import_react21.useState(0),nameError=import_react21.useMemo(()=>validateName(teamName),[teamName]),nameValid=teamName.length>0&&nameError===null,intent=import_react21.useMemo(()=>({kind:"create-team",name:teamName.length>0?teamName:"TEAM_NAME",repo:workspaceRoot}),[teamName,workspaceRoot]),handleNameChange=import_react21.useCallback((value)=>{setTeamName(value)},[]),advanceFromName=import_react21.useCallback(()=>{if(!nameValid)return;setStep("members")},[nameValid]),toggleMember=import_react21.useCallback((name)=>{setSelected((prev)=>{let next=new Set(prev);if(next.has(name))next.delete(name);else next.add(name);return next})},[]),confirmMembers=import_react21.useCallback(()=>{onConfirm({teamName,members:Array.from(selected)})},[onConfirm,teamName,selected]);return useKeyboard((key)=>{if(step==="name"){handleNameStepKey(key,{onCancel,nameValid,advanceFromName});return}handleMembersStepKey(key,{availableAgents,memberCursor,setStep,setMemberCursor,toggleMember,confirmMembers})}),import_jsx_dev_runtime2.jsxDEV("box",{position:"absolute",width:"100%",height:"100%",justifyContent:"center",alignItems:"center",backgroundColor:palette.bgOverlay,children:import_jsx_dev_runtime2.jsxDEV("box",{border:!0,borderColor:palette.borderActive,backgroundColor:palette.bgRaised,paddingX:3,paddingY:1,flexDirection:"column",width:64,gap:1,children:[import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.accent,children:"New team"},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:step==="name"?" \u2014 step 1 of 2":" \u2014 step 2 of 2"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),step==="name"?import_jsx_dev_runtime2.jsxDEV(NameStep,{value:teamName,onChange:handleNameChange,onSubmit:advanceFromName,errorMessage:nameError},void 0,!1,void 0,this):import_jsx_dev_runtime2.jsxDEV(MembersStep,{agents:availableAgents,selected,cursor:memberCursor},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV(CliPreviewLine,{intent,hint:step==="name"?"Enter: next \xB7 Esc: cancel":"Space: toggle \xB7 Enter: create \xB7 Esc: back"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function NameStep({value,onChange,onSubmit,errorMessage}){return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",gap:1,children:[import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:"Team name (git-branch-safe):"},void 0,!1,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("input",{value,onInput:onChange,onChange,onSubmit,focused:!0,placeholder:"feat/auth-bug",backgroundColor:palette.bg,textColor:palette.text,placeholderColor:palette.textMuted},void 0,!1,void 0,this),errorMessage!==null?import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:`\u26A0 ${errorMessage}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function MembersStep({agents,selected,cursor}){if(agents.length===0)return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",children:[import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:"No agents registered"},void 0,!1,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:"Members can be hired later via `genie team hire`."},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this);return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",children:[import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:"Select members (space to toggle):"},void 0,!1,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",children:agents.map((name,idx)=>{let isSelected=selected.has(name),isCursor=idx===cursor,tick=isSelected?"[x]":"[ ]",prefix=isCursor?"\u25B6 ":" ";return import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:isCursor?palette.accent:palette.textMuted,children:prefix},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:isSelected?palette.success:palette.textDim,children:tick},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.text,children:` ${name}`},void 0,!1,void 0,this)]},name,!0,void 0,this)})},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function isEnter(key){return key.name==="enter"||key.name==="return"}function handleNameStepKey(key,opts){if(key.name==="escape"){opts.onCancel();return}if(isEnter(key)&&opts.nameValid)opts.advanceFromName()}function handleMembersStepKey(key,opts){if(key.name==="escape"){opts.setStep("name");return}if(opts.availableAgents.length===0){if(isEnter(key))opts.confirmMembers();return}if(key.name==="up"||key.name==="k"){opts.setMemberCursor((prev)=>prev<=0?opts.availableAgents.length-1:prev-1);return}if(key.name==="down"||key.name==="j"){opts.setMemberCursor((prev)=>prev>=opts.availableAgents.length-1?0:prev+1);return}if(key.name==="space"){let name=opts.availableAgents[opts.memberCursor];if(name)opts.toggleMember(name);return}if(isEnter(key))opts.confirmMembers()}var import_react21;var init_TeamCreate=__esm(async()=>{init_team_manager();init_theme2();init_CliPreviewLine();init_jsx_dev_runtime();await init_react();import_react21=__toESM(require_react_development(),1)});function getNodeIcon(node){if(node.type==="agent")return getAgentIcon(node);switch(node.type){case"session":return node.data.attached?"\u25B6":"\u25B8";case"window":return node.data.active?"\u25A0":"\u25A1";case"pane":return getPaneIcon(node);default:return" "}}function getAgentIcon(node){switch(node.workState){case"in_flight":return"\u25C6";case"paused":return"\u25D0";case"done":return"\u2714";case"stuck":return"\u2718";default:break}switch(node.wsAgentState){case"running":return"\u25CF";case"stopped":return"\u25CB";case"error":return"\u2298";case"spawning":return"\u231B";default:return"\u25CB"}}function getPaneIcon(node){if(node.data.isDead)return"\u2718";if(node.agentState==="working")return"\u25CF";if(node.agentState==="idle")return"\u25CB";if(node.agentState==="permission")return"\u26A0";if(node.agentState==="error")return"\u2718";if(node.data.command==="claude")return"\u25C6";return"\u25CB"}function getNodeColor(node){if(node.type==="agent")return getAgentColor(node);switch(node.type){case"session":return node.data.attached?palette.success:palette.textDim;case"window":return node.data.active?palette.info:palette.text;case"pane":return getPaneColor(node);default:return palette.text}}function getAgentColor(node){switch(node.workState){case"in_flight":return palette.accentBright;case"paused":return palette.textDim;case"done":return palette.info;case"stuck":return palette.error;default:break}switch(node.wsAgentState){case"running":return palette.success;case"stopped":return palette.textDim;case"error":return palette.error;case"spawning":return palette.warning;default:return palette.textDim}}function getPaneColor(node){if(node.data.isDead)return palette.error;if(node.agentState==="working")return palette.info;if(node.agentState==="permission")return palette.warning;if(node.agentState==="error")return palette.error;if(node.agentState==="idle")return palette.textDim;if(node.data.command==="claude")return palette.info;return palette.textDim}function getAgentSuffix(node){if(node.workState==="stuck")return" [stuck \u2014 press R to retry]";if(node.workState==="paused")return" [paused \u2014 auto-resume off]";if(node.workState==="done")return" [done]";if(node.wsAgentState==="spawning"&&node.activePanes===0)return" [stuck \u2014 press R to retry]";let wc=node.data.windowCount;if(wc>1)return` (${wc} windows)`;if(wc===1)return" (1 window)";return""}function getNodeSuffix(node){if(node.type==="agent")return getAgentSuffix(node);if(node.type==="session"||node.type==="pane"){let count=node.activePanes;if(count>0)return` ${icons.agent}${count}`}return""}function getStateColor(state){switch(state){case"working":return palette.info;case"idle":return palette.textDim;case"permission":return palette.warning;case"error":return palette.error;default:return palette.textMuted}}var import_react22,TreeNodeRow;var init_TreeNode=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react22=__toESM(require_react_development(),1),TreeNodeRow=import_react22.memo(function({node,selected,onSelect,onToggle,onContextMenu}){let indent2=" ".repeat(node.depth),hasChildren=node.children.length>0,expandIcon=hasChildren?node.expanded?icons.expanded:icons.collapsed:" ",icon=getNodeIcon(node),color2=getNodeColor(node),suffix=getNodeSuffix(node);return import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",backgroundColor:selected?palette.accentDim:void 0,onMouseDown:(event)=>{if(event.button===2&&onContextMenu){onSelect(node.id),onContextMenu(node.id);return}if(onSelect(node.id),hasChildren)onToggle(node.id)},children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[indent2,expandIcon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:color2,children:[icon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:selected?palette.accentBright:palette.text,children:node.label},void 0,!1,void 0,this),suffix?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:suffix},void 0,!1,void 0,this):null,node.agentState?import_jsx_dev_runtime2.jsxDEV("span",{fg:getStateColor(node.agentState),children:[" ",node.agentState]},void 0,!0,void 0,this):null,process.env.GENIE_TUI_DEBUG==="1"?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:` [${node.type}]`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)})});function useDiagnosticsRefresh(setDiagnostics,setRequestedInitialAgent){import_react24.useEffect(()=>{let active=!0;async function refresh(){try{let snap=await collectDiagnostics();if(!active)return;setDiagnostics(snap);let signaledAgent=consumeInitialAgentSignal();if(signaledAgent)setRequestedInitialAgent(signaledAgent)}catch(err){console.error("TUI: diagnostics failed:",err)}}refresh();let timer2=setInterval(refresh,2000);return()=>{active=!1,clearInterval(timer2)}},[setDiagnostics,setRequestedInitialAgent])}function useSessionTreeBuilder(diagnostics,workspaceRoot,setSessionTree){import_react24.useEffect(()=>{if(!diagnostics)return;let newTree;if(workspaceRoot){let agentNames=scanAgents(workspaceRoot);newTree=buildWorkspaceTree({agentNames,sessions:diagnostics.sessions,executors:diagnostics.executors,workStates:diagnostics.workStates})}else newTree=buildSessionTree(diagnostics);setSessionTree((prev)=>mergeExpandedState(prev,newTree))},[diagnostics,workspaceRoot,setSessionTree])}function useStableSelection(flatNodes,selectedIndex,setSelectedIndex,selectedNodeId){import_react24.useEffect(()=>{let node=flatNodes[selectedIndex]?.node;if(node)selectedNodeId.current=node.id},[selectedIndex,flatNodes,selectedNodeId]),import_react24.useLayoutEffect(()=>{if(flatNodes.length===0)return;if(selectedIndex>=flatNodes.length){setSelectedIndex(flatNodes.length-1);return}if(!selectedNodeId.current)return;let currentAtIndex=flatNodes[selectedIndex]?.node;if(currentAtIndex&¤tAtIndex.id===selectedNodeId.current)return;let restored=flatNodes.findIndex((n)=>n.node.id===selectedNodeId.current);if(restored>=0)setSelectedIndex(restored)},[flatNodes])}function useInitialAgentSelection(requestedInitialAgent,flatNodes,setSelectedIndex,setRequestedInitialAgent,onTmuxSessionSelect){import_react24.useEffect(()=>{if(!requestedInitialAgent||flatNodes.length===0)return;let idx=flatNodes.findIndex((n)=>n.node.id===`agent:${requestedInitialAgent}`);if(idx<0)return;setSelectedIndex(idx);let node=flatNodes[idx].node;if(node.type==="agent"&&node.wsAgentState!=="running"&&node.wsAgentState!=="spawning")spawnAgent(agentNameFromNode(node),onTmuxSessionSelect);setRequestedInitialAgent(void 0)},[requestedInitialAgent,flatNodes,onTmuxSessionSelect,setSelectedIndex,setRequestedInitialAgent])}function useAutoAttach(flatNodes,selectedIndex,lastTarget,onTmuxSessionSelect){import_react24.useEffect(()=>{let current=flatNodes[selectedIndex]?.node;if(!current)return;let target=getSessionTarget(current);if(!target)return;if(current.type==="agent"&¤t.wsAgentState!=="running")return;let key=`${target.sessionName}:${target.windowIndex??""}`;if(key===lastTarget.current)return;lastTarget.current=key,onTmuxSessionSelect(target.sessionName,target.windowIndex)},[selectedIndex,flatNodes,onTmuxSessionSelect,lastTarget])}function useNavKeyboard(opts){useKeyboard((key)=>{if(opts.keyboardDisabled)return;if(opts.spawnIntoAgent!==null||opts.spawnPickerTarget!==null)return;if(tryOpenTeamCreate(key,{workspaceRoot:opts.workspaceRoot,showTeamCreate:opts.showTeamCreate,contextMenuNodeId:opts.contextMenuNodeId,handleOpenTeamCreate:opts.handleOpenTeamCreate}))return;if(opts.showTeamCreate)return;handleKeyboardInput(key,opts)})}function renderAlertBadge(alertCount){if(alertCount<=0)return null;return import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:[" ","\u25CF ",alertCount," alert",alertCount===1?"":"s"]},void 0,!0,void 0,this)}function computeNavCounts(workspaceRoot,sessionTree,diagnostics){if(workspaceRoot)return{agentCount:sessionTree.filter((n)=>n.type==="agent").length,runningCount:sessionTree.filter((n)=>n.wsAgentState==="running").length};let paneSum=diagnostics?.sessions.reduce((sum,s2)=>sum+s2.windows.reduce((ws,w2)=>ws+w2.panes.length,0),0)??0;return{agentCount:diagnostics?.sessions.length??0,runningCount:paneSum}}function Nav({onTmuxSessionSelect,onNewAgentWindow,workspaceRoot,initialAgent,keyboardDisabled=!1}){let[diagnostics,setDiagnostics]=import_react24.useState(null),[sessionTree,setSessionTree]=import_react24.useState([]),[selectedIndex,setSelectedIndex]=import_react24.useState(0),[requestedInitialAgent,setRequestedInitialAgent]=import_react24.useState(initialAgent),[contextMenuNodeId,setContextMenuNodeId]=import_react24.useState(null),[spawnIntoAgent,setSpawnIntoAgent]=import_react24.useState(null),[spawnPickerTarget,setSpawnPickerTarget]=import_react24.useState(null),lastTarget=import_react24.useRef(null),selectedNodeId=import_react24.useRef(null);useDiagnosticsRefresh(setDiagnostics,setRequestedInitialAgent),useSessionTreeBuilder(diagnostics,workspaceRoot,setSessionTree);let flatNodes=import_react24.useMemo(()=>flattenTree(sessionTree),[sessionTree]);useStableSelection(flatNodes,selectedIndex,setSelectedIndex,selectedNodeId),useInitialAgentSelection(requestedInitialAgent,flatNodes,setSelectedIndex,setRequestedInitialAgent,onTmuxSessionSelect),useAutoAttach(flatNodes,selectedIndex,lastTarget,onTmuxSessionSelect);let handleSelect=import_react24.useCallback((id)=>{let idx=flatNodes.findIndex((n)=>n.node.id===id);if(idx>=0)setSelectedIndex(idx)},[flatNodes]),handleToggle=import_react24.useCallback((id)=>{setSessionTree((prev)=>toggleNode(prev,id))},[]),handleVerticalNav=import_react24.useCallback((keyName2)=>{let rowCount=flatNodes.length;if(rowCount===0)return;if(keyName2==="up"||keyName2==="k")setSelectedIndex((prev)=>prev===0?rowCount-1:prev-1);else if(keyName2==="down"||keyName2==="j")setSelectedIndex((prev)=>prev>=rowCount-1?0:prev+1)},[flatNodes.length]),handleExpandCollapse=import_react24.useCallback((keyName2)=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if((keyName2==="right"||keyName2==="l")&&node.children.length>0&&!node.expanded)handleToggle(node.id);else if((keyName2==="left"||keyName2==="h")&&node.expanded)handleToggle(node.id)},[flatNodes,selectedIndex,handleToggle]),handleEnter=import_react24.useCallback(()=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if(node.type==="agent"){handleEnterAgent(node,onTmuxSessionSelect);return}if(node.children.length>0)handleToggle(node.id);let target=getSessionTarget(node);if(target)onTmuxSessionSelect(target.sessionName,target.windowIndex)},[flatNodes,selectedIndex,handleToggle,onTmuxSessionSelect]),handleRetry=import_react24.useCallback(()=>{let node=flatNodes[selectedIndex]?.node;if(!node||node.type!=="agent")return;if(node.wsAgentState!=="spawning"&&node.wsAgentState!=="error")return;(async()=>{try{let{reconcileStaleSpawns:reconcileStaleSpawns2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));await reconcileStaleSpawns2()}catch{}spawnAgent(agentNameFromNode(node),onTmuxSessionSelect)})()},[flatNodes,selectedIndex,onTmuxSessionSelect]),handleContextMenu=import_react24.useCallback((nodeId)=>{let flat=flatNodes.find((n)=>n.node.id===nodeId);if(flat&&buildMenuItems(flat.node).length>0)setContextMenuNodeId(nodeId)},[flatNodes]),handleContextMenuAction=import_react24.useCallback((action,payload)=>{let node=flatNodes.find((n)=>n.node.id===contextMenuNodeId)?.node;if(!node)return;if(setContextMenuNodeId(null),action==="spawn-here"){let target=resolveSpawnHereTarget(node);if(target)setSpawnPickerTarget(target);return}dispatchContextMenuAction(action,node,payload,{sessionTree,onTmuxSessionSelect,onNewAgentWindow,openSpawnInto:setSpawnIntoAgent})},[flatNodes,contextMenuNodeId,sessionTree,onTmuxSessionSelect,onNewAgentWindow]),handleSpawnIntoConfirm=import_react24.useCallback((intent)=>{executeSpawnIntent(intent),setSpawnIntoAgent(null)},[]),handleSpawnIntoCancel=import_react24.useCallback(()=>{setSpawnIntoAgent(null)},[]),handleSpawnPickerConfirm=import_react24.useCallback((intent)=>{setSpawnPickerTarget(null),executeSpawnIntent(intent)},[]),handleSpawnPickerCancel=import_react24.useCallback(()=>{setSpawnPickerTarget(null)},[]),_menuDisabled=keyboardDisabled||contextMenuNodeId!==null,{showTeamCreate,handleOpenTeamCreate,handleTeamCreateConfirm,handleTeamCreateCancel}=useTeamCreateControls({workspaceRoot,diagnostics,onTmuxSessionSelect});useNavKeyboard({keyboardDisabled,spawnIntoAgent,spawnPickerTarget,workspaceRoot,showTeamCreate,contextMenuNodeId,handleOpenTeamCreate,flatNodes,selectedIndex,setContextMenuNodeId,handleVerticalNav,handleExpandCollapse,handleEnter,handleRetry,onNewAgentWindow});let{agentCount,runningCount}=computeNavCounts(workspaceRoot,sessionTree,diagnostics),headerLabel=workspaceRoot?"Agents":"Sessions",alertCount=diagnostics?.alertCount??0;return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",backgroundColor:palette.bg,children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgRaised,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.accent,children:headerLabel},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[" ",workspaceRoot?`${runningCount}/${agentCount}`:`${agentCount}s ${runningCount}p`]},void 0,!0,void 0,this):null,renderAlertBadge(alertCount)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("scrollbox",{focused:!0,height:"100%",style:{scrollbarOptions:{showArrows:!1,trackOptions:{foregroundColor:palette.scrollThumb,backgroundColor:palette.scrollTrack}}},children:flatNodes.map((flat,i2)=>import_jsx_dev_runtime2.jsxDEV(TreeNodeRow,{node:flat.node,selected:i2===selectedIndex,onSelect:handleSelect,onToggle:handleToggle,onContextMenu:handleContextMenu},flat.node.id,!1,void 0,this))},void 0,!1,void 0,this):import_jsx_dev_runtime2.jsxDEV("box",{flexGrow:1,justifyContent:"center",alignItems:"center",children:import_jsx_dev_runtime2.jsxDEV("text",{fg:palette.textDim,children:"Collecting..."},void 0,!1,void 0,this)},void 0,!1,void 0,this),contextMenuNodeId?import_jsx_dev_runtime2.jsxDEV(ContextMenu,{items:buildMenuItems(flatNodes.find((n)=>n.node.id===contextMenuNodeId)?.node??{}),onAction:handleContextMenuAction,onClose:()=>setContextMenuNodeId(null),positionY:flatNodes.findIndex((n)=>n.node.id===contextMenuNodeId)+1},void 0,!1,void 0,this):null,spawnIntoAgent!==null?import_jsx_dev_runtime2.jsxDEV(SpawnTargetPicker,{agentName:spawnIntoAgent,sessions:diagnostics?.sessions??[],onConfirm:handleSpawnIntoConfirm,onCancel:handleSpawnIntoCancel},void 0,!1,void 0,this):null,spawnPickerTarget!==null?import_jsx_dev_runtime2.jsxDEV(AgentPicker,{target:spawnPickerTarget,onConfirm:handleSpawnPickerConfirm,onCancel:handleSpawnPickerCancel},void 0,!1,void 0,this):null,showTeamCreate?import_jsx_dev_runtime2.jsxDEV(TeamCreate,{availableAgents:workspaceRoot?scanAgents(workspaceRoot):[],workspaceRoot,onConfirm:handleTeamCreateConfirm,onCancel:handleTeamCreateCancel},void 0,!1,void 0,this):null,import_jsx_dev_runtime2.jsxDEV(SystemStats,{},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgRaised,children:import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:buildFooterHint(workspaceRoot)},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function useTeamCreateControls(opts){let{workspaceRoot,diagnostics,onTmuxSessionSelect}=opts,[showTeamCreate,setShowTeamCreate]=import_react24.useState(!1),pendingTeamNameRef=import_react24.useRef(null),handleOpenTeamCreate=import_react24.useCallback(()=>{if(!workspaceRoot)return;setShowTeamCreate(!0)},[workspaceRoot]),handleTeamCreateConfirm=import_react24.useCallback((result2)=>{setShowTeamCreate(!1),runTeamCreation(result2,workspaceRoot),pendingTeamNameRef.current=result2.teamName},[workspaceRoot]),handleTeamCreateCancel=import_react24.useCallback(()=>{setShowTeamCreate(!1)},[]);return import_react24.useEffect(()=>{let pending=pendingTeamNameRef.current;if(!pending||!diagnostics)return;let session=diagnostics.sessions.find((s2)=>s2.name===pending);if(!session)return;pendingTeamNameRef.current=null,onTmuxSessionSelect(session.name,resolvePreferredWindowIndex(session,pending))},[diagnostics,onTmuxSessionSelect]),{showTeamCreate,handleOpenTeamCreate,handleTeamCreateConfirm,handleTeamCreateCancel}}function handleEnterAgent(node,onTmuxSessionSelect){if(node.wsAgentState!=="running"&&node.wsAgentState!=="spawning")spawnAgent(agentNameFromNode(node),onTmuxSessionSelect);else if(node.wsAgentState==="running"){let target=getSessionTarget(node);if(target)onTmuxSessionSelect(target.sessionName,target.windowIndex)}}function dispatchContextMenuAction(action,node,payload,deps){let name=node.label;if(action==="spawn-into"&&node.type==="agent"&&deps.openSpawnInto){deps.openSpawnInto(agentNameFromNode(node));return}if(handleAttachAction(action,node,deps.onTmuxSessionSelect))return;if(handleRetryAction(action,name,deps.onTmuxSessionSelect))return;if(handleGenieAction(action,name,payload))return;let tmuxServer=process.env.GENIE_TMUX_SERVER||"genie";if(handleRenameAction(action,node,tmuxServer,payload))return;if(handleAgentWindowActions(action,node,name,tmuxServer,deps.onNewAgentWindow))return;if(handleSessionNodeActions(action,node,tmuxServer,payload))return;if(handleWindowNodeActions(action,node,deps.sessionTree,tmuxServer,payload))return;handlePaneNodeActions(action,node,deps.sessionTree,tmuxServer,deps.onNewAgentWindow)}function handleAttachAction(action,node,onTmuxSessionSelect){if(action!=="attach")return!1;let target=getSessionTarget(node);if(target)onTmuxSessionSelect(target.sessionName,target.windowIndex);return!0}function handleRetryAction(action,name,onTmuxSessionSelect){if(action!=="retry")return!1;return(async()=>{try{let{reconcileStaleSpawns:reconcileStaleSpawns2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));await reconcileStaleSpawns2()}catch{}spawnAgent(name,onTmuxSessionSelect)})(),!0}function handleGenieAction(action,name,payload){if(action==="send"&&payload)return executeGenie(["agent","send",payload,"--to",name]),!0;if(action==="answer-text"&&payload)return executeGenie(["agent","answer",name,`text:${payload}`]),!0;let genieArgs={spawn:["spawn",name],"spawn-plan":["spawn",name,"--plan-mode"],stop:["agent","stop",name],kill:["agent","kill",name],log:["agent","log",name],show:["agent","show",name],read:["read",name],"answer-yes":["agent","answer",name,"yes"],"answer-no":["agent","answer",name,"no"]}[action];if(genieArgs)return executeGenie(genieArgs),!0;return!1}function handleRenameAction(action,node,tmuxServer,payload){if(action==="rename-session"&&payload){let sess=node.type==="agent"?node.data.sessionName||node.label:node.id.split(":").slice(1).join(":");return executeTmux3(["-L",tmuxServer,"rename-session","-t",sess,payload]),!0}if(action==="rename-window"&&payload){let idParts=node.id.split(":"),windowTarget=`${idParts[1]}:${idParts[2]}`;return executeTmux3(["-L",tmuxServer,"rename-window","-t",windowTarget,payload]),!0}if(action==="rename-pane"&&payload&&node.type==="pane"){let paneId=node.data.paneId;return executeTmux3(["-L",tmuxServer,"select-pane","-t",`${paneId}`,"-T",payload]),!0}return!1}function handleAgentWindowActions(action,node,name,tmuxServer,onNewAgentWindow){if(action==="agent-new-window"&&node.type==="agent"){if(onNewAgentWindow)onNewAgentWindow(agentNameFromNode(node));return!0}if(action==="new-empty-window"&&node.type==="agent"){let sessionName=node.data.sessionName||name;return executeTmux3(["-L",tmuxServer,"new-window","-a","-t",sessionName]),!0}return!1}function handleSessionNodeActions(action,node,tmuxServer,payload){if(node.type!=="session")return!1;let sess=node.id.split(":").slice(1).join(":");if(action==="kill-session")return executeTmux3(["-L",tmuxServer,"kill-session","-t",sess]),!0;if(action==="new-window")return executeTmux3(["-L",tmuxServer,"new-window","-a","-t",sess]),!0;if(action==="clone-session")return executeTmux3(["-L",tmuxServer,"new-session","-d","-s",`${sess}-clone`,"-t",sess]),!0;if(action==="spawn-in-session"&&payload)return executeGenie(["spawn",payload,"--session",sess]),!0;return!1}function handleWindowNodeActions(action,node,sessionTree,tmuxServer,payload){if(node.type!=="window")return!1;let idParts=node.id.split(":"),windowTarget=`${idParts[1]}:${idParts[2]}`;if(action==="kill-window")return executeTmux3(["-L",tmuxServer,"kill-window","-t",windowTarget]),!0;if(action==="window-new-agent"){let parentAgent=findParentAgent(sessionTree,node.id);if(parentAgent){let agentFullName=agentNameFromNode(parentAgent),suffix=Date.now()%1e4,role=`${agentFullName}-${suffix}`;executeGenie(["spawn",agentFullName,"--role",role,"--window",windowTarget])}return!0}if(action==="split-pane")return executeTmux3(["-L",tmuxServer,"split-window","-t",windowTarget]),!0;if(action==="spawn-in-window"&&payload)return executeGenie(["spawn",payload,"--session",idParts[1]]),!0;return!1}function handlePaneNodeActions(action,node,sessionTree,tmuxServer,onNewAgentWindow){if(node.type!=="pane")return!1;let paneId=node.data.paneId;if(action==="clone-agent"){let parentAgent=findParentAgent(sessionTree,node.id);if(parentAgent&&onNewAgentWindow)onNewAgentWindow(agentNameFromNode(parentAgent));return!0}if(action==="kill-pane")return executeTmux3(["-L",tmuxServer,"kill-pane","-t",`${paneId}`]),!0;if(action==="split-h")return executeTmux3(["-L",tmuxServer,"split-window","-h","-t",`${paneId}`]),!0;if(action==="split-v")return executeTmux3(["-L",tmuxServer,"split-window","-v","-t",`${paneId}`]),!0;return!1}function buildFooterHint(workspaceRoot){return`\u2191\u2193:nav \u2190\u2192:expand Enter:${workspaceRoot?"spawn/attach":"attach"} ^T:new${workspaceRoot?" ^N:team":""} R:retry .:menu`}function tryOpenTeamCreate(key,opts){if(!key.ctrl||key.name!=="n")return!1;if(!opts.workspaceRoot||opts.showTeamCreate||opts.contextMenuNodeId)return!1;return opts.handleOpenTeamCreate(),!0}function tryOpenContextMenu(flatNodes,selectedIndex,setContextMenuNodeId){let node=flatNodes[selectedIndex]?.node;if(node&&buildMenuItems(node).length>0)return setContextMenuNodeId(node.id),!0;return!1}function dispatchNavKey(key,handlers2,agentAction){let n=key.name;if(n==="up"||n==="k"||n==="down"||n==="j")handlers2.handleVerticalNav(n);else if(n==="right"||n==="l"||n==="left"||n==="h")handlers2.handleExpandCollapse(n);else if(n==="enter"||n==="return")handlers2.handleEnter();else if(n==="r")handlers2.handleRetry();else if(key.ctrl&&n==="t")agentAction()}function handleKeyboardInput(key,opts){if(key.name==="."&&!opts.contextMenuNodeId){if(tryOpenContextMenu(opts.flatNodes,opts.selectedIndex,opts.setContextMenuNodeId))return}if(opts.contextMenuNodeId)return;dispatchNavKey(key,opts,()=>{let node=opts.flatNodes[opts.selectedIndex]?.node;if(node?.type==="agent"&&node.wsAgentState==="running"&&opts.onNewAgentWindow)opts.onNewAgentWindow(agentNameFromNode(node))})}function agentNameFromNode(node){return node.id.replace(/^agent:/,"")}function spawnAgent(name,onTmuxSessionSelect){try{let{spawn:spawn5}=__require("child_process"),{join:join75,resolve:resolve16}=__require("path"),{existsSync:existsSync61,mkdirSync:mkdirSync27,openSync:openSync3}=__require("fs"),{homedir:homedir40}=__require("os"),bunPath=process.execPath||"bun",genieBin=process.argv[1],wsRoot=process.env.GENIE_TUI_WORKSPACE,sessionName=name.replace(/\//g,"-"),cwd;if(wsRoot){let parentName=name.includes("/")?name.slice(0,name.indexOf("/")):name,agentDir=resolve16(join75(wsRoot,"agents",parentName));if(existsSync61(agentDir))cwd=agentDir}let{GENIE_TUI_PANE:_a,GENIE_TUI_RIGHT:_b,GENIE_TUI_WORKSPACE:_c,GENIE_IS_DAEMON:_d,...cleanEnv}=process.env,logDir=join75(homedir40(),".genie","logs","tui-spawn");try{mkdirSync27(logDir,{recursive:!0})}catch{}let logPath=join75(logDir,`${sessionName}-${Date.now()}.log`),logFd;try{logFd=openSync3(logPath,"a")}catch{logFd=void 0}let spawnOpts=logFd!==void 0?{detached:!0,stdio:["ignore",logFd,logFd],cwd,env:cleanEnv}:{detached:!0,stdio:"ignore",cwd,env:cleanEnv},child=genieBin&&genieBin!=="genie"?spawn5(bunPath,[genieBin,"spawn",name,"--session",sessionName,"--new-window"],spawnOpts):spawn5("genie",["spawn",name,"--session",sessionName,"--new-window"],spawnOpts);if(child.on("exit",(code)=>{if(code&&code!==0)console.error(`TUI: spawn "${name}" exited ${code}. See ${logPath}`)}),child.on("error",(err)=>{console.error(`TUI: spawn "${name}" error: ${err.message}. See ${logPath}`)}),child.unref(),onTmuxSessionSelect)attachSpawnedAgentWhenReady(sessionName,onTmuxSessionSelect)}catch(err){console.error(`TUI: spawn failed for ${name}:`,err instanceof Error?err.message:err)}}function attachSpawnedAgentWhenReady(sessionName,onTmuxSessionSelect,attempt=0){(async()=>{try{let session=(await collectDiagnostics()).sessions.find((candidate)=>candidate.name===sessionName);if(session){let windowIndex=resolvePreferredWindowIndex(session,sessionName);if(windowIndex!==void 0){onTmuxSessionSelect(sessionName,windowIndex);return}}}catch{}if(attempt>=40){onTmuxSessionSelect(sessionName);return}setTimeout(()=>{attachSpawnedAgentWhenReady(sessionName,onTmuxSessionSelect,attempt+1)},250)})()}function executeTmux3(args){try{let{spawn:spawn5}=__require("child_process");spawn5("tmux",args,{detached:!0,stdio:"ignore"}).unref()}catch{}}function executeGenie(args){try{let{spawn:spawn5}=__require("child_process"),bunPath=process.execPath||"bun",genieBin=process.argv[1];(genieBin&&genieBin!=="genie"?spawn5(bunPath,[genieBin,...args],{detached:!0,stdio:"ignore"}):spawn5("genie",args,{detached:!0,stdio:"ignore"})).unref()}catch{}}function executeGenieAwaited(args){return new Promise((resolve16,reject)=>{try{let{spawn:spawn5}=__require("child_process"),bunPath=process.execPath||"bun",genieBin=process.argv[1],child=genieBin&&genieBin!=="genie"?spawn5(bunPath,[genieBin,...args],{stdio:"ignore"}):spawn5("genie",args,{stdio:"ignore"});child.on("exit",(code)=>resolve16(code)),child.on("error",reject)}catch(err){reject(err instanceof Error?err:Error(String(err)))}})}function resolveSpawnHereTarget(node){if(node.type==="session"){let sess=node.id.split(":").slice(1).join(":");if(sess.length===0)return null;return{session:sess}}if(node.type==="window"){let idParts=node.id.split(":");if(idParts.length<3)return null;return{session:idParts[1],window:`${idParts[1]}:${idParts[2]}`}}return null}function executeSpawnIntent(intent){try{let{argv}=buildSpawnInvocation(intent);executeGenie(argv)}catch(err){console.error("TUI: spawn-intent execution failed:",err instanceof Error?err.message:err)}}async function runTeamCreation(result2,workspaceRoot){let argv;try{({argv}=buildSpawnInvocation({kind:"create-team",name:result2.teamName,repo:workspaceRoot}))}catch(err){console.error("TUI: team create intent build failed:",err instanceof Error?err.message:err);return}let createExit=null;try{createExit=await executeGenieAwaited(argv)}catch(err){console.error("TUI: team create spawn failed:",err instanceof Error?err.message:err);return}if(createExit!==0){console.error(`TUI: team create exited ${createExit} \u2014 skipping member hires for "${result2.teamName}"`);return}for(let member of result2.members)try{let code=await executeGenieAwaited(["team","hire",member,"--team",result2.teamName]);if(code!==0)console.error(`TUI: team hire "${member}" exited ${code} \u2014 continuing with remaining members`)}catch(err){console.error(`TUI: team hire "${member}" failed:`,err instanceof Error?err.message:err)}}function findParentAgent(tree,targetId){for(let node of tree){if(node.type==="agent"&&containsNode(node,targetId))return node;let found=findParentAgent(node.children,targetId);if(found)return found}return null}function containsNode(node,targetId){if(node.id===targetId)return!0;return node.children.some((c)=>containsNode(c,targetId))}function mergeExpandedState(oldTree,newTree){if(oldTree.length===0)return newTree;let oldState=new Map;function collect(nodes){for(let n of nodes)oldState.set(n.id,{expanded:n.expanded,childCount:n.children.length}),collect(n.children)}collect(oldTree);function apply(nodes){return nodes.map((n)=>({...n,expanded:(()=>{let previous=oldState.get(n.id);if(!previous)return n.expanded;if(previous.childCount===0&&n.children.length>0)return n.expanded;return previous.expanded})(),children:apply(n.children)}))}return apply(newTree)}var import_react24;var init_Nav=__esm(async()=>{init_spawn_invocation();init_workspace();init_diagnostics();init_initial_agent();init_theme2();init_SystemStats();init_TreeNode();init_jsx_dev_runtime();await __promiseAll([init_react(),init_AgentPicker(),init_ContextMenu(),init_SpawnTargetPicker(),init_TeamCreate()]);import_react24=__toESM(require_react_development(),1)});function QuitDialog({onConfirm,onCancel}){return useKeyboard((key)=>{if(key.name==="enter"||key.name==="return"||key.name==="y")onConfirm();else if(key.name==="escape"||key.name==="n")onCancel()}),import_jsx_dev_runtime2.jsxDEV("box",{position:"absolute",width:"100%",height:"100%",justifyContent:"center",alignItems:"center",backgroundColor:palette.bgOverlay,children:import_jsx_dev_runtime2.jsxDEV("box",{border:!0,borderColor:palette.borderActive,backgroundColor:palette.bgRaised,paddingX:3,paddingY:1,flexDirection:"column",alignItems:"center",gap:1,children:[import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.accent,children:"Quit genie?"},void 0,!1,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.text,children:"Enter"},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:" to quit "},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:" | "},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.text,children:" Esc"},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:" to cancel"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:"Hint: Ctrl+D to detach (keep running)"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var init_QuitDialog=__esm(async()=>{init_theme2();init_jsx_dev_runtime();await init_react()});import{execSync as execSync18}from"child_process";import{readFileSync as readFileSync38}from"fs";function App({rightPane,workspaceRoot,initialAgent}){let[showQuit,setShowQuit]=import_react27.useState(!1);useKeyboard((key)=>{if(key.ctrl&&key.name==="q")if(showQuit)handleQuit();else setShowQuit(!0)});let handleQuit=import_react27.useCallback(()=>{try{let genieHome6=process.env.GENIE_HOME??`${process.env.HOME}/.genie`,pid=readFileSync38(`${genieHome6}/serve.pid`,"utf-8").trim();process.kill(Number.parseInt(pid,10),"SIGTERM")}catch{}try{execSync18("tmux -L genie-tui kill-server",{stdio:"ignore"})}catch{}},[]),handleTmuxSessionSelect=import_react27.useCallback((sessionName,windowIndex)=>{if(!rightPane)return;attachProjectWindow(rightPane,sessionName,windowIndex)},[rightPane]);return import_jsx_dev_runtime2.jsxDEV("box",{width:"100%",height:"100%",children:[import_jsx_dev_runtime2.jsxDEV(Nav,{onTmuxSessionSelect:handleTmuxSessionSelect,onNewAgentWindow:newAgentWindow,workspaceRoot,initialAgent,keyboardDisabled:showQuit},void 0,!1,void 0,this),showQuit?import_jsx_dev_runtime2.jsxDEV(QuitDialog,{onConfirm:handleQuit,onCancel:()=>setShowQuit(!1)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var import_react27;var init_app=__esm(async()=>{init_tmux2();init_jsx_dev_runtime();await __promiseAll([init_react(),init_Nav(),init_QuitDialog()]);import_react27=__toESM(require_react_development(),1)});var exports_render={};__export(exports_render,{renderNav:()=>renderNav});async function renderNav(){let rightPane=process.env.GENIE_TUI_RIGHT||void 0,workspaceRoot=process.env.GENIE_TUI_WORKSPACE||void 0,initialAgent=process.env.GENIE_TUI_AGENT||void 0,renderer=await createCliRenderer({exitOnCtrlC:!1,useMouse:!0,useThread:process.platform!=="darwin"});createRoot(renderer).render(import_jsx_dev_runtime2.jsxDEV(App,{rightPane,workspaceRoot,initialAgent},void 0,!1,void 0,this)),await new Promise((resolve16)=>{renderer.once("destroy",resolve16)})}var init_render=__esm(async()=>{init_jsx_dev_runtime();await __promiseAll([init_core(),init_react(),init_app()])});var exports_tui={};__export(exports_tui,{launchTui:()=>launchTui});import{appendFileSync as appendFileSync4,closeSync as closeSync3,mkdirSync as mkdirSync27,openSync as openSync3,readSync,statSync as statSync10}from"fs";import{homedir as homedir40}from"os";import{join as join75}from"path";function genieHome6(){return process.env.GENIE_HOME??join75(homedir40(),".genie")}function extractPreviousRunCrashOutput(logPath){let fd=null;try{let size=statSync10(logPath).size;if(size===0)return"";let readSize=Math.min(size,TUI_CRASH_LOG_RECOVERY_MAX_BYTES);fd=openSync3(logPath,"r");let buffer2=Buffer.alloc(readSize);readSync(fd,buffer2,0,readSize,size-readSize);let text=buffer2.toString("utf-8"),lastBannerIndex=text.lastIndexOf(TUI_CRASH_LOG_BANNER_PREFIX);if(lastBannerIndex<0)return"";let afterBannerNewline=text.indexOf(`
|
|
3766
3768
|
`,lastBannerIndex);if(afterBannerNewline<0)return"";return text.slice(afterBannerNewline+1).trim()}catch{return""}finally{if(fd!==null)try{closeSync3(fd)}catch{}}}async function ingestPreviousRunCrash(logPath){let body=extractPreviousRunCrashOutput(logPath);if(!body)return;let truncated=body.length>TUI_CRASH_LOG_RECOVERY_MAX_MSG_CHARS?`${body.slice(0,TUI_CRASH_LOG_RECOVERY_MAX_MSG_CHARS)}
|
|
3767
3769
|
\u2026[truncated]`:body;try{let{emitEvent:emitEvent2}=await Promise.resolve().then(() => (init_emit(),exports_emit));emitEvent2("error.raised",{error_class:"TuiCrash",message:truncated,subsystem:"tui",severity:"error",retryable:!0})}catch{}}async function recordTuiLaunchBreadcrumb(){try{let logsDir=join75(genieHome6(),"logs");mkdirSync27(logsDir,{recursive:!0});let logPath=join75(logsDir,"tui-crash.log");await ingestPreviousRunCrash(logPath);let ts3=new Date().toISOString(),line=`${TUI_CRASH_LOG_BANNER_PREFIX}${ts3} pid=${process.pid} platform=${process.platform} arch=${process.arch} ---
|
|
3768
|
-
`;appendFileSync4(logPath,line,{mode:420})}catch{}}async function launchTui(){await recordTuiLaunchBreadcrumb();let{renderNav:renderNav2}=await init_render().then(() => exports_render);await renderNav2()}var TUI_CRASH_LOG_BANNER_PREFIX="--- tui-launch ",TUI_CRASH_LOG_RECOVERY_MAX_BYTES=65536,TUI_CRASH_LOG_RECOVERY_MAX_MSG_CHARS=3000;var init_tui=()=>{};var exports_resolve_agent_cwd={};__export(exports_resolve_agent_cwd,{resolveAgentFromCwd:()=>resolveAgentFromCwd});import{existsSync as existsSync61}from"fs";import{basename as basename15,dirname as dirname21,join as join76,relative as relative7,sep as sep3}from"path";function isRelativeWithin(rel,original){return!rel.startsWith("..")&&rel!==original}function resolveFromCanonicalDir(cwd,agentsDir){let relToAgents=relative7(agentsDir,cwd);if(!isRelativeWithin(relToAgents,cwd))return null;let segments=relToAgents.split(sep3).filter(Boolean);if(segments.length===0)return null;let agentName=segments[0];if(!existsSync61(join76(agentsDir,agentName,"AGENTS.md")))return null;let source=segments.length===1?"exact":"parent";return{agent:agentName,source}}function resolveFromWalkUp(cwd,workspaceRoot){let wsRel=relative7(workspaceRoot,cwd);if(!isRelativeWithin(wsRel,cwd))return null;let current=cwd;while(current!==workspaceRoot&¤t!==dirname21(current)){if(existsSync61(join76(current,"AGENTS.md")))return{agent:basename15(current),source:"parent"};current=dirname21(current)}return null}function resolveAgentFromCwd(cwd,workspaceRoot){return resolveFromCanonicalDir(cwd,join76(workspaceRoot,"agents"))??resolveFromWalkUp(cwd,workspaceRoot)??{agent:"genie",source:"default"}}var init_resolve_agent_cwd=()=>{};var import__=__toESM(require_commander(),1),{program,createCommand,createArgument,createOption,CommanderError,InvalidArgumentError,InvalidOptionArgumentError,Command,Argument,Option,Help}=import__.default;init_doctor();init_setup();init_shortcuts();import{existsSync as existsSync22}from"fs";import{homedir as homedir23}from"os";import{join as join28}from"path";async function shortcutsShowCommand(){displayShortcuts();let home=homedir23(),tmuxConf=join28(home,".tmux.conf"),zshrc=join28(home,".zshrc"),bashrc=join28(home,".bashrc");if(console.log("Installation status:"),isShortcutsInstalled(tmuxConf))console.log(" \x1B[32m\u2713\x1B[0m tmux.conf");else console.log(" \x1B[33m-\x1B[0m tmux.conf");let shellRc=existsSync22(zshrc)?zshrc:bashrc;if(isShortcutsInstalled(shellRc))console.log(` \x1B[32m\u2713\x1B[0m ${shellRc.replace(home,"~")}`);else console.log(` \x1B[33m-\x1B[0m ${shellRc.replace(home,"~")}`);console.log(),console.log("Run \x1B[36mgenie shortcuts install\x1B[0m to install shortcuts."),console.log("Run \x1B[36mgenie shortcuts uninstall\x1B[0m to remove shortcuts."),console.log()}async function shortcutsInstallCommand(){await installShortcuts()}async function shortcutsUninstallCommand(){await uninstallShortcuts()}init_esm14();init_claude_settings();init_genie_config2();import{existsSync as existsSync23,lstatSync,rmSync as rmSync2,unlinkSync as unlinkSync8}from"fs";import{homedir as homedir24}from"os";import{join as join29}from"path";var ORCHESTRATION_RULES_PATH=join29(homedir24(),".claude","rules","genie-orchestration.md"),LOCAL_BIN=join29(homedir24(),".local","bin"),SYMLINKS=["genie","term"];function isGenieSymlink(path3){try{if(!existsSync23(path3))return!1;if(!lstatSync(path3).isSymbolicLink())return!1;return!0}catch{return!1}}function removeSymlinks(){let removed=[];for(let name of SYMLINKS){let symlinkPath=join29(LOCAL_BIN,name);if(isGenieSymlink(symlinkPath))try{unlinkSync8(symlinkPath),removed.push(name)}catch{}}return removed}function tryRemoveStep(label,successMsg,fn){console.log(`\x1B[2m${label}\x1B[0m`);try{fn(),console.log(` \x1B[32m+\x1B[0m ${successMsg}`)}catch(error){let message=error instanceof Error?error.message:String(error);console.log(` \x1B[33m!\x1B[0m ${label.replace("...","")} failed: ${message}`)}}function performUninstall(hasHookScript,existingSymlinks,genieDir,hasGenieDir){if(hasHookScript)tryRemoveStep("Removing hook script...","Hook script removed",()=>removeHookScript());if(existingSymlinks.length>0){console.log("\x1B[2mRemoving symlinks...\x1B[0m");let removed=removeSymlinks();if(removed.length>0)console.log(` \x1B[32m+\x1B[0m Removed: ${removed.join(", ")}`)}if(existsSync23(ORCHESTRATION_RULES_PATH))tryRemoveStep("Removing orchestration rules...","Orchestration rules removed (~/.claude/rules/genie-orchestration.md)",()=>unlinkSync8(ORCHESTRATION_RULES_PATH));if(hasGenieDir)tryRemoveStep("Removing genie directory...","Directory removed",()=>rmSync2(genieDir,{recursive:!0,force:!0}))}async function uninstallCommand(){console.log(),console.log("\x1B[1m\x1B[33m Uninstall Genie CLI\x1B[0m"),console.log();let genieDir=getGenieDir(),hasGenieDir=existsSync23(genieDir),hasHookScript=hookScriptExists(),hasOrchestrationRules=existsSync23(ORCHESTRATION_RULES_PATH),existingSymlinks=SYMLINKS.filter((name)=>isGenieSymlink(join29(LOCAL_BIN,name)));if(console.log("\x1B[2mThis will remove:\x1B[0m"),hasHookScript)console.log(" \x1B[31m-\x1B[0m Hook script (~/.claude/hooks/genie-bash-hook.sh)");if(hasOrchestrationRules)console.log(" \x1B[31m-\x1B[0m Orchestration rules (~/.claude/rules/genie-orchestration.md)");if(hasGenieDir)console.log(` \x1B[31m-\x1B[0m Genie directory (${contractPath(genieDir)})`);if(existingSymlinks.length>0)console.log(` \x1B[31m-\x1B[0m Symlinks from ~/.local/bin: ${existingSymlinks.join(", ")}`);if(console.log(),!hasGenieDir&&!hasHookScript&&!hasOrchestrationRules&&existingSymlinks.length===0){console.log("\x1B[33mNothing to uninstall.\x1B[0m"),console.log();return}if(!await esm_default4({message:"Are you sure you want to uninstall Genie CLI?",default:!1})){console.log(),console.log("\x1B[2mUninstall cancelled.\x1B[0m"),console.log();return}console.log(),performUninstall(hasHookScript,existingSymlinks,genieDir,hasGenieDir),console.log(),console.log("\x1B[32m+\x1B[0m Genie CLI uninstalled."),console.log(),console.log("\x1B[2mNote: If you installed via npm/bun, also run:\x1B[0m"),console.log(" \x1B[36mbun remove -g @automagik/genie\x1B[0m"),console.log(" \x1B[2mor\x1B[0m"),console.log(" \x1B[36mnpm uninstall -g @automagik/genie\x1B[0m"),console.log()}init_genie_config2();import{execSync as execSync4,spawn as spawn3}from"child_process";import{chmodSync as chmodSync2,copyFileSync as copyFileSync3,existsSync as existsSync24,mkdirSync as mkdirSync13,readFileSync as readFileSync16,readdirSync as readdirSync8,rmSync as rmSync3,writeFileSync as writeFileSync11}from"fs";import{chmod,copyFile,mkdir as mkdir4,unlink as unlink2}from"fs/promises";import{homedir as homedir25}from"os";import{join as join30}from"path";var GENIE_HOME2=process.env.GENIE_HOME||join30(homedir25(),".genie"),GENIE_SRC=join30(GENIE_HOME2,"src"),GENIE_BIN=join30(GENIE_HOME2,"bin"),LOCAL_BIN2=join30(homedir25(),".local","bin");function log(message){console.log(`\x1B[32m\u25B8\x1B[0m ${message}`)}function success(message){console.log(`\x1B[32m\u2714\x1B[0m ${message}`)}function error(message){console.log(`\x1B[31m\u2716\x1B[0m ${message}`)}async function runCommand(command,args,cwd){return new Promise((resolve4)=>{let output=[],child=spawn3(command,args,{cwd,stdio:["inherit","pipe","pipe"],env:{...process.env,FORCE_COLOR:"1"}});child.stdout?.on("data",(data)=>{let str2=data.toString();output.push(str2),process.stdout.write(str2)}),child.stderr?.on("data",(data)=>{let str2=data.toString();output.push(str2),process.stderr.write(str2)}),child.on("close",(code)=>{resolve4({success:code===0,output:output.join("")})}),child.on("error",(err)=>{error(err.message),resolve4({success:!1,output:err.message})})})}async function getGitInfo(cwd){try{let branchResult=await runCommandSilent("git",["rev-parse","--abbrev-ref","HEAD"],cwd),commitResult=await runCommandSilent("git",["rev-parse","--short","HEAD"],cwd),dateResult=await runCommandSilent("git",["log","-1","--format=%ci"],cwd);if(branchResult.success&&commitResult.success&&dateResult.success)return{branch:branchResult.output.trim(),commit:commitResult.output.trim(),commitDate:dateResult.output.trim().split(" ")[0]}}catch{}return null}async function runCommandSilent(command,args,cwd,timeoutMs=4000){return new Promise((resolve4)=>{let output=[],settled=!1,child=spawn3(command,args,{cwd,stdio:["inherit","pipe","pipe"]}),timer2=setTimeout(()=>{if(settled)return;settled=!0,child.kill("SIGTERM"),resolve4({success:!1,output:`Timed out after ${timeoutMs}ms`})},timeoutMs);child.stdout?.on("data",(data)=>{output.push(data.toString())}),child.stderr?.on("data",(data)=>{output.push(data.toString())}),child.on("close",(code)=>{if(settled)return;settled=!0,clearTimeout(timer2),resolve4({success:code===0,output:output.join("")})}),child.on("error",(err)=>{if(settled)return;settled=!0,clearTimeout(timer2),resolve4({success:!1,output:err.message})})})}function detectFromBinaryPath(path3){if(path3.includes(".bun"))return"bun";if(path3.includes("node_modules"))return"npm";if(path3===join30(LOCAL_BIN2,"genie")||path3.startsWith(GENIE_BIN))return"source";return null}async function detectInstallationType(){if(genieConfigExists())try{let config=await loadGenieConfig();if(config.installMethod)return config.installMethod}catch{}if(existsSync24(join30(GENIE_SRC,".git")))return"source";let result2=await runCommandSilent("which",["genie"]);if(!result2.success)return"unknown";let detected=detectFromBinaryPath(result2.output.trim());if(detected)return detected;return(await runCommandSilent("which",["bun"])).success?"bun":"npm"}async function updateViaBun(channel){try{__require("fs").unlinkSync(join30(homedir25(),".bun","install","global","bun.lock"))}catch{}if(log(`Updating via bun (channel: ${channel})...`),!(await runCommand("bun",["add","-g","--force","--no-cache",`@automagik/genie@${channel}`])).success)return error("Failed to update via bun"),!1;return console.log(),success(`Genie CLI updated via bun (${channel})!`),!0}async function updateViaNpm(channel){if(log(`Updating via npm (channel: ${channel})...`),!(await runCommand("npm",["install","-g",`@automagik/genie@${channel}`])).success)return error("Failed to update via npm"),!1;return console.log(),success(`Genie CLI updated via npm (${channel})!`),!0}async function detectGlobalInstalls(){let found=new Set,[npmResult,bunResult]=await Promise.all([runCommandSilent("npm",["list","-g","@automagik/genie"]),runCommandSilent("bun",["pm","ls","-g"])]);if(npmResult.success&&!npmResult.output.includes("(empty)"))found.add("npm");if(bunResult.success&&bunResult.output.includes("@automagik/genie"))found.add("bun");return found}async function updateSource(){let beforeInfo=await getGitInfo(GENIE_SRC);if(beforeInfo)console.log(`Current: \x1B[2m${beforeInfo.branch}@${beforeInfo.commit} (${beforeInfo.commitDate})\x1B[0m`),console.log();if(log("Fetching latest changes..."),!(await runCommand("git",["fetch","origin"],GENIE_SRC)).success)error("Failed to fetch from origin"),process.exit(1);if(log("Resetting to origin/main..."),!(await runCommand("git",["reset","--hard","origin/main"],GENIE_SRC)).success)error("Failed to reset to origin/main"),process.exit(1);console.log();let afterInfo=await getGitInfo(GENIE_SRC);if(beforeInfo&&afterInfo&&beforeInfo.commit===afterInfo.commit){success("Already up to date!"),console.log();return}if(log("Installing dependencies..."),!(await runCommand("bun",["install"],GENIE_SRC)).success)error("Failed to install dependencies"),process.exit(1);if(console.log(),log("Building..."),!(await runCommand("bun",["run","build"],GENIE_SRC)).success)error("Failed to build"),process.exit(1);console.log(),log("Installing binaries...");try{await mkdir4(GENIE_BIN,{recursive:!0}),await mkdir4(LOCAL_BIN2,{recursive:!0});let binaries=["genie.js","term.js"],names=["genie","term"];for(let i2=0;i2<binaries.length;i2++){let src=join30(GENIE_SRC,"dist",binaries[i2]),binDest=join30(GENIE_BIN,binaries[i2]),linkDest=join30(LOCAL_BIN2,names[i2]);await copyFile(src,binDest),await chmod(binDest,493),await symlinkOrCopy(binDest,linkDest)}for(let legacy of["claudio.js","claudio"]){let legacyBin=join30(GENIE_BIN,legacy),legacyLink=join30(LOCAL_BIN2,legacy);try{await unlink2(legacyBin)}catch{}try{await unlink2(legacyLink)}catch{}}success("Binaries installed")}catch(err){error(`Failed to install binaries: ${err}`),process.exit(1)}if(console.log(),console.log("\x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m"),success("Genie CLI updated successfully!"),console.log(),afterInfo)console.log(`Version: \x1B[36m${afterInfo.branch}@${afterInfo.commit}\x1B[0m (${afterInfo.commitDate})`),console.log()}async function symlinkOrCopy(src,dest){let{symlink:symlink2,unlink:unlink3}=await import("fs/promises");try{if(existsSync24(dest))await unlink3(dest);await symlink2(src,dest)}catch{await copyFile(src,dest)}}function copyDirSync(src,dest){mkdirSync13(dest,{recursive:!0});for(let entry2 of readdirSync8(src,{withFileTypes:!0})){let srcPath=join30(src,entry2.name),destPath=join30(dest,entry2.name);if(entry2.isDirectory())copyDirSync(srcPath,destPath);else copyFileSync3(srcPath,destPath)}}async function resolveGlobalPkgDir(installType){if(installType==="bun"){let bunPath=join30(homedir25(),".bun","install","global","node_modules","@automagik","genie");if(existsSync24(bunPath))return bunPath}if(installType==="npm"){let npmRootResult=await runCommandSilent("npm",["root","-g"]);if(npmRootResult.success){let npmPath=join30(npmRootResult.output.trim(),"@automagik","genie");if(existsSync24(npmPath))return npmPath}}let bunFallback=join30(homedir25(),".bun","install","global","node_modules","@automagik","genie");if(existsSync24(bunFallback))return bunFallback;let npmRootFallback=await runCommandSilent("npm",["root","-g"]);if(npmRootFallback.success){let npmPath=join30(npmRootFallback.output.trim(),"@automagik","genie");if(existsSync24(npmPath))return npmPath}return null}function updatePluginRegistry(claudePlugins,cacheDir,version){let registryPath=join30(claudePlugins,"installed_plugins.json");try{if(!existsSync24(registryPath))return;let registry=JSON.parse(readFileSync16(registryPath,"utf-8")),entries=registry.plugins?.["genie@automagik"];if(!Array.isArray(entries))return;for(let entry2 of entries)if(entry2.scope==="user")entry2.installPath=cacheDir,entry2.version=version,entry2.lastUpdated=new Date().toISOString();writeFileSync11(registryPath,JSON.stringify(registry,null,2))}catch(err){log(`Registry update failed (non-fatal): ${err}`)}}function syncTmuxConf(tmuxScriptsSrc){mkdirSync13(GENIE_HOME2,{recursive:!0});let tmuxConfSrc=join30(tmuxScriptsSrc,"genie.tmux.conf"),tmuxConfDest=join30(GENIE_HOME2,"tmux.conf");if(existsSync24(tmuxConfSrc))try{copyFileSync3(tmuxConfSrc,tmuxConfDest),success(`Installed tmux config to ${tmuxConfDest}`);try{let{tmuxBin:tmuxBin2}=(init_ensure_tmux(),__toCommonJS(exports_ensure_tmux));execSync4(`${tmuxBin2()} -L genie source-file '${tmuxConfDest}'`,{stdio:"ignore"}),success("Reloaded genie tmux server configuration")}catch{}}catch{}let tuiConfSrc=join30(tmuxScriptsSrc,"tui-tmux.conf"),tuiConfDest=join30(GENIE_HOME2,"tui-tmux.conf");if(existsSync24(tuiConfSrc))try{copyFileSync3(tuiConfSrc,tuiConfDest),success(`Installed TUI tmux config to ${tuiConfDest}`)}catch{}let themeSrc=join30(tmuxScriptsSrc,".generated.theme.conf"),themeDest=join30(GENIE_HOME2,".generated.theme.conf");if(existsSync24(themeSrc))try{copyFileSync3(themeSrc,themeDest),success(`Installed tmux theme to ${themeDest}`)}catch{}let osc52Src=join30(tmuxScriptsSrc,"osc52-copy.sh"),osc52Dest=join30(GENIE_HOME2,"scripts","osc52-copy.sh");if(existsSync24(osc52Src))try{copyFileSync3(osc52Src,osc52Dest),chmodSync2(osc52Dest,493),success(`Installed OSC 52 clipboard helper to ${osc52Dest}`)}catch{}}function syncTmuxScripts(globalPkgDir){let tmuxScriptsSrc=join30(globalPkgDir,"scripts","tmux");if(!existsSync24(tmuxScriptsSrc))return;let scriptsDir=join30(GENIE_HOME2,"scripts");mkdirSync13(scriptsDir,{recursive:!0});let scriptCount=0;for(let entry2 of readdirSync8(tmuxScriptsSrc))if(entry2.endsWith(".sh")||entry2==="genie.tmux.conf"||entry2==="tui-tmux.conf"||entry2===".generated.theme.conf"){let src=join30(tmuxScriptsSrc,entry2),dest=join30(scriptsDir,entry2);copyFileSync3(src,dest);try{chmodSync2(dest,entry2.endsWith(".sh")?493:420)}catch{}scriptCount++}if(scriptCount>0)success(`Refreshed ${scriptCount} tmux scripts at ${scriptsDir}`);syncTmuxConf(tmuxScriptsSrc)}function syncMarketplaceVersion(claudePlugins,version){let marketplacePath=join30(claudePlugins,"marketplaces","automagik",".claude-plugin","marketplace.json");try{if(!existsSync24(marketplacePath))return;let data=JSON.parse(readFileSync16(marketplacePath,"utf-8"));if(Array.isArray(data.plugins)){for(let plugin of data.plugins)if(plugin.name==="genie")plugin.version=version}writeFileSync11(marketplacePath,JSON.stringify(data,null,2)),success(`Updated marketplace.json to v${version}`)}catch(err){log(`Marketplace version update failed (non-fatal): ${err}`)}}function syncPluginPackageVersion(claudePlugins,version){let pkgPath=join30(claudePlugins,"marketplaces","automagik","plugins","genie","package.json");try{if(!existsSync24(pkgPath))return;let data=JSON.parse(readFileSync16(pkgPath,"utf-8"));data.version=version,writeFileSync11(pkgPath,JSON.stringify(data,null,2)),success(`Updated plugin package.json to v${version}`)}catch(err){log(`Plugin package.json update failed (non-fatal): ${err}`)}}function syncSkillsSymlink(claudePlugins,version){let skillsLink=join30(claudePlugins,"marketplaces","automagik","plugins","genie","skills"),cacheSkills=join30("..","..","..","..","cache","automagik","genie",version,"skills");try{let{symlinkSync,unlinkSync:unlinkSync9,lstatSync:lstatSync2}=__require("fs");try{lstatSync2(skillsLink),unlinkSync9(skillsLink)}catch{}symlinkSync(cacheSkills,skillsLink),success(`Skills symlink \u2192 cache/${version}/skills`)}catch(err){log(`Skills symlink update failed (non-fatal): ${err}`)}}async function syncPlugin(installType){log("Syncing Claude Code plugin...");let globalPkgDir=await resolveGlobalPkgDir(installType);if(!globalPkgDir){log("Could not find installed package \u2014 skipping plugin sync");return}let pluginSrc=join30(globalPkgDir,"plugins","genie");if(!existsSync24(pluginSrc)){log("Plugin source not found in package \u2014 skipping plugin sync");return}let version;try{version=JSON.parse(readFileSync16(join30(globalPkgDir,"package.json"),"utf-8")).version}catch{log("Could not read package version \u2014 skipping plugin sync");return}let claudePlugins=join30(homedir25(),".claude","plugins"),cacheDir=join30(claudePlugins,"cache","automagik","genie",version);try{if(existsSync24(cacheDir))rmSync3(cacheDir,{recursive:!0,force:!0});copyDirSync(pluginSrc,cacheDir);let skillsSrc=join30(globalPkgDir,"skills");if(existsSync24(skillsSrc)&&!existsSync24(join30(cacheDir,"skills")))copyDirSync(skillsSrc,join30(cacheDir,"skills"))}catch(err){error(`Failed to copy plugin: ${err}`);return}updatePluginRegistry(claudePlugins,cacheDir,version),syncMarketplaceVersion(claudePlugins,version),syncPluginPackageVersion(claudePlugins,version),syncSkillsSymlink(claudePlugins,version),syncTmuxScripts(globalPkgDir),success(`Plugin synced to v${version}`)}async function resolveChannel(options){if(options.next)return"next";if(options.stable)return"latest";if(genieConfigExists())try{let config=await loadGenieConfig();if(config.updateChannel)return config.updateChannel}catch{}return"latest"}async function persistChannel(channel){try{let config=await loadGenieConfig();config.updateChannel=channel,await saveGenieConfig(config)}catch{}}async function updateCommand(options={}){console.log(),console.log("\x1B[1m\uD83E\uDDDE Genie CLI Update\x1B[0m"),console.log("\x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m"),console.log();let channel=await resolveChannel(options);if(options.next||options.stable)await persistChannel(channel);let installType=await detectInstallationType();if(log(`Detected installation: ${installType}`),log(`Channel: ${channel}${channel==="next"?" (dev builds)":" (stable)"}`),console.log(),installType==="unknown")error("No Genie CLI installation found"),console.log(),console.log("Install method not configured. Please reinstall genie:"),console.log("\x1B[36m curl -fsSL https://raw.githubusercontent.com/automagik-dev/genie/main/install.sh | bash\x1B[0m"),console.log(),process.exit(1);if(installType==="source"){await updateSource();return}let globalInstalls=await detectGlobalInstalls(),primaryMethod=installType;if(!(primaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))process.exit(1);let secondaryMethod=primaryMethod==="bun"?"npm":"bun";if(globalInstalls.has(secondaryMethod)){if(console.log(),log(`Also updating ${secondaryMethod}-global install...`),!(secondaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))error(`Secondary update via ${secondaryMethod} failed (non-blocking)`)}await syncPlugin(installType),await runPostUpdateMaintenanceSafe()}async function runPostUpdateMaintenanceSafe(){try{let{runPostUpdateMaintenance:runPostUpdateMaintenance2}=await Promise.resolve().then(() => (init_doctor(),exports_doctor));console.log(),log("Running post-update maintenance..."),await runPostUpdateMaintenance2()}catch(err){let msg=err instanceof Error?err.message:String(err);error(`Post-update maintenance skipped: ${msg}`)}}init_version();init_emit();init_trace_context();import{execSync as execSync5}from"child_process";var MAX_COMMITS=5;function getRecentGitHistory(filePath,cwd){try{let trimmed=execSync5(`git log --oneline -n ${MAX_COMMITS} -- ${JSON.stringify(filePath)}`,{encoding:"utf-8",timeout:5000,cwd,stdio:["pipe","pipe","pipe"]}).trim();if(!trimmed)return null;return trimmed}catch{return null}}async function auditContext(payload){let input=payload.tool_input;if(!input)return;let filePath=input.file_path;if(!filePath)return;let cwd=payload.cwd??process.cwd(),history=getRecentGitHistory(filePath,cwd);if(!history)return;return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[audit-context] Recent git history for ${filePath}:
|
|
3770
|
+
`;appendFileSync4(logPath,line,{mode:420})}catch{}}async function launchTui(){await recordTuiLaunchBreadcrumb();let{renderNav:renderNav2}=await init_render().then(() => exports_render);await renderNav2()}var TUI_CRASH_LOG_BANNER_PREFIX="--- tui-launch ",TUI_CRASH_LOG_RECOVERY_MAX_BYTES=65536,TUI_CRASH_LOG_RECOVERY_MAX_MSG_CHARS=3000;var init_tui=()=>{};var exports_resolve_agent_cwd={};__export(exports_resolve_agent_cwd,{resolveAgentFromCwd:()=>resolveAgentFromCwd});import{existsSync as existsSync61}from"fs";import{basename as basename15,dirname as dirname21,join as join76,relative as relative7,sep as sep3}from"path";function isRelativeWithin(rel,original){return!rel.startsWith("..")&&rel!==original}function resolveFromCanonicalDir(cwd,agentsDir){let relToAgents=relative7(agentsDir,cwd);if(!isRelativeWithin(relToAgents,cwd))return null;let segments=relToAgents.split(sep3).filter(Boolean);if(segments.length===0)return null;let agentName=segments[0];if(!existsSync61(join76(agentsDir,agentName,"AGENTS.md")))return null;let source=segments.length===1?"exact":"parent";return{agent:agentName,source}}function resolveFromWalkUp(cwd,workspaceRoot){let wsRel=relative7(workspaceRoot,cwd);if(!isRelativeWithin(wsRel,cwd))return null;let current=cwd;while(current!==workspaceRoot&¤t!==dirname21(current)){if(existsSync61(join76(current,"AGENTS.md")))return{agent:basename15(current),source:"parent"};current=dirname21(current)}return null}function resolveAgentFromCwd(cwd,workspaceRoot){return resolveFromCanonicalDir(cwd,join76(workspaceRoot,"agents"))??resolveFromWalkUp(cwd,workspaceRoot)??{agent:"genie",source:"default"}}var init_resolve_agent_cwd=()=>{};var import__=__toESM(require_commander(),1),{program,createCommand,createArgument,createOption,CommanderError,InvalidArgumentError,InvalidOptionArgumentError,Command,Argument,Option,Help}=import__.default;init_doctor();init_setup();init_shortcuts();import{existsSync as existsSync22}from"fs";import{homedir as homedir23}from"os";import{join as join28}from"path";async function shortcutsShowCommand(){displayShortcuts();let home=homedir23(),tmuxConf=join28(home,".tmux.conf"),zshrc=join28(home,".zshrc"),bashrc=join28(home,".bashrc");if(console.log("Installation status:"),isShortcutsInstalled(tmuxConf))console.log(" \x1B[32m\u2713\x1B[0m tmux.conf");else console.log(" \x1B[33m-\x1B[0m tmux.conf");let shellRc=existsSync22(zshrc)?zshrc:bashrc;if(isShortcutsInstalled(shellRc))console.log(` \x1B[32m\u2713\x1B[0m ${shellRc.replace(home,"~")}`);else console.log(` \x1B[33m-\x1B[0m ${shellRc.replace(home,"~")}`);console.log(),console.log("Run \x1B[36mgenie shortcuts install\x1B[0m to install shortcuts."),console.log("Run \x1B[36mgenie shortcuts uninstall\x1B[0m to remove shortcuts."),console.log()}async function shortcutsInstallCommand(){await installShortcuts()}async function shortcutsUninstallCommand(){await uninstallShortcuts()}init_esm14();init_claude_settings();init_genie_config2();import{existsSync as existsSync23,lstatSync,rmSync as rmSync2,unlinkSync as unlinkSync8}from"fs";import{homedir as homedir24}from"os";import{join as join29}from"path";var ORCHESTRATION_RULES_PATH=join29(homedir24(),".claude","rules","genie-orchestration.md"),LOCAL_BIN=join29(homedir24(),".local","bin"),SYMLINKS=["genie","term"];function isGenieSymlink(path3){try{if(!existsSync23(path3))return!1;if(!lstatSync(path3).isSymbolicLink())return!1;return!0}catch{return!1}}function removeSymlinks(){let removed=[];for(let name of SYMLINKS){let symlinkPath=join29(LOCAL_BIN,name);if(isGenieSymlink(symlinkPath))try{unlinkSync8(symlinkPath),removed.push(name)}catch{}}return removed}function tryRemoveStep(label,successMsg,fn){console.log(`\x1B[2m${label}\x1B[0m`);try{fn(),console.log(` \x1B[32m+\x1B[0m ${successMsg}`)}catch(error){let message=error instanceof Error?error.message:String(error);console.log(` \x1B[33m!\x1B[0m ${label.replace("...","")} failed: ${message}`)}}function performUninstall(hasHookScript,existingSymlinks,genieDir,hasGenieDir){if(hasHookScript)tryRemoveStep("Removing hook script...","Hook script removed",()=>removeHookScript());if(existingSymlinks.length>0){console.log("\x1B[2mRemoving symlinks...\x1B[0m");let removed=removeSymlinks();if(removed.length>0)console.log(` \x1B[32m+\x1B[0m Removed: ${removed.join(", ")}`)}if(existsSync23(ORCHESTRATION_RULES_PATH))tryRemoveStep("Removing orchestration rules...","Orchestration rules removed (~/.claude/rules/genie-orchestration.md)",()=>unlinkSync8(ORCHESTRATION_RULES_PATH));if(hasGenieDir)tryRemoveStep("Removing genie directory...","Directory removed",()=>rmSync2(genieDir,{recursive:!0,force:!0}))}async function uninstallCommand(){console.log(),console.log("\x1B[1m\x1B[33m Uninstall Genie CLI\x1B[0m"),console.log();let genieDir=getGenieDir(),hasGenieDir=existsSync23(genieDir),hasHookScript=hookScriptExists(),hasOrchestrationRules=existsSync23(ORCHESTRATION_RULES_PATH),existingSymlinks=SYMLINKS.filter((name)=>isGenieSymlink(join29(LOCAL_BIN,name)));if(console.log("\x1B[2mThis will remove:\x1B[0m"),hasHookScript)console.log(" \x1B[31m-\x1B[0m Hook script (~/.claude/hooks/genie-bash-hook.sh)");if(hasOrchestrationRules)console.log(" \x1B[31m-\x1B[0m Orchestration rules (~/.claude/rules/genie-orchestration.md)");if(hasGenieDir)console.log(` \x1B[31m-\x1B[0m Genie directory (${contractPath(genieDir)})`);if(existingSymlinks.length>0)console.log(` \x1B[31m-\x1B[0m Symlinks from ~/.local/bin: ${existingSymlinks.join(", ")}`);if(console.log(),!hasGenieDir&&!hasHookScript&&!hasOrchestrationRules&&existingSymlinks.length===0){console.log("\x1B[33mNothing to uninstall.\x1B[0m"),console.log();return}if(!await esm_default4({message:"Are you sure you want to uninstall Genie CLI?",default:!1})){console.log(),console.log("\x1B[2mUninstall cancelled.\x1B[0m"),console.log();return}console.log(),performUninstall(hasHookScript,existingSymlinks,genieDir,hasGenieDir),console.log(),console.log("\x1B[32m+\x1B[0m Genie CLI uninstalled."),console.log(),console.log("\x1B[2mNote: If you installed via npm/bun, also run:\x1B[0m"),console.log(" \x1B[36mbun remove -g @automagik/genie\x1B[0m"),console.log(" \x1B[2mor\x1B[0m"),console.log(" \x1B[36mnpm uninstall -g @automagik/genie\x1B[0m"),console.log()}init_genie_config2();import{execSync as execSync4,spawn as spawn3}from"child_process";import{chmodSync as chmodSync2,copyFileSync as copyFileSync3,existsSync as existsSync24,mkdirSync as mkdirSync13,readFileSync as readFileSync16,readdirSync as readdirSync8,rmSync as rmSync3,writeFileSync as writeFileSync11}from"fs";import{chmod,copyFile,mkdir as mkdir4,unlink as unlink2}from"fs/promises";import{homedir as homedir25}from"os";import{join as join30}from"path";var GENIE_HOME2=process.env.GENIE_HOME||join30(homedir25(),".genie"),GENIE_SRC=join30(GENIE_HOME2,"src"),GENIE_BIN=join30(GENIE_HOME2,"bin"),LOCAL_BIN2=join30(homedir25(),".local","bin");function log(message){console.log(`\x1B[32m\u25B8\x1B[0m ${message}`)}function success(message){console.log(`\x1B[32m\u2714\x1B[0m ${message}`)}function error(message){console.log(`\x1B[31m\u2716\x1B[0m ${message}`)}async function runCommand(command,args,cwd){return new Promise((resolve4)=>{let output=[],child=spawn3(command,args,{cwd,stdio:["inherit","pipe","pipe"],env:{...process.env,FORCE_COLOR:"1"}});child.stdout?.on("data",(data)=>{let str2=data.toString();output.push(str2),process.stdout.write(str2)}),child.stderr?.on("data",(data)=>{let str2=data.toString();output.push(str2),process.stderr.write(str2)}),child.on("close",(code)=>{resolve4({success:code===0,output:output.join("")})}),child.on("error",(err)=>{error(err.message),resolve4({success:!1,output:err.message})})})}async function getGitInfo(cwd){try{let branchResult=await runCommandSilent("git",["rev-parse","--abbrev-ref","HEAD"],cwd),commitResult=await runCommandSilent("git",["rev-parse","--short","HEAD"],cwd),dateResult=await runCommandSilent("git",["log","-1","--format=%ci"],cwd);if(branchResult.success&&commitResult.success&&dateResult.success)return{branch:branchResult.output.trim(),commit:commitResult.output.trim(),commitDate:dateResult.output.trim().split(" ")[0]}}catch{}return null}async function runCommandSilent(command,args,cwd,timeoutMs=4000){return new Promise((resolve4)=>{let output=[],settled=!1,child=spawn3(command,args,{cwd,stdio:["inherit","pipe","pipe"]}),timer2=setTimeout(()=>{if(settled)return;settled=!0,child.kill("SIGTERM"),resolve4({success:!1,output:`Timed out after ${timeoutMs}ms`})},timeoutMs);child.stdout?.on("data",(data)=>{output.push(data.toString())}),child.stderr?.on("data",(data)=>{output.push(data.toString())}),child.on("close",(code)=>{if(settled)return;settled=!0,clearTimeout(timer2),resolve4({success:code===0,output:output.join("")})}),child.on("error",(err)=>{if(settled)return;settled=!0,clearTimeout(timer2),resolve4({success:!1,output:err.message})})})}function detectFromBinaryPath(path3){let resolved=path3;try{resolved=__require("fs").realpathSync(path3)}catch{}if(resolved.includes(".bun"))return"bun";if(resolved.includes("node_modules"))return"npm";if(path3===join30(LOCAL_BIN2,"genie")||resolved.startsWith(GENIE_BIN))return"source";return null}async function detectInstallationType(){let whichResult=await runCommandSilent("which",["genie"]);if(whichResult.success){let detected=detectFromBinaryPath(whichResult.output.trim());if(detected)return detected}if(genieConfigExists())try{let config=await loadGenieConfig();if(config.installMethod)return config.installMethod}catch{}if(existsSync24(join30(GENIE_SRC,".git")))return"source";return(await runCommandSilent("which",["bun"])).success?"bun":"npm"}async function updateViaBun(channel){try{__require("fs").unlinkSync(join30(homedir25(),".bun","install","global","bun.lock"))}catch{}if(log(`Updating via bun (channel: ${channel})...`),!(await runCommand("bun",["add","-g","--force","--no-cache",`@automagik/genie@${channel}`])).success)return error("Failed to update via bun"),!1;return console.log(),success(`Genie CLI updated via bun (${channel})!`),!0}async function updateViaNpm(channel){if(log(`Updating via npm (channel: ${channel})...`),!(await runCommand("npm",["install","-g",`@automagik/genie@${channel}`])).success)return error("Failed to update via npm"),!1;return console.log(),success(`Genie CLI updated via npm (${channel})!`),!0}async function detectGlobalInstalls(){let found=new Set,[npmResult,bunResult]=await Promise.all([runCommandSilent("npm",["list","-g","@automagik/genie"]),runCommandSilent("bun",["pm","ls","-g"])]);if(npmResult.success&&!npmResult.output.includes("(empty)"))found.add("npm");if(bunResult.success&&bunResult.output.includes("@automagik/genie"))found.add("bun");return found}async function updateSource(){let beforeInfo=await getGitInfo(GENIE_SRC);if(beforeInfo)console.log(`Current: \x1B[2m${beforeInfo.branch}@${beforeInfo.commit} (${beforeInfo.commitDate})\x1B[0m`),console.log();if(log("Fetching latest changes..."),!(await runCommand("git",["fetch","origin"],GENIE_SRC)).success)error("Failed to fetch from origin"),process.exit(1);if(log("Resetting to origin/main..."),!(await runCommand("git",["reset","--hard","origin/main"],GENIE_SRC)).success)error("Failed to reset to origin/main"),process.exit(1);console.log();let afterInfo=await getGitInfo(GENIE_SRC);if(beforeInfo&&afterInfo&&beforeInfo.commit===afterInfo.commit){success("Already up to date!"),console.log();return}if(log("Installing dependencies..."),!(await runCommand("bun",["install"],GENIE_SRC)).success)error("Failed to install dependencies"),process.exit(1);if(console.log(),log("Building..."),!(await runCommand("bun",["run","build"],GENIE_SRC)).success)error("Failed to build"),process.exit(1);console.log(),log("Installing binaries...");try{await mkdir4(GENIE_BIN,{recursive:!0}),await mkdir4(LOCAL_BIN2,{recursive:!0});let binaries=["genie.js","term.js"],names=["genie","term"];for(let i2=0;i2<binaries.length;i2++){let src=join30(GENIE_SRC,"dist",binaries[i2]),binDest=join30(GENIE_BIN,binaries[i2]),linkDest=join30(LOCAL_BIN2,names[i2]);await copyFile(src,binDest),await chmod(binDest,493),await symlinkOrCopy(binDest,linkDest)}for(let legacy of["claudio.js","claudio"]){let legacyBin=join30(GENIE_BIN,legacy),legacyLink=join30(LOCAL_BIN2,legacy);try{await unlink2(legacyBin)}catch{}try{await unlink2(legacyLink)}catch{}}success("Binaries installed")}catch(err){error(`Failed to install binaries: ${err}`),process.exit(1)}if(console.log(),console.log("\x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m"),success("Genie CLI updated successfully!"),console.log(),afterInfo)console.log(`Version: \x1B[36m${afterInfo.branch}@${afterInfo.commit}\x1B[0m (${afterInfo.commitDate})`),console.log()}async function symlinkOrCopy(src,dest){let{symlink:symlink2,unlink:unlink3}=await import("fs/promises");try{if(existsSync24(dest))await unlink3(dest);await symlink2(src,dest)}catch{await copyFile(src,dest)}}function copyDirSync(src,dest){mkdirSync13(dest,{recursive:!0});for(let entry2 of readdirSync8(src,{withFileTypes:!0})){let srcPath=join30(src,entry2.name),destPath=join30(dest,entry2.name);if(entry2.isDirectory())copyDirSync(srcPath,destPath);else copyFileSync3(srcPath,destPath)}}async function resolveGlobalPkgDir(installType){if(installType==="bun"){let bunPath=join30(homedir25(),".bun","install","global","node_modules","@automagik","genie");if(existsSync24(bunPath))return bunPath}if(installType==="npm"){let npmRootResult=await runCommandSilent("npm",["root","-g"]);if(npmRootResult.success){let npmPath=join30(npmRootResult.output.trim(),"@automagik","genie");if(existsSync24(npmPath))return npmPath}}let bunFallback=join30(homedir25(),".bun","install","global","node_modules","@automagik","genie");if(existsSync24(bunFallback))return bunFallback;let npmRootFallback=await runCommandSilent("npm",["root","-g"]);if(npmRootFallback.success){let npmPath=join30(npmRootFallback.output.trim(),"@automagik","genie");if(existsSync24(npmPath))return npmPath}return null}function updatePluginRegistry(claudePlugins,cacheDir,version){let registryPath=join30(claudePlugins,"installed_plugins.json");try{if(!existsSync24(registryPath))return;let registry=JSON.parse(readFileSync16(registryPath,"utf-8")),entries=registry.plugins?.["genie@automagik"];if(!Array.isArray(entries))return;for(let entry2 of entries)if(entry2.scope==="user")entry2.installPath=cacheDir,entry2.version=version,entry2.lastUpdated=new Date().toISOString();writeFileSync11(registryPath,JSON.stringify(registry,null,2))}catch(err){log(`Registry update failed (non-fatal): ${err}`)}}function syncTmuxConf(tmuxScriptsSrc){mkdirSync13(GENIE_HOME2,{recursive:!0});let tmuxConfSrc=join30(tmuxScriptsSrc,"genie.tmux.conf"),tmuxConfDest=join30(GENIE_HOME2,"tmux.conf");if(existsSync24(tmuxConfSrc))try{copyFileSync3(tmuxConfSrc,tmuxConfDest),success(`Installed tmux config to ${tmuxConfDest}`);try{let{tmuxBin:tmuxBin2}=(init_ensure_tmux(),__toCommonJS(exports_ensure_tmux));execSync4(`${tmuxBin2()} -L genie source-file '${tmuxConfDest}'`,{stdio:"ignore"}),success("Reloaded genie tmux server configuration")}catch{}}catch{}let tuiConfSrc=join30(tmuxScriptsSrc,"tui-tmux.conf"),tuiConfDest=join30(GENIE_HOME2,"tui-tmux.conf");if(existsSync24(tuiConfSrc))try{copyFileSync3(tuiConfSrc,tuiConfDest),success(`Installed TUI tmux config to ${tuiConfDest}`)}catch{}let themeSrc=join30(tmuxScriptsSrc,".generated.theme.conf"),themeDest=join30(GENIE_HOME2,".generated.theme.conf");if(existsSync24(themeSrc))try{copyFileSync3(themeSrc,themeDest),success(`Installed tmux theme to ${themeDest}`)}catch{}let osc52Src=join30(tmuxScriptsSrc,"osc52-copy.sh"),osc52Dest=join30(GENIE_HOME2,"scripts","osc52-copy.sh");if(existsSync24(osc52Src))try{copyFileSync3(osc52Src,osc52Dest),chmodSync2(osc52Dest,493),success(`Installed OSC 52 clipboard helper to ${osc52Dest}`)}catch{}}function syncTmuxScripts(globalPkgDir){let tmuxScriptsSrc=join30(globalPkgDir,"scripts","tmux");if(!existsSync24(tmuxScriptsSrc))return;let scriptsDir=join30(GENIE_HOME2,"scripts");mkdirSync13(scriptsDir,{recursive:!0});let scriptCount=0;for(let entry2 of readdirSync8(tmuxScriptsSrc))if(entry2.endsWith(".sh")||entry2==="genie.tmux.conf"||entry2==="tui-tmux.conf"||entry2===".generated.theme.conf"){let src=join30(tmuxScriptsSrc,entry2),dest=join30(scriptsDir,entry2);copyFileSync3(src,dest);try{chmodSync2(dest,entry2.endsWith(".sh")?493:420)}catch{}scriptCount++}if(scriptCount>0)success(`Refreshed ${scriptCount} tmux scripts at ${scriptsDir}`);syncTmuxConf(tmuxScriptsSrc)}function syncMarketplaceVersion(claudePlugins,version){let marketplacePath=join30(claudePlugins,"marketplaces","automagik",".claude-plugin","marketplace.json");try{if(!existsSync24(marketplacePath))return;let data=JSON.parse(readFileSync16(marketplacePath,"utf-8"));if(Array.isArray(data.plugins)){for(let plugin of data.plugins)if(plugin.name==="genie")plugin.version=version}writeFileSync11(marketplacePath,JSON.stringify(data,null,2)),success(`Updated marketplace.json to v${version}`)}catch(err){log(`Marketplace version update failed (non-fatal): ${err}`)}}function syncPluginPackageVersion(claudePlugins,version){let pkgPath=join30(claudePlugins,"marketplaces","automagik","plugins","genie","package.json");try{if(!existsSync24(pkgPath))return;let data=JSON.parse(readFileSync16(pkgPath,"utf-8"));data.version=version,writeFileSync11(pkgPath,JSON.stringify(data,null,2)),success(`Updated plugin package.json to v${version}`)}catch(err){log(`Plugin package.json update failed (non-fatal): ${err}`)}}function syncSkillsSymlink(claudePlugins,version){let skillsLink=join30(claudePlugins,"marketplaces","automagik","plugins","genie","skills"),cacheSkills=join30("..","..","..","..","cache","automagik","genie",version,"skills");try{let{symlinkSync,unlinkSync:unlinkSync9,lstatSync:lstatSync2}=__require("fs");try{lstatSync2(skillsLink),unlinkSync9(skillsLink)}catch{}symlinkSync(cacheSkills,skillsLink),success(`Skills symlink \u2192 cache/${version}/skills`)}catch(err){log(`Skills symlink update failed (non-fatal): ${err}`)}}async function syncPlugin(installType){log("Syncing Claude Code plugin...");let globalPkgDir=await resolveGlobalPkgDir(installType);if(!globalPkgDir){log("Could not find installed package \u2014 skipping plugin sync");return}let pluginSrc=join30(globalPkgDir,"plugins","genie");if(!existsSync24(pluginSrc)){log("Plugin source not found in package \u2014 skipping plugin sync");return}let version;try{version=JSON.parse(readFileSync16(join30(globalPkgDir,"package.json"),"utf-8")).version}catch{log("Could not read package version \u2014 skipping plugin sync");return}let claudePlugins=join30(homedir25(),".claude","plugins"),cacheDir=join30(claudePlugins,"cache","automagik","genie",version);try{if(existsSync24(cacheDir))rmSync3(cacheDir,{recursive:!0,force:!0});copyDirSync(pluginSrc,cacheDir);let skillsSrc=join30(globalPkgDir,"skills");if(existsSync24(skillsSrc)&&!existsSync24(join30(cacheDir,"skills")))copyDirSync(skillsSrc,join30(cacheDir,"skills"))}catch(err){error(`Failed to copy plugin: ${err}`);return}updatePluginRegistry(claudePlugins,cacheDir,version),syncMarketplaceVersion(claudePlugins,version),syncPluginPackageVersion(claudePlugins,version),syncSkillsSymlink(claudePlugins,version),syncTmuxScripts(globalPkgDir),success(`Plugin synced to v${version}`)}async function resolveChannel(options){if(options.next)return"next";if(options.stable)return"latest";if(genieConfigExists())try{let config=await loadGenieConfig();if(config.updateChannel)return config.updateChannel}catch{}return"latest"}async function persistChannel(channel){try{let config=await loadGenieConfig();config.updateChannel=channel,await saveGenieConfig(config)}catch{}}async function updateCommand(options={}){console.log(),console.log("\x1B[1m\uD83E\uDDDE Genie CLI Update\x1B[0m"),console.log("\x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m"),console.log();let channel=await resolveChannel(options);if(options.next||options.stable)await persistChannel(channel);let installType=await detectInstallationType();if(log(`Detected installation: ${installType}`),log(`Channel: ${channel}${channel==="next"?" (dev builds)":" (stable)"}`),console.log(),installType==="unknown")error("No Genie CLI installation found"),console.log(),console.log("Install method not configured. Please reinstall genie:"),console.log("\x1B[36m curl -fsSL https://raw.githubusercontent.com/automagik-dev/genie/main/install.sh | bash\x1B[0m"),console.log(),process.exit(1);if(installType==="source"){await updateSource();return}let globalInstalls=await detectGlobalInstalls(),primaryMethod=installType;if(!(primaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))process.exit(1);let secondaryMethod=primaryMethod==="bun"?"npm":"bun";if(globalInstalls.has(secondaryMethod)){if(console.log(),log(`Also updating ${secondaryMethod}-global install...`),!(secondaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))error(`Secondary update via ${secondaryMethod} failed (non-blocking)`)}await syncPlugin(installType),await runPostUpdateMaintenanceSafe()}async function runPostUpdateMaintenanceSafe(){try{let{runPostUpdateMaintenance:runPostUpdateMaintenance2}=await Promise.resolve().then(() => (init_doctor(),exports_doctor));console.log(),log("Running post-update maintenance..."),await runPostUpdateMaintenance2()}catch(err){let msg=err instanceof Error?err.message:String(err);error(`Post-update maintenance skipped: ${msg}`)}}init_version();init_emit();init_trace_context();import{execSync as execSync5}from"child_process";var MAX_COMMITS=5;function getRecentGitHistory(filePath,cwd){try{let trimmed=execSync5(`git log --oneline -n ${MAX_COMMITS} -- ${JSON.stringify(filePath)}`,{encoding:"utf-8",timeout:5000,cwd,stdio:["pipe","pipe","pipe"]}).trim();if(!trimmed)return null;return trimmed}catch{return null}}async function auditContext(payload){let input=payload.tool_input;if(!input)return;let filePath=input.file_path;if(!filePath)return;let cwd=payload.cwd??process.cwd(),history=getRecentGitHistory(filePath,cwd);if(!history)return;return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[audit-context] Recent git history for ${filePath}:
|
|
3769
3771
|
${history}`}}}function buildSearchNames(recipient,dirEntry){let names=new Set([recipient]);if(dirEntry){if(names.add(dirEntry.entry.name),dirEntry.entry.roles)for(let role of dirEntry.entry.roles)names.add(role)}return names}function buildSpawnArgs(template){let args=["spawn","--provider",template.provider,"--team",template.team];if(template.role)args.push("--role",template.role);if(template.skill)args.push("--skill",template.skill);if(template.cwd)args.push("--cwd",template.cwd);if(template.extraArgs)args.push(...template.extraArgs);return args}async function isRecipientLeader(recipient,teamName){try{let{getTeam:getTeam2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),config=await getTeam2(teamName);return!!config?.leader&&recipient===config.leader}catch{return!1}}function extractAutoSpawnTarget(payload){let input=payload.tool_input;if(!input||input.type!=="message")return null;let recipient=input.recipient;if(!recipient)return null;let teamName=process.env.GENIE_TEAM??payload.team_name;if(!teamName)return null;if(recipient==="team-lead")return null;return{recipient,teamName}}async function executeAutoSpawn(recipient,teamName){let registryMod=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),executorRegistryMod=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),directoryMod=await Promise.resolve().then(() => (init_agent_directory(),exports_agent_directory)),existing=(await registryMod.list()).find((a)=>(a.role===recipient||a.id===recipient)&&a.team===teamName);if(existing&&await executorRegistryMod.resolveWorkerLivenessByTransport(existing))return;let dirEntry=await directoryMod.resolve(recipient),templates=await registryMod.listTemplates(),searchNames=buildSearchNames(recipient,dirEntry),template=templates.find((t)=>{if(t.team!==teamName)return!1;return[...searchNames].some((q)=>t.id===q||t.role===q)});if(!template){if(dirEntry)console.error(`[genie-hook] Agent "${recipient}" is registered in directory but has no spawn template in team "${teamName}".`);return}let{spawnSync:spawnSync3}=__require("child_process");spawnSync3("genie",buildSpawnArgs(template),{timeout:1e4,stdio:"ignore",env:{...process.env,GENIE_TEAM:teamName}}),console.error(`[genie-hook] Auto-spawned "${recipient}" in team "${teamName}"`)}async function autoSpawn(payload){let target=extractAutoSpawnTarget(payload);if(!target)return;let{recipient,teamName}=target;if(await isRecipientLeader(recipient,teamName))return;try{await executeAutoSpawn(recipient,teamName)}catch(err){let msg=err instanceof Error?err.message:String(err);return console.error(`[genie-hook] Auto-spawn failed for "${recipient}": ${msg}`),{hookSpecificOutput:{hookEventName:"PreToolUse",additionalContext:`auto-spawn warning: failed to spawn "${recipient}": ${msg}`}}}}import{existsSync as existsSync27}from"fs";import{basename as basename4,join as join32}from"path";var BRAIN_PKG="@khal-os/brain",BRAIN_DIR="node_modules/@khal-os/brain",enrichedSessions=new Set;function sessionKey(payload){return payload.session_id??`${process.pid}`}function isBrainAvailable(){return existsSync27(join32(BRAIN_DIR,"package.json"))}async function queryBrain(cwd){try{let brain=await import(BRAIN_PKG);if(!brain.search)return null;let projectName=basename4(cwd),results=await brain.search({query:`context for ${projectName}`,limit:5,minScore:0.5});if(!results||results.length===0)return null;return results.map((r)=>{return`- ${(r.content??r.text??"").slice(0,200)}`}).join(`
|
|
3770
3772
|
`)}catch{return null}}async function brainInject(payload){let key=sessionKey(payload);if(enrichedSessions.has(key))return;if(enrichedSessions.add(key),!isBrainAvailable())return;let cwd=payload.cwd??process.cwd();try{let context=await queryBrain(cwd);if(!context)return;return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[brain-inject] Prior context from knowledge base:
|
|
3771
3773
|
${context}`}}}catch{return}}import{spawnSync as spawnSync3}from"child_process";var ALLOWED_MERGE_BASES=new Set(["dev"]),SYNC_DENY_PATTERNS=[{test:(cmd)=>/git\s+push\b/i.test(cmd)&&/(?:^|\s)(main|master)(?:\s|$)/.test(cmd),reason:"BLOCKED: Push to main/master is FORBIDDEN. Push to a feature branch and create a PR targeting dev."},{test:(cmd)=>/git\s+push\b/.test(cmd)&&/:(main|master)\b/.test(cmd),reason:"BLOCKED: Push refspec targeting main/master is FORBIDDEN."},{test:(cmd)=>/gh\s+pr\s+create\b/.test(cmd)&&!/(--base|-B)\s+\S/.test(cmd),reason:"BLOCKED: gh pr create requires explicit --base flag. Use: gh pr create --base dev (or --base main for releases)"},{test:(cmd)=>/git\s+checkout\s+(main|master)\s*[;&|]+\s*git\s+(commit|merge|cherry-pick|rebase|push|add)\b/.test(cmd),reason:"BLOCKED: Committing or mutating on main/master is FORBIDDEN. Work on feature branches."}],STDERR_SURFACE_CAP=500,defaultDeps={async resolvePrBase(prNum,repo){let args=["pr","view",prNum,"--json","baseRefName","-q",".baseRefName"];if(repo)args.push("--repo",repo);let result2;try{result2=spawnSync3("gh",args,{encoding:"utf8",timeout:1e4,stdio:["ignore","pipe","pipe"]})}catch(err){return{reason:`spawnSync threw: ${(err instanceof Error?err.message:String(err)).slice(0,STDERR_SURFACE_CAP)}`}}if(result2.error)return{reason:`subprocess error: ${result2.error.message.slice(0,STDERR_SURFACE_CAP)}`};let stderr=typeof result2.stderr==="string"?result2.stderr.trim():"";if(result2.status!==0){let tail=stderr.slice(-STDERR_SURFACE_CAP)||"no stderr captured";return{reason:`gh pr view exited ${result2.status}: ${tail}`}}let stdout=typeof result2.stdout==="string"?result2.stdout.trim():"";if(!stdout)return{reason:`gh pr view exited 0 with empty stdout (stderr: ${stderr.slice(-STDERR_SURFACE_CAP)||"none"})`};return{base:stdout}}};function extractPrNumber(cmd){let match=cmd.match(/gh\s+pr\s+merge\s+(\d+)\b/);return match?match[1]:null}function extractRepoFlag(cmd){let match=cmd.match(/(?:--repo|-R)(?:\s+|=)([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)\b/);return match?match[1]:null}function stepUnquoted(ch){if(ch==="'")return{out:" ",next:"single",consumed:1};if(ch==='"')return{out:" ",next:"double",consumed:1};return{out:ch,next:"none",consumed:1}}function stepSingleQuoted(ch){return{out:" ",next:ch==="'"?"none":"single",consumed:1}}function stepDoubleQuoted(ch,hasNext){if(ch==="\\"&&hasNext)return{out:" ",next:"double",consumed:2};return{out:" ",next:ch==='"'?"none":"double",consumed:1}}function maskQuotedRegions(cmd){let out="",state="none",i2=0;while(i2<cmd.length){let step=state==="double"?stepDoubleQuoted(cmd[i2],i2+1<cmd.length):state==="single"?stepSingleQuoted(cmd[i2]):stepUnquoted(cmd[i2]);out+=step.out,state=step.next,i2+=step.consumed}return out}async function branchGuard(payload,deps=defaultDeps){let input=payload.tool_input;if(!input)return;let command=input.command;if(!command)return;let matchTarget=maskQuotedRegions(command);if(!/\b(git|gh)\b/.test(matchTarget))return;for(let pattern of SYNC_DENY_PATTERNS)if(pattern.test(matchTarget))return{decision:"deny",reason:pattern.reason};if(/gh\s+pr\s+merge\b/.test(matchTarget)){let prNum=extractPrNumber(matchTarget);if(!prNum)return{decision:"deny",reason:"BLOCKED: `gh pr merge` requires an explicit PR number so the target base branch can be verified. \xA719 (v2): agents merge PRs targeting `dev` only; main/master is humans-only via GitHub UI."};let repo=extractRepoFlag(matchTarget)??void 0,resolved=await deps.resolvePrBase(prNum,repo);if("reason"in resolved)return{decision:"deny",reason:`BLOCKED: could not resolve base branch of PR #${prNum} \u2014 ${resolved.reason}. \xA719 (v2): cannot merge without verifying base is \`dev\`. Check the PR exists and try again, or ask a human to merge via GitHub UI.`};let base=resolved.base;if(!ALLOWED_MERGE_BASES.has(base))return{decision:"deny",reason:`BLOCKED: PR #${prNum} targets \`${base}\`. \xA719 (v2): agents may merge PRs targeting \`dev\` only. Main/master merges are humans-only via GitHub UI.`}}return}import{execSync as execSync6}from"child_process";import{statSync as statSync5}from"fs";var STALENESS_THRESHOLD_SECS=120;function getLastCommitInfo(filePath,cwd){try{let trimmed=execSync6(`git log -1 --format="%at|%an|%s" -- ${JSON.stringify(filePath)}`,{encoding:"utf-8",timeout:5000,cwd,stdio:["pipe","pipe","pipe"]}).trim();if(!trimmed)return null;let[timestampStr,author,...messageParts]=trimmed.split("|"),timestamp2=Number.parseInt(timestampStr,10);if(Number.isNaN(timestamp2))return null;let age=Math.floor(Date.now()/1000)-timestamp2;return{author:author??"unknown",age,message:messageParts.join("|")}}catch{return null}}function getFileModAge(filePath){try{let stat4=statSync5(filePath);return Math.floor((Date.now()-stat4.mtimeMs)/1000)}catch{return null}}function buildCommitWarning(filePath,commitInfo){return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[freshness] Stale read warning: ${filePath} was modified ${commitInfo.age}s ago by "${commitInfo.author}" (${commitInfo.message}). Contents may have changed since you last read it.`}}}function checkUncommittedChanges(filePath,cwd,diskAge){try{if(execSync6(`git status --porcelain -- ${JSON.stringify(filePath)}`,{encoding:"utf-8",timeout:5000,cwd,stdio:["pipe","pipe","pipe"]}).trim())return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[freshness] Stale read warning: ${filePath} has uncommitted changes (modified ${diskAge}s ago). Another agent may be editing this file concurrently.`}}}catch{}return}async function freshness(payload){let input=payload.tool_input;if(!input)return;let filePath=input.file_path;if(!filePath)return;let cwd=payload.cwd??process.cwd(),currentAgent=process.env.GENIE_AGENT_NAME,diskAge=getFileModAge(filePath);if(diskAge===null||diskAge>=STALENESS_THRESHOLD_SECS)return;let commitInfo=getLastCommitInfo(filePath,cwd);if(commitInfo&&commitInfo.age<STALENESS_THRESHOLD_SECS){if(currentAgent&&commitInfo.author.includes(currentAgent))return;return buildCommitWarning(filePath,commitInfo)}if(currentAgent)return checkUncommittedChanges(filePath,cwd,diskAge);return}async function identityInject(payload){let input=payload.tool_input;if(!input)return;let msgType=input.type;if(msgType&&msgType!=="message"&&msgType!=="broadcast")return;let agentName=process.env.GENIE_AGENT_NAME;if(!agentName)return;let contentField=input.content!==void 0?"content":"message",content=input[contentField];if(!content)return;if(content.startsWith(`[from:${agentName}]`))return;return{updatedInput:{...input,[contentField]:`[from:${agentName}] ${content}`}}}var NUDGE_PATTERNS=[{test:/tmux\s+capture-pane/,message:`If you're checking genie agent progress, use structured monitoring instead:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genie",
|
|
3
|
-
"version": "4.260427.
|
|
3
|
+
"version": "4.260427.8",
|
|
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"
|