@automagik/genie 4.260418.1 → 4.260418.3

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
@@ -654,7 +654,7 @@ ${body}`;writeFileSync9(filePath,output,"utf-8")}function serializeSdkConfig(sdk
654
654
  LIMIT $${values2.length+1}
655
655
  `,[...values2,limit])).map(rowToRuntimeEvent)}async function getLatestRuntimeEventId(){let rows=await(await getConnection())`
656
656
  SELECT COALESCE(MAX(id), 0) AS max_id FROM genie_runtime_events
657
- `;return Number(rows[0]?.max_id??0)}async function followRuntimeEvents(query,onEvent,options){let sql=await getConnection(),active=!0,lastSeenId=query.afterId??await getLatestRuntimeEventId(),drainChain=Promise.resolve(),drain=async()=>{if(!active)return;let events=await listRuntimeEvents({...query,afterId:lastSeenId});for(let event of events)lastSeenId=event.id,onEvent(event)},queueDrain=(context)=>{drainChain=drainChain.then(drain).catch((error2)=>{logFollowDrainError(context,error2,active)})},listener=await sql.listen("genie_runtime_event",()=>{queueDrain("notify")});await drain();let pollIntervalMs=options?.pollIntervalMs??1000,pollTimer=setInterval(()=>{queueDrain("poll")},pollIntervalMs);return{mode:"pg",stop:async()=>{active=!1,clearInterval(pollTimer),await drainChain,await listener.unlisten()}}}async function waitForRuntimeEvent(query,timeoutMs,predicate){let afterId=query.afterId??0;return new Promise((resolve4)=>{let settled=!1,handle=null,timer2=null,finish=async(event)=>{if(settled)return;if(settled=!0,timer2)clearTimeout(timer2);if(handle)await handle.stop();resolve4(event)};(async()=>{handle=await followRuntimeEvents({...query,afterId},(event)=>{if(predicate&&!predicate(event))return;finish(event)},{pollIntervalMs:250}),timer2=setTimeout(()=>{finish(null)},timeoutMs)})()})}var circuitBreaker,eventsEmitted=0,eventsFailed=0,lastEmitDuration=0;var init_runtime_events=__esm(()=>{init_db();circuitBreaker=new EventCircuitBreaker});var exports_workspace={};__export(exports_workspace,{validateWorkspaceDefaults:()=>validateWorkspaceDefaults,scanAgents:()=>scanAgents2,migrateWorkspaceConfig:()=>migrateWorkspaceConfig,getWorkspaceConfig:()=>getWorkspaceConfig,genieHome:()=>genieHome2,findWorkspace:()=>findWorkspace,AgentDefaultsSchema:()=>AgentDefaultsSchema});import{existsSync as existsSync22,mkdirSync as mkdirSync9,readFileSync as readFileSync14,readdirSync as readdirSync5,realpathSync as realpathSync2,writeFileSync as writeFileSync10}from"fs";import{homedir as homedir18,tmpdir as tmpdir2}from"os";import{dirname as dirname4,join as join24,resolve as resolve4,sep}from"path";function findWorkspace(cwd){let startDir=resolve4(cwd??process.cwd()),current=startDir;while(!0){let candidate=join24(current,WORKSPACE_MARKER);if(existsSync22(candidate)){saveWorkspaceRoot(current);let agent=detectAgent(startDir,current);return{root:current,agent:agent??void 0}}let parent=dirname4(current);if(parent===current)break;current=parent}let savedRoot=loadWorkspaceRoot();if(savedRoot&&existsSync22(join24(savedRoot,WORKSPACE_MARKER))){let agent=detectAgent(startDir,savedRoot);return{root:savedRoot,agent:agent??void 0}}return null}function genieHome2(){return process.env.GENIE_HOME??join24(homedir18(),".genie")}function isTempPath(root){try{let canonicalTmp=realpathSync2(tmpdir2()),canonicalRoot=realpathSync2(root);return canonicalRoot===canonicalTmp||canonicalRoot.startsWith(canonicalTmp+sep)}catch{return!0}}function saveWorkspaceRoot(root){if(isTempPath(root))return;try{let home=genieHome2(),configPath2=join24(home,"config.json"),config=existsSync22(configPath2)?JSON.parse(readFileSync14(configPath2,"utf-8")):{};if(config.workspaceRoot===root)return;config.workspaceRoot=root,mkdirSync9(home,{recursive:!0}),writeFileSync10(configPath2,`${JSON.stringify(config,null,2)}
657
+ `;return Number(rows[0]?.max_id??0)}async function followRuntimeEvents(query,onEvent,options){let sql=await getConnection(),active=!0,lastSeenId=query.afterId??await getLatestRuntimeEventId(),drainChain=Promise.resolve(),drain=async()=>{if(!active)return;let events=await listRuntimeEvents({...query,afterId:lastSeenId});for(let event of events)lastSeenId=event.id,onEvent(event)},queueDrain=(context)=>{drainChain=drainChain.then(drain).catch((error2)=>{logFollowDrainError(context,error2,active)})},listener=await sql.listen("genie_runtime_event",()=>{queueDrain("notify")});await drain();let pollIntervalMs=options?.pollIntervalMs??1000,pollTimer=setInterval(()=>{queueDrain("poll")},pollIntervalMs);return{mode:"pg",stop:async()=>{active=!1,clearInterval(pollTimer);try{await drainChain}catch{}try{await listener.unlisten()}catch{}}}}async function waitForRuntimeEvent(query,timeoutMs,predicate){let afterId=query.afterId??0;return new Promise((resolve4,reject)=>{let settled=!1,handle=null,timer2=null,finish=async(event)=>{if(settled)return;if(settled=!0,timer2)clearTimeout(timer2);try{if(handle)await handle.stop()}catch{}resolve4(event)};(async()=>{try{if(handle=await followRuntimeEvents({...query,afterId},(event)=>{if(predicate&&!predicate(event))return;finish(event)},{pollIntervalMs:250}),settled){try{await handle.stop()}catch{}return}timer2=setTimeout(()=>{finish(null)},timeoutMs)}catch(err){if(!settled)settled=!0,reject(err)}})()})}var circuitBreaker,eventsEmitted=0,eventsFailed=0,lastEmitDuration=0;var init_runtime_events=__esm(()=>{init_db();circuitBreaker=new EventCircuitBreaker});var exports_workspace={};__export(exports_workspace,{validateWorkspaceDefaults:()=>validateWorkspaceDefaults,scanAgents:()=>scanAgents2,migrateWorkspaceConfig:()=>migrateWorkspaceConfig,getWorkspaceConfig:()=>getWorkspaceConfig,genieHome:()=>genieHome2,findWorkspace:()=>findWorkspace,AgentDefaultsSchema:()=>AgentDefaultsSchema});import{existsSync as existsSync22,mkdirSync as mkdirSync9,readFileSync as readFileSync14,readdirSync as readdirSync5,realpathSync as realpathSync2,writeFileSync as writeFileSync10}from"fs";import{homedir as homedir18,tmpdir as tmpdir2}from"os";import{dirname as dirname4,join as join24,resolve as resolve4,sep}from"path";function findWorkspace(cwd){let startDir=resolve4(cwd??process.cwd()),current=startDir;while(!0){let candidate=join24(current,WORKSPACE_MARKER);if(existsSync22(candidate)){saveWorkspaceRoot(current);let agent=detectAgent(startDir,current);return{root:current,agent:agent??void 0}}let parent=dirname4(current);if(parent===current)break;current=parent}let savedRoot=loadWorkspaceRoot();if(savedRoot&&existsSync22(join24(savedRoot,WORKSPACE_MARKER))){let agent=detectAgent(startDir,savedRoot);return{root:savedRoot,agent:agent??void 0}}return null}function genieHome2(){return process.env.GENIE_HOME??join24(homedir18(),".genie")}function isTempPath(root){try{let canonicalTmp=realpathSync2(tmpdir2()),canonicalRoot=realpathSync2(root);return canonicalRoot===canonicalTmp||canonicalRoot.startsWith(canonicalTmp+sep)}catch{return!0}}function saveWorkspaceRoot(root){if(isTempPath(root))return;try{let home=genieHome2(),configPath2=join24(home,"config.json"),config=existsSync22(configPath2)?JSON.parse(readFileSync14(configPath2,"utf-8")):{};if(config.workspaceRoot===root)return;config.workspaceRoot=root,mkdirSync9(home,{recursive:!0}),writeFileSync10(configPath2,`${JSON.stringify(config,null,2)}
658
658
  `,"utf-8")}catch{}}function clearWorkspaceRoot(){try{let configPath2=join24(genieHome2(),"config.json");if(!existsSync22(configPath2))return;let config=JSON.parse(readFileSync14(configPath2,"utf-8"));if(config.workspaceRoot===void 0)return;config.workspaceRoot=void 0,writeFileSync10(configPath2,`${JSON.stringify(config,null,2)}
659
659
  `,"utf-8")}catch{}}function loadWorkspaceRoot(){try{let configPath2=join24(genieHome2(),"config.json");if(!existsSync22(configPath2))return null;let config=JSON.parse(readFileSync14(configPath2,"utf-8")),saved=typeof config.workspaceRoot==="string"?config.workspaceRoot:null;if(!saved)return null;if(!existsSync22(join24(saved,WORKSPACE_MARKER)))return clearWorkspaceRoot(),null;return saved}catch{return null}}function detectAgent(startDir,workspaceRoot){let agentsDir=join24(workspaceRoot,"agents"),relative=startDir.slice(agentsDir.length);if(!startDir.startsWith(agentsDir)||relative.length>0&&relative[0]!==sep)return null;let parts=relative.split(sep).filter(Boolean);if(parts.length===0)return null;let agentName=parts[0],agentsMd=join24(agentsDir,agentName,"AGENTS.md");if(existsSync22(agentsMd))return agentName;return null}function getWorkspaceConfig(root){let configPath2=join24(root,WORKSPACE_MARKER),raw=readFileSync14(configPath2,"utf-8"),parsed=JSON.parse(raw);return migrateWorkspaceConfig(parsed)}function validateWorkspaceDefaults(config){if(!config.agents?.defaults)return;let result2=AgentDefaultsSchema.safeParse(config.agents.defaults);if(!result2.success){let issues=result2.error.issues.map((i2)=>` ${i2.path.join(".")}: ${i2.message}`).join(`
660
660
  `);throw Error(`Invalid agents.defaults in workspace.json:
@@ -668,7 +668,7 @@ ${issues}`)}}function migrateWorkspaceConfig(raw){let config={...raw};if("tmuxSo
668
668
  ${sql.array(rows.map((r)=>r.actor??""))}::text[],
669
669
  ${sql.array(rows.map((r)=>JSON.stringify(r.details)))}::jsonb[]
670
670
  )
671
- `}catch{}}function getOtelPort(){let envPort=process.env.GENIE_OTEL_PORT;if(envPort){let parsed=Number.parseInt(envPort,10);if(!Number.isNaN(parsed)&&parsed>0&&parsed<65536)return parsed}return getActivePort()+1}async function startOtelReceiver(){if(server)return!0;let port=getOtelPort();try{return server=Bun.serve({port,hostname:"127.0.0.1",fetch:async(req)=>{let url=new URL(req.url);if(req.method==="POST"&&url.pathname==="/v1/logs"){try{let payload=await req.json(),rows=processLogs(payload);flushToPg(rows).catch(()=>{})}catch{}return new Response("",{status:200})}if(req.method==="POST"&&url.pathname==="/v1/metrics"){try{let payload=await req.json(),rows=processMetrics(payload);flushToPg(rows).catch(()=>{})}catch{}return new Response("",{status:200})}if(req.method==="GET"&&url.pathname==="/health")return Response.json({status:"ok",port});return new Response("Not Found",{status:404})}}),!0}catch(err){let message=err instanceof Error?err.message:String(err);if(message.includes("EADDRINUSE")||message.includes("address already in use"))console.warn(`OTel receiver: port ${port} already in use \u2014 skipping (another instance may be running)`);else console.warn(`OTel receiver: failed to start on port ${port}: ${message}`);return!1}}function stopOtelReceiver(){if(server)server.stop(),server=null}var server=null;var init_otel_receiver=__esm(()=>{init_db()});function debug(msg){if(process.env.DEBUG)console.error(`[target-resolver] ${msg}`)}async function defaultTmuxLookup(sessionName,windowName){try{let tmux=await Promise.resolve().then(() => (init_tmux(),exports_tmux)),session=await tmux.findSessionByName(sessionName);if(!session)return null;let windows=await tmux.listWindows(session.id);if(!windows||windows.length===0)return null;let targetWindow;if(windowName){if(targetWindow=windows.find((w)=>w.name===windowName),!targetWindow)return null}else targetWindow=windows.find((w)=>w.active)||windows[0];let panes=await tmux.listPanes(targetWindow.id);if(!panes||panes.length===0)return null;return{paneId:(panes.find((p)=>p.active)||panes[0]).id,session:sessionName}}catch{return null}}async function defaultIsPaneLive(paneId){try{return(await(await Promise.resolve().then(() => (init_tmux(),exports_tmux))).executeTmux(`display-message -p -t '${paneId}' '#{pane_id}'`)).trim()===paneId}catch{return!1}}async function defaultCleanupDeadPane(workerId,paneId){try{await(await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry))).removeSubPane(workerId,paneId)}catch{}}async function defaultDeriveSession(paneId){try{return(await(await Promise.resolve().then(() => (init_tmux(),exports_tmux))).executeTmux(`display-message -p -t '${paneId}' '#{session_name}'`)).trim()||null}catch{return null}}async function assertLive(paneId,isPaneLive,errorMsg,cleanup){if(!await isPaneLive(paneId)){if(cleanup)await cleanup();throw Error(errorMsg)}}async function resolveRawPane(target,opts){if(opts.checkLiveness)await assertLive(target,opts.isPaneLive,`Pane ${target} is dead or does not exist. Check with: tmux list-panes -a`);let session=await opts.deriveSession(target);return{paneId:target,session:session??void 0,resolvedVia:"raw"}}async function resolveWindowId(target,workers,opts){let matchingWorker=Object.values(workers).find((w)=>w.windowId===target);if(!matchingWorker)throw Error(`Window "${target}" not found in worker registry.
671
+ `}catch{}}function getOtelPort(){let envPort=process.env.GENIE_OTEL_PORT;if(envPort){let parsed=Number.parseInt(envPort,10);if(!Number.isNaN(parsed)&&parsed>0&&parsed<65536)return parsed}return getActivePort()+1}async function startOtelReceiver(){if(server)return!0;let port=getOtelPort();try{return server=Bun.serve({port,hostname:"127.0.0.1",fetch:async(req)=>{let url=new URL(req.url);if(req.method==="POST"&&url.pathname==="/v1/logs"){try{let payload=await req.json(),rows=processLogs(payload);flushToPg(rows).catch(()=>{})}catch{}return new Response("",{status:200})}if(req.method==="POST"&&url.pathname==="/v1/metrics"){try{let payload=await req.json(),rows=processMetrics(payload);flushToPg(rows).catch(()=>{})}catch{}return new Response("",{status:200})}if(req.method==="GET"&&url.pathname==="/health")return Response.json({status:"ok",port});return new Response("Not Found",{status:404})}}),!0}catch(err){let message=err instanceof Error?err.message:String(err);if(message.includes("EADDRINUSE")||message.includes("address already in use"))console.warn(`OTel receiver: port ${port} already in use \u2014 skipping (another instance may be running)`);else console.warn(`OTel receiver: failed to start on port ${port}: ${message}`);return!1}}async function stopOtelReceiver(){if(server)await server.stop(!0),server=null}var server=null;var init_otel_receiver=__esm(()=>{init_db()});function debug(msg){if(process.env.DEBUG)console.error(`[target-resolver] ${msg}`)}async function defaultTmuxLookup(sessionName,windowName){try{let tmux=await Promise.resolve().then(() => (init_tmux(),exports_tmux)),session=await tmux.findSessionByName(sessionName);if(!session)return null;let windows=await tmux.listWindows(session.id);if(!windows||windows.length===0)return null;let targetWindow;if(windowName){if(targetWindow=windows.find((w)=>w.name===windowName),!targetWindow)return null}else targetWindow=windows.find((w)=>w.active)||windows[0];let panes=await tmux.listPanes(targetWindow.id);if(!panes||panes.length===0)return null;return{paneId:(panes.find((p)=>p.active)||panes[0]).id,session:sessionName}}catch{return null}}async function defaultIsPaneLive(paneId){try{return(await(await Promise.resolve().then(() => (init_tmux(),exports_tmux))).executeTmux(`display-message -p -t '${paneId}' '#{pane_id}'`)).trim()===paneId}catch{return!1}}async function defaultCleanupDeadPane(workerId,paneId){try{await(await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry))).removeSubPane(workerId,paneId)}catch{}}async function defaultDeriveSession(paneId){try{return(await(await Promise.resolve().then(() => (init_tmux(),exports_tmux))).executeTmux(`display-message -p -t '${paneId}' '#{session_name}'`)).trim()||null}catch{return null}}async function assertLive(paneId,isPaneLive,errorMsg,cleanup){if(!await isPaneLive(paneId)){if(cleanup)await cleanup();throw Error(errorMsg)}}async function resolveRawPane(target,opts){if(opts.checkLiveness)await assertLive(target,opts.isPaneLive,`Pane ${target} is dead or does not exist. Check with: tmux list-panes -a`);let session=await opts.deriveSession(target);return{paneId:target,session:session??void 0,resolvedVia:"raw"}}async function resolveWindowId(target,workers,opts){let matchingWorker=Object.values(workers).find((w)=>w.windowId===target);if(!matchingWorker)throw Error(`Window "${target}" not found in worker registry.
672
672
  Run 'genie agent list' to list agents.`);if(opts.checkLiveness)await assertLive(matchingWorker.paneId,opts.isPaneLive,`Window ${target}: worker ${matchingWorker.id} pane ${matchingWorker.paneId} is dead. Run 'genie agent kill${matchingWorker.id}' to clean up.`);return{paneId:matchingWorker.paneId,session:matchingWorker.session,workerId:matchingWorker.id,resolvedVia:"worker"}}function resolveWorkerSubPane(worker,leftSide,rightSide){let index=Number.parseInt(rightSide,10);if(Number.isNaN(index)||index<0)throw Error(`Invalid sub-pane index "${rightSide}" for worker "${leftSide}". Use a non-negative integer (0 = primary, 1+ = sub-panes).`);let paneId=getPaneByIndex(worker,index);if(!paneId){let maxIndex=worker.subPanes?worker.subPanes.length:0;throw Error(`Worker "${leftSide}" has no sub-pane index ${index}. Available: 0 (primary)${maxIndex>0?`, 1-${maxIndex} (sub-panes)`:""}. Sub-pane index ${index} does not exist.`)}return paneId}function pickUnique(target,candidates,label){if(candidates.length===0)return null;if(candidates.length===1){let[id,w]=candidates[0];return{paneId:w.paneId,session:w.session,workerId:id,resolvedVia:"worker"}}let ids=candidates.map(([id])=>id).join(", ");throw Error(`Ambiguous target "${target}" \u2014 ${label}: ${ids}
673
673
  Use the full ID instead.`)}function resolveByRole(target,workers,currentTeam){if(!currentTeam)return null;let candidates=Object.entries(workers).filter(([,w])=>w.role===target&&w.team===currentTeam);return pickUnique(target,candidates,`${candidates.length} workers with role "${target}" in team "${currentTeam}"`)}function resolveByCustomName(target,workers,currentTeam){if(currentTeam){let teamCandidates=Object.entries(workers).filter(([,w])=>w.customName===target&&w.team===currentTeam),teamHit=pickUnique(target,teamCandidates,`${teamCandidates.length} workers with customName "${target}" in team "${currentTeam}"`);if(teamHit)return teamHit}let allCandidates=Object.entries(workers).filter(([,w])=>w.customName===target);return pickUnique(target,allCandidates,`${allCandidates.length} workers with customName "${target}"`)}function resolveByPartialId(target,workers,currentTeam){let candidates=Object.entries(workers).filter(([id])=>id!==target&&id.endsWith(target));if(candidates.length===0)return null;if(candidates.length===1){let[id,w]=candidates[0];return{paneId:w.paneId,session:w.session,workerId:id,resolvedVia:"worker"}}if(currentTeam){let teamCandidates=candidates.filter(([,w])=>w.team===currentTeam);if(teamCandidates.length===1){let[id,w]=teamCandidates[0];return{paneId:w.paneId,session:w.session,workerId:id,resolvedVia:"worker"}}}let ids=candidates.map(([id])=>id).join(", ");throw Error(`Ambiguous target "${target}" \u2014 matches ${candidates.length} workers: ${ids}
674
674
  Use the full ID instead.`)}function resolveBySubstring(target,workers,currentTeam){let candidates=Object.entries(workers).filter(([id])=>id!==target&&!id.endsWith(target)&&id.includes(target));if(candidates.length===0)return null;if(candidates.length===1){let[id,w]=candidates[0];return{paneId:w.paneId,session:w.session,workerId:id,resolvedVia:"worker"}}if(currentTeam){let teamCandidates=candidates.filter(([,w])=>w.team===currentTeam);if(teamCandidates.length===1){let[id,w]=teamCandidates[0];return{paneId:w.paneId,session:w.session,workerId:id,resolvedVia:"worker"}}}let ids=candidates.map(([id])=>id).join(", ");throw Error(`Ambiguous target "${target}" \u2014 matches ${candidates.length} workers: ${ids}
@@ -3590,4 +3590,4 @@ Examples:
3590
3590
  genie spawn researcher --model sonnet # Spawn with model override
3591
3591
  genie spawn my-agent --team my-feature # Spawn into a specific team
3592
3592
  genie spawn council--questioner --provider codex # Use Codex provider`).action(async(name,options)=>{try{await handleWorkerSpawn(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}});program2.command("kill <name>").description("Force kill an agent by name").action(async(name)=>{try{await handleWorkerKill(name)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}});program2.command("stop <name>").description("Stop an agent (preserves session for resume)").action(async(name)=>{try{await handleWorkerStop(name)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}});program2.command("resume [name]").description("Resume a suspended/failed agent with its Claude session").option("--all","Resume all eligible agents").action(async(name,options)=>{try{await handleWorkerResume(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}});program2.command("history <name>").description("Show compressed session history for an agent").option("--full","Show full conversation without compression").option("--since <n>","Show last N user/assistant exchanges",Number.parseInt).option("--last <n>","Show last N transcript entries",Number.parseInt).option("--type <role>","Filter by role (user, assistant, tool_call)").option("--after <timestamp>","Only entries after ISO timestamp").option("--json","Output as JSON").option("--ndjson","Output as newline-delimited JSON (pipeable to jq)").option("--raw","Output raw JSONL entries").option("--log-file <path>","Direct path to log file (for testing)").action(async(name,options)=>{await historyCommand(name,options)});program2.command("log [agent]").description("Unified observability feed \u2014 aggregates transcript, DMs, team chat").option("--team <name>","Show interleaved feed for all agents in a team").option("--type <kind>","Filter by event kind (transcript, message, tool_call, state, system)").option("--since <timestamp>","Only events after ISO timestamp").option("--last <n>","Show last N events",Number.parseInt).option("--ndjson","Output as newline-delimited JSON (pipeable to jq)").option("--json","Output as pretty JSON").option("-f, --follow","Follow mode \u2014 real-time streaming").action(async(agent,options)=>{await logCommand(agent,options)});var qaCmd=program2.command("qa").description("QA \u2014 self-testing system for genie CLI");qaCmd.command("run [target]",{isDefault:!0}).description("Run QA specs (all, a domain, or a single spec)").option("--timeout <seconds>","Max seconds per spec",(v2)=>Number(v2),3600).option("--parallel <n>","Max specs to run in parallel",(v2)=>Number(v2),5).option("--verbose","Show all collected events").option("--ndjson","Machine-readable NDJSON output").action(async(target,options)=>{await qaCommand(target,options)});qaCmd.command("status").description("Show QA dashboard with last results per spec").option("--json","Output as JSON").action(async(options)=>{await qaStatusCommand(options)});qaCmd.command("history").description("Show recent QA runs").action(async()=>{await qaHistoryCommand()});qaCmd.command("check <specFile>").description("Evaluate a QA spec against current team logs and publish qa-report").option("--team <name>","Team name (defaults to GENIE_TEAM)").option("--since <timestamp>","Only consider events after this ISO timestamp").option("--since-file <path>","Read the lower-bound timestamp from a file").action(async(specFile,options)=>{await qaCheckCommand(specFile,options)});program2.command("qa-report <json>").description("Publish QA result to the PG event log (called by QA team-lead)").action(async(json2)=>{let team=process.env.GENIE_TEAM;if(!team)console.error("Error: GENIE_TEAM not set. This command must be run by a QA team-lead agent."),process.exit(1);try{let data=JSON.parse(json2),{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(process.cwd(),`genie.qa.${team}.result`,{kind:"qa",agent:"qa",team,text:`QA result: ${String(data.result??"unknown")}`,data,source:"hook"}),console.log(`QA result published to PG event log as genie.qa.${team}.result`)}catch(err){console.error(`Failed to publish QA result: ${err}`),process.exit(1)}});program2.command("read <name>").description("Read terminal output from an agent pane").option("-n, --lines <number>","Number of lines to read").option("--from <line>","Start line").option("--to <line>","End line").option("--range <range>",'Line range (e.g., "10-20")').option("--search <text>","Search for text").option("--grep <pattern>","Grep for pattern").option("-f, --follow","Follow mode (like tail -f)").option("--all","Show all output").option("-r, --reverse","Reverse order").option("--json","Output as JSON").action(async(name,options)=>{await readSessionLogs2(name,options)});program2.command("answer <name> <choice>").description('Answer a question for an agent (use "text:..." for text input)').action(async(name,choice)=>{await answerQuestion(name,choice)});program2.command("ls").description("List registered agents with runtime status").option("--json","Output as JSON").option("--source <name>","Filter by executor metadata source (e.g. omni)").action(async(options)=>{try{await handleLsCommand(options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}});var args=process.argv.slice(2),isTuiPane=process.env.GENIE_TUI_PANE==="left"&&args.length===0,tuiRightPane=process.env.GENIE_TUI_RIGHT;delete process.env.GENIE_TUI_PANE;delete process.env.GENIE_TUI_RIGHT;delete process.env.GENIE_IS_DAEMON;if(isTuiPane){if(tuiRightPane)process.env.GENIE_TUI_RIGHT=tuiRightPane;let{launchTui:launchTui2}=await Promise.resolve().then(() => exports_tui);await launchTui2(),process.exit(0)}if(args.length===0){if(process.env.TMUX?.includes("genie-tui")){let{findWorkspace:findWorkspace3}=await Promise.resolve().then(() => (init_workspace(),exports_workspace)),ws2=findWorkspace3();if(ws2){let{resolveAgentFromCwd:resolveAgentFromCwd3}=await Promise.resolve().then(() => (init_resolve_agent_cwd(),exports_resolve_agent_cwd)),resolved2=resolveAgentFromCwd3(process.cwd(),ws2.root);if(resolved2.source!=="default"){let{writeFileSync:writeFileSync24}=await import("fs"),{join:join62}=await import("path"),home=process.env.GENIE_HOME??join62((await import("os")).homedir(),".genie");try{writeFileSync24(join62(home,"tui-initial-agent"),resolved2.agent,"utf-8")}catch{}console.log(`Navigating to ${resolved2.agent}...`)}else console.log("Already inside the genie TUI. Use Ctrl-b d to detach, or run genie commands directly.")}else console.log("Already inside the genie TUI. Use Ctrl-b d to detach, or run genie commands directly.");process.exit(0)}if(process.env.TMUX)console.warn("Note: switching to genie TUI from within another tmux session.");let{findWorkspace:findWorkspace2}=await Promise.resolve().then(() => (init_workspace(),exports_workspace)),ws=findWorkspace2();if(!ws){let{isInteractive:isInteractive2}=await Promise.resolve().then(() => (init_interactivity(),exports_interactivity));if(!isInteractive2())console.error("No workspace found. Run `genie init` to set up."),process.exit(2);let{confirm:confirm2}=await Promise.resolve().then(() => (init_esm14(),exports_esm));if(!await confirm2({message:"No workspace found. Initialize? [Y/n]",default:!0}))console.error("No workspace found. Run `genie init` to set up."),process.exit(2);let{mkdirSync:mkdirSync21,writeFileSync:writeFileSync24}=await import("fs"),{basename:basename15,join:join62}=await import("path"),cwd=process.cwd(),genieDir=join62(cwd,".genie");mkdirSync21(genieDir,{recursive:!0});let config={name:basename15(cwd),agents:{defaults:{}},tmux:{socket:"genie"},sdk:{}};if(writeFileSync24(join62(genieDir,"workspace.json"),`${JSON.stringify(config,null,2)}
3593
- `),console.log(`Workspace initialized: ${cwd}`),ws=findWorkspace2(),!ws)console.error("Failed to initialize workspace."),process.exit(1)}let{resolveAgentFromCwd:resolveAgentFromCwd2}=await Promise.resolve().then(() => (init_resolve_agent_cwd(),exports_resolve_agent_cwd)),resolved=resolveAgentFromCwd2(process.cwd(),ws.root),initialAgent=resolved.agent,{isServeRunning:isServeRunning2,autoStartServe:autoStartServe2,isTuiSessionReady:isTuiSessionReady2,ensureTuiSession:ensureTuiSession2}=await Promise.resolve().then(() => (init_serve(),exports_serve));if(!isServeRunning2())console.log("Starting genie serve..."),await autoStartServe2();else if(!isTuiSessionReady2())ensureTuiSession2(ws.root);if(ws.root)process.env.GENIE_TUI_WORKSPACE=ws.root;if(initialAgent)process.env.GENIE_TUI_AGENT=initialAgent;if(resolved.source!=="default"){let{execSync:execSync18}=await import("child_process");try{execSync18(`tmux has-session -t ${initialAgent} 2>/dev/null`,{stdio:"pipe"})}catch{console.log(`Spawning ${initialAgent}...`);try{execSync18(`genie spawn ${initialAgent}`,{stdio:"inherit",timeout:15000})}catch{}}}if(initialAgent){let{writeFileSync:writeFileSync24}=await import("fs"),{join:join62}=await import("path"),home=process.env.GENIE_HOME??join62((await import("os")).homedir(),".genie");try{writeFileSync24(join62(home,"tui-initial-agent"),initialAgent,"utf-8")}catch{}}let{attachTuiSession:attachTuiSession2}=await Promise.resolve().then(() => (init_tmux2(),exports_tmux2));attachTuiSession2(),process.exit(0)}if(args.every((a)=>a==="--reset")){let{sessionCommand:sessionCommand2}=await Promise.resolve().then(() => (init_session(),exports_session));await sessionCommand2({reset:!0}),process.exit(0)}var sessionIdx=args.indexOf("--session");if(sessionIdx!==-1&&sessionIdx+1<args.length){let sessionName=args[sessionIdx+1];if(!args.filter((_2,i2)=>i2!==sessionIdx&&i2!==sessionIdx+1).some((a)=>!a.startsWith("-")))try{await startNamedSession(sessionName),process.exit(0)}catch(err){console.error(`Error: ${err instanceof Error?err.message:err}`),process.exit(1)}else try{await program2.parseAsync(process.argv)}finally{stopOtelReceiver(),await shutdown().catch(()=>{})}}else try{let _cmdStart=Date.now();if(await program2.parseAsync(process.argv),process.env.GENIE_PROFILE_DB)console.error(`[profile] parseAsync=${Date.now()-_cmdStart}ms`)}finally{let _shutStart=Date.now();if(stopOtelReceiver(),await shutdown().catch(()=>{}),process.env.GENIE_PROFILE_DB)console.error(`[profile] shutdown=${Date.now()-_shutStart}ms`)}
3593
+ `),console.log(`Workspace initialized: ${cwd}`),ws=findWorkspace2(),!ws)console.error("Failed to initialize workspace."),process.exit(1)}let{resolveAgentFromCwd:resolveAgentFromCwd2}=await Promise.resolve().then(() => (init_resolve_agent_cwd(),exports_resolve_agent_cwd)),resolved=resolveAgentFromCwd2(process.cwd(),ws.root),initialAgent=resolved.agent,{isServeRunning:isServeRunning2,autoStartServe:autoStartServe2,isTuiSessionReady:isTuiSessionReady2,ensureTuiSession:ensureTuiSession2}=await Promise.resolve().then(() => (init_serve(),exports_serve));if(!isServeRunning2())console.log("Starting genie serve..."),await autoStartServe2();else if(!isTuiSessionReady2())ensureTuiSession2(ws.root);if(ws.root)process.env.GENIE_TUI_WORKSPACE=ws.root;if(initialAgent)process.env.GENIE_TUI_AGENT=initialAgent;if(resolved.source!=="default"){let{execSync:execSync18}=await import("child_process");try{execSync18(`tmux has-session -t ${initialAgent} 2>/dev/null`,{stdio:"pipe"})}catch{console.log(`Spawning ${initialAgent}...`);try{execSync18(`genie spawn ${initialAgent}`,{stdio:"inherit",timeout:15000})}catch{}}}if(initialAgent){let{writeFileSync:writeFileSync24}=await import("fs"),{join:join62}=await import("path"),home=process.env.GENIE_HOME??join62((await import("os")).homedir(),".genie");try{writeFileSync24(join62(home,"tui-initial-agent"),initialAgent,"utf-8")}catch{}}let{attachTuiSession:attachTuiSession2}=await Promise.resolve().then(() => (init_tmux2(),exports_tmux2));attachTuiSession2(),process.exit(0)}if(args.every((a)=>a==="--reset")){let{sessionCommand:sessionCommand2}=await Promise.resolve().then(() => (init_session(),exports_session));await sessionCommand2({reset:!0}),process.exit(0)}var sessionIdx=args.indexOf("--session");if(sessionIdx!==-1&&sessionIdx+1<args.length){let sessionName=args[sessionIdx+1];if(!args.filter((_2,i2)=>i2!==sessionIdx&&i2!==sessionIdx+1).some((a)=>!a.startsWith("-")))try{await startNamedSession(sessionName),process.exit(0)}catch(err){console.error(`Error: ${err instanceof Error?err.message:err}`),process.exit(1)}else try{await program2.parseAsync(process.argv)}finally{await stopOtelReceiver().catch(()=>{}),await shutdown().catch(()=>{})}}else try{let _cmdStart=Date.now();if(await program2.parseAsync(process.argv),process.env.GENIE_PROFILE_DB)console.error(`[profile] parseAsync=${Date.now()-_cmdStart}ms`)}finally{let _shutStart=Date.now();if(await stopOtelReceiver().catch(()=>{}),await shutdown().catch(()=>{}),process.env.GENIE_PROFILE_DB)console.error(`[profile] shutdown=${Date.now()-_shutStart}ms`)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260418.1",
3
+ "version": "4.260418.3",
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.260418.1",
3
+ "version": "4.260418.3",
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.260418.1",
3
+ "version": "4.260418.3",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",