@automagik/genie 4.260421.13 → 4.260421.15

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
@@ -1819,9 +1819,10 @@ ${body}`;writeFileSync12(filePath,output,"utf-8")}function serializeSdkConfig(sd
1819
1819
  INSERT INTO sessions (id, agent_id, executor_id, team, wish_slug, task_id, role, project_path, jsonl_path, status, last_ingested_offset, total_turns, parent_session_id, is_subagent, file_size, file_mtime)
1820
1820
  VALUES (${sessionId}, ${worker?.agentId??null}, ${worker?.executorId??null}, ${worker?.team??null}, ${worker?.wishSlug??null}, ${worker?.taskId??null}, ${worker?.role??null}, ${projectPath}, ${jsonlPath}, ${worker?"active":"orphaned"}, 0, 0, ${parentSessionId}, ${opts?.isSubagent??!1}, ${opts?.fileSize??0}, ${opts?.mtime??0})
1821
1821
  ON CONFLICT (id) DO NOTHING
1822
- `,workerToContext(worker)}async function reconcileSubagentParents(sql){return(await sql`
1822
+ `,workerToContext(worker)}async function reconcileSubagentParents(sql){let linkResult=await sql`
1823
1823
  UPDATE sessions s
1824
- SET parent_session_id = p.id
1824
+ SET parent_session_id = p.id,
1825
+ updated_at = now()
1825
1826
  FROM sessions p
1826
1827
  WHERE s.is_subagent = true
1827
1828
  AND s.parent_session_id IS NULL
@@ -1831,7 +1832,35 @@ ${body}`;writeFileSync12(filePath,output,"utf-8")}function serializeSdkConfig(sd
1831
1832
  '.*/',
1832
1833
  ''
1833
1834
  )
1834
- `).count??0}function stringifyInput(input){if(!input)return null;return typeof input==="string"?input:JSON.stringify(input)}function extractBlockOutput(block){if(typeof block.content==="string")return block.content;return block.content?extractTextContent(block.content):null}function extractContentBlocks(entry2){if(!entry2.message?.content||!Array.isArray(entry2.message.content))return[];return entry2.message.content.filter((b2)=>typeof b2==="object"&&b2!==null)}function buildToolEventRow(pending,toolUseId,output,isError,ctx){return{session_id:ctx.sessionId,turn_index:pending.turnIndex,timestamp:pending.timestamp,tool_name:pending.name,sub_tool:extractSubTool(pending.name,pending.input),tool_use_id:toolUseId,input_raw:stringifyInput(pending.input),output_raw:output,is_error:isError,error_message:isError?output?.slice(0,1000)??null:null,duration_ms:null,agent_id:ctx.agentId,team:ctx.team,wish_slug:ctx.wishSlug,task_id:ctx.taskId}}function processToolUseBlocks(blocks,sessionId,turnIndex,timestamp2,contentRows,pendingToolUses){let idx=turnIndex;for(let block of blocks){if(block.type!=="tool_use"||!block.name||!block.id)continue;contentRows.push({session_id:sessionId,turn_index:idx,role:"tool_input",content:stringifyInput(block.input)??"",tool_name:block.name,timestamp:timestamp2}),idx++,pendingToolUses.set(block.id,{name:block.name,input:block.input,turnIndex:idx-1,timestamp:timestamp2})}return idx}function processToolResultBlocks(blocks,sessionId,turnIndex,timestamp2,contentRows,toolEvents,pendingToolUses,ctx){let idx=turnIndex;for(let block of blocks){if(block.type!=="tool_result"||!block.tool_use_id)continue;let output=extractBlockOutput(block);if(output&&output.length>0)contentRows.push({session_id:sessionId,turn_index:idx,role:"tool_output",content:output,tool_name:null,timestamp:timestamp2}),idx++;let pending=pendingToolUses.get(block.tool_use_id);if(pending){let isError=block.is_error===!0||typeof output==="string"&&output.includes("<tool_use_error>");toolEvents.push(buildToolEventRow(pending,block.tool_use_id,output,isError,ctx)),pendingToolUses.delete(block.tool_use_id)}}return idx}function parseJsonlChunk(data,sessionId,startTurnIndex,context){let contentRows=[],toolEvents=[],turnIndex=startTurnIndex,pendingToolUses=new Map,ctx={sessionId,...context};for(let line of data.split(`
1835
+ `,metaResult=await sql`
1836
+ UPDATE sessions s
1837
+ SET
1838
+ agent_id = COALESCE(s.agent_id, p.agent_id),
1839
+ executor_id = COALESCE(
1840
+ s.executor_id,
1841
+ CASE
1842
+ WHEN s.agent_id IS NULL OR s.agent_id = p.agent_id THEN p.executor_id
1843
+ ELSE NULL
1844
+ END
1845
+ ),
1846
+ team = COALESCE(s.team, p.team),
1847
+ wish_slug = COALESCE(s.wish_slug, p.wish_slug),
1848
+ task_id = COALESCE(s.task_id, p.task_id),
1849
+ role = COALESCE(s.role, p.role),
1850
+ updated_at = now()
1851
+ FROM sessions p
1852
+ WHERE s.is_subagent = true
1853
+ AND s.parent_session_id = p.id
1854
+ AND (
1855
+ (s.agent_id IS NULL AND p.agent_id IS NOT NULL) OR
1856
+ (s.executor_id IS NULL AND p.executor_id IS NOT NULL
1857
+ AND (s.agent_id IS NULL OR s.agent_id = p.agent_id)) OR
1858
+ (s.team IS NULL AND p.team IS NOT NULL) OR
1859
+ (s.wish_slug IS NULL AND p.wish_slug IS NOT NULL) OR
1860
+ (s.task_id IS NULL AND p.task_id IS NOT NULL) OR
1861
+ (s.role IS NULL AND p.role IS NOT NULL)
1862
+ )
1863
+ `;return{linked:linkResult.count??0,metadataFilled:metaResult.count??0}}function stringifyInput(input){if(!input)return null;return typeof input==="string"?input:JSON.stringify(input)}function extractBlockOutput(block){if(typeof block.content==="string")return block.content;return block.content?extractTextContent(block.content):null}function extractContentBlocks(entry2){if(!entry2.message?.content||!Array.isArray(entry2.message.content))return[];return entry2.message.content.filter((b2)=>typeof b2==="object"&&b2!==null)}function buildToolEventRow(pending,toolUseId,output,isError,ctx){return{session_id:ctx.sessionId,turn_index:pending.turnIndex,timestamp:pending.timestamp,tool_name:pending.name,sub_tool:extractSubTool(pending.name,pending.input),tool_use_id:toolUseId,input_raw:stringifyInput(pending.input),output_raw:output,is_error:isError,error_message:isError?output?.slice(0,1000)??null:null,duration_ms:null,agent_id:ctx.agentId,team:ctx.team,wish_slug:ctx.wishSlug,task_id:ctx.taskId}}function processToolUseBlocks(blocks,sessionId,turnIndex,timestamp2,contentRows,pendingToolUses){let idx=turnIndex;for(let block of blocks){if(block.type!=="tool_use"||!block.name||!block.id)continue;contentRows.push({session_id:sessionId,turn_index:idx,role:"tool_input",content:stringifyInput(block.input)??"",tool_name:block.name,timestamp:timestamp2}),idx++,pendingToolUses.set(block.id,{name:block.name,input:block.input,turnIndex:idx-1,timestamp:timestamp2})}return idx}function processToolResultBlocks(blocks,sessionId,turnIndex,timestamp2,contentRows,toolEvents,pendingToolUses,ctx){let idx=turnIndex;for(let block of blocks){if(block.type!=="tool_result"||!block.tool_use_id)continue;let output=extractBlockOutput(block);if(output&&output.length>0)contentRows.push({session_id:sessionId,turn_index:idx,role:"tool_output",content:output,tool_name:null,timestamp:timestamp2}),idx++;let pending=pendingToolUses.get(block.tool_use_id);if(pending){let isError=block.is_error===!0||typeof output==="string"&&output.includes("<tool_use_error>");toolEvents.push(buildToolEventRow(pending,block.tool_use_id,output,isError,ctx)),pendingToolUses.delete(block.tool_use_id)}}return idx}function parseJsonlChunk(data,sessionId,startTurnIndex,context){let contentRows=[],toolEvents=[],turnIndex=startTurnIndex,pendingToolUses=new Map,ctx={sessionId,...context};for(let line of data.split(`
1835
1864
  `)){if(!line.trim())continue;let entry2;try{entry2=JSON.parse(line)}catch{continue}if(entry2.type!=="assistant")continue;let timestamp2=entry2.timestamp??new Date().toISOString(),blocks=extractContentBlocks(entry2),text=extractTextContent(entry2.message?.content);if(text&&text.length>0)contentRows.push({session_id:sessionId,turn_index:turnIndex,role:"assistant",content:text,tool_name:null,timestamp:timestamp2}),turnIndex++;turnIndex=processToolUseBlocks(blocks,sessionId,turnIndex,timestamp2,contentRows,pendingToolUses),turnIndex=processToolResultBlocks(blocks,sessionId,turnIndex,timestamp2,contentRows,toolEvents,pendingToolUses,ctx)}for(let[toolUseId,pending]of pendingToolUses)toolEvents.push(buildToolEventRow(pending,toolUseId,null,!1,ctx));return{contentRows,toolEvents,turnCount:turnIndex-startTurnIndex}}async function batchInsertContent(sql,rows){if(rows.length===0)return;await sql`
1836
1865
  INSERT INTO session_content (session_id, turn_index, role, content, tool_name, timestamp)
1837
1866
  SELECT * FROM unnest(
@@ -1879,7 +1908,7 @@ ${body}`;writeFileSync12(filePath,output,"utf-8")}function serializeSdkConfig(sd
1879
1908
  processed_bytes = ${progress.processedBytes},
1880
1909
  errors = ${progress.errors},
1881
1910
  updated_at = now()
1882
- `}async function shouldSkipBackfill(sql){try{let existing=await sql`SELECT status FROM session_sync WHERE id = 'backfill'`;if(existing.length>0&&existing[0].status==="complete")return!0}catch{}try{let[{count}]=await sql`SELECT count(*)::int as count FROM sessions`;if(count>0){let existing=await sql`SELECT status FROM session_sync WHERE id = 'backfill'`;if(existing.length===0||existing[0].status==="complete")return!0}}catch{return!0}return!1}async function yieldToLiveWork(){while(liveWorkPending)await sleep2(LIVE_YIELD_POLL_MS)}async function getFileStartOffset(sql,file){let existing=await sql`SELECT last_ingested_offset FROM sessions WHERE id = ${file.sessionId}`;if(existing.length>0)return existing[0].last_ingested_offset??0;return 0}async function processBackfillFile(sql,file,progress,workerMap){let offset=await getFileStartOffset(sql,file);if(offset>=file.fileSize){progress.processedFiles++,progress.processedBytes+=file.fileSize;return}let currentOffset=offset;while(currentOffset<file.fileSize){await yieldToLiveWork();let result2=await ingestFile(sql,file.sessionId,file.jsonlPath,file.projectPath,currentOffset,{chunkSize:CHUNK_SIZE,parentSessionId:file.parentSessionId,isSubagent:file.isSubagent,fileSize:file.fileSize,mtime:file.mtime,workerMap});if(result2.newOffset<=currentOffset)break;progress.processedBytes+=result2.newOffset-currentOffset,currentOffset=result2.newOffset}progress.processedFiles++}async function processAllFiles(sql,allFiles,progress,workerMap){for(let file of allFiles){if(!running)break;await yieldToLiveWork();try{await processBackfillFile(sql,file,progress,workerMap)}catch(err){progress.errors++;let message=err instanceof Error?err.message:String(err);console.error(`[backfill] error on ${file.jsonlPath}: ${message}`)}if(progress.processedFiles%50===0)await updateSyncState(sql,progress);await sleep2(SLEEP_BETWEEN_FILES_MS)}}function resolveBackfillStatus(progress){if(!running)progress.status="paused",console.log(`[backfill] paused: ${progress.processedFiles}/${progress.totalFiles} files (will resume on next daemon start)`);else if(progress.errors>0&&progress.errors>=progress.totalFiles)progress.status="failed",console.error(`[backfill] failed: ${progress.errors}/${progress.totalFiles} files errored \u2014 will retry on next daemon start`);else progress.status="complete",console.log(`[backfill] complete: ${progress.processedFiles}/${progress.totalFiles} files, ${progress.errors} errors`)}async function startBackfill(sql){if(running)return;if(await shouldSkipBackfill(sql))return;running=!0,console.log("[backfill] starting session backfill...");try{let allFiles=await discoverAllJsonlFiles();allFiles.sort(compareBackfillFiles);let totalBytes=allFiles.reduce((sum,f)=>sum+f.fileSize,0),progress={totalFiles:allFiles.length,processedFiles:0,totalBytes,processedBytes:0,errors:0,status:"running"};await updateSyncState(sql,progress),console.log(`[backfill] discovered ${allFiles.length} files (${(totalBytes/1024/1024).toFixed(1)} MB)`);let workerMap=await buildWorkerMap(sql);await processAllFiles(sql,allFiles,progress,workerMap);try{let fixed=await reconcileSubagentParents(sql);if(fixed>0)console.log(`[backfill] reconciled parent_session_id for ${fixed} subagent(s)`)}catch(err){let message=err instanceof Error?err.message:String(err);console.warn(`[backfill] parent reconcile skipped: ${message}`)}resolveBackfillStatus(progress),await updateSyncState(sql,progress)}catch(err){let message=err instanceof Error?err.message:String(err);console.error(`[backfill] fatal error: ${message}`)}finally{running=!1}}function stopBackfill(){running=!1}async function getBackfillStatus(sql){try{let rows=await sql`SELECT * FROM session_sync WHERE id = 'backfill'`;if(rows.length===0)return null;let row=rows[0];return{totalFiles:row.total_files,processedFiles:row.processed_files,totalBytes:row.total_bytes,processedBytes:row.processed_bytes,errors:row.errors,status:row.status}}catch{return null}}var CHUNK_SIZE=65536,SLEEP_BETWEEN_FILES_MS=100,LIVE_YIELD_POLL_MS=200,running=!1;var init_session_backfill=__esm(()=>{init_session_capture()});var exports_scheduler_daemon={};__export(exports_scheduler_daemon,{terminalizeCleanExitUnverified:()=>terminalizeCleanExitUnverified,startDaemon:()=>startDaemon,runAgentRecoveryPass:()=>runAgentRecoveryPass,recoverOnStartup:()=>recoverOnStartup,reconcileUnresumable:()=>reconcileUnresumable,reconcileOrphans:()=>reconcileOrphans,reconcileOrphanedRuns:()=>reconcileOrphanedRuns,reclaimExpiredLeases:()=>reclaimExpiredLeases,processMailboxRetryMessage:()=>processMailboxRetryMessage,logToFile:()=>logToFile,logReconcilerMode:()=>logReconcilerMode,isTurnAwareReconcilerEnabled:()=>isTurnAwareReconcilerEnabled,fireTrigger:()=>fireTrigger,emitWorkerEvents:()=>emitWorkerEvents,collectMachineSnapshot:()=>collectMachineSnapshot,collectHeartbeats:()=>collectHeartbeats,claimDueTriggers:()=>claimDueTriggers,attemptAgentResume:()=>attemptAgentResume,_resetWorkerStatesForTesting:()=>_resetWorkerStatesForTesting,TURN_AWARE_RECONCILER_FLAG:()=>TURN_AWARE_RECONCILER_FLAG,MAX_DELIVERY_ATTEMPTS:()=>MAX_DELIVERY_ATTEMPTS,ESCALATION_RECIPIENT:()=>ESCALATION_RECIPIENT});import{randomUUID as randomUUID9}from"crypto";import{appendFileSync as appendFileSync3,mkdirSync as mkdirSync12}from"fs";import{homedir as homedir26}from"os";import{join as join40}from"path";function isTurnAwareReconcilerEnabled(env=process.env){let raw=env[TURN_AWARE_RECONCILER_FLAG];if(raw===void 0)return!0;let v=raw.trim().toLowerCase();if(v==="")return!0;if(v==="0"||v==="false"||v==="no")return!1;if(v==="1"||v==="true"||v==="yes")return!0;return!0}function logReconcilerMode(deps,daemonId){let enabled=isTurnAwareReconcilerEnabled();deps.log({timestamp:deps.now().toISOString(),level:"info",event:enabled?"reconciler_mode_turn_aware":"reconciler_mode_legacy",daemon_id:daemonId,flag:TURN_AWARE_RECONCILER_FLAG,enabled,message:enabled?"turn-aware reconciler enabled":"flag off, using legacy reconciler"})}function getLogDir2(){return join40(process.env.GENIE_HOME??join40(homedir26(),".genie"),"logs")}function getLogFile(){return join40(getLogDir2(),"scheduler.log")}function logToFile(entry2){let logDir=getLogDir2();mkdirSync12(logDir,{recursive:!0});let enriched=entry2.trace_id?entry2:withAmbientTraceId(entry2);appendFileSync3(getLogFile(),`${JSON.stringify(enriched)}
1911
+ `}async function shouldSkipBackfill(sql){try{let existing=await sql`SELECT status FROM session_sync WHERE id = 'backfill'`;if(existing.length>0&&existing[0].status==="complete")return!0}catch{}try{let[{count}]=await sql`SELECT count(*)::int as count FROM sessions`;if(count>0){let existing=await sql`SELECT status FROM session_sync WHERE id = 'backfill'`;if(existing.length===0||existing[0].status==="complete")return!0}}catch{return!0}return!1}async function yieldToLiveWork(){while(liveWorkPending)await sleep2(LIVE_YIELD_POLL_MS)}async function getFileStartOffset(sql,file){let existing=await sql`SELECT last_ingested_offset FROM sessions WHERE id = ${file.sessionId}`;if(existing.length>0)return existing[0].last_ingested_offset??0;return 0}async function processBackfillFile(sql,file,progress,workerMap){let offset=await getFileStartOffset(sql,file);if(offset>=file.fileSize){progress.processedFiles++,progress.processedBytes+=file.fileSize;return}let currentOffset=offset;while(currentOffset<file.fileSize){await yieldToLiveWork();let result2=await ingestFile(sql,file.sessionId,file.jsonlPath,file.projectPath,currentOffset,{chunkSize:CHUNK_SIZE,parentSessionId:file.parentSessionId,isSubagent:file.isSubagent,fileSize:file.fileSize,mtime:file.mtime,workerMap});if(result2.newOffset<=currentOffset)break;progress.processedBytes+=result2.newOffset-currentOffset,currentOffset=result2.newOffset}progress.processedFiles++}async function processAllFiles(sql,allFiles,progress,workerMap){for(let file of allFiles){if(!running)break;await yieldToLiveWork();try{await processBackfillFile(sql,file,progress,workerMap)}catch(err){progress.errors++;let message=err instanceof Error?err.message:String(err);console.error(`[backfill] error on ${file.jsonlPath}: ${message}`)}if(progress.processedFiles%50===0)await updateSyncState(sql,progress);await sleep2(SLEEP_BETWEEN_FILES_MS)}}function resolveBackfillStatus(progress){if(!running)progress.status="paused",console.log(`[backfill] paused: ${progress.processedFiles}/${progress.totalFiles} files (will resume on next daemon start)`);else if(progress.errors>0&&progress.errors>=progress.totalFiles)progress.status="failed",console.error(`[backfill] failed: ${progress.errors}/${progress.totalFiles} files errored \u2014 will retry on next daemon start`);else progress.status="complete",console.log(`[backfill] complete: ${progress.processedFiles}/${progress.totalFiles} files, ${progress.errors} errors`)}async function startBackfill(sql){if(running)return;if(await shouldSkipBackfill(sql))return;running=!0,console.log("[backfill] starting session backfill...");try{let allFiles=await discoverAllJsonlFiles();allFiles.sort(compareBackfillFiles);let totalBytes=allFiles.reduce((sum,f)=>sum+f.fileSize,0),progress={totalFiles:allFiles.length,processedFiles:0,totalBytes,processedBytes:0,errors:0,status:"running"};await updateSyncState(sql,progress),console.log(`[backfill] discovered ${allFiles.length} files (${(totalBytes/1024/1024).toFixed(1)} MB)`);let workerMap=await buildWorkerMap(sql);await processAllFiles(sql,allFiles,progress,workerMap);try{let{linked,metadataFilled}=await reconcileSubagentParents(sql);if(linked>0)console.log(`[backfill] reconciled parent_session_id for ${linked} subagent(s)`);if(metadataFilled>0)console.log(`[backfill] inherited parent metadata for ${metadataFilled} subagent(s)`)}catch(err){let message=err instanceof Error?err.message:String(err);console.warn(`[backfill] parent reconcile skipped: ${message}`)}resolveBackfillStatus(progress),await updateSyncState(sql,progress)}catch(err){let message=err instanceof Error?err.message:String(err);console.error(`[backfill] fatal error: ${message}`)}finally{running=!1}}function stopBackfill(){running=!1}async function getBackfillStatus(sql){try{let rows=await sql`SELECT * FROM session_sync WHERE id = 'backfill'`;if(rows.length===0)return null;let row=rows[0];return{totalFiles:row.total_files,processedFiles:row.processed_files,totalBytes:row.total_bytes,processedBytes:row.processed_bytes,errors:row.errors,status:row.status}}catch{return null}}var CHUNK_SIZE=65536,SLEEP_BETWEEN_FILES_MS=100,LIVE_YIELD_POLL_MS=200,running=!1;var init_session_backfill=__esm(()=>{init_session_capture()});var exports_scheduler_daemon={};__export(exports_scheduler_daemon,{terminalizeCleanExitUnverified:()=>terminalizeCleanExitUnverified,startDaemon:()=>startDaemon,runAgentRecoveryPass:()=>runAgentRecoveryPass,recoverOnStartup:()=>recoverOnStartup,reconcileUnresumable:()=>reconcileUnresumable,reconcileOrphans:()=>reconcileOrphans,reconcileOrphanedRuns:()=>reconcileOrphanedRuns,reclaimExpiredLeases:()=>reclaimExpiredLeases,processMailboxRetryMessage:()=>processMailboxRetryMessage,logToFile:()=>logToFile,logReconcilerMode:()=>logReconcilerMode,isTurnAwareReconcilerEnabled:()=>isTurnAwareReconcilerEnabled,fireTrigger:()=>fireTrigger,emitWorkerEvents:()=>emitWorkerEvents,collectMachineSnapshot:()=>collectMachineSnapshot,collectHeartbeats:()=>collectHeartbeats,claimDueTriggers:()=>claimDueTriggers,attemptAgentResume:()=>attemptAgentResume,_resetWorkerStatesForTesting:()=>_resetWorkerStatesForTesting,TURN_AWARE_RECONCILER_FLAG:()=>TURN_AWARE_RECONCILER_FLAG,MAX_DELIVERY_ATTEMPTS:()=>MAX_DELIVERY_ATTEMPTS,ESCALATION_RECIPIENT:()=>ESCALATION_RECIPIENT});import{randomUUID as randomUUID9}from"crypto";import{appendFileSync as appendFileSync3,mkdirSync as mkdirSync12}from"fs";import{homedir as homedir26}from"os";import{join as join40}from"path";function isTurnAwareReconcilerEnabled(env=process.env){let raw=env[TURN_AWARE_RECONCILER_FLAG];if(raw===void 0)return!0;let v=raw.trim().toLowerCase();if(v==="")return!0;if(v==="0"||v==="false"||v==="no")return!1;if(v==="1"||v==="true"||v==="yes")return!0;return!0}function logReconcilerMode(deps,daemonId){let enabled=isTurnAwareReconcilerEnabled();deps.log({timestamp:deps.now().toISOString(),level:"info",event:enabled?"reconciler_mode_turn_aware":"reconciler_mode_legacy",daemon_id:daemonId,flag:TURN_AWARE_RECONCILER_FLAG,enabled,message:enabled?"turn-aware reconciler enabled":"flag off, using legacy reconciler"})}function getLogDir2(){return join40(process.env.GENIE_HOME??join40(homedir26(),".genie"),"logs")}function getLogFile(){return join40(getLogDir2(),"scheduler.log")}function logToFile(entry2){let logDir=getLogDir2();mkdirSync12(logDir,{recursive:!0});let enriched=entry2.trace_id?entry2:withAmbientTraceId(entry2);appendFileSync3(getLogFile(),`${JSON.stringify(enriched)}
1883
1912
  `)}function withAmbientTraceId(entry2){let ctx=getAmbient();if(!ctx)return entry2;return{...entry2,trace_id:ctx.trace_id}}async function defaultSpawnCommand(command,env){return{pid:Bun.spawn(["sh","-c",command],{env:{...process.env,...env},stdio:["ignore","ignore","ignore"]}).pid}}function defaultJitter(maxMs){return Math.floor(Math.random()*maxMs)}function defaultSleep(ms){return new Promise((resolve5)=>setTimeout(resolve5,ms))}async function defaultIsPaneAlive(paneId){let{isPaneAlive:isPaneAlive2}=await Promise.resolve().then(() => (init_tmux(),exports_tmux));return isPaneAlive2(paneId)}async function defaultListWorkers(){let{list:list2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));return(await list2()).map((a)=>({id:a.id,paneId:a.paneId,repoPath:a.repoPath,state:a.state,team:a.team,wishSlug:a.wishSlug,groupNumber:a.groupNumber,autoResume:a.autoResume,resumeAttempts:a.resumeAttempts,maxResumeAttempts:a.maxResumeAttempts,lastResumeAttempt:a.lastResumeAttempt,claudeSessionId:a.claudeSessionId}))}async function defaultPublishEvent(subject,data,repoPath){let payload=data,{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(repoPath,subject,{timestamp:payload.timestamp,kind:payload.kind??"system",agent:payload.agent??"scheduler",team:payload.team,direction:payload.direction,peer:payload.peer,text:payload.text??subject,data:payload.data,source:payload.source??"registry"})}async function defaultCountTmuxSessions(){try{let{execSync:execSync10}=await import("child_process"),{genieTmuxCmd:genieTmuxCmd2}=await Promise.resolve().then(() => (init_tmux_wrapper(),exports_tmux_wrapper));return execSync10(`${genieTmuxCmd2("list-sessions")} 2>/dev/null`,{encoding:"utf-8"}).trim().split(`
1884
1913
  `).filter(Boolean).length}catch{return 0}}async function defaultResumeAgent(agentId){try{let{execSync:execSync10}=await import("child_process");return execSync10(`genie agent resume ${agentId} --no-reset-attempts`,{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}),!0}catch{return!1}}async function defaultUpdateAgent(agentId,updates){let{update:update2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));await update2(agentId,updates)}function createDefaultDeps(){return{getConnection:async()=>{let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db));return getConnection2()},spawnCommand:defaultSpawnCommand,log:logToFile,generateId:randomUUID9,now:()=>new Date,sleep:defaultSleep,jitter:defaultJitter,isPaneAlive:defaultIsPaneAlive,listWorkers:defaultListWorkers,countTmuxSessions:defaultCountTmuxSessions,publishEvent:defaultPublishEvent,resumeAgent:defaultResumeAgent,updateAgent:defaultUpdateAgent}}function resolveConfig(overrides){let envMax=process.env.GENIE_MAX_CONCURRENT,maxConcurrent=envMax?Number.parseInt(envMax,10):5;return{maxConcurrent:overrides?.maxConcurrent??(Number.isNaN(maxConcurrent)?5:maxConcurrent),pollIntervalMs:overrides?.pollIntervalMs??30000,maxJitterMs:overrides?.maxJitterMs??30000,jitterThreshold:overrides?.jitterThreshold??3,heartbeatIntervalMs:overrides?.heartbeatIntervalMs??60000,orphanCheckIntervalMs:overrides?.orphanCheckIntervalMs??300000,deadHeartbeatThreshold:overrides?.deadHeartbeatThreshold??2,leaseRecoveryIntervalMs:overrides?.leaseRecoveryIntervalMs??60000}}async function claimDueTriggers(deps,config,daemonId){let sql=await deps.getConnection(),now=deps.now(),leaseUntil=new Date(now.getTime()+300000),runningCount=(await sql`
1885
1914
  SELECT count(*)::int AS cnt FROM runs
@@ -3580,7 +3609,7 @@ Bus `);for(let i2=1;i2<parts.length;i2++){let usb2=parseLinuxUsb(parts[i2]);resu
3580
3609
  `)}init_setup();init_shortcuts();import{existsSync as existsSync19}from"fs";import{homedir as homedir19}from"os";import{join as join22}from"path";async function shortcutsShowCommand(){displayShortcuts();let home=homedir19(),tmuxConf=join22(home,".tmux.conf"),zshrc=join22(home,".zshrc"),bashrc=join22(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=existsSync19(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 existsSync20,lstatSync,rmSync as rmSync2,unlinkSync as unlinkSync8}from"fs";import{homedir as homedir20}from"os";import{join as join23}from"path";var ORCHESTRATION_RULES_PATH=join23(homedir20(),".claude","rules","genie-orchestration.md"),LOCAL_BIN=join23(homedir20(),".local","bin"),SYMLINKS=["genie","term"];function isGenieSymlink(path3){try{if(!existsSync20(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=join23(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(existsSync20(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=existsSync20(genieDir),hasHookScript=hookScriptExists(),hasOrchestrationRules=existsSync20(ORCHESTRATION_RULES_PATH),existingSymlinks=SYMLINKS.filter((name)=>isGenieSymlink(join23(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 copyFileSync2,existsSync as existsSync21,mkdirSync as mkdirSync10,readFileSync as readFileSync15,readdirSync as readdirSync6,rmSync as rmSync3,writeFileSync as writeFileSync9}from"fs";import{chmod,copyFile,mkdir as mkdir4,unlink as unlink2}from"fs/promises";import{homedir as homedir21}from"os";import{join as join24}from"path";var GENIE_HOME2=process.env.GENIE_HOME||join24(homedir21(),".genie"),GENIE_SRC=join24(GENIE_HOME2,"src"),GENIE_BIN=join24(GENIE_HOME2,"bin"),LOCAL_BIN2=join24(homedir21(),".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((resolve3)=>{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)=>{resolve3({success:code===0,output:output.join("")})}),child.on("error",(err)=>{error(err.message),resolve3({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((resolve3)=>{let output=[],settled=!1,child=spawn3(command,args,{cwd,stdio:["inherit","pipe","pipe"]}),timer2=setTimeout(()=>{if(settled)return;settled=!0,child.kill("SIGTERM"),resolve3({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),resolve3({success:code===0,output:output.join("")})}),child.on("error",(err)=>{if(settled)return;settled=!0,clearTimeout(timer2),resolve3({success:!1,output:err.message})})})}function detectFromBinaryPath(path3){if(path3.includes(".bun"))return"bun";if(path3.includes("node_modules"))return"npm";if(path3===join24(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(existsSync21(join24(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(join24(homedir21(),".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=join24(GENIE_SRC,"dist",binaries[i2]),binDest=join24(GENIE_BIN,binaries[i2]),linkDest=join24(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=join24(GENIE_BIN,legacy),legacyLink=join24(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(existsSync21(dest))await unlink3(dest);await symlink2(src,dest)}catch{await copyFile(src,dest)}}function copyDirSync(src,dest){mkdirSync10(dest,{recursive:!0});for(let entry2 of readdirSync6(src,{withFileTypes:!0})){let srcPath=join24(src,entry2.name),destPath=join24(dest,entry2.name);if(entry2.isDirectory())copyDirSync(srcPath,destPath);else copyFileSync2(srcPath,destPath)}}async function resolveGlobalPkgDir(installType){if(installType==="bun"){let bunPath=join24(homedir21(),".bun","install","global","node_modules","@automagik","genie");if(existsSync21(bunPath))return bunPath}if(installType==="npm"){let npmRootResult=await runCommandSilent("npm",["root","-g"]);if(npmRootResult.success){let npmPath=join24(npmRootResult.output.trim(),"@automagik","genie");if(existsSync21(npmPath))return npmPath}}let bunFallback=join24(homedir21(),".bun","install","global","node_modules","@automagik","genie");if(existsSync21(bunFallback))return bunFallback;let npmRootFallback=await runCommandSilent("npm",["root","-g"]);if(npmRootFallback.success){let npmPath=join24(npmRootFallback.output.trim(),"@automagik","genie");if(existsSync21(npmPath))return npmPath}return null}function updatePluginRegistry(claudePlugins,cacheDir,version){let registryPath=join24(claudePlugins,"installed_plugins.json");try{if(!existsSync21(registryPath))return;let registry=JSON.parse(readFileSync15(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();writeFileSync9(registryPath,JSON.stringify(registry,null,2))}catch(err){log(`Registry update failed (non-fatal): ${err}`)}}function syncTmuxConf(tmuxScriptsSrc){mkdirSync10(GENIE_HOME2,{recursive:!0});let tmuxConfSrc=join24(tmuxScriptsSrc,"genie.tmux.conf"),tmuxConfDest=join24(GENIE_HOME2,"tmux.conf");if(existsSync21(tmuxConfSrc))try{copyFileSync2(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=join24(tmuxScriptsSrc,"tui-tmux.conf"),tuiConfDest=join24(GENIE_HOME2,"tui-tmux.conf");if(existsSync21(tuiConfSrc))try{copyFileSync2(tuiConfSrc,tuiConfDest),success(`Installed TUI tmux config to ${tuiConfDest}`)}catch{}let osc52Src=join24(tmuxScriptsSrc,"osc52-copy.sh"),osc52Dest=join24(GENIE_HOME2,"scripts","osc52-copy.sh");if(existsSync21(osc52Src))try{copyFileSync2(osc52Src,osc52Dest),chmodSync2(osc52Dest,493),success(`Installed OSC 52 clipboard helper to ${osc52Dest}`)}catch{}}function syncTmuxScripts(globalPkgDir){let tmuxScriptsSrc=join24(globalPkgDir,"scripts","tmux");if(!existsSync21(tmuxScriptsSrc))return;let scriptsDir=join24(GENIE_HOME2,"scripts");mkdirSync10(scriptsDir,{recursive:!0});let scriptCount=0;for(let entry2 of readdirSync6(tmuxScriptsSrc))if(entry2.endsWith(".sh")||entry2==="genie.tmux.conf"||entry2==="tui-tmux.conf"){let src=join24(tmuxScriptsSrc,entry2),dest=join24(scriptsDir,entry2);copyFileSync2(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=join24(claudePlugins,"marketplaces","automagik",".claude-plugin","marketplace.json");try{if(!existsSync21(marketplacePath))return;let data=JSON.parse(readFileSync15(marketplacePath,"utf-8"));if(Array.isArray(data.plugins)){for(let plugin of data.plugins)if(plugin.name==="genie")plugin.version=version}writeFileSync9(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=join24(claudePlugins,"marketplaces","automagik","plugins","genie","package.json");try{if(!existsSync21(pkgPath))return;let data=JSON.parse(readFileSync15(pkgPath,"utf-8"));data.version=version,writeFileSync9(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=join24(claudePlugins,"marketplaces","automagik","plugins","genie","skills"),cacheSkills=join24("..","..","..","..","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=join24(globalPkgDir,"plugins","genie");if(!existsSync21(pluginSrc)){log("Plugin source not found in package \u2014 skipping plugin sync");return}let version;try{version=JSON.parse(readFileSync15(join24(globalPkgDir,"package.json"),"utf-8")).version}catch{log("Could not read package version \u2014 skipping plugin sync");return}let claudePlugins=join24(homedir21(),".claude","plugins"),cacheDir=join24(claudePlugins,"cache","automagik","genie",version);try{if(existsSync21(cacheDir))rmSync3(cacheDir,{recursive:!0,force:!0});copyDirSync(pluginSrc,cacheDir);let skillsSrc=join24(globalPkgDir,"skills");if(existsSync21(skillsSrc)&&!existsSync21(join24(cacheDir,"skills")))copyDirSync(skillsSrc,join24(cacheDir,"skills"))}catch(err){error(`Failed to copy plugin: ${err}`);return}updatePluginRegistry(claudePlugins,cacheDir,version),syncMarketplaceVersion(claudePlugins,version),syncPluginPackageVersion(claudePlugins,version),syncSkillsSymlink(claudePlugins,version),syncTmuxScripts(globalPkgDir),success(`Plugin synced to v${version}`)}async function resolveChannel(options){if(options.next)return"next";if(options.stable)return"latest";if(genieConfigExists())try{let config=await loadGenieConfig();if(config.updateChannel)return config.updateChannel}catch{}return"latest"}async function persistChannel(channel){try{let config=await loadGenieConfig();config.updateChannel=channel,await saveGenieConfig(config)}catch{}}async function updateCommand(options={}){console.log(),console.log("\x1B[1m\uD83E\uDDDE Genie CLI Update\x1B[0m"),console.log("\x1B[2m\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\x1B[0m"),console.log();let channel=await resolveChannel(options);if(options.next||options.stable)await persistChannel(channel);let installType=await detectInstallationType();if(log(`Detected installation: ${installType}`),log(`Channel: ${channel}${channel==="next"?" (dev builds)":" (stable)"}`),console.log(),installType==="unknown")error("No Genie CLI installation found"),console.log(),console.log("Install method not configured. Please reinstall genie:"),console.log("\x1B[36m curl -fsSL https://raw.githubusercontent.com/automagik-dev/genie/main/install.sh | bash\x1B[0m"),console.log(),process.exit(1);if(installType==="source"){await updateSource();return}let globalInstalls=await detectGlobalInstalls(),primaryMethod=installType;if(!(primaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))process.exit(1);let secondaryMethod=primaryMethod==="bun"?"npm":"bun";if(globalInstalls.has(secondaryMethod)){if(console.log(),log(`Also updating ${secondaryMethod}-global install...`),!(secondaryMethod==="bun"?await updateViaBun(channel):await updateViaNpm(channel)))error(`Secondary update via ${secondaryMethod} failed (non-blocking)`)}await syncPlugin(installType)}init_version();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}:
3581
3610
  ${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:spawnSync2}=__require("child_process");spawnSync2("genie",buildSpawnArgs(template),{timeout:1e4,stdio:"ignore",env:{...process.env,GENIE_TEAM:teamName}}),console.error(`[genie-hook] Auto-spawned "${recipient}" in team "${teamName}"`)}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 existsSync24}from"fs";import{basename as basename3,join as join26}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 existsSync24(join26(BRAIN_DIR,"package.json"))}async function queryBrain(cwd){try{let brain=await import(BRAIN_PKG);if(!brain.search)return null;let projectName=basename3(cwd),results=await brain.search({query:`context for ${projectName}`,limit:5,minScore:0.5});if(!results||results.length===0)return null;return results.map((r)=>{return`- ${(r.content??r.text??"").slice(0,200)}`}).join(`
3582
3611
  `)}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:
3583
- ${context}`}}}catch{return}}import{spawnSync as spawnSync2}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){let result2;try{result2=spawnSync2("gh",["pr","view",prNum,"--json","baseRefName","-q",".baseRefName"],{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 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 resolved=await deps.resolvePrBase(prNum);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 statSync3}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 stat2=statSync3(filePath);return Math.floor((Date.now()-stat2.mtimeMs)/1000)}catch{return null}}function buildCommitWarning(filePath,commitInfo){return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[freshness] Stale read warning: ${filePath} was modified ${commitInfo.age}s ago by "${commitInfo.author}" (${commitInfo.message}). Contents may have changed since you last read it.`}}}function checkUncommittedChanges(filePath,cwd,diskAge){try{if(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:
3612
+ ${context}`}}}catch{return}}import{spawnSync as spawnSync2}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=spawnSync2("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 statSync3}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 stat2=statSync3(filePath);return Math.floor((Date.now()-stat2.mtimeMs)/1000)}catch{return null}}function buildCommitWarning(filePath,commitInfo){return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[freshness] Stale read warning: ${filePath} was modified ${commitInfo.age}s ago by "${commitInfo.author}" (${commitInfo.message}). Contents may have changed since you last read it.`}}}function checkUncommittedChanges(filePath,cwd,diskAge){try{if(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:
3584
3613
  `+` genie task status <slug> \u2014 wish progress from PG
3585
3614
  `+` genie agent list --json \u2014 executor state machine
3586
3615
  `+` genie events timeline <id> \u2014 structured event log
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260421.13",
3
+ "version": "4.260421.15",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260421.13",
3
+ "version": "4.260421.15",
4
4
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
5
5
  "author": {
6
6
  "name": "Namastex Labs"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260421.13",
3
+ "version": "4.260421.15",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",