@automagik/genie 4.260329.24 → 4.260329.26
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/.claude-plugin/marketplace.json +1 -1
- package/dist/genie.js +6 -6
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/genie/.claude-plugin/plugin.json +1 -1
- package/plugins/genie/package.json +1 -1
- package/src/hooks/handlers/orchestration-guard.ts +10 -3
- package/src/hooks/index.ts +50 -15
- package/src/hooks/types.ts +9 -2
- package/src/lib/task-service.ts +13 -6
- package/src/tui/app.tsx +4 -2
- package/src/tui/session-tree.ts +6 -1
- package/src/tui/tmux.ts +2 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "genie",
|
|
13
|
-
"version": "4.260329.
|
|
13
|
+
"version": "4.260329.26",
|
|
14
14
|
"source": "./plugins/genie",
|
|
15
15
|
"description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, wish them into plans, make with parallel agents, ship as one team. A coding genie that grows with your project."
|
|
16
16
|
}
|
package/dist/genie.js
CHANGED
|
@@ -819,7 +819,7 @@ Agent "${ctx.workerId}" session ended.`),process.exit(result.status??0)}function
|
|
|
819
819
|
Use a different --role name for a second worker, e.g.: --role ${role}-2`),process.exit(1);await unregister(w.id)}}async function resolveNativeTeam(team,_repoPath,options){let parentSessionId=(await getTeam2(team))?.nativeTeamParentSessionId;if(!parentSessionId)parentSessionId=await discoverClaudeParentSessionId()??`genie-${team}`;await ensureNativeTeam(team,`Genie team: ${team}`,parentSessionId);let spawnColor=options.color??await assignColor(team),nativeTeam;if(options.provider==="claude")nativeTeam={enabled:!0,parentSessionId,color:spawnColor,agentType:options.role??"general-purpose",planModeRequired:options.planMode,permissionMode:options.permissionMode,agentName:options.role};return{parentSessionId,spawnColor,nativeTeam}}async function resolveAgentForSpawn(name,options){let resolved=await resolve3(name);if(!resolved)console.error(`Error: Agent "${name}" not found in directory or built-ins.`),console.error(` Register with: genie dir add ${name} --dir <path>`),console.error(" Or use a built-in: engineer, reviewer, qa, fix, ..."),process.exit(1);let entry=resolved.entry,identityPath=null;if(resolved.builtin)identityPath=resolveBuiltinAgentPath(name);else if(entry.dir)identityPath=loadIdentity(entry);return{entry,repoPath:options.cwd??(entry.repo||void 0)??(entry.dir||void 0)??process.cwd(),identityPath,model:options.model??entry.model}}async function buildSpawnParams2(name,team,options,agent){let params={provider:options.provider,team,role:name,skill:options.skill,extraArgs:options.extraArgs,model:agent.model,systemPromptFile:agent.identityPath??void 0,promptMode:agent.entry.promptMode,initialPrompt:options.initialPrompt},{parentSessionId,spawnColor,nativeTeam}=await resolveNativeTeam(team,agent.repoPath,{...options,role:name});if(nativeTeam)params.nativeTeam=nativeTeam;try{let{injectTeamHooks:injectTeamHooks2}=await Promise.resolve().then(() => (init_inject(),exports_inject));if(await injectTeamHooks2(team))console.log(` Hooks: injected genie hook dispatch into team "${team}"`)}catch(err){console.warn(`Warning: could not inject hooks for team "${team}": ${err instanceof Error?err.message:err}`)}if(params.provider==="claude")params.sessionId=crypto.randomUUID();if(params.provider==="claude"){if(await startOtelReceiver())params.otelPort=getOtelPort(),params.otelLogPrompts=!0}return{params,parentSessionId,spawnColor}}async function handleWorkerSpawn(name,options){let effectiveRole=options.role??name,agent=await resolveAgentForSpawn(name,options),teamWasExplicit=Boolean(options.team),team=options.team||await discoverTeamName();if(!team)return console.error("Error: --team is required (or set GENIE_TEAM, or run inside a genie session)"),process.exit(1);await rejectDuplicateRole(team,effectiveRole);let teamConfig=await getTeam2(team);if(teamConfig?.worktreePath)agent={...agent,repoPath:teamConfig.worktreePath};let{params,parentSessionId,spawnColor}=await buildSpawnParams2(effectiveRole,team,options,agent);if(!params.name)params.name=`${params.team}-${effectiveRole}`;let validated=validateSpawnParams(params),launch=buildLaunchCommand(validated),layoutMode=resolveLayoutMode(options.layout),workerId=await generateWorkerId2(validated.team,effectiveRole),insideTmux=Boolean(process.env.TMUX||options.session),nt=validated.nativeTeam,now=new Date().toISOString(),agentName=nt?.agentName??effectiveRole,agentIdentity=await findOrCreateAgent(agentName,team,effectiveRole);await terminateActiveExecutorWithCleanup(agentIdentity.id);let executorId=crypto.randomUUID(),otelRelayActive=!1;if(!nt?.enabled&&validated.provider==="codex"&&insideTmux)ensureCodexOtelConfig(),otelRelayActive=await ensureOtelRelay(validated.team);let fullCommand=prependEnvVars(launch.command,launch.env),ctx={workerId,validated,launch,layoutMode,fullCommand,agentName,spawnColor,parentSessionId,claudeSessionId:validated.sessionId,otelRelayActive,now,transport:insideTmux?"tmux":"inline",extraArgs:options.extraArgs,cwd:agent.repoPath,spawnIntoCurrentWindow:!teamWasExplicit&&insideTmux&&!options.session,sessionOverride:options.session,autoResume:options.autoResume,agentIdentityId:agentIdentity.id,executorId};if(recordAuditEvent("worker",workerId,"spawn",getActor(),{name,team:validated.team,provider:validated.provider}).catch(()=>{}),insideTmux)return await launchTmuxSpawn(ctx);return await launchInlineSpawn(ctx)}async function cleanupWorkerNativeTeam(w){if(!w.team||!w.nativeAgentId)return;let agentName=w.nativeAgentId.split("@")[0];await clearNativeInbox(w.team,agentName).catch(()=>{}),await unregisterNativeMember(w.team,agentName).catch(()=>{})}function killWorkerPane(w){try{let{execSync:execSync4}=__require("child_process"),currentPane=execSync4("tmux display-message -p '#{pane_id}'",{encoding:"utf-8"}).trim();if(w.paneId&&/^(%\d+|inline)$/.test(w.paneId)&&w.paneId!==currentPane)execSync4(`tmux kill-pane -t ${w.paneId}`,{stdio:"ignore"});else if(w.paneId===currentPane)console.log(" (skipped pane kill \u2014 would kill current session)")}catch{}}function cleanupRelayFiles(id){try{let{join:join22}=__require("path"),{homedir:homedir16}=__require("os"),{unlinkSync:unlinkSync5}=__require("fs"),relayDir=join22(homedir16(),".genie","relay");for(let suffix of["-pane","-meta"])try{unlinkSync5(join22(relayDir,`${id}${suffix}`))}catch{}}catch{}}async function resolveWorkerByName(name){let exact=await get(name);if(exact)return exact;let workers=await list(),byRole=workers.filter((w)=>w.role===name);if(byRole.length===1)return byRole[0];if(byRole.length>1){console.error(`Multiple agents with role "${name}". Specify full ID:`);for(let w of byRole)console.error(` ${w.id} (team: ${w.team})`);process.exit(1)}let bySuffix=workers.filter((w)=>w.id.endsWith(`-${name}`));if(bySuffix.length===1)return bySuffix[0];if(bySuffix.length>1){console.error(`Multiple agents matching "${name}". Specify full ID:`);for(let w of bySuffix)console.error(` ${w.id}`);process.exit(1)}console.error(`Agent "${name}" not found.`),console.error(" Run `genie agent list` to see agents."),process.exit(1)}async function handleWorkerKill(name){let w=await resolveWorkerByName(name);killWorkerPane(w),cleanupRelayFiles(w.id),await cleanupWorkerNativeTeam(w),await unregister(w.id),console.log(`Agent "${w.id}" killed and unregistered (template preserved).`),recordAuditEvent("worker",w.id,"kill",getActor(),{name}).catch(()=>{})}async function handleWorkerStop(name){let w=await resolveWorkerByName(name);if(w.state==="suspended"){console.log(`Agent "${w.id}" is already stopped.`);return}let{suspendWorker:suspendWorker2}=await Promise.resolve().then(() => (init_idle_timeout(),exports_idle_timeout));if(await suspendWorker2(w.id)){if(console.log(`Agent "${w.id}" stopped.`),w.claudeSessionId)console.log(` Session preserved: ${w.claudeSessionId}`);console.log(` Send a message to auto-resume: genie send '...' --to ${w.id}`),recordAuditEvent("worker",w.id,"stop",getActor(),{name}).catch(()=>{})}else console.error(`Failed to stop agent "${w.id}".`),process.exit(1)}async function isResumeEligible(w){if(!w.claudeSessionId)return!1;if(w.state==="done")return!1;let paneAlive=await isPaneAlive(w.paneId);if((w.state==="suspended"||w.state==="error")&&!paneAlive)return!0;if(!paneAlive&&(w.state==="working"||w.state==="idle"||w.state==="spawning"))return!0;return!1}async function resumeAllAgents(){let workers=await list(),toResume=[];for(let w of workers)if(await isResumeEligible(w))toResume.push(w);if(toResume.length===0){console.log("No eligible agents to resume.");return}console.log(`Resuming ${toResume.length} agent(s)...`);for(let w of toResume)try{await resumeAgent(w)}catch(err){console.error(` Failed to resume "${w.id}": ${err instanceof Error?err.message:err}`)}}async function handleWorkerResume(name,options){if(options.all)return resumeAllAgents();if(!name)console.error("Error: provide an agent name, or use --all to resume all eligible agents."),process.exit(1);let w=await resolveWorkerByName(name);if(!w.claudeSessionId)console.error(`Error: Agent "${w.id}" has no Claude session ID \u2014 cannot resume.`),console.error(" Only agents spawned with the Claude provider have resumable sessions."),process.exit(1);if(await isPaneAlive(w.paneId)){console.log(`Agent "${w.id}" is already running (pane ${w.paneId} is alive).`);return}await resumeAgent(w)}function buildResumeParams(agent,template){let agentName=agent.role??agent.id,provider=template?.provider??agent.provider??"claude",team=template?.team??agent.team??"genie";return{provider,team,role:agentName,skill:template?.skill??agent.skill,extraArgs:template?.extraArgs,resume:agent.claudeSessionId,name:`${team}-${agentName}`}}function formatGroupStatus(name,group,allGroups){let detail=group.status;if(group.completedAt)detail+=` (completed at ${group.completedAt})`;else if(group.startedAt)detail+=` (started at ${group.startedAt})`;if(group.status==="blocked"&&group.dependsOn.length>0){let pending=group.dependsOn.filter((dep)=>allGroups[dep]?.status!=="done");if(pending.length>0)detail+=` (depends on ${pending.join(", ")})`}return`Group ${name}: ${detail}`}async function buildResumeContext(agent){if(agent.role==="team-lead"&&agent.wishSlug)try{let state=await(await Promise.resolve().then(() => (init_wish_state(),exports_wish_state))).getState(agent.wishSlug,agent.repoPath);if(state){let groupLines=Object.entries(state.groups).map(([name,group])=>formatGroupStatus(name,group,state.groups));return["You were resumed after a crash. Here's where you left off:",`Wish: ${state.wish}`,"",...groupLines,"",`Continue from where you left off. Run \`genie status ${state.wish}\` to verify, then dispatch the next wave.`].join(`
|
|
820
820
|
`)}}catch{}if(agent.team)return"You were resumed. Check your team's current state with `genie status`.";return}async function buildFullResumeParams(agent,template){let params=buildResumeParams(agent,template),resumeContext=await buildResumeContext(agent);if(resumeContext)params.initialPrompt=resumeContext;if(agent.nativeTeamEnabled){let nativeResult=await resolveNativeTeam(params.team,agent.repoPath,{provider:params.provider,role:params.role,color:agent.nativeColor});if(nativeResult.nativeTeam)params.nativeTeam=nativeResult.nativeTeam}return params}async function createResumeExecutor(agent,params,paneId,teamWindow,cwd,spawnColor){let resumeAgentName=agent.role??agent.id,resumeTeam=agent.team??params.team,agentIdentity=await findOrCreateAgent(resumeAgentName,resumeTeam,agent.role);await terminateActiveExecutorWithCleanup(agentIdentity.id);let pid=await capturePanePid(paneId);await createAndLinkExecutor(agentIdentity.id,params.provider,resolveExecutorTransport(params.provider,"tmux"),{pid,tmuxSession:params.team,tmuxPaneId:paneId,tmuxWindow:teamWindow?.windowName??null,tmuxWindowId:teamWindow?.windowId??null,claudeSessionId:agent.claudeSessionId??null,state:"spawning",repoPath:cwd,paneColor:spawnColor})}async function resumeAgent(agent){let template=(await listTemplates()).find((t)=>t.id===(agent.role??agent.id));await update(agent.id,{resumeAttempts:0});let params=await buildFullResumeParams(agent,template),validated=validateSpawnParams(params),launch=buildLaunchCommand(validated),fullCommand=prependEnvVars(launch.command,launch.env),now=new Date().toISOString();if(!process.env.TMUX)console.error("Error: resume requires tmux. Start a tmux session first."),process.exit(1);let ctx={workerId:agent.id,validated,launch,layoutMode:resolveLayoutMode(void 0),fullCommand,agentName:agent.role??agent.id,spawnColor:agent.nativeColor??"blue",parentSessionId:agent.parentSessionId??`genie-${params.team}`,claudeSessionId:agent.claudeSessionId,otelRelayActive:!1,now,transport:"tmux",extraArgs:template?.extraArgs,cwd:template?.cwd??agent.repoPath,spawnIntoCurrentWindow:!1,autoResume:agent.autoResume},teamWindow=await resolveSpawnTeamWindow(validated.team,ctx.cwd),paneId;try{paneId=createTmuxPane(ctx,teamWindow)}catch(err){console.error(`Failed to create tmux pane: ${err instanceof Error?err.message:"unknown error"}`),process.exit(1)}if(await createResumeExecutor(agent,validated,paneId,teamWindow,ctx.cwd,ctx.spawnColor),await applySpawnLayout(ctx,teamWindow),await update(agent.id,{paneId,state:"spawning",startedAt:now,lastStateChange:now,suspendedAt:void 0,windowName:teamWindow?.windowName,windowId:teamWindow?.windowId,window:teamWindow?.windowName}),await notifySpawnJoin(ctx,paneId),await injectResumeContext(ctx.cwd??agent.repoPath??process.cwd(),agent.id,agent.role??agent.id,params.team),ctx.spawnColor&&paneId!=="inline")await applyPaneColor(paneId,ctx.spawnColor,teamWindow?.windowId);if(recordAuditEvent("worker",agent.id,"resumed",getActor(),{claudeSessionId:agent.claudeSessionId,team:agent.team}).catch(()=>{}),console.log(`Agent "${agent.id}" resumed.`),console.log(` Session: ${agent.claudeSessionId}`),console.log(` Pane: ${paneId}`),teamWindow)console.log(` Window: ${teamWindow.windowName} (${teamWindow.windowId})`)}async function buildWorkerStatusMap(workers){let statusMap=new Map;for(let w of workers){let name=w.role||w.id;if(await isPaneAlive(w.paneId))statusMap.set(name,{state:w.state,team:w.team||"-"});else if(w.state==="suspended"||w.state==="error"){let attempts=w.resumeAttempts??0,max=w.maxResumeAttempts??3,autoStr=w.autoResume===!1?"off":"on";statusMap.set(name,{state:`${w.state} (${attempts}/${max} resumes, auto-resume: ${autoStr})`,team:w.team||"-",resumeAttempts:attempts,maxResumeAttempts:max,autoResume:w.autoResume!==!1})}}return statusMap}async function handleLsCommand(options){let dirEntries=await ls(),workers=await list(),statusMap=await buildWorkerStatusMap(workers),entries=[];for(let entry of dirEntries){let running=statusMap.get(entry.name);entries.push({name:entry.name,dir:entry.dir||"-",status:running?running.state:"offline",team:running?.team||"-",model:entry.model||"-",resumeAttempts:running?.resumeAttempts,maxResumeAttempts:running?.maxResumeAttempts,autoResume:running?.autoResume}),statusMap.delete(entry.name)}for(let[name,info]of statusMap)entries.push({name,dir:"(built-in)",status:info.state,team:info.team,model:"-",resumeAttempts:info.resumeAttempts,maxResumeAttempts:info.maxResumeAttempts,autoResume:info.autoResume});if(options.json){console.log(JSON.stringify(entries,null,2));return}if(entries.length===0){console.log("No agents registered. Use `genie dir add <name> --dir <path>` to register one.");return}console.log(""),console.log(formatLsRow("NAME","DIR","STATUS","TEAM","MODEL")),console.log("-".repeat(106));for(let e of entries)console.log(formatLsRow(e.name,e.dir,e.status,e.team,e.model));console.log("")}function formatLsRow(name,dir,status,team,model){return`${name.padEnd(20).substring(0,20)}${dir.padEnd(30).substring(0,30)}${status.padEnd(44).substring(0,44)}${team.padEnd(12).substring(0,12)}${model}`}var init_agents=__esm(()=>{init_agent_directory();init_agent_registry();init_audit();init_builtin_agents();init_claude_native_teams();init_codex_config();init_executor_registry();init_otel_receiver();init_protocol_router_spawn();init_provider_adapters();init_registry();init_spawn_command();init_team_manager();init_tmux();init_tmux()});var palette,icons,tmuxStyle;var init_theme2=__esm(()=>{palette={purple:"#a855f7",violet:"#7c3aed",cyan:"#22d3ee",emerald:"#34d399",bg:"#1a1028",bgLight:"#241838",bgLighter:"#2e2048",text:"#e2e8f0",textDim:"#94a3b8",textMuted:"#64748b",border:"#414868",borderActive:"#7c3aed",scrollTrack:"#414868",scrollThumb:"#7aa2f7",active:"#22d3ee",success:"#34d399",warning:"#fbbf24",error:"#f87171",idle:"#94a3b8"},icons={org:"\u25C6",project:"\u25B8",projectOpen:"\u25BE",board:"\u2261",boardOpen:"\u2261",column:"\u2502",task:"\u25CB",taskActive:"\u25CF",taskDone:"\u2713",agent:"\u25B6",collapsed:"\u25B8",expanded:"\u25BE"},tmuxStyle={statusBg:"#1a1028",statusFg:"#e2e8f0",activeBorder:"#7c3aed",inactiveBorder:"#414868",activeTab:"#7c3aed",inactiveTab:"#414868"}});import{execSync as execSync4,spawnSync}from"child_process";function hasTmux(){try{return execSync4("which tmux",{stdio:"ignore"}),!0}catch{return!1}}function isInsideTuiSession(){return process.env.GENIE_TUI_PANE==="left"}function createTuiSession(){let cols=process.stdout.columns||120,rows=process.stdout.rows||40;try{execSync4(`tmux kill-session -t ${SESSION_NAME} 2>/dev/null`,{stdio:"ignore"})}catch{}execSync4(`tmux new-session -d -s ${SESSION_NAME} -x ${cols} -y ${rows} -e GENIE_TUI_PANE=left`,{stdio:"ignore"}),execSync4(`tmux split-window -h -t ${SESSION_NAME}:0 -l ${cols-NAV_WIDTH-1}`,{stdio:"ignore"});let panes=execSync4(`tmux list-panes -t ${SESSION_NAME}:0 -F '#{pane_id}'`,{encoding:"utf-8"}).trim().split(`
|
|
821
821
|
`),leftPane=panes[0],rightPane=panes[1]||panes[0];return applyTmuxStyle(SESSION_NAME),setupKeybindings(SESSION_NAME),{session:SESSION_NAME,leftPane,rightPane}}function resolveRightPane(rightPane){try{return execSync4(`tmux display-message -t ${rightPane} -p ''`,{stdio:"ignore"}),rightPane}catch{try{let panes=execSync4(`tmux list-panes -t ${SESSION_NAME}:0 -F '#{pane_id}'`,{encoding:"utf-8"}).trim().split(`
|
|
822
|
-
`);return panes[1]||panes[0]}catch{return rightPane}}}function ensureSession(sessionName){try{execSync4(`tmux has-session -t '${sessionName}' 2>/dev/null`,{stdio:"ignore"})}catch{try{execSync4(`tmux new-session -d -s '${sessionName}'`,{stdio:"ignore"})}catch{}}}function attachProjectWindow(rightPane,targetSession,windowIndex){let pane=resolveRightPane(rightPane);if(ensureSession(targetSession),windowIndex!==void 0)try{execSync4(`tmux select-window -t '${targetSession}:${windowIndex}'`,{stdio:"ignore"})}catch{}try{execSync4(`tmux respawn-pane -k -t ${pane} "TMUX='' tmux attach-session -t '${targetSession}'"`,{stdio:"ignore"})}catch{}}function setupKeybindings(session){try{execSync4(`tmux bind-key -T ${KEY_TABLE} Tab select-pane -t ${session}:0.1 \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync4(`tmux bind-key -T ${KEY_TABLE} C-b if-shell "[ $(tmux display-message -p '#{pane_width}' -t ${session}:0.0) -gt 5 ]" "resize-pane -t ${session}:0.0 -x 0" "resize-pane -t ${session}:0.0 -x ${NAV_WIDTH}" \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync4(`tmux bind-key -T ${KEY_TABLE} C-t send-keys -t ${session}:0.1 C-b c \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync4(`tmux bind-key -T ${KEY_TABLE} 'C-\\' run-shell "tmux kill-session -t ${session}"`,{stdio:"ignore"}),execSync4(`tmux set-hook -t ${session} client-session-changed "switch-client -T ${KEY_TABLE}"`,{stdio:"ignore"}),execSync4(`tmux switch-client -T ${KEY_TABLE}`,{stdio:"ignore"})}catch{}}function applyTmuxStyle(session){try{let cmds=[`set-option -t ${session} pane-border-style 'fg=${tmuxStyle.inactiveBorder}'`,`set-option -t ${session} pane-active-border-style 'fg=${tmuxStyle.activeBorder}'`,`set-option -t ${session} status off`,`set-option -t ${session} mouse on`];for(let cmd of cmds)execSync4(`tmux ${cmd}`,{stdio:"ignore"})}catch{}}function cleanup(session=SESSION_NAME){try{execSync4(`tmux unbind-key -T ${KEY_TABLE} Tab 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux unbind-key -T ${KEY_TABLE} C-b 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux unbind-key -T ${KEY_TABLE} C-t 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux unbind-key -T ${KEY_TABLE} 'C-\\' 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux set-hook -u -t ${session} client-session-changed 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux kill-session -t ${session} 2>/dev/null`,{stdio:"ignore"})}catch{}}function attachTuiSession(){spawnSync("tmux",["attach-session","-t",SESSION_NAME],{stdio:"inherit"})}var SESSION_NAME="genie-tui",KEY_TABLE="genie-tui",NAV_WIDTH=30;var init_tmux2=__esm(()=>{init_theme2()});var highlights_default="./highlights-ghv9g403.scm";var init_highlights=()=>{};var tree_sitter_javascript_default="./tree-sitter-javascript-nd0q4pe9.wasm";var init_tree_sitter_javascript=()=>{};var highlights_default2="./highlights-eq9cgrbb.scm";var init_highlights2=()=>{};var tree_sitter_typescript_default="./tree-sitter-typescript-zxjzwt75.wasm";var init_tree_sitter_typescript=()=>{};var highlights_default3="./highlights-r812a2qc.scm";var init_highlights3=()=>{};var tree_sitter_markdown_default="./tree-sitter-markdown-411r6y9b.wasm";var init_tree_sitter_markdown=()=>{};var injections_default="./injections-73j83es3.scm";var init_injections=()=>{};var highlights_default4="./highlights-x6tmsnaa.scm";var init_highlights4=()=>{};var tree_sitter_markdown_inline_default="./tree-sitter-markdown_inline-j5349f42.wasm";var init_tree_sitter_markdown_inline=()=>{};var highlights_default5="./highlights-hk7bwhj4.scm";var init_highlights5=()=>{};var tree_sitter_zig_default="./tree-sitter-zig-e78zbjpm.wasm";var init_tree_sitter_zig=()=>{};import{EventEmitter}from"events";import{Buffer as Buffer2}from"buffer";import{Buffer as Buffer3}from"buffer";import{EventEmitter as EventEmitter2}from"events";import{resolve as resolve4,dirname as dirname5}from"path";import{fileURLToPath as fileURLToPath2}from"url";import{resolve as resolve22,isAbsolute,parse as parse2}from"path";import{existsSync as existsSync19}from"fs";import{basename as basename2,join as join22}from"path";import os2 from"os";import path2 from"path";import{EventEmitter as EventEmitter3}from"events";import path22 from"path";import{readFile as readFile22,writeFile as writeFile22,mkdir as mkdir22}from"fs/promises";import*as path4 from"path";import{mkdir as mkdir5,readFile as readFile6,writeFile as writeFile3}from"fs/promises";import*as path3 from"path";import{readdir as readdir4}from"fs/promises";import{dlopen,toArrayBuffer as toArrayBuffer4,JSCallback,ptr as ptr4}from"bun:ffi";import{existsSync as existsSync22,writeFileSync as writeFileSync6}from"fs";import{EventEmitter as EventEmitter4}from"events";import{toArrayBuffer,ptr}from"bun:ffi";import{ptr as ptr2,toArrayBuffer as toArrayBuffer2}from"bun:ffi";import{ptr as ptr3,toArrayBuffer as toArrayBuffer3}from"bun:ffi";import{EventEmitter as EventEmitter5}from"events";import util3 from"util";import{EventEmitter as EventEmitter7}from"events";import{Console}from"console";import fs2 from"fs";import path5 from"path";import util22 from"util";import{Writable}from"stream";import{EventEmitter as EventEmitter6}from"events";import{EventEmitter as EventEmitter8}from"events";function __exportSetter2(name,newValue){this[name]=__returnValue2.bind(null,newValue)}function wrapAssembly(lib){function patch(prototype,name,fn){let original=prototype[name];prototype[name]=function(){for(var _len=arguments.length,args=Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];return fn.call(this,original,...args)}}for(let fnName of["setPosition","setMargin","setFlexBasis","setWidth","setHeight","setMinWidth","setMinHeight","setMaxWidth","setMaxHeight","setPadding","setGap"]){let methods={[Unit.Point]:lib.Node.prototype[fnName],[Unit.Percent]:lib.Node.prototype[`${fnName}Percent`],[Unit.Auto]:lib.Node.prototype[`${fnName}Auto`]};patch(lib.Node.prototype,fnName,function(original){for(var _len2=arguments.length,args=Array(_len2>1?_len2-1:0),_key2=1;_key2<_len2;_key2++)args[_key2-1]=arguments[_key2];let value=args.pop(),unit,asNumber;if(value==="auto")unit=Unit.Auto,asNumber=void 0;else if(typeof value==="object")unit=value.unit,asNumber=value.valueOf();else if(unit=typeof value==="string"&&value.endsWith("%")?Unit.Percent:Unit.Point,asNumber=parseFloat(value),value!==void 0&&!Number.isNaN(value)&&Number.isNaN(asNumber))throw Error(`Invalid value ${value} for ${fnName}`);if(!methods[unit])throw Error(`Failed to execute "${fnName}": Unsupported unit '${value}'`);if(asNumber!==void 0)return methods[unit].call(this,...args,asNumber);else return methods[unit].call(this,...args)})}function wrapMeasureFunction(measureFunction){return lib.MeasureCallback.implement({measure:function(){let{width,height:height2}=measureFunction(...arguments);return{width:width??NaN,height:height2??NaN}}})}patch(lib.Node.prototype,"setMeasureFunc",function(original,measureFunc){if(measureFunc)return original.call(this,wrapMeasureFunction(measureFunc));else return this.unsetMeasureFunc()});function wrapDirtiedFunc(dirtiedFunction){return lib.DirtiedCallback.implement({dirtied:dirtiedFunction})}return patch(lib.Node.prototype,"setDirtiedFunc",function(original,dirtiedFunc){original.call(this,wrapDirtiedFunc(dirtiedFunc))}),patch(lib.Config.prototype,"free",function(){lib.Config.destroy(this)}),patch(lib.Node,"create",(_,config)=>{return config?lib.Node.createWithConfig(config):lib.Node.createDefault()}),patch(lib.Node.prototype,"free",function(){lib.Node.destroy(this)}),patch(lib.Node.prototype,"freeRecursive",function(){for(let t=0,T=this.getChildCount();t<T;++t)this.getChild(0).freeRecursive();this.free()}),patch(lib.Node.prototype,"calculateLayout",function(original){let width=arguments.length>1&&arguments[1]!==void 0?arguments[1]:NaN,height2=arguments.length>2&&arguments[2]!==void 0?arguments[2]:NaN,direction=arguments.length>3&&arguments[3]!==void 0?arguments[3]:Direction.LTR;return original.call(this,width,height2,direction)}),{Config:lib.Config,Node:lib.Node,...YGEnums_default}}function isValidBorderStyle(value){return typeof value==="string"&&VALID_BORDER_STYLES.includes(value)}function parseBorderStyle(value,fallback="single"){if(isValidBorderStyle(value))return value;if(value!==void 0&&value!==null)console.warn(`Invalid borderStyle "${value}", falling back to "${fallback}". Valid values are: ${VALID_BORDER_STYLES.join(", ")}`);return fallback}function getBorderFromSides(sides){let result=[];if(sides.top)result.push("top");if(sides.right)result.push("right");if(sides.bottom)result.push("bottom");if(sides.left)result.push("left");return result.length>0?result:!1}function getBorderSides(border){return border===!0?{top:!0,right:!0,bottom:!0,left:!0}:Array.isArray(border)?{top:border.includes("top"),right:border.includes("right"),bottom:border.includes("bottom"),left:border.includes("left")}:{top:!1,right:!1,bottom:!1,left:!1}}function borderCharsToArray(chars){let array=new Uint32Array(11);return array[0]=chars.topLeft.codePointAt(0),array[1]=chars.topRight.codePointAt(0),array[2]=chars.bottomLeft.codePointAt(0),array[3]=chars.bottomRight.codePointAt(0),array[4]=chars.horizontal.codePointAt(0),array[5]=chars.vertical.codePointAt(0),array[6]=chars.topT.codePointAt(0),array[7]=chars.bottomT.codePointAt(0),array[8]=chars.leftT.codePointAt(0),array[9]=chars.rightT.codePointAt(0),array[10]=chars.cross.codePointAt(0),array}class KeyEvent{name;ctrl;meta;shift;option;sequence;number;raw;eventType;source;code;super;hyper;capsLock;numLock;baseCode;repeated;_defaultPrevented=!1;_propagationStopped=!1;constructor(key){this.name=key.name,this.ctrl=key.ctrl,this.meta=key.meta,this.shift=key.shift,this.option=key.option,this.sequence=key.sequence,this.number=key.number,this.raw=key.raw,this.eventType=key.eventType,this.source=key.source,this.code=key.code,this.super=key.super,this.hyper=key.hyper,this.capsLock=key.capsLock,this.numLock=key.numLock,this.baseCode=key.baseCode,this.repeated=key.repeated}get defaultPrevented(){return this._defaultPrevented}get propagationStopped(){return this._propagationStopped}preventDefault(){this._defaultPrevented=!0}stopPropagation(){this._propagationStopped=!0}}class PasteEvent{type="paste";bytes;metadata;_defaultPrevented=!1;_propagationStopped=!1;constructor(bytes,metadata){this.bytes=bytes,this.metadata=metadata}get defaultPrevented(){return this._defaultPrevented}get propagationStopped(){return this._propagationStopped}preventDefault(){this._defaultPrevented=!0}stopPropagation(){this._propagationStopped=!0}}class RGBA{buffer;constructor(buffer2){this.buffer=buffer2}static fromArray(array){return new RGBA(array)}static fromValues(r,g,b2,a=1){return new RGBA(new Float32Array([r,g,b2,a]))}static fromInts(r,g,b2,a=255){return new RGBA(new Float32Array([r/255,g/255,b2/255,a/255]))}static fromHex(hex){return hexToRgb(hex)}toInts(){return[Math.round(this.r*255),Math.round(this.g*255),Math.round(this.b*255),Math.round(this.a*255)]}get r(){return this.buffer[0]}set r(value){this.buffer[0]=value}get g(){return this.buffer[1]}set g(value){this.buffer[1]=value}get b(){return this.buffer[2]}set b(value){this.buffer[2]=value}get a(){return this.buffer[3]}set a(value){this.buffer[3]=value}map(fn){return[fn(this.r),fn(this.g),fn(this.b),fn(this.a)]}toString(){return`rgba(${this.r.toFixed(2)}, ${this.g.toFixed(2)}, ${this.b.toFixed(2)}, ${this.a.toFixed(2)})`}equals(other){if(!other)return!1;return this.r===other.r&&this.g===other.g&&this.b===other.b&&this.a===other.a}}function hexToRgb(hex){if(hex=hex.replace(/^#/,""),hex.length===3)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];else if(hex.length===4)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]+hex[3]+hex[3];if(!/^[0-9A-Fa-f]{6}$/.test(hex)&&!/^[0-9A-Fa-f]{8}$/.test(hex))return console.warn(`Invalid hex color: ${hex}, defaulting to magenta`),RGBA.fromValues(1,0,1,1);let r=parseInt(hex.substring(0,2),16)/255,g=parseInt(hex.substring(2,4),16)/255,b2=parseInt(hex.substring(4,6),16)/255,a=hex.length===8?parseInt(hex.substring(6,8),16)/255:1;return RGBA.fromValues(r,g,b2,a)}function rgbToHex(rgb){return"#"+(rgb.a===1?[rgb.r,rgb.g,rgb.b]:[rgb.r,rgb.g,rgb.b,rgb.a]).map((x)=>{let hex=Math.floor(Math.max(0,Math.min(1,x)*255)).toString(16);return hex.length===1?"0"+hex:hex}).join("")}function hsvToRgb(h,s,v){let r=0,g=0,b2=0,i2=Math.floor(h/60)%6,f=h/60-Math.floor(h/60),p=v*(1-s),q=v*(1-f*s),t=v*(1-(1-f)*s);switch(i2){case 0:r=v,g=t,b2=p;break;case 1:r=q,g=v,b2=p;break;case 2:r=p,g=v,b2=t;break;case 3:r=p,g=q,b2=v;break;case 4:r=t,g=p,b2=v;break;case 5:r=v,g=p,b2=q;break}return RGBA.fromValues(r,g,b2,1)}function parseColor(color){if(typeof color==="string"){let lowerColor=color.toLowerCase();if(lowerColor==="transparent")return RGBA.fromValues(0,0,0,0);if(CSS_COLOR_NAMES[lowerColor])return hexToRgb(CSS_COLOR_NAMES[lowerColor]);return hexToRgb(color)}return color}function parseColorTags(text){let segments=[],currentIndex=0,colorTagRegex=/<c(\d+)>(.*?)<\/c\d+>/g,lastIndex=0,match;while((match=colorTagRegex.exec(text))!==null){if(match.index>lastIndex){let plainText=text.slice(lastIndex,match.index);if(plainText)segments.push({text:plainText,colorIndex:0})}let colorIndex=parseInt(match[1])-1,taggedText=match[2];segments.push({text:taggedText,colorIndex:Math.max(0,colorIndex)}),lastIndex=match.index+match[0].length}if(lastIndex<text.length){let remainingText=text.slice(lastIndex);if(remainingText)segments.push({text:remainingText,colorIndex:0})}return segments}function getParsedFont(fontKey){if(!parsedFonts[fontKey]){let fontDef=fonts[fontKey],parsedChars={};for(let[char,lines]of Object.entries(fontDef.chars))parsedChars[char]=lines.map((line)=>parseColorTags(line));parsedFonts[fontKey]={...fontDef,colors:fontDef.colors||1,chars:parsedChars}}return parsedFonts[fontKey]}function measureText({text,font="tiny"}){let fontDef=getParsedFont(font);if(!fontDef)return console.warn(`Font '${font}' not found`),{width:0,height:0};let currentX=0;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char];if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0]){let spaceWidth=0;for(let segment of spaceChar[0])spaceWidth+=segment.text.length;currentX+=spaceWidth}else currentX+=1;continue}let charWidth=0;if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size}return{width:currentX,height:fontDef.lines}}function getCharacterPositions(text,font="tiny"){let fontDef=getParsedFont(font);if(!fontDef)return[0];let positions=[0],currentX=0;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char],charWidth=0;if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0])for(let segment of spaceChar[0])charWidth+=segment.text.length;else charWidth=1}else if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size;positions.push(currentX)}return positions}function coordinateToCharacterIndex(x,text,font="tiny"){let positions=getCharacterPositions(text,font);if(x<0)return 0;for(let i2=0;i2<positions.length-1;i2++){let currentPos=positions[i2],nextPos=positions[i2+1];if(x>=currentPos&&x<nextPos){let charMidpoint=currentPos+(nextPos-currentPos)/2;return x<charMidpoint?i2:i2+1}}if(positions.length>0&&x>=positions[positions.length-1])return text.length;return 0}function renderFontToFrameBuffer(buffer2,{text,x=0,y=0,color=[RGBA.fromInts(255,255,255,255)],backgroundColor=RGBA.fromInts(0,0,0,255),font="tiny"}){let{width,height:height2}=buffer2,fontDef=getParsedFont(font);if(!fontDef)return console.warn(`Font '${font}' not found`),{width:0,height:0};let colors4=Array.isArray(color)?color:[color];if(y<0||y+fontDef.lines>height2)return{width:0,height:fontDef.lines};let currentX=x,startX=x;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char];if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0]){let spaceWidth=0;for(let segment of spaceChar[0])spaceWidth+=segment.text.length;currentX+=spaceWidth}else currentX+=1;continue}let charWidth=0;if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX>=width)break;if(currentX+charWidth<0){currentX+=charWidth+fontDef.letterspace_size;continue}for(let lineIdx=0;lineIdx<fontDef.lines&&lineIdx<charDef.length;lineIdx++){let segments=charDef[lineIdx],renderY=y+lineIdx;if(renderY>=0&&renderY<height2){let segmentX=currentX;for(let segment of segments){let segmentColor=colors4[segment.colorIndex]||colors4[0];for(let charIdx=0;charIdx<segment.text.length;charIdx++){let renderX=segmentX+charIdx;if(renderX>=0&&renderX<width){let fontChar=segment.text[charIdx];if(fontChar!==" ")buffer2.setCellWithAlphaBlending(renderX,renderY,fontChar,parseColor(segmentColor),parseColor(backgroundColor))}}segmentX+=segment.text.length}}}if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size}return{width:currentX-startX,height:fontDef.lines}}function getBaseAttributes(attr){return attr&ATTRIBUTE_BASE_MASK}function createTextAttributes({bold=!1,italic=!1,underline=!1,dim=!1,blink=!1,inverse=!1,hidden=!1,strikethrough=!1}={}){let attributes=TextAttributes.NONE;if(bold)attributes|=TextAttributes.BOLD;if(italic)attributes|=TextAttributes.ITALIC;if(underline)attributes|=TextAttributes.UNDERLINE;if(dim)attributes|=TextAttributes.DIM;if(blink)attributes|=TextAttributes.BLINK;if(inverse)attributes|=TextAttributes.INVERSE;if(hidden)attributes|=TextAttributes.HIDDEN;if(strikethrough)attributes|=TextAttributes.STRIKETHROUGH;return attributes}function attributesWithLink(baseAttributes,linkId){let base=baseAttributes&ATTRIBUTE_BASE_MASK2,linkBits=(linkId&LINK_ID_PAYLOAD_MASK)<<LINK_ID_SHIFT;return base|linkBits}function getLinkId(attributes){return attributes>>>LINK_ID_SHIFT&LINK_ID_PAYLOAD_MASK}function visualizeRenderableTree(renderable,maxDepth=10){function buildTreeLines(node,prefix="",parentPrefix="",isLastChild=!0,depth=0){if(depth>=maxDepth)return[`${prefix}${node.id} ... (max depth reached)`];let lines=[],children=node.getChildren();if(lines.push(`${prefix}${node.id}`),children.length>0){let lastChildIndex=children.length-1;children.forEach((child,index)=>{let childIsLast=index===lastChildIndex,connector=childIsLast?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",childPrefix=parentPrefix+(isLastChild?" ":"\u2502 "),childLines=buildTreeLines(child,childPrefix+connector,childPrefix,childIsLast,depth+1);lines.push(...childLines)})}return lines}let treeLines=buildTreeLines(renderable);console.log(`Renderable Tree:
|
|
822
|
+
`);return panes[1]||panes[0]}catch{return rightPane}}}function ensureSession(sessionName){try{execSync4(`tmux has-session -t '${sessionName}' 2>/dev/null`,{stdio:"ignore"})}catch{try{execSync4(`tmux new-session -d -s '${sessionName}'`,{stdio:"ignore"})}catch{}}}function attachProjectWindow(rightPane,targetSession,windowIndex){if(targetSession===SESSION_NAME)return;let pane=resolveRightPane(rightPane);if(ensureSession(targetSession),windowIndex!==void 0)try{execSync4(`tmux select-window -t '${targetSession}:${windowIndex}'`,{stdio:"ignore"})}catch{}try{execSync4(`tmux respawn-pane -k -t ${pane} "TMUX='' tmux attach-session -t '${targetSession}'"`,{stdio:"ignore"})}catch{}}function setupKeybindings(session){try{execSync4(`tmux bind-key -T ${KEY_TABLE} Tab select-pane -t ${session}:0.1 \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync4(`tmux bind-key -T ${KEY_TABLE} C-b if-shell "[ $(tmux display-message -p '#{pane_width}' -t ${session}:0.0) -gt 5 ]" "resize-pane -t ${session}:0.0 -x 0" "resize-pane -t ${session}:0.0 -x ${NAV_WIDTH}" \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync4(`tmux bind-key -T ${KEY_TABLE} C-t send-keys -t ${session}:0.1 C-b c \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync4(`tmux bind-key -T ${KEY_TABLE} 'C-\\' run-shell "tmux kill-session -t ${session}"`,{stdio:"ignore"}),execSync4(`tmux set-hook -t ${session} client-session-changed "switch-client -T ${KEY_TABLE}"`,{stdio:"ignore"}),execSync4(`tmux switch-client -T ${KEY_TABLE}`,{stdio:"ignore"})}catch{}}function applyTmuxStyle(session){try{let cmds=[`set-option -t ${session} pane-border-style 'fg=${tmuxStyle.inactiveBorder}'`,`set-option -t ${session} pane-active-border-style 'fg=${tmuxStyle.activeBorder}'`,`set-option -t ${session} status off`,`set-option -t ${session} mouse on`];for(let cmd of cmds)execSync4(`tmux ${cmd}`,{stdio:"ignore"})}catch{}}function cleanup(session=SESSION_NAME){try{execSync4(`tmux unbind-key -T ${KEY_TABLE} Tab 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux unbind-key -T ${KEY_TABLE} C-b 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux unbind-key -T ${KEY_TABLE} C-t 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux unbind-key -T ${KEY_TABLE} 'C-\\' 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux set-hook -u -t ${session} client-session-changed 2>/dev/null`,{stdio:"ignore"}),execSync4(`tmux kill-session -t ${session} 2>/dev/null`,{stdio:"ignore"})}catch{}}function attachTuiSession(){spawnSync("tmux",["attach-session","-t",SESSION_NAME],{stdio:"inherit"})}var SESSION_NAME="genie-tui",KEY_TABLE="genie-tui",NAV_WIDTH=30;var init_tmux2=__esm(()=>{init_theme2()});var highlights_default="./highlights-ghv9g403.scm";var init_highlights=()=>{};var tree_sitter_javascript_default="./tree-sitter-javascript-nd0q4pe9.wasm";var init_tree_sitter_javascript=()=>{};var highlights_default2="./highlights-eq9cgrbb.scm";var init_highlights2=()=>{};var tree_sitter_typescript_default="./tree-sitter-typescript-zxjzwt75.wasm";var init_tree_sitter_typescript=()=>{};var highlights_default3="./highlights-r812a2qc.scm";var init_highlights3=()=>{};var tree_sitter_markdown_default="./tree-sitter-markdown-411r6y9b.wasm";var init_tree_sitter_markdown=()=>{};var injections_default="./injections-73j83es3.scm";var init_injections=()=>{};var highlights_default4="./highlights-x6tmsnaa.scm";var init_highlights4=()=>{};var tree_sitter_markdown_inline_default="./tree-sitter-markdown_inline-j5349f42.wasm";var init_tree_sitter_markdown_inline=()=>{};var highlights_default5="./highlights-hk7bwhj4.scm";var init_highlights5=()=>{};var tree_sitter_zig_default="./tree-sitter-zig-e78zbjpm.wasm";var init_tree_sitter_zig=()=>{};import{EventEmitter}from"events";import{Buffer as Buffer2}from"buffer";import{Buffer as Buffer3}from"buffer";import{EventEmitter as EventEmitter2}from"events";import{resolve as resolve4,dirname as dirname5}from"path";import{fileURLToPath as fileURLToPath2}from"url";import{resolve as resolve22,isAbsolute,parse as parse2}from"path";import{existsSync as existsSync19}from"fs";import{basename as basename2,join as join22}from"path";import os2 from"os";import path2 from"path";import{EventEmitter as EventEmitter3}from"events";import path22 from"path";import{readFile as readFile22,writeFile as writeFile22,mkdir as mkdir22}from"fs/promises";import*as path4 from"path";import{mkdir as mkdir5,readFile as readFile6,writeFile as writeFile3}from"fs/promises";import*as path3 from"path";import{readdir as readdir4}from"fs/promises";import{dlopen,toArrayBuffer as toArrayBuffer4,JSCallback,ptr as ptr4}from"bun:ffi";import{existsSync as existsSync22,writeFileSync as writeFileSync6}from"fs";import{EventEmitter as EventEmitter4}from"events";import{toArrayBuffer,ptr}from"bun:ffi";import{ptr as ptr2,toArrayBuffer as toArrayBuffer2}from"bun:ffi";import{ptr as ptr3,toArrayBuffer as toArrayBuffer3}from"bun:ffi";import{EventEmitter as EventEmitter5}from"events";import util3 from"util";import{EventEmitter as EventEmitter7}from"events";import{Console}from"console";import fs2 from"fs";import path5 from"path";import util22 from"util";import{Writable}from"stream";import{EventEmitter as EventEmitter6}from"events";import{EventEmitter as EventEmitter8}from"events";function __exportSetter2(name,newValue){this[name]=__returnValue2.bind(null,newValue)}function wrapAssembly(lib){function patch(prototype,name,fn){let original=prototype[name];prototype[name]=function(){for(var _len=arguments.length,args=Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];return fn.call(this,original,...args)}}for(let fnName of["setPosition","setMargin","setFlexBasis","setWidth","setHeight","setMinWidth","setMinHeight","setMaxWidth","setMaxHeight","setPadding","setGap"]){let methods={[Unit.Point]:lib.Node.prototype[fnName],[Unit.Percent]:lib.Node.prototype[`${fnName}Percent`],[Unit.Auto]:lib.Node.prototype[`${fnName}Auto`]};patch(lib.Node.prototype,fnName,function(original){for(var _len2=arguments.length,args=Array(_len2>1?_len2-1:0),_key2=1;_key2<_len2;_key2++)args[_key2-1]=arguments[_key2];let value=args.pop(),unit,asNumber;if(value==="auto")unit=Unit.Auto,asNumber=void 0;else if(typeof value==="object")unit=value.unit,asNumber=value.valueOf();else if(unit=typeof value==="string"&&value.endsWith("%")?Unit.Percent:Unit.Point,asNumber=parseFloat(value),value!==void 0&&!Number.isNaN(value)&&Number.isNaN(asNumber))throw Error(`Invalid value ${value} for ${fnName}`);if(!methods[unit])throw Error(`Failed to execute "${fnName}": Unsupported unit '${value}'`);if(asNumber!==void 0)return methods[unit].call(this,...args,asNumber);else return methods[unit].call(this,...args)})}function wrapMeasureFunction(measureFunction){return lib.MeasureCallback.implement({measure:function(){let{width,height:height2}=measureFunction(...arguments);return{width:width??NaN,height:height2??NaN}}})}patch(lib.Node.prototype,"setMeasureFunc",function(original,measureFunc){if(measureFunc)return original.call(this,wrapMeasureFunction(measureFunc));else return this.unsetMeasureFunc()});function wrapDirtiedFunc(dirtiedFunction){return lib.DirtiedCallback.implement({dirtied:dirtiedFunction})}return patch(lib.Node.prototype,"setDirtiedFunc",function(original,dirtiedFunc){original.call(this,wrapDirtiedFunc(dirtiedFunc))}),patch(lib.Config.prototype,"free",function(){lib.Config.destroy(this)}),patch(lib.Node,"create",(_,config)=>{return config?lib.Node.createWithConfig(config):lib.Node.createDefault()}),patch(lib.Node.prototype,"free",function(){lib.Node.destroy(this)}),patch(lib.Node.prototype,"freeRecursive",function(){for(let t=0,T=this.getChildCount();t<T;++t)this.getChild(0).freeRecursive();this.free()}),patch(lib.Node.prototype,"calculateLayout",function(original){let width=arguments.length>1&&arguments[1]!==void 0?arguments[1]:NaN,height2=arguments.length>2&&arguments[2]!==void 0?arguments[2]:NaN,direction=arguments.length>3&&arguments[3]!==void 0?arguments[3]:Direction.LTR;return original.call(this,width,height2,direction)}),{Config:lib.Config,Node:lib.Node,...YGEnums_default}}function isValidBorderStyle(value){return typeof value==="string"&&VALID_BORDER_STYLES.includes(value)}function parseBorderStyle(value,fallback="single"){if(isValidBorderStyle(value))return value;if(value!==void 0&&value!==null)console.warn(`Invalid borderStyle "${value}", falling back to "${fallback}". Valid values are: ${VALID_BORDER_STYLES.join(", ")}`);return fallback}function getBorderFromSides(sides){let result=[];if(sides.top)result.push("top");if(sides.right)result.push("right");if(sides.bottom)result.push("bottom");if(sides.left)result.push("left");return result.length>0?result:!1}function getBorderSides(border){return border===!0?{top:!0,right:!0,bottom:!0,left:!0}:Array.isArray(border)?{top:border.includes("top"),right:border.includes("right"),bottom:border.includes("bottom"),left:border.includes("left")}:{top:!1,right:!1,bottom:!1,left:!1}}function borderCharsToArray(chars){let array=new Uint32Array(11);return array[0]=chars.topLeft.codePointAt(0),array[1]=chars.topRight.codePointAt(0),array[2]=chars.bottomLeft.codePointAt(0),array[3]=chars.bottomRight.codePointAt(0),array[4]=chars.horizontal.codePointAt(0),array[5]=chars.vertical.codePointAt(0),array[6]=chars.topT.codePointAt(0),array[7]=chars.bottomT.codePointAt(0),array[8]=chars.leftT.codePointAt(0),array[9]=chars.rightT.codePointAt(0),array[10]=chars.cross.codePointAt(0),array}class KeyEvent{name;ctrl;meta;shift;option;sequence;number;raw;eventType;source;code;super;hyper;capsLock;numLock;baseCode;repeated;_defaultPrevented=!1;_propagationStopped=!1;constructor(key){this.name=key.name,this.ctrl=key.ctrl,this.meta=key.meta,this.shift=key.shift,this.option=key.option,this.sequence=key.sequence,this.number=key.number,this.raw=key.raw,this.eventType=key.eventType,this.source=key.source,this.code=key.code,this.super=key.super,this.hyper=key.hyper,this.capsLock=key.capsLock,this.numLock=key.numLock,this.baseCode=key.baseCode,this.repeated=key.repeated}get defaultPrevented(){return this._defaultPrevented}get propagationStopped(){return this._propagationStopped}preventDefault(){this._defaultPrevented=!0}stopPropagation(){this._propagationStopped=!0}}class PasteEvent{type="paste";bytes;metadata;_defaultPrevented=!1;_propagationStopped=!1;constructor(bytes,metadata){this.bytes=bytes,this.metadata=metadata}get defaultPrevented(){return this._defaultPrevented}get propagationStopped(){return this._propagationStopped}preventDefault(){this._defaultPrevented=!0}stopPropagation(){this._propagationStopped=!0}}class RGBA{buffer;constructor(buffer2){this.buffer=buffer2}static fromArray(array){return new RGBA(array)}static fromValues(r,g,b2,a=1){return new RGBA(new Float32Array([r,g,b2,a]))}static fromInts(r,g,b2,a=255){return new RGBA(new Float32Array([r/255,g/255,b2/255,a/255]))}static fromHex(hex){return hexToRgb(hex)}toInts(){return[Math.round(this.r*255),Math.round(this.g*255),Math.round(this.b*255),Math.round(this.a*255)]}get r(){return this.buffer[0]}set r(value){this.buffer[0]=value}get g(){return this.buffer[1]}set g(value){this.buffer[1]=value}get b(){return this.buffer[2]}set b(value){this.buffer[2]=value}get a(){return this.buffer[3]}set a(value){this.buffer[3]=value}map(fn){return[fn(this.r),fn(this.g),fn(this.b),fn(this.a)]}toString(){return`rgba(${this.r.toFixed(2)}, ${this.g.toFixed(2)}, ${this.b.toFixed(2)}, ${this.a.toFixed(2)})`}equals(other){if(!other)return!1;return this.r===other.r&&this.g===other.g&&this.b===other.b&&this.a===other.a}}function hexToRgb(hex){if(hex=hex.replace(/^#/,""),hex.length===3)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];else if(hex.length===4)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]+hex[3]+hex[3];if(!/^[0-9A-Fa-f]{6}$/.test(hex)&&!/^[0-9A-Fa-f]{8}$/.test(hex))return console.warn(`Invalid hex color: ${hex}, defaulting to magenta`),RGBA.fromValues(1,0,1,1);let r=parseInt(hex.substring(0,2),16)/255,g=parseInt(hex.substring(2,4),16)/255,b2=parseInt(hex.substring(4,6),16)/255,a=hex.length===8?parseInt(hex.substring(6,8),16)/255:1;return RGBA.fromValues(r,g,b2,a)}function rgbToHex(rgb){return"#"+(rgb.a===1?[rgb.r,rgb.g,rgb.b]:[rgb.r,rgb.g,rgb.b,rgb.a]).map((x)=>{let hex=Math.floor(Math.max(0,Math.min(1,x)*255)).toString(16);return hex.length===1?"0"+hex:hex}).join("")}function hsvToRgb(h,s,v){let r=0,g=0,b2=0,i2=Math.floor(h/60)%6,f=h/60-Math.floor(h/60),p=v*(1-s),q=v*(1-f*s),t=v*(1-(1-f)*s);switch(i2){case 0:r=v,g=t,b2=p;break;case 1:r=q,g=v,b2=p;break;case 2:r=p,g=v,b2=t;break;case 3:r=p,g=q,b2=v;break;case 4:r=t,g=p,b2=v;break;case 5:r=v,g=p,b2=q;break}return RGBA.fromValues(r,g,b2,1)}function parseColor(color){if(typeof color==="string"){let lowerColor=color.toLowerCase();if(lowerColor==="transparent")return RGBA.fromValues(0,0,0,0);if(CSS_COLOR_NAMES[lowerColor])return hexToRgb(CSS_COLOR_NAMES[lowerColor]);return hexToRgb(color)}return color}function parseColorTags(text){let segments=[],currentIndex=0,colorTagRegex=/<c(\d+)>(.*?)<\/c\d+>/g,lastIndex=0,match;while((match=colorTagRegex.exec(text))!==null){if(match.index>lastIndex){let plainText=text.slice(lastIndex,match.index);if(plainText)segments.push({text:plainText,colorIndex:0})}let colorIndex=parseInt(match[1])-1,taggedText=match[2];segments.push({text:taggedText,colorIndex:Math.max(0,colorIndex)}),lastIndex=match.index+match[0].length}if(lastIndex<text.length){let remainingText=text.slice(lastIndex);if(remainingText)segments.push({text:remainingText,colorIndex:0})}return segments}function getParsedFont(fontKey){if(!parsedFonts[fontKey]){let fontDef=fonts[fontKey],parsedChars={};for(let[char,lines]of Object.entries(fontDef.chars))parsedChars[char]=lines.map((line)=>parseColorTags(line));parsedFonts[fontKey]={...fontDef,colors:fontDef.colors||1,chars:parsedChars}}return parsedFonts[fontKey]}function measureText({text,font="tiny"}){let fontDef=getParsedFont(font);if(!fontDef)return console.warn(`Font '${font}' not found`),{width:0,height:0};let currentX=0;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char];if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0]){let spaceWidth=0;for(let segment of spaceChar[0])spaceWidth+=segment.text.length;currentX+=spaceWidth}else currentX+=1;continue}let charWidth=0;if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size}return{width:currentX,height:fontDef.lines}}function getCharacterPositions(text,font="tiny"){let fontDef=getParsedFont(font);if(!fontDef)return[0];let positions=[0],currentX=0;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char],charWidth=0;if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0])for(let segment of spaceChar[0])charWidth+=segment.text.length;else charWidth=1}else if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size;positions.push(currentX)}return positions}function coordinateToCharacterIndex(x,text,font="tiny"){let positions=getCharacterPositions(text,font);if(x<0)return 0;for(let i2=0;i2<positions.length-1;i2++){let currentPos=positions[i2],nextPos=positions[i2+1];if(x>=currentPos&&x<nextPos){let charMidpoint=currentPos+(nextPos-currentPos)/2;return x<charMidpoint?i2:i2+1}}if(positions.length>0&&x>=positions[positions.length-1])return text.length;return 0}function renderFontToFrameBuffer(buffer2,{text,x=0,y=0,color=[RGBA.fromInts(255,255,255,255)],backgroundColor=RGBA.fromInts(0,0,0,255),font="tiny"}){let{width,height:height2}=buffer2,fontDef=getParsedFont(font);if(!fontDef)return console.warn(`Font '${font}' not found`),{width:0,height:0};let colors4=Array.isArray(color)?color:[color];if(y<0||y+fontDef.lines>height2)return{width:0,height:fontDef.lines};let currentX=x,startX=x;for(let i2=0;i2<text.length;i2++){let char=text[i2].toUpperCase(),charDef=fontDef.chars[char];if(!charDef){let spaceChar=fontDef.chars[" "];if(spaceChar&&spaceChar[0]){let spaceWidth=0;for(let segment of spaceChar[0])spaceWidth+=segment.text.length;currentX+=spaceWidth}else currentX+=1;continue}let charWidth=0;if(charDef[0])for(let segment of charDef[0])charWidth+=segment.text.length;if(currentX>=width)break;if(currentX+charWidth<0){currentX+=charWidth+fontDef.letterspace_size;continue}for(let lineIdx=0;lineIdx<fontDef.lines&&lineIdx<charDef.length;lineIdx++){let segments=charDef[lineIdx],renderY=y+lineIdx;if(renderY>=0&&renderY<height2){let segmentX=currentX;for(let segment of segments){let segmentColor=colors4[segment.colorIndex]||colors4[0];for(let charIdx=0;charIdx<segment.text.length;charIdx++){let renderX=segmentX+charIdx;if(renderX>=0&&renderX<width){let fontChar=segment.text[charIdx];if(fontChar!==" ")buffer2.setCellWithAlphaBlending(renderX,renderY,fontChar,parseColor(segmentColor),parseColor(backgroundColor))}}segmentX+=segment.text.length}}}if(currentX+=charWidth,i2<text.length-1)currentX+=fontDef.letterspace_size}return{width:currentX-startX,height:fontDef.lines}}function getBaseAttributes(attr){return attr&ATTRIBUTE_BASE_MASK}function createTextAttributes({bold=!1,italic=!1,underline=!1,dim=!1,blink=!1,inverse=!1,hidden=!1,strikethrough=!1}={}){let attributes=TextAttributes.NONE;if(bold)attributes|=TextAttributes.BOLD;if(italic)attributes|=TextAttributes.ITALIC;if(underline)attributes|=TextAttributes.UNDERLINE;if(dim)attributes|=TextAttributes.DIM;if(blink)attributes|=TextAttributes.BLINK;if(inverse)attributes|=TextAttributes.INVERSE;if(hidden)attributes|=TextAttributes.HIDDEN;if(strikethrough)attributes|=TextAttributes.STRIKETHROUGH;return attributes}function attributesWithLink(baseAttributes,linkId){let base=baseAttributes&ATTRIBUTE_BASE_MASK2,linkBits=(linkId&LINK_ID_PAYLOAD_MASK)<<LINK_ID_SHIFT;return base|linkBits}function getLinkId(attributes){return attributes>>>LINK_ID_SHIFT&LINK_ID_PAYLOAD_MASK}function visualizeRenderableTree(renderable,maxDepth=10){function buildTreeLines(node,prefix="",parentPrefix="",isLastChild=!0,depth=0){if(depth>=maxDepth)return[`${prefix}${node.id} ... (max depth reached)`];let lines=[],children=node.getChildren();if(lines.push(`${prefix}${node.id}`),children.length>0){let lastChildIndex=children.length-1;children.forEach((child,index)=>{let childIsLast=index===lastChildIndex,connector=childIsLast?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",childPrefix=parentPrefix+(isLastChild?" ":"\u2502 "),childLines=buildTreeLines(child,childPrefix+connector,childPrefix,childIsLast,depth+1);lines.push(...childLines)})}return lines}let treeLines=buildTreeLines(renderable);console.log(`Renderable Tree:
|
|
823
823
|
`+treeLines.join(`
|
|
824
824
|
`))}function isStyledText(obj){return obj&&obj[BrandedStyledText]}function stringToStyledText(content){return new StyledText([{__isChunk:!0,text:content}])}function applyStyle(input,style){if(typeof input==="object"&&"__isChunk"in input){let existingChunk=input,fg=style.fg?parseColor(style.fg):existingChunk.fg,bg=style.bg?parseColor(style.bg):existingChunk.bg,newAttrs=createTextAttributes(style),mergedAttrs=existingChunk.attributes?existingChunk.attributes|newAttrs:newAttrs;return{__isChunk:!0,text:existingChunk.text,fg,bg,attributes:mergedAttrs,link:existingChunk.link}}else{let plainTextStr=String(input),fg=style.fg?parseColor(style.fg):void 0,bg=style.bg?parseColor(style.bg):void 0,attributes=createTextAttributes(style);return{__isChunk:!0,text:plainTextStr,fg,bg,attributes}}}function t(strings,...values2){let chunks=[];for(let i2=0;i2<strings.length;i2++){let raw=strings[i2];if(raw)chunks.push({__isChunk:!0,text:raw,attributes:0});let val=values2[i2];if(typeof val==="object"&&"__isChunk"in val)chunks.push(val);else if(val!==void 0){let plainTextStr=String(val);chunks.push({__isChunk:!0,text:plainTextStr,attributes:0})}}return new StyledText(chunks)}function hastToTextChunks(node,syntaxStyle,parentStyles=[]){let chunks=[];if(node.type==="text"){let stylesToMerge=parentStyles.length>0?parentStyles:["default"],mergedStyle=syntaxStyle.mergeStyles(...stylesToMerge);chunks.push({__isChunk:!0,text:node.value,fg:mergedStyle.fg,bg:mergedStyle.bg,attributes:mergedStyle.attributes})}else if(node.type==="element"){let currentStyles=[...parentStyles];if(node.properties?.className){let classes=node.properties.className.split(" ");for(let cls of classes)currentStyles.push(cls)}for(let child of node.children)chunks.push(...hastToTextChunks(child,syntaxStyle,currentStyles))}return chunks}function hastToStyledText(hast,syntaxStyle){let chunks=hastToTextChunks(hast,syntaxStyle);return new StyledText(chunks)}class SystemClock{now(){if(!globalThis.performance||typeof globalThis.performance.now!=="function")throw Error("SystemClock requires globalThis.performance.now()");return globalThis.performance.now()}setTimeout(fn,delayMs){return globalThis.setTimeout(fn,delayMs)}clearTimeout(handle){globalThis.clearTimeout(handle)}setInterval(fn,delayMs){return globalThis.setInterval(fn,delayMs)}clearInterval(handle){globalThis.clearInterval(handle)}}function fromKittyMods(mod){return{shift:!!(mod&1),alt:!!(mod&2),ctrl:!!(mod&4),super:!!(mod&8),hyper:!!(mod&16),meta:!!(mod&32),capsLock:!!(mod&64),numLock:!!(mod&128)}}function parseKittySpecialKey(sequence){let match=/^\x1b\[(\d+);(\d+):(\d+)([A-Z~])$/.exec(sequence);if(!match)return null;let keyNumOrOne=match[1],modifierStr=match[2],eventTypeStr=match[3],terminator=match[4],keyName;if(terminator==="~")keyName=tildeKeyMap[keyNumOrOne];else{if(keyNumOrOne!=="1")return null;keyName=functionalKeyMap[terminator]}if(!keyName)return null;let key={name:keyName,ctrl:!1,meta:!1,shift:!1,option:!1,number:!1,sequence,raw:sequence,eventType:"press",source:"kitty",super:!1,hyper:!1,capsLock:!1,numLock:!1};if(modifierStr){let modifierMask=parseInt(modifierStr,10);if(!isNaN(modifierMask)&&modifierMask>1){let mods=fromKittyMods(modifierMask-1);key.shift=mods.shift,key.ctrl=mods.ctrl,key.meta=mods.alt||mods.meta,key.option=mods.alt,key.super=mods.super,key.hyper=mods.hyper,key.capsLock=mods.capsLock,key.numLock=mods.numLock}}if(eventTypeStr==="1"||!eventTypeStr)key.eventType="press";else if(eventTypeStr==="2")key.eventType="press",key.repeated=!0;else if(eventTypeStr==="3")key.eventType="release";return key}function parseKittyKeyboard(sequence){let specialResult=parseKittySpecialKey(sequence);if(specialResult)return specialResult;let match=/^\x1b\[([^\x1b]+)u$/.exec(sequence);if(!match)return null;let fields=match[1].split(";");if(fields.length<1)return null;let key={name:"",ctrl:!1,meta:!1,shift:!1,option:!1,number:!1,sequence,raw:sequence,eventType:"press",source:"kitty",super:!1,hyper:!1,capsLock:!1,numLock:!1},text="",field1=fields[0]?.split(":")||[],codepointStr=field1[0];if(!codepointStr)return null;let codepoint=parseInt(codepointStr,10);if(isNaN(codepoint))return null;let shiftedCodepoint,baseCodepoint;if(field1[1]){let shifted=parseInt(field1[1],10);if(!isNaN(shifted)&&shifted>0&&shifted<=1114111)shiftedCodepoint=shifted}if(field1[2]){let base=parseInt(field1[2],10);if(!isNaN(base)&&base>0&&base<=1114111)baseCodepoint=base}let knownKey=kittyKeyMap[codepoint];if(knownKey)key.name=knownKey,key.code=`[${codepoint}u`;else if(codepoint===0)key.name="";else if(codepoint>0&&codepoint<=1114111){let char=String.fromCodePoint(codepoint);if(key.name=char,baseCodepoint)key.baseCode=baseCodepoint}else return null;if(fields[1]){let field2=fields[1].split(":"),modifierStr=field2[0],eventTypeStr=field2[1];if(modifierStr){let modifierMask=parseInt(modifierStr,10);if(!isNaN(modifierMask)&&modifierMask>1){let mods=fromKittyMods(modifierMask-1);key.shift=mods.shift,key.ctrl=mods.ctrl,key.meta=mods.alt||mods.meta,key.option=mods.alt,key.super=mods.super,key.hyper=mods.hyper,key.capsLock=mods.capsLock,key.numLock=mods.numLock}}if(eventTypeStr==="1"||!eventTypeStr)key.eventType="press";else if(eventTypeStr==="2")key.eventType="press",key.repeated=!0;else if(eventTypeStr==="3")key.eventType="release";else key.eventType="press"}if(fields[2]){let codepoints=fields[2].split(":");for(let cpStr of codepoints){let cp=parseInt(cpStr,10);if(!isNaN(cp)&&cp>0&&cp<=1114111)text+=String.fromCodePoint(cp)}}if(text===""){if(key.name.length>0&&!kittyKeyMap[codepoint])if(key.shift&&shiftedCodepoint)text=String.fromCodePoint(shiftedCodepoint);else if(key.shift&&key.name.length===1)text=key.name.toLocaleUpperCase();else text=key.name}if(key.name===" "&&key.shift&&!key.ctrl&&!key.meta)text=" ";if(text){if(codepoint===0)key.name=text;key.sequence=text}if(codepoint===0&&text==="")return null;return key}class LinearScrollAccel{tick(_now){return 1}reset(){}}class MacOSScrollAccel{opts;lastTickTime=0;velocityHistory=[];historySize=3;streakTimeout=150;minTickInterval=6;constructor(opts={}){this.opts=opts}tick(now=Date.now()){let A=this.opts.A??0.8,tau=this.opts.tau??3,maxMultiplier=this.opts.maxMultiplier??6,dt=this.lastTickTime?now-this.lastTickTime:1/0;if(dt===1/0||dt>this.streakTimeout)return this.lastTickTime=now,this.velocityHistory=[],1;if(dt<this.minTickInterval)return 1;if(this.lastTickTime=now,this.velocityHistory.push(dt),this.velocityHistory.length>this.historySize)this.velocityHistory.shift();let x=100/(this.velocityHistory.reduce((a,b2)=>a+b2,0)/this.velocityHistory.length)/tau,multiplier=1+A*(Math.exp(x)-1);return Math.min(multiplier,maxMultiplier)}reset(){this.lastTickTime=0,this.velocityHistory=[]}}function parseAlign(value){if(value==null)return Align.Auto;switch(value.toLowerCase()){case"auto":return Align.Auto;case"flex-start":return Align.FlexStart;case"center":return Align.Center;case"flex-end":return Align.FlexEnd;case"stretch":return Align.Stretch;case"baseline":return Align.Baseline;case"space-between":return Align.SpaceBetween;case"space-around":return Align.SpaceAround;case"space-evenly":return Align.SpaceEvenly;default:return Align.Auto}}function parseAlignItems(value){if(value==null)return Align.Stretch;switch(value.toLowerCase()){case"auto":return Align.Auto;case"flex-start":return Align.FlexStart;case"center":return Align.Center;case"flex-end":return Align.FlexEnd;case"stretch":return Align.Stretch;case"baseline":return Align.Baseline;case"space-between":return Align.SpaceBetween;case"space-around":return Align.SpaceAround;case"space-evenly":return Align.SpaceEvenly;default:return Align.Stretch}}function parseBoxSizing(value){if(value==null)return BoxSizing.BorderBox;switch(value.toLowerCase()){case"border-box":return BoxSizing.BorderBox;case"content-box":return BoxSizing.ContentBox;default:return BoxSizing.BorderBox}}function parseDimension(value){if(value==null)return Dimension.Width;switch(value.toLowerCase()){case"width":return Dimension.Width;case"height":return Dimension.Height;default:return Dimension.Width}}function parseDirection(value){if(value==null)return Direction.LTR;switch(value.toLowerCase()){case"inherit":return Direction.Inherit;case"ltr":return Direction.LTR;case"rtl":return Direction.RTL;default:return Direction.LTR}}function parseDisplay(value){if(value==null)return Display.Flex;switch(value.toLowerCase()){case"flex":return Display.Flex;case"none":return Display.None;case"contents":return Display.Contents;default:return Display.Flex}}function parseEdge(value){if(value==null)return Edge.All;switch(value.toLowerCase()){case"left":return Edge.Left;case"top":return Edge.Top;case"right":return Edge.Right;case"bottom":return Edge.Bottom;case"start":return Edge.Start;case"end":return Edge.End;case"horizontal":return Edge.Horizontal;case"vertical":return Edge.Vertical;case"all":return Edge.All;default:return Edge.All}}function parseFlexDirection(value){if(value==null)return FlexDirection.Column;switch(value.toLowerCase()){case"column":return FlexDirection.Column;case"column-reverse":return FlexDirection.ColumnReverse;case"row":return FlexDirection.Row;case"row-reverse":return FlexDirection.RowReverse;default:return FlexDirection.Column}}function parseGutter(value){if(value==null)return Gutter.All;switch(value.toLowerCase()){case"column":return Gutter.Column;case"row":return Gutter.Row;case"all":return Gutter.All;default:return Gutter.All}}function parseJustify(value){if(value==null)return Justify.FlexStart;switch(value.toLowerCase()){case"flex-start":return Justify.FlexStart;case"center":return Justify.Center;case"flex-end":return Justify.FlexEnd;case"space-between":return Justify.SpaceBetween;case"space-around":return Justify.SpaceAround;case"space-evenly":return Justify.SpaceEvenly;default:return Justify.FlexStart}}function parseLogLevel(value){if(value==null)return LogLevel.Info;switch(value.toLowerCase()){case"error":return LogLevel.Error;case"warn":return LogLevel.Warn;case"info":return LogLevel.Info;case"debug":return LogLevel.Debug;case"verbose":return LogLevel.Verbose;case"fatal":return LogLevel.Fatal;default:return LogLevel.Info}}function parseMeasureMode(value){if(value==null)return MeasureMode.Undefined;switch(value.toLowerCase()){case"undefined":return MeasureMode.Undefined;case"exactly":return MeasureMode.Exactly;case"at-most":return MeasureMode.AtMost;default:return MeasureMode.Undefined}}function parseOverflow(value){if(value==null)return Overflow.Visible;switch(value.toLowerCase()){case"visible":return Overflow.Visible;case"hidden":return Overflow.Hidden;case"scroll":return Overflow.Scroll;default:return Overflow.Visible}}function parsePositionType(value){if(value==null)return PositionType.Relative;switch(value.toLowerCase()){case"static":return PositionType.Static;case"relative":return PositionType.Relative;case"absolute":return PositionType.Absolute;default:return PositionType.Static}}function parseUnit(value){if(value==null)return Unit.Point;switch(value.toLowerCase()){case"undefined":return Unit.Undefined;case"point":return Unit.Point;case"percent":return Unit.Percent;case"auto":return Unit.Auto;default:return Unit.Point}}function parseWrap(value){if(value==null)return Wrap.NoWrap;switch(value.toLowerCase()){case"no-wrap":return Wrap.NoWrap;case"wrap":return Wrap.Wrap;case"wrap-reverse":return Wrap.WrapReverse;default:return Wrap.NoWrap}}class SelectionAnchor{renderable;relativeX;relativeY;constructor(renderable,absoluteX,absoluteY){this.renderable=renderable,this.relativeX=absoluteX-this.renderable.x,this.relativeY=absoluteY-this.renderable.y}get x(){return this.renderable.x+this.relativeX}get y(){return this.renderable.y+this.relativeY}}class Selection{_anchor;_focus;_selectedRenderables=[];_touchedRenderables=[];_isActive=!0;_isDragging=!0;_isStart=!1;constructor(anchorRenderable,anchor,focus){this._anchor=new SelectionAnchor(anchorRenderable,anchor.x,anchor.y),this._focus={...focus}}get isStart(){return this._isStart}set isStart(value){this._isStart=value}get anchor(){return{x:this._anchor.x,y:this._anchor.y}}get focus(){return{...this._focus}}set focus(value){this._focus={...value}}get isActive(){return this._isActive}set isActive(value){this._isActive=value}get isDragging(){return this._isDragging}set isDragging(value){this._isDragging=value}get bounds(){let minX=Math.min(this._anchor.x,this._focus.x),maxX=Math.max(this._anchor.x,this._focus.x),minY=Math.min(this._anchor.y,this._focus.y),maxY=Math.max(this._anchor.y,this._focus.y),width=maxX-minX+1,height2=maxY-minY+1;return{x:minX,y:minY,width,height:height2}}updateSelectedRenderables(selectedRenderables){this._selectedRenderables=selectedRenderables}get selectedRenderables(){return this._selectedRenderables}updateTouchedRenderables(touchedRenderables){this._touchedRenderables=touchedRenderables}get touchedRenderables(){return this._touchedRenderables}getSelectedText(){return this._selectedRenderables.sort((a,b2)=>{let aY=a.y,bY=b2.y;if(aY!==bY)return aY-bY;return a.x-b2.x}).filter((renderable)=>!renderable.isDestroyed).map((renderable)=>renderable.getSelectedText()).filter((text)=>text).join(`
|
|
825
825
|
`)}}function convertGlobalToLocalSelection(globalSelection,localX,localY){if(!globalSelection?.isActive)return null;return{anchorX:globalSelection.anchor.x-localX,anchorY:globalSelection.anchor.y-localY,focusX:globalSelection.focus.x-localX,focusY:globalSelection.focus.y-localY,isActive:!0}}class ASCIIFontSelectionHelper{getText;getFont;localSelection=null;constructor(getText,getFont){this.getText=getText,this.getFont=getFont}hasSelection(){return this.localSelection!==null}getSelection(){return this.localSelection}shouldStartSelection(localX,localY,width,height2){if(localX<0||localX>=width||localY<0||localY>=height2)return!1;let text=this.getText(),font=this.getFont(),charIndex=coordinateToCharacterIndex(localX,text,font);return charIndex>=0&&charIndex<=text.length}onLocalSelectionChanged(localSelection,width,height2){let previousSelection=this.localSelection;if(!localSelection?.isActive)return this.localSelection=null,previousSelection!==null;let text=this.getText(),font=this.getFont(),selStart={x:localSelection.anchorX,y:localSelection.anchorY},selEnd={x:localSelection.focusX,y:localSelection.focusY};if(height2-1<selStart.y||0>selEnd.y)return this.localSelection=null,previousSelection!==null;let startCharIndex=0,endCharIndex=text.length;if(selStart.y>height2-1)return this.localSelection=null,previousSelection!==null;else if(selStart.y>=0&&selStart.y<=height2-1){if(selStart.x>0)startCharIndex=coordinateToCharacterIndex(selStart.x,text,font)}if(selEnd.y<0)return this.localSelection=null,previousSelection!==null;else if(selEnd.y>=0&&selEnd.y<=height2-1)if(selEnd.x>=0)endCharIndex=coordinateToCharacterIndex(selEnd.x,text,font);else endCharIndex=0;if(startCharIndex<endCharIndex&&startCharIndex>=0&&endCharIndex<=text.length)this.localSelection={start:startCharIndex,end:endCharIndex};else this.localSelection=null;return previousSelection?.start!==this.localSelection?.start||previousSelection?.end!==this.localSelection?.end}}function singleton(key,factory){let bag=globalThis[singletonCacheSymbol]??={};if(!(key in bag))bag[key]=factory();return bag[key]}function destroySingleton(key){let bag=globalThis[singletonCacheSymbol];if(bag&&key in bag)delete bag[key]}function hasSingleton(key){let bag=globalThis[singletonCacheSymbol];return bag&&key in bag}function registerEnvVar(config){let existing=envRegistry[config.name];if(existing){if(existing.description!==config.description||existing.type!==config.type||existing.default!==config.default)throw Error(`Environment variable "${config.name}" is already registered with different configuration. Existing: ${JSON.stringify(existing)}, New: ${JSON.stringify(config)}`);return}envRegistry[config.name]=config}function normalizeBoolean(value){let lowerValue=value.toLowerCase();return["true","1","on","yes"].includes(lowerValue)}function parseEnvValue(config){let envValue=process.env[config.name];if(envValue===void 0&&config.default!==void 0)return config.default;if(envValue===void 0)throw Error(`Required environment variable ${config.name} is not set. ${config.description}`);switch(config.type){case"boolean":return typeof envValue==="boolean"?envValue:normalizeBoolean(envValue);case"number":let numValue=Number(envValue);if(isNaN(numValue))throw Error(`Environment variable ${config.name} must be a valid number, got: ${envValue}`);return numValue;case"string":default:return envValue}}class EnvStore{parsedValues=new Map;get(key){if(this.parsedValues.has(key))return this.parsedValues.get(key);if(!(key in envRegistry))throw Error(`Environment variable ${key} is not registered.`);try{let value=parseEnvValue(envRegistry[key]);return this.parsedValues.set(key,value),value}catch(error2){throw Error(`Failed to parse env var ${key}: ${error2 instanceof Error?error2.message:String(error2)}`)}}has(key){return key in envRegistry}clearCache(){this.parsedValues.clear()}}function clearEnvCache(){envStore.clearCache()}function generateEnvMarkdown(){let configs=Object.values(envRegistry);if(configs.length===0)return`# Environment Variables
|
|
@@ -1284,7 +1284,7 @@ $ bun add react-devtools-core@7 -d
|
|
|
1284
1284
|
AND a.ended_at IS NULL
|
|
1285
1285
|
ORDER BY a.started_at DESC
|
|
1286
1286
|
`).map(mapAssignment)}function mapExecutor(row){let meta=row.metadata;return{id:String(row.id),agentId:String(row.agent_id),agentName:row.agent_name?String(row.agent_name):null,provider:String(row.provider),transport:String(row.transport),pid:row.pid!=null?Number(row.pid):null,tmuxSession:row.tmux_session?String(row.tmux_session):null,tmuxPaneId:row.tmux_pane_id?String(row.tmux_pane_id):null,state:String(row.state),metadata:typeof meta==="string"?JSON.parse(meta):meta??{},startedAt:row.started_at instanceof Date?row.started_at.toISOString():String(row.started_at),role:row.role?String(row.role):null,team:row.team?String(row.team):null}}function mapAssignment(row){return{id:String(row.id),executorId:String(row.executor_id),taskId:row.task_id?String(row.task_id):null,taskTitle:row.task_title?String(row.task_title):null,wishSlug:row.wish_slug?String(row.wish_slug):null,groupNumber:row.group_number!=null?Number(row.group_number):null,startedAt:row.started_at instanceof Date?row.started_at.toISOString():String(row.started_at)}}import{execSync as execSync5}from"child_process";function execQuiet(cmd){try{return execSync5(cmd,{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return""}}function parsePaneLine(parts){let[sessionName,winIdxStr,winName,winActive,winPanes,paneIdxStr,paneId,panePidStr,paneCmd,paneTitle,paneSize,sessAttached,sessWindows,sessCreated,paneDead]=parts;return{sessionName,winIdxStr,session:{name:sessionName,attached:sessAttached==="1",windowCount:Number.parseInt(sessWindows,10)||0,created:Number.parseInt(sessCreated,10)||0},window:{sessionName,index:Number.parseInt(winIdxStr,10)||0,name:winName,active:winActive==="1",paneCount:Number.parseInt(winPanes,10)||0},pane:{sessionName,windowIndex:Number.parseInt(winIdxStr,10)||0,paneIndex:Number.parseInt(paneIdxStr,10)||0,paneId,pid:Number.parseInt(panePidStr,10)||0,command:paneCmd,title:paneTitle,size:paneSize,isDead:paneDead==="1"}}}function getTmuxInventory(){let paneOutput=execQuiet("tmux list-panes -a -F '#{session_name}|#{window_index}|#{window_name}|#{window_active}|#{window_panes}|#{pane_index}|#{pane_id}|#{pane_pid}|#{pane_current_command}|#{pane_title}|#{pane_width}x#{pane_height}|#{session_attached}|#{session_windows}|#{session_created}|#{pane_dead}'");if(!paneOutput)return[];let sessionMap=new Map,windowMap=new Map;for(let line of paneOutput.split(`
|
|
1287
|
-
`)){if(!line)continue;let parts=line.split("|");if(parts.length<15)continue;let parsed=parsePaneLine(parts);if(!sessionMap.has(parsed.sessionName))sessionMap.set(parsed.sessionName,{...parsed.session,windows:[]});let winKey=`${parsed.sessionName}:${parsed.winIdxStr}`;if(!windowMap.has(winKey)){let win={...parsed.window,panes:[]};windowMap.set(winKey,win),sessionMap.get(parsed.sessionName)?.windows.push(win)}windowMap.get(winKey)?.panes.push(parsed.pane)}return Array.from(sessionMap.values()).sort((a,b3)=>a.name.localeCompare(b3.name))}function isPidAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function allClaudePanes(sessions){return sessions.flatMap((s)=>s.windows.flatMap((w2)=>w2.panes)).filter((p)=>p.command==="claude"||p.title.includes("claude"))}function detectGaps(executors,sessions){let deadPidExecutors=executors.filter((e)=>e.pid!=null&&!isPidAlive(e.pid)),executorPaneIds=new Set(executors.map((e)=>e.tmuxPaneId).filter(Boolean)),claudePanes=allClaudePanes(sessions),orphanPanes=claudePanes.filter((p)=>!executorPaneIds.has(p.paneId)),linkedCount=executors.filter((e)=>e.tmuxPaneId&&!deadPidExecutors.some((d2)=>d2.id===e.id)).length,deadPaneCount=sessions.flatMap((s)=>s.windows.flatMap((w2)=>w2.panes)).filter((p)=>p.isDead).length;return{deadPidExecutors,orphanPanes,linkedCount,totalExecutors:executors.length,totalClaudePanes:claudePanes.length,deadPaneCount}}async function collectDiagnostics(){let{loadExecutors:loadExecutors2,loadAssignments:loadAssignments2}=await Promise.resolve().then(() => exports_db2),sessions=getTmuxInventory(),executors=await loadExecutors2(),executorIds=executors.map((e)=>e.id),assignments=await loadAssignments2(executorIds),gaps=detectGaps(executors,sessions);return{sessions,executors,assignments,gaps,timestamp:Date.now()}}var init_diagnostics=()=>{};function buildSessionTree(snapshot){let executorByPaneId=new Map;for(let exec3 of snapshot.executors)if(exec3.tmuxPaneId)executorByPaneId.set(exec3.tmuxPaneId,exec3);return snapshot.sessions.map((session)=>sessionToNode(session,executorByPaneId))}function sessionToNode(session,executorMap){let claudePanes=session.windows.reduce((sum,w2)=>sum+w2.panes.filter((p)=>p.command==="claude"||p.title.includes("claude")).length,0);return{id:`session:${session.name}`,type:"session",label:session.name,depth:0,expanded:!0,children:session.windows.map((w2)=>windowToNode(session.name,w2,executorMap)),data:{attached:session.attached,windowCount:session.windowCount},activePanes:claudePanes,agentState:void 0}}function windowToNode(sessionName,window2,executorMap){let activePanes=window2.panes.filter((p)=>!p.isDead&&(p.command==="claude"||p.title.includes("claude"))).length;return{id:`window:${sessionName}:${window2.index}`,type:"window",label:window2.name,depth:1,expanded:!0,children:window2.panes.map((p)=>paneToNode(sessionName,window2.index,p,executorMap)),data:{active:window2.active,paneCount:window2.paneCount},activePanes,agentState:void 0}}function paneToNode(sessionName,windowIndex,pane,executorMap){let executor=executorMap.get(pane.paneId),isClaude=pane.command==="claude"||pane.title.includes("claude");return{id:`pane:${pane.paneId}`,type:"pane",label:derivePaneLabel(pane,executor,isClaude),depth:2,expanded:!1,children:[],data:{command:pane.command,isDead:pane.isDead,pid:pane.pid,size:pane.size,paneId:pane.paneId,sessionName,windowIndex},activePanes:0,agentState:derivePaneState(pane,executor)}}function derivePaneLabel(pane,executor,isClaude){if(executor?.agentName&&executor?.team)return`${executor.team}/${executor.agentName}`;if(executor?.agentName)return executor.agentName;if(isClaude)return"claude";return pane.command}function derivePaneState(pane,executor){if(pane.isDead)return"error";if(!executor)return;let s=executor.state;if(s==="working")return"working";if(s==="idle"||s==="spawning")return"idle";if(s==="permission")return"permission";if(s==="error"||s==="terminated")return"error";return}function getSessionTarget(node){if(node.type==="session")return{sessionName:node.label};if(node.type==="window"){let parts=node.id.split(":");return{sessionName:parts[1],windowIndex:Number(parts[2])}}if(node.type==="pane"){let data=node.data;return{sessionName:data.sessionName,windowIndex:data.windowIndex}}return null}function flattenTree(nodes){let result=[];function walk(node,depth){if(result.push({node,depth,visible:!0}),node.expanded)for(let child of node.children)walk(child,depth+1)}for(let node of nodes)walk(node,0);return result}function toggleNode(nodes,id){return nodes.map((node)=>{if(node.id===id)return{...node,expanded:!node.expanded};return{...node,children:toggleNode(node.children,id)}})}var import_jsx_dev_runtime2;var init_jsx_dev_runtime=__esm(()=>{import_jsx_dev_runtime2=__toESM(require_react_jsx_dev_runtime_development(),1)});function getNodeIcon(node){switch(node.type){case"session":return node.data.attached?"\u25B6":"\u25B8";case"window":return node.data.active?"\u25A0":"\u25A1";case"pane":return getPaneIcon(node);default:return" "}}function getPaneIcon(node){if(node.data.isDead)return"\u2718";if(node.agentState==="working")return"\u25CF";if(node.agentState==="idle")return"\u25CB";if(node.agentState==="permission")return"\u26A0";if(node.agentState==="error")return"\u2718";if(node.data.command==="claude")return"\u25C6";return"\u25CB"}function getNodeColor(node){switch(node.type){case"session":return node.data.attached?palette.emerald:palette.textDim;case"window":return node.data.active?palette.cyan:palette.text;case"pane":return getPaneColor(node);default:return palette.text}}function getPaneColor(node){if(node.data.isDead)return palette.error;if(node.agentState==="working")return palette.cyan;if(node.agentState==="permission")return palette.warning;if(node.agentState==="error")return palette.error;if(node.agentState==="idle")return palette.textDim;if(node.data.command==="claude")return palette.cyan;return palette.textDim}function getStateColor(state){switch(state){case"working":return palette.cyan;case"idle":return palette.textDim;case"permission":return palette.warning;case"error":return palette.error;default:return palette.textMuted}}var import_react13,TreeNodeRow;var init_TreeNode=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react13=__toESM(require_react_development(),1),TreeNodeRow=import_react13.memo(function({node,selected,onSelect,onToggle}){let indent=" ".repeat(node.depth),hasChildren=node.children.length>0,expandIcon=hasChildren?node.expanded?icons.expanded:icons.collapsed:" ",icon=getNodeIcon(node),color=getNodeColor(node),activeIndicator=node.activePanes>0?` ${icons.agent}${node.activePanes}`:"";return import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",backgroundColor:selected?palette.violet:void 0,onMouseDown:()=>{if(onSelect(node.id),hasChildren)onToggle(node.id)},children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[indent,expandIcon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:color,children:[icon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:selected?"#ffffff":palette.text,children:node.label},void 0,!1,void 0,this),activeIndicator?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.cyan,children:activeIndicator},void 0,!1,void 0,this):null,node.agentState?import_jsx_dev_runtime2.jsxDEV("span",{fg:getStateColor(node.agentState),children:[" ",node.agentState]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)})});function Nav({onTmuxSessionSelect}){let[diagnostics,setDiagnostics]=import_react15.useState(null),[sessionTree,setSessionTree]=import_react15.useState([]),[selectedIndex,setSelectedIndex]=import_react15.useState(0),lastTarget=import_react15.useRef(null);import_react15.useEffect(()=>{let active=!0;async function refresh(){try{let snap=await collectDiagnostics();if(active)setDiagnostics(snap)}catch(err){console.error("TUI: diagnostics failed:",err)}}refresh();let timer2=setInterval(refresh,2000);return()=>{active=!1,clearInterval(timer2)}},[]),import_react15.useEffect(()=>{if(!diagnostics)return;let newTree=buildSessionTree(diagnostics);setSessionTree((prev)=>mergeExpandedState(prev,newTree))},[diagnostics]);let flatNodes=import_react15.useMemo(()=>flattenTree(sessionTree),[sessionTree]);import_react15.useEffect(()=>{if(flatNodes.length>0&&selectedIndex>=flatNodes.length)setSelectedIndex(flatNodes.length-1)},[flatNodes.length,selectedIndex]),import_react15.useEffect(()=>{let current=flatNodes[selectedIndex]?.node;if(!current)return;let target=getSessionTarget(current);if(!target)return;let key=`${target.sessionName}:${target.windowIndex??""}`;if(key===lastTarget.current)return;lastTarget.current=key,onTmuxSessionSelect(target.sessionName,target.windowIndex)},[selectedIndex,flatNodes,onTmuxSessionSelect]);let handleSelect=import_react15.useCallback((id)=>{let idx=flatNodes.findIndex((n)=>n.node.id===id);if(idx>=0)setSelectedIndex(idx)},[flatNodes]),handleToggle=import_react15.useCallback((id)=>{setSessionTree((prev)=>toggleNode(prev,id))},[]),handleVerticalNav=import_react15.useCallback((keyName2)=>{let rowCount=flatNodes.length;if(rowCount===0)return;if(keyName2==="up"||keyName2==="k")setSelectedIndex((prev)=>prev===0?rowCount-1:prev-1);else if(keyName2==="down"||keyName2==="j")setSelectedIndex((prev)=>prev>=rowCount-1?0:prev+1)},[flatNodes.length]),handleExpandCollapse=import_react15.useCallback((keyName2)=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if((keyName2==="right"||keyName2==="l")&&node.children.length>0&&!node.expanded)handleToggle(node.id);else if((keyName2==="left"||keyName2==="h")&&node.expanded)handleToggle(node.id)},[flatNodes,selectedIndex,handleToggle]),handleEnter=import_react15.useCallback(()=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if(node.children.length>0)handleToggle(node.id);let target=getSessionTarget(node);if(target)onTmuxSessionSelect(target.sessionName,target.windowIndex)},[flatNodes,selectedIndex,handleToggle,onTmuxSessionSelect]);useKeyboard((key)=>{if(key.name==="up"||key.name==="k"||key.name==="down"||key.name==="j")handleVerticalNav(key.name);else if(key.name==="right"||key.name==="l"||key.name==="left"||key.name==="h")handleExpandCollapse(key.name);else if(key.name==="enter"||key.name==="return")handleEnter()});let sessionCount=diagnostics?.sessions.length??0,totalPanes=diagnostics?.sessions.reduce((sum,s)=>sum+s.windows.reduce((ws,w2)=>ws+w2.panes.length,0),0)??0,deadPanes=diagnostics?.gaps.deadPaneCount??0;return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",backgroundColor:palette.bg,children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLight,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.purple,children:"Sessions"},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[" ",sessionCount,"s ",totalPanes,"p"]},void 0,!0,void 0,this):null,deadPanes>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:[" ",deadPanes,"\\u2620"]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("scrollbox",{focused:!0,height:"100%",style:{scrollbarOptions:{showArrows:!1,trackOptions:{foregroundColor:palette.scrollThumb,backgroundColor:palette.scrollTrack}}},children:flatNodes.map((flat,i2)=>import_jsx_dev_runtime2.jsxDEV(TreeNodeRow,{node:flat.node,selected:i2===selectedIndex,onSelect:handleSelect,onToggle:handleToggle},flat.node.id,!1,void 0,this))},void 0,!1,void 0,this):import_jsx_dev_runtime2.jsxDEV("box",{flexGrow:1,justifyContent:"center",alignItems:"center",children:import_jsx_dev_runtime2.jsxDEV("text",{fg:palette.textDim,children:"Collecting..."},void 0,!1,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLight,children:import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:["\u2191\u2193",":nav ","\u2190\u2192",":expand Enter:attach"]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function mergeExpandedState(oldTree,newTree){if(oldTree.length===0)return newTree;let oldState=new Map;function collect(nodes){for(let n of nodes)oldState.set(n.id,n.expanded),collect(n.children)}collect(oldTree);function apply(nodes){return nodes.map((n)=>({...n,expanded:oldState.has(n.id)?oldState.get(n.id):n.expanded,children:apply(n.children)}))}return apply(newTree)}var import_react15;var init_Nav=__esm(async()=>{init_diagnostics();init_theme2();init_TreeNode();init_jsx_dev_runtime();await init_react();import_react15=__toESM(require_react_development(),1)});function App({rightPane}){let renderer=useRenderer();useKeyboard((key)=>{if(key.name==="q"||key.ctrl&&key.name==="c")renderer.destroy(),cleanup()});let handleTmuxSessionSelect=import_react17.useCallback((sessionName,windowIndex)=>{if(!rightPane)return;attachProjectWindow(rightPane,sessionName,windowIndex)},[rightPane]);return import_jsx_dev_runtime2.jsxDEV(Nav,{onTmuxSessionSelect:handleTmuxSessionSelect},void 0,!1,void 0,this)}var import_react17;var init_app=__esm(async()=>{init_tmux2();init_jsx_dev_runtime();await __promiseAll([init_react(),init_Nav()]);import_react17=__toESM(require_react_development(),1)});var exports_render={};__export(exports_render,{renderNav:()=>renderNav});async function renderNav(){let rightPane=process.env.GENIE_TUI_RIGHT||void 0,renderer=await createCliRenderer({exitOnCtrlC:!1,useMouse:!0});createRoot(renderer).render(import_jsx_dev_runtime2.jsxDEV(App,{rightPane},void 0,!1,void 0,this))}var init_render=__esm(async()=>{init_jsx_dev_runtime();await __promiseAll([init_core(),init_react(),init_app()])});var exports_tui={};__export(exports_tui,{launchTui:()=>launchTui});async function launchTui(options={}){if(isInsideTuiSession()){let{renderNav:renderNav2}=await init_render().then(() => exports_render);await renderNav2();return}if(!hasTmux()){console.error("Error: tmux is required for genie tui");return}let{session,leftPane,rightPane}=createTuiSession(),bunPath=process.execPath||"bun",genieBin=process.argv[1]||"genie",{execSync:execSync6}=await import("child_process");if(options.dev)execSync6(`tmux send-keys -t '${leftPane}' "GENIE_TUI_PANE=left GENIE_TUI_RIGHT=${rightPane} bun --watch ${genieBin} tui" Enter`,{stdio:"ignore"});else execSync6(`tmux send-keys -t '${leftPane}' "GENIE_TUI_PANE=left GENIE_TUI_RIGHT=${rightPane} ${bunPath} ${genieBin} tui" Enter`,{stdio:"ignore"});attachTuiSession(),cleanup(session)}var init_tui=__esm(()=>{init_tmux2()});function onBridgeEvent(handler){return listeners.add(handler),()=>{listeners.delete(handler)}}function emit2(event){for(let handler of listeners)handler(event)}async function listAgents2(){return(await getConnection())`
|
|
1287
|
+
`)){if(!line)continue;let parts=line.split("|");if(parts.length<15)continue;let parsed=parsePaneLine(parts);if(!sessionMap.has(parsed.sessionName))sessionMap.set(parsed.sessionName,{...parsed.session,windows:[]});let winKey=`${parsed.sessionName}:${parsed.winIdxStr}`;if(!windowMap.has(winKey)){let win={...parsed.window,panes:[]};windowMap.set(winKey,win),sessionMap.get(parsed.sessionName)?.windows.push(win)}windowMap.get(winKey)?.panes.push(parsed.pane)}return Array.from(sessionMap.values()).sort((a,b3)=>a.name.localeCompare(b3.name))}function isPidAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function allClaudePanes(sessions){return sessions.flatMap((s)=>s.windows.flatMap((w2)=>w2.panes)).filter((p)=>p.command==="claude"||p.title.includes("claude"))}function detectGaps(executors,sessions){let deadPidExecutors=executors.filter((e)=>e.pid!=null&&!isPidAlive(e.pid)),executorPaneIds=new Set(executors.map((e)=>e.tmuxPaneId).filter(Boolean)),claudePanes=allClaudePanes(sessions),orphanPanes=claudePanes.filter((p)=>!executorPaneIds.has(p.paneId)),linkedCount=executors.filter((e)=>e.tmuxPaneId&&!deadPidExecutors.some((d2)=>d2.id===e.id)).length,deadPaneCount=sessions.flatMap((s)=>s.windows.flatMap((w2)=>w2.panes)).filter((p)=>p.isDead).length;return{deadPidExecutors,orphanPanes,linkedCount,totalExecutors:executors.length,totalClaudePanes:claudePanes.length,deadPaneCount}}async function collectDiagnostics(){let{loadExecutors:loadExecutors2,loadAssignments:loadAssignments2}=await Promise.resolve().then(() => exports_db2),sessions=getTmuxInventory(),executors=await loadExecutors2(),executorIds=executors.map((e)=>e.id),assignments=await loadAssignments2(executorIds),gaps=detectGaps(executors,sessions);return{sessions,executors,assignments,gaps,timestamp:Date.now()}}var init_diagnostics=()=>{};function buildSessionTree(snapshot){let executorByPaneId=new Map;for(let exec3 of snapshot.executors)if(exec3.tmuxPaneId)executorByPaneId.set(exec3.tmuxPaneId,exec3);return snapshot.sessions.filter((s)=>s.name!=="genie-tui").map((session)=>sessionToNode(session,executorByPaneId))}function sessionToNode(session,executorMap){let claudePanes=session.windows.reduce((sum,w2)=>sum+w2.panes.filter((p)=>p.command==="claude"||p.title.includes("claude")).length,0);return{id:`session:${session.name}`,type:"session",label:session.name,depth:0,expanded:!0,children:session.windows.map((w2)=>windowToNode(session.name,w2,executorMap)),data:{attached:session.attached,windowCount:session.windowCount},activePanes:claudePanes,agentState:void 0}}function windowToNode(sessionName,window2,executorMap){let activePanes=window2.panes.filter((p)=>!p.isDead&&(p.command==="claude"||p.title.includes("claude"))).length;return{id:`window:${sessionName}:${window2.index}`,type:"window",label:window2.name,depth:1,expanded:!0,children:window2.panes.map((p)=>paneToNode(sessionName,window2.index,p,executorMap)),data:{active:window2.active,paneCount:window2.paneCount},activePanes,agentState:void 0}}function paneToNode(sessionName,windowIndex,pane,executorMap){let executor=executorMap.get(pane.paneId),isClaude=pane.command==="claude"||pane.title.includes("claude");return{id:`pane:${pane.paneId}`,type:"pane",label:derivePaneLabel(pane,executor,isClaude),depth:2,expanded:!1,children:[],data:{command:pane.command,isDead:pane.isDead,pid:pane.pid,size:pane.size,paneId:pane.paneId,sessionName,windowIndex},activePanes:0,agentState:derivePaneState(pane,executor)}}function derivePaneLabel(pane,executor,isClaude){if(executor?.agentName&&executor?.team)return`${executor.team}/${executor.agentName}`;if(executor?.agentName)return executor.agentName;if(isClaude)return"claude";return pane.command}function derivePaneState(pane,executor){if(pane.isDead)return"error";if(!executor)return;let s=executor.state;if(s==="working")return"working";if(s==="idle"||s==="spawning")return"idle";if(s==="permission")return"permission";if(s==="error"||s==="terminated")return"error";return}function getSessionTarget(node){if(node.type==="session")return{sessionName:node.label};if(node.type==="window"){let parts=node.id.split(":");return{sessionName:parts[1],windowIndex:Number(parts[2])}}if(node.type==="pane"){let data=node.data;return{sessionName:data.sessionName,windowIndex:data.windowIndex}}return null}function flattenTree(nodes){let result=[];function walk(node,depth){if(result.push({node,depth,visible:!0}),node.expanded)for(let child of node.children)walk(child,depth+1)}for(let node of nodes)walk(node,0);return result}function toggleNode(nodes,id){return nodes.map((node)=>{if(node.id===id)return{...node,expanded:!node.expanded};return{...node,children:toggleNode(node.children,id)}})}var import_jsx_dev_runtime2;var init_jsx_dev_runtime=__esm(()=>{import_jsx_dev_runtime2=__toESM(require_react_jsx_dev_runtime_development(),1)});function getNodeIcon(node){switch(node.type){case"session":return node.data.attached?"\u25B6":"\u25B8";case"window":return node.data.active?"\u25A0":"\u25A1";case"pane":return getPaneIcon(node);default:return" "}}function getPaneIcon(node){if(node.data.isDead)return"\u2718";if(node.agentState==="working")return"\u25CF";if(node.agentState==="idle")return"\u25CB";if(node.agentState==="permission")return"\u26A0";if(node.agentState==="error")return"\u2718";if(node.data.command==="claude")return"\u25C6";return"\u25CB"}function getNodeColor(node){switch(node.type){case"session":return node.data.attached?palette.emerald:palette.textDim;case"window":return node.data.active?palette.cyan:palette.text;case"pane":return getPaneColor(node);default:return palette.text}}function getPaneColor(node){if(node.data.isDead)return palette.error;if(node.agentState==="working")return palette.cyan;if(node.agentState==="permission")return palette.warning;if(node.agentState==="error")return palette.error;if(node.agentState==="idle")return palette.textDim;if(node.data.command==="claude")return palette.cyan;return palette.textDim}function getStateColor(state){switch(state){case"working":return palette.cyan;case"idle":return palette.textDim;case"permission":return palette.warning;case"error":return palette.error;default:return palette.textMuted}}var import_react13,TreeNodeRow;var init_TreeNode=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react13=__toESM(require_react_development(),1),TreeNodeRow=import_react13.memo(function({node,selected,onSelect,onToggle}){let indent=" ".repeat(node.depth),hasChildren=node.children.length>0,expandIcon=hasChildren?node.expanded?icons.expanded:icons.collapsed:" ",icon=getNodeIcon(node),color=getNodeColor(node),activeIndicator=node.activePanes>0?` ${icons.agent}${node.activePanes}`:"";return import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",backgroundColor:selected?palette.violet:void 0,onMouseDown:()=>{if(onSelect(node.id),hasChildren)onToggle(node.id)},children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[indent,expandIcon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:color,children:[icon," "]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:selected?"#ffffff":palette.text,children:node.label},void 0,!1,void 0,this),activeIndicator?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.cyan,children:activeIndicator},void 0,!1,void 0,this):null,node.agentState?import_jsx_dev_runtime2.jsxDEV("span",{fg:getStateColor(node.agentState),children:[" ",node.agentState]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)})});function Nav({onTmuxSessionSelect}){let[diagnostics,setDiagnostics]=import_react15.useState(null),[sessionTree,setSessionTree]=import_react15.useState([]),[selectedIndex,setSelectedIndex]=import_react15.useState(0),lastTarget=import_react15.useRef(null);import_react15.useEffect(()=>{let active=!0;async function refresh(){try{let snap=await collectDiagnostics();if(active)setDiagnostics(snap)}catch(err){console.error("TUI: diagnostics failed:",err)}}refresh();let timer2=setInterval(refresh,2000);return()=>{active=!1,clearInterval(timer2)}},[]),import_react15.useEffect(()=>{if(!diagnostics)return;let newTree=buildSessionTree(diagnostics);setSessionTree((prev)=>mergeExpandedState(prev,newTree))},[diagnostics]);let flatNodes=import_react15.useMemo(()=>flattenTree(sessionTree),[sessionTree]);import_react15.useEffect(()=>{if(flatNodes.length>0&&selectedIndex>=flatNodes.length)setSelectedIndex(flatNodes.length-1)},[flatNodes.length,selectedIndex]),import_react15.useEffect(()=>{let current=flatNodes[selectedIndex]?.node;if(!current)return;let target=getSessionTarget(current);if(!target)return;let key=`${target.sessionName}:${target.windowIndex??""}`;if(key===lastTarget.current)return;lastTarget.current=key,onTmuxSessionSelect(target.sessionName,target.windowIndex)},[selectedIndex,flatNodes,onTmuxSessionSelect]);let handleSelect=import_react15.useCallback((id)=>{let idx=flatNodes.findIndex((n)=>n.node.id===id);if(idx>=0)setSelectedIndex(idx)},[flatNodes]),handleToggle=import_react15.useCallback((id)=>{setSessionTree((prev)=>toggleNode(prev,id))},[]),handleVerticalNav=import_react15.useCallback((keyName2)=>{let rowCount=flatNodes.length;if(rowCount===0)return;if(keyName2==="up"||keyName2==="k")setSelectedIndex((prev)=>prev===0?rowCount-1:prev-1);else if(keyName2==="down"||keyName2==="j")setSelectedIndex((prev)=>prev>=rowCount-1?0:prev+1)},[flatNodes.length]),handleExpandCollapse=import_react15.useCallback((keyName2)=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if((keyName2==="right"||keyName2==="l")&&node.children.length>0&&!node.expanded)handleToggle(node.id);else if((keyName2==="left"||keyName2==="h")&&node.expanded)handleToggle(node.id)},[flatNodes,selectedIndex,handleToggle]),handleEnter=import_react15.useCallback(()=>{let node=flatNodes[selectedIndex]?.node;if(!node)return;if(node.children.length>0)handleToggle(node.id);let target=getSessionTarget(node);if(target)onTmuxSessionSelect(target.sessionName,target.windowIndex)},[flatNodes,selectedIndex,handleToggle,onTmuxSessionSelect]);useKeyboard((key)=>{if(key.name==="up"||key.name==="k"||key.name==="down"||key.name==="j")handleVerticalNav(key.name);else if(key.name==="right"||key.name==="l"||key.name==="left"||key.name==="h")handleExpandCollapse(key.name);else if(key.name==="enter"||key.name==="return")handleEnter()});let sessionCount=diagnostics?.sessions.length??0,totalPanes=diagnostics?.sessions.reduce((sum,s)=>sum+s.windows.reduce((ws,w2)=>ws+w2.panes.length,0),0)??0,deadPanes=diagnostics?.gaps.deadPaneCount??0;return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",backgroundColor:palette.bg,children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLight,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.purple,children:"Sessions"},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[" ",sessionCount,"s ",totalPanes,"p"]},void 0,!0,void 0,this):null,deadPanes>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:[" ",deadPanes,"\\u2620"]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),diagnostics?import_jsx_dev_runtime2.jsxDEV("scrollbox",{focused:!0,height:"100%",style:{scrollbarOptions:{showArrows:!1,trackOptions:{foregroundColor:palette.scrollThumb,backgroundColor:palette.scrollTrack}}},children:flatNodes.map((flat,i2)=>import_jsx_dev_runtime2.jsxDEV(TreeNodeRow,{node:flat.node,selected:i2===selectedIndex,onSelect:handleSelect,onToggle:handleToggle},flat.node.id,!1,void 0,this))},void 0,!1,void 0,this):import_jsx_dev_runtime2.jsxDEV("box",{flexGrow:1,justifyContent:"center",alignItems:"center",children:import_jsx_dev_runtime2.jsxDEV("text",{fg:palette.textDim,children:"Collecting..."},void 0,!1,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLight,children:import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:["\u2191\u2193",":nav ","\u2190\u2192",":expand Enter:attach"]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function mergeExpandedState(oldTree,newTree){if(oldTree.length===0)return newTree;let oldState=new Map;function collect(nodes){for(let n of nodes)oldState.set(n.id,n.expanded),collect(n.children)}collect(oldTree);function apply(nodes){return nodes.map((n)=>({...n,expanded:oldState.has(n.id)?oldState.get(n.id):n.expanded,children:apply(n.children)}))}return apply(newTree)}var import_react15;var init_Nav=__esm(async()=>{init_diagnostics();init_theme2();init_TreeNode();init_jsx_dev_runtime();await init_react();import_react15=__toESM(require_react_development(),1)});function App({rightPane}){let renderer=useRenderer();useKeyboard((key)=>{if(key.ctrl&&key.name==="q"||key.ctrl&&key.name==="c")cleanup(),renderer.destroy()});let handleTmuxSessionSelect=import_react17.useCallback((sessionName,windowIndex)=>{if(!rightPane)return;attachProjectWindow(rightPane,sessionName,windowIndex)},[rightPane]);return import_jsx_dev_runtime2.jsxDEV(Nav,{onTmuxSessionSelect:handleTmuxSessionSelect},void 0,!1,void 0,this)}var import_react17;var init_app=__esm(async()=>{init_tmux2();init_jsx_dev_runtime();await __promiseAll([init_react(),init_Nav()]);import_react17=__toESM(require_react_development(),1)});var exports_render={};__export(exports_render,{renderNav:()=>renderNav});async function renderNav(){let rightPane=process.env.GENIE_TUI_RIGHT||void 0,renderer=await createCliRenderer({exitOnCtrlC:!1,useMouse:!0});createRoot(renderer).render(import_jsx_dev_runtime2.jsxDEV(App,{rightPane},void 0,!1,void 0,this))}var init_render=__esm(async()=>{init_jsx_dev_runtime();await __promiseAll([init_core(),init_react(),init_app()])});var exports_tui={};__export(exports_tui,{launchTui:()=>launchTui});async function launchTui(options={}){if(isInsideTuiSession()){let{renderNav:renderNav2}=await init_render().then(() => exports_render);await renderNav2();return}if(!hasTmux()){console.error("Error: tmux is required for genie tui");return}let{session,leftPane,rightPane}=createTuiSession(),bunPath=process.execPath||"bun",genieBin=process.argv[1]||"genie",{execSync:execSync6}=await import("child_process");if(options.dev)execSync6(`tmux send-keys -t '${leftPane}' "GENIE_TUI_PANE=left GENIE_TUI_RIGHT=${rightPane} bun --watch ${genieBin} tui" Enter`,{stdio:"ignore"});else execSync6(`tmux send-keys -t '${leftPane}' "GENIE_TUI_PANE=left GENIE_TUI_RIGHT=${rightPane} ${bunPath} ${genieBin} tui" Enter`,{stdio:"ignore"});attachTuiSession(),cleanup(session)}var init_tui=__esm(()=>{init_tmux2()});function onBridgeEvent(handler){return listeners.add(handler),()=>{listeners.delete(handler)}}function emit2(event){for(let handler of listeners)handler(event)}async function listAgents2(){return(await getConnection())`
|
|
1288
1288
|
SELECT a.id, a.custom_name, a.role, a.team, a.title, a.state,
|
|
1289
1289
|
a.reports_to, a.current_executor_id, a.started_at
|
|
1290
1290
|
FROM agents a
|
|
@@ -1430,7 +1430,7 @@ $ bun add react-devtools-core@7 -d
|
|
|
1430
1430
|
VALUES (${name}, ${repoPath})
|
|
1431
1431
|
ON CONFLICT (name) DO UPDATE SET repo_path = COALESCE(projects.repo_path, EXCLUDED.repo_path)
|
|
1432
1432
|
RETURNING id
|
|
1433
|
-
`)[0].id}function toDateOrNull(v2){return v2?new Date(v2):null}async function resolveColumnId(sql,boardId,stageName){let rows=await sql`SELECT columns FROM boards WHERE id = ${boardId} LIMIT 1`;if(rows.length===0)return null;return rows[0].columns.find((c)=>c.name===stageName)?.id??null}function
|
|
1433
|
+
`)[0].id}function toDateOrNull(v2){return v2?new Date(v2):null}async function resolveColumnId(sql,boardId,stageName){let rows=await sql`SELECT columns FROM boards WHERE id = ${boardId} LIMIT 1`;if(rows.length===0)return null;return rows[0].columns.find((c)=>c.name===stageName)?.id??null}function taskNullables(input){return{desc:input.description??null,ac:input.acceptanceCriteria??null,parent:input.parentId??null,wish:input.wishFile??null,group:input.groupName??null,effort:input.estimatedEffort??null,blocked:input.blockedReason??null,release:input.releaseId??null,boardId:input.boardId??null,columnId:input.columnId??null,externalId:input.externalId??null,externalUrl:input.externalUrl??null}}function buildTaskVals(input){return{...taskNullables(input),type:input.typeId??"software",stage:input.stage??"draft",status:input.status??"ready",priority:input.priority??"normal"}}async function createTask(input,repoPath,projectId){let sql=await getConnection(),repo=repoPath??getRepoPath(),projId=projectId??await ensureProject(repo),vals=buildTaskVals(input);if(vals.boardId&&!vals.columnId&&vals.stage)vals.columnId=await resolveColumnId(sql,vals.boardId,vals.stage);let rows=await sql`
|
|
1434
1434
|
INSERT INTO tasks (
|
|
1435
1435
|
repo_path, project_id, title, description, acceptance_criteria,
|
|
1436
1436
|
type_id, stage, status, priority,
|
|
@@ -2067,9 +2067,9 @@ Next steps:`),console.log(" 1. Reload tmux: tmux source ~/.tmux.conf"),console.
|
|
|
2067
2067
|
`+` genie task status <slug> \u2014 check progress
|
|
2068
2068
|
`+" genie agent send --to <a> \u2014 communicate directly"},{test:/sleep\s+\d+\s*&&\s*.*(?:capture-pane|tmux\s+list)/,message:`Consider using genie primitives instead of terminal polling:
|
|
2069
2069
|
genie task status <slug>
|
|
2070
|
-
genie events list --since 5m`}];async function orchestrationGuard(payload){let command=payload.tool_input?.command;if(typeof command!=="string")return;for(let{test,message}of NUDGE_PATTERNS)if(test.test(command))return{
|
|
2071
|
-
`)[0]}`;case"Grep":return`Grep "${input.pattern}" ${input.path??""}`;case"Glob":return`Glob ${input.pattern}`;case"Agent":return`Agent: ${input.description??""}`;case"SendMessage":return`SendMessage \u2192 ${input.to}: ${String(input.message??"").slice(0,80)}`;default:return name}}init_types3();var handlers=[{name:"branch-guard",event:"PreToolUse",matcher:/^Bash$/,priority:1,fn:branchGuard},{name:"orchestration-guard",event:"PreToolUse",matcher:/^Bash$/,priority:2,fn:orchestrationGuard},{name:"identity-inject",event:"PreToolUse",matcher:/^SendMessage$/,priority:10,fn:identityInject},{name:"auto-spawn",event:"PreToolUse",matcher:/^SendMessage$/,priority:20,fn:autoSpawn},{name:"runtime-emit-tool",event:"PreToolUse",matcher:/.*/,priority:30,fn:emitToolCallEvent},{name:"runtime-emit-msg",event:"PostToolUse",matcher:/^SendMessage$/,priority:30,fn:emitMessageEvent},{name:"runtime-emit-user-prompt",event:"UserPromptSubmit",priority:30,fn:emitUserPromptEvent},{name:"runtime-emit-assistant-response",event:"Stop",priority:30,fn:emitAssistantResponseEvent}];function resolveHandlers(event,toolName){return handlers.filter((h)=>{if(h.event!==event)return!1;if(h.matcher&&toolName&&!h.matcher.test(toolName))return!1;if(h.matcher&&!toolName)return!1;return!0}).sort((a,b2)=>a.priority-b2.priority)}async function runHandler(handler,payload,currentInput){let handlerPayload={...payload};if(currentInput)handlerPayload.tool_input=currentInput;try{return await handler.fn(handlerPayload)}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(`[genie-hook] Handler "${handler.name}" threw: ${msg}`);return}}async function executeBlockingChain(matched,payload){let currentInput=payload.tool_input?{...payload.tool_input}:void 0,
|
|
2072
|
-
`);return response}async function executeNonBlockingHandlers(matched,payload){await Promise.allSettled(matched.map((h)=>h.fn(payload).catch((err)=>{let msg=err instanceof Error?err.message:String(err);console.error(`[genie-hook] Handler "${h.name}" threw: ${msg}`)})))}async function dispatch(stdin){let payload;try{payload=JSON.parse(stdin)}catch{return console.error("[genie-hook] Invalid JSON on stdin"),""}let event=payload.hook_event_name;if(!event)return console.error("[genie-hook] Missing hook_event_name in payload"),"";let toolName=payload.tool_name,matched=resolveHandlers(event,toolName);if(matched.length===0)return"";if(isBlockingEvent(event)){let result=await executeBlockingChain(matched,payload);if(
|
|
2070
|
+
genie events list --since 5m`}];async function orchestrationGuard(payload){let command=payload.tool_input?.command;if(typeof command!=="string")return;for(let{test,message}of NUDGE_PATTERNS)if(test.test(command))return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:`[orchestration-guard] ${message}`}};return}var getAgent2=()=>process.env.GENIE_AGENT_NAME??"unknown",getTeam=()=>process.env.GENIE_TEAM;async function emit(subject,event){try{let{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(process.cwd(),subject,event)}catch{}}async function emitToolCallEvent(payload){let{tool_name:toolName,tool_input:input}=payload;if(!toolName||!input)return;await emit(`genie.tool.${getAgent2()}.call`,{timestamp:new Date().toISOString(),kind:"tool_call",agent:getAgent2(),team:getTeam(),text:summarizeToolCall(toolName,input),data:{toolCall:{name:toolName,input}},source:"hook"});return}async function emitMessageEvent(payload){let input=payload.tool_input;if(!input)return;let msgType=input.type;if(msgType&&msgType!=="message"&&msgType!=="broadcast")return;let to=input.to,content=input.content??input.message;if(!to||!content)return;let subject=msgType==="broadcast"?"genie.msg.broadcast":`genie.msg.${to}`;await emit(subject,{timestamp:new Date().toISOString(),kind:"message",agent:getAgent2(),team:getTeam(),peer:to,direction:"out",text:content,source:"hook"});return}async function emitUserPromptEvent(payload){let prompt2=payload.prompt;if(!prompt2)return;await emit(`genie.user.${getAgent2()}.prompt`,{timestamp:new Date().toISOString(),kind:"user",agent:getAgent2(),team:getTeam(),text:prompt2,source:"hook"});return}async function emitAssistantResponseEvent(payload){let lastMessage=payload.last_assistant_message;if(!lastMessage)return;await emit(`genie.agent.${getAgent2()}.response`,{timestamp:new Date().toISOString(),kind:"assistant",agent:getAgent2(),team:getTeam(),text:lastMessage,source:"hook"});return}function summarizeToolCall(name,input){switch(name){case"Read":case"Edit":case"Write":return`${name} ${input.file_path??""}`;case"Bash":return`$ ${String(input.command??"").split(`
|
|
2071
|
+
`)[0]}`;case"Grep":return`Grep "${input.pattern}" ${input.path??""}`;case"Glob":return`Glob ${input.pattern}`;case"Agent":return`Agent: ${input.description??""}`;case"SendMessage":return`SendMessage \u2192 ${input.to}: ${String(input.message??"").slice(0,80)}`;default:return name}}init_types3();var handlers=[{name:"branch-guard",event:"PreToolUse",matcher:/^Bash$/,priority:1,fn:branchGuard},{name:"orchestration-guard",event:"PreToolUse",matcher:/^Bash$/,priority:2,fn:orchestrationGuard},{name:"identity-inject",event:"PreToolUse",matcher:/^SendMessage$/,priority:10,fn:identityInject},{name:"auto-spawn",event:"PreToolUse",matcher:/^SendMessage$/,priority:20,fn:autoSpawn},{name:"runtime-emit-tool",event:"PreToolUse",matcher:/.*/,priority:30,fn:emitToolCallEvent},{name:"runtime-emit-msg",event:"PostToolUse",matcher:/^SendMessage$/,priority:30,fn:emitMessageEvent},{name:"runtime-emit-user-prompt",event:"UserPromptSubmit",priority:30,fn:emitUserPromptEvent},{name:"runtime-emit-assistant-response",event:"Stop",priority:30,fn:emitAssistantResponseEvent}];function resolveHandlers(event,toolName){return handlers.filter((h)=>{if(h.event!==event)return!1;if(h.matcher&&toolName&&!h.matcher.test(toolName))return!1;if(h.matcher&&!toolName)return!1;return!0}).sort((a,b2)=>a.priority-b2.priority)}async function runHandler(handler,payload,currentInput){let handlerPayload={...payload};if(currentInput)handlerPayload.tool_input=currentInput;try{return await handler.fn(handlerPayload)}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(`[genie-hook] Handler "${handler.name}" threw: ${msg}`);return}}async function executeBlockingChain(matched,payload){let currentInput=payload.tool_input?{...payload.tool_input}:void 0,contextMessages=[],hookEventName=payload.hook_event_name;for(let handler of matched){let result=await runHandler(handler,payload,currentInput);if(!result)continue;if(result.decision==="deny"){if(hookEventName==="PreToolUse")return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:result.reason??`Denied by handler: ${handler.name}`}};return{decision:"block",reason:result.reason??`Denied by handler: ${handler.name}`}}if(result.hookSpecificOutput?.additionalContext)contextMessages.push(result.hookSpecificOutput.additionalContext);let inputUpdate=result.hookSpecificOutput?.updatedInput??result.updatedInput;if(inputUpdate)currentInput={...currentInput,...inputUpdate}}let response={},hasContext=contextMessages.length>0,hasInputChange=currentInput&&payload.tool_input&&JSON.stringify(currentInput)!==JSON.stringify(payload.tool_input);if(hasInputChange)response.updatedInput=currentInput;if(hookEventName==="PreToolUse"&&(hasContext||hasInputChange)){let output={hookEventName:"PreToolUse"};if(hasContext)output.additionalContext=contextMessages.join(`
|
|
2072
|
+
`);if(hasInputChange)output.permissionDecision="allow",output.updatedInput=currentInput;response.hookSpecificOutput=output}return response}async function executeNonBlockingHandlers(matched,payload){await Promise.allSettled(matched.map((h)=>h.fn(payload).catch((err)=>{let msg=err instanceof Error?err.message:String(err);console.error(`[genie-hook] Handler "${h.name}" threw: ${msg}`)})))}async function dispatch(stdin){let payload;try{payload=JSON.parse(stdin)}catch{return console.error("[genie-hook] Invalid JSON on stdin"),""}let event=payload.hook_event_name;if(!event)return console.error("[genie-hook] Missing hook_event_name in payload"),"";let toolName=payload.tool_name,matched=resolveHandlers(event,toolName);if(matched.length===0)return"";if(isBlockingEvent(event)){let result=await executeBlockingChain(matched,payload);if(Object.keys(result).length>0)return JSON.stringify(result);return""}return await executeNonBlockingHandlers(matched,payload),""}async function readStdin(){let chunks=[];for await(let chunk of Bun.stdin.stream())chunks.push(Buffer.from(chunk));return Buffer.concat(chunks).toString("utf-8")}async function dispatchAction(){let stdin=await readStdin();if(!stdin.trim())process.exit(0);let result=await dispatch(stdin);if(result)process.stdout.write(result)}function registerHookNamespace(program2){program2.command("hook").description("Hook middleware for Claude Code integration").command("dispatch").description("Dispatch a CC hook event (reads JSON from stdin, writes decision to stdout)").action(dispatchAction)}init_audit();init_db();init_otel_receiver();init_agents();function registerAppCommand(program2){program2.command("app").description("Launch Genie desktop app (backend sidecar + views)").option("--backend-only","Start only the backend sidecar (IPC on stdin/stdout)").option("--tui","Fall back to terminal UI mode").option("--dev","Development mode").action(async(options)=>{if(options.tui){let{launchTui:launchTui2}=await Promise.resolve().then(() => (init_tui(),exports_tui));await launchTui2({dev:options.dev});return}if(options.backendOnly){await Promise.resolve().then(() => (init_src_backend(),exports_src_backend));return}let{existsSync:existsSync21}=await import("fs"),{join:join26,dirname:dirname8}=await import("path"),{execFileSync,execSync:execSync6}=await import("child_process"),appName="genie-desktop",rootDir=join26(dirname8(new URL(import.meta.url).pathname),"..",".."),searchPaths=[join26(rootDir,"packages","genie-app","src-tauri","target","release",appName),join26(rootDir,"packages","genie-app","src-tauri","target","debug",appName),join26(rootDir,"dist","app",appName),`/usr/local/bin/${appName}`],inPath=!1;try{execSync6(`which ${appName}`,{stdio:"ignore"}),inPath=!0}catch{}let tauriBin=searchPaths.find((p)=>existsSync21(p));if(tauriBin||inPath){console.log("\x1B[35m\u25C6 Genie App\x1B[0m Launching desktop...");try{execFileSync(tauriBin??appName,[],{stdio:"inherit"})}catch{}return}console.log("\x1B[35m\u25C6 Genie App\x1B[0m Starting backend sidecar..."),console.log("\x1B[2mDesktop binary not found \u2014 running in sidecar mode.\x1B[0m"),console.log("\x1B[2mPG bridge + PTY manager + IPC on stdin/stdout\x1B[0m"),console.log(`\x1B[2mUse --tui for terminal UI, or pipe to a frontend shell.\x1B[0m
|
|
2073
2073
|
`),await Promise.resolve().then(() => (init_src_backend(),exports_src_backend))})}init_audit();function padRight(str2,len){return str2.length>=len?str2:str2+" ".repeat(len-str2.length)}function truncate(str2,len){return str2.length<=len?str2:`${str2.slice(0,len-1)}\u2026`}function formatDate(iso){if(!iso)return"-";return new Date(iso).toLocaleDateString("en-US",{month:"short",day:"numeric"})}function formatRelativeTimestamp(ts3){let d2=new Date(ts3),diffMs=Date.now()-d2.getTime();if(diffMs<60000)return`${Math.floor(diffMs/1000)}s ago`;if(diffMs<3600000)return`${Math.floor(diffMs/60000)}m ago`;if(diffMs<86400000)return`${Math.floor(diffMs/3600000)}h ago`;return d2.toISOString().replace("T"," ").slice(0,19)}function formatTimestamp(iso,opts){if(!iso)return opts?.fallback??"-";let d2=iso instanceof Date?iso:new Date(iso),fmt={month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1};if(opts?.seconds)fmt.second="2-digit";return d2.toLocaleString("en-US",fmt)}function formatTime(iso,opts){try{let date=new Date(iso),fmt={hour:"2-digit",minute:"2-digit",hour12:!1};if(opts?.seconds)fmt.second="2-digit";return date.toLocaleTimeString("en-US",fmt)}catch{return opts?.fallback??"??:??"}}function printEventsTable(rows){if(rows.length===0){console.log("No audit events found.");return}let sorted=[...rows].reverse(),headers=["Time","Type","Entity","Event","Actor","Details"],data=sorted.map((r)=>[formatRelativeTimestamp(r.created_at),r.entity_type,r.entity_id,r.event_type,r.actor??"-",summarizeDetails(r.details)]),widths=headers.map((h2,i2)=>{let colVals=data.map((row)=>row[i2]);return Math.min(40,Math.max(h2.length,...colVals.map((v2)=>v2.length)))}),header=headers.map((h2,i2)=>padRight(h2,widths[i2])).join(" | ");console.log(header),console.log(widths.map((w2)=>"-".repeat(w2)).join("-+-"));for(let row of data){let line=row.map((v2,i2)=>padRight(v2.slice(0,widths[i2]),widths[i2])).join(" | ");console.log(line)}console.log(`
|
|
2074
2074
|
(${rows.length} event${rows.length===1?"":"s"})`)}function summarizeDetails(details){if(!details)return"";if(typeof details==="string")try{return summarizeDetails(JSON.parse(details))}catch{return details.slice(0,40)}if(Object.keys(details).length===0)return"";let keys=Object.keys(details);if(keys.length===1){let val=details[keys[0]];if(typeof val==="string")return val.slice(0,40);return JSON.stringify(val).slice(0,40)}if(details.error)return`error: ${String(details.error).slice(0,35)}`;if(details.duration_ms)return`${details.duration_ms}ms`;return JSON.stringify(details).slice(0,40)}function printErrorsTable(patterns2){if(patterns2.length===0){console.log("No error patterns found.");return}let headers=["Count","Event","Command","Error","Last Seen"],data=patterns2.map((p)=>[String(p.count),p.event_type,p.entity_id,p.error_message.slice(0,50),formatRelativeTimestamp(p.last_seen)]),widths=headers.map((h2,i2)=>{let colVals=data.map((row)=>row[i2]);return Math.min(50,Math.max(h2.length,...colVals.map((v2)=>v2.length)))}),header=headers.map((h2,i2)=>padRight(h2,widths[i2])).join(" | ");console.log(header),console.log(widths.map((w2)=>"-".repeat(w2)).join("-+-"));for(let row of data){let line=row.map((v2,i2)=>padRight(v2.slice(0,widths[i2]),widths[i2])).join(" | ");console.log(line)}console.log(`
|
|
2075
2075
|
(${patterns2.length} pattern${patterns2.length===1?"":"s"})`)}async function eventsListCommand(options){try{let queryOpts={type:options.type,entity:options.entity,since:options.since??"1h",errorsOnly:options.errorsOnly,limit:options.limit?Number.parseInt(options.limit,10):50},rows=await queryAuditEvents(queryOpts);if(options.json)console.log(JSON.stringify(rows,null,2));else printEventsTable(rows)}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(`Error querying events: ${msg}`),process.exit(1)}}async function eventsErrorsCommand(options){try{let patterns2=await queryErrorPatterns(options.since);if(options.json)console.log(JSON.stringify(patterns2,null,2));else printErrorsTable(patterns2)}catch(err){let msg=err instanceof Error?err.message:String(err);console.error(`Error querying error patterns: ${msg}`),process.exit(1)}}function resolveCostsGroupBy(options){if(options.byWish)return"wish";if(options.byModel)return"model";return"agent"}function printCostsTable(rows,groupBy){if(rows.length===0){console.log("No cost data found.");return}let headers=[groupBy==="agent"?"Agent":groupBy==="wish"?"Wish":"Model","Total Cost","Requests","Avg Cost"],data=rows.map((r)=>[r.group_key,`$${r.total_cost.toFixed(4)}`,String(r.request_count),`$${r.avg_cost.toFixed(4)}`]),widths=headers.map((h2,i2)=>{let colVals=data.map((row)=>row[i2]);return Math.min(40,Math.max(h2.length,...colVals.map((v2)=>v2.length)))});console.log(headers.map((h2,i2)=>padRight(h2,widths[i2])).join(" | ")),console.log(widths.map((w2)=>"-".repeat(w2)).join("-+-"));for(let row of data)console.log(row.map((v2,i2)=>padRight(v2.slice(0,widths[i2]),widths[i2])).join(" | "));let totalCost=rows.reduce((sum,r)=>sum+r.total_cost,0),totalReqs=rows.reduce((sum,r)=>sum+r.request_count,0);console.log(`
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genie",
|
|
3
|
-
"version": "4.260329.
|
|
3
|
+
"version": "4.260329.26",
|
|
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"
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
* loops to monitor workers. Suggests structured genie primitives but
|
|
6
6
|
* does NOT block — people may legitimately use tmux directly.
|
|
7
7
|
*
|
|
8
|
+
* Uses hookSpecificOutput.additionalContext to inject the nudge into
|
|
9
|
+
* Claude's context before the tool executes, with permissionDecision: "allow".
|
|
10
|
+
*
|
|
8
11
|
* Priority: 2 (runs after branch-guard, before identity-inject)
|
|
9
12
|
*/
|
|
10
13
|
|
|
@@ -47,9 +50,13 @@ export async function orchestrationGuard(payload: HookPayload): Promise<HandlerR
|
|
|
47
50
|
|
|
48
51
|
for (const { test, message } of NUDGE_PATTERNS) {
|
|
49
52
|
if (test.test(command)) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
return {
|
|
54
|
+
hookSpecificOutput: {
|
|
55
|
+
hookEventName: 'PreToolUse',
|
|
56
|
+
permissionDecision: 'allow',
|
|
57
|
+
additionalContext: `[orchestration-guard] ${message}`,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
62
|
|
package/src/hooks/index.ts
CHANGED
|
@@ -5,9 +5,15 @@
|
|
|
5
5
|
* Reads JSON from stdin, resolves matching handlers, executes the chain,
|
|
6
6
|
* and writes JSON result to stdout.
|
|
7
7
|
*
|
|
8
|
+
* Output format follows CC hook protocol:
|
|
9
|
+
* - PreToolUse: { hookSpecificOutput: { hookEventName, permissionDecision, ... } }
|
|
10
|
+
* - Other blocking: { decision: "block", reason: "..." }
|
|
11
|
+
* - Non-blocking: fire-and-forget, no output
|
|
12
|
+
*
|
|
8
13
|
* Blocking events (PreToolUse, TeammateIdle, TaskCompleted):
|
|
9
14
|
* Chain of responsibility — handlers run in priority order.
|
|
10
15
|
* - deny: short-circuits, returns immediately
|
|
16
|
+
* - hookSpecificOutput: collected, merged at end
|
|
11
17
|
* - updatedInput: merges into payload for next handler
|
|
12
18
|
* - allow/void: continues to next handler
|
|
13
19
|
*
|
|
@@ -25,7 +31,7 @@ import {
|
|
|
25
31
|
emitToolCallEvent,
|
|
26
32
|
emitUserPromptEvent,
|
|
27
33
|
} from './handlers/runtime-emit.js';
|
|
28
|
-
import type { Handler, HandlerResult,
|
|
34
|
+
import type { Handler, HandlerResult, HookPayload } from './types.js';
|
|
29
35
|
import { isBlockingEvent } from './types.js';
|
|
30
36
|
|
|
31
37
|
// ============================================================================
|
|
@@ -121,31 +127,62 @@ async function runHandler(
|
|
|
121
127
|
}
|
|
122
128
|
}
|
|
123
129
|
|
|
124
|
-
async function executeBlockingChain(matched: Handler[], payload: HookPayload): Promise<
|
|
130
|
+
async function executeBlockingChain(matched: Handler[], payload: HookPayload): Promise<Record<string, unknown>> {
|
|
125
131
|
let currentInput = payload.tool_input ? { ...payload.tool_input } : undefined;
|
|
126
|
-
const
|
|
132
|
+
const contextMessages: string[] = [];
|
|
133
|
+
const hookEventName = payload.hook_event_name;
|
|
127
134
|
|
|
128
135
|
for (const handler of matched) {
|
|
129
136
|
const result = await runHandler(handler, payload, currentInput);
|
|
130
137
|
if (!result) continue;
|
|
131
138
|
|
|
139
|
+
// Legacy deny format — convert to hookSpecificOutput for PreToolUse
|
|
132
140
|
if (result.decision === 'deny') {
|
|
133
|
-
|
|
141
|
+
if (hookEventName === 'PreToolUse') {
|
|
142
|
+
return {
|
|
143
|
+
hookSpecificOutput: {
|
|
144
|
+
hookEventName: 'PreToolUse',
|
|
145
|
+
permissionDecision: 'deny',
|
|
146
|
+
permissionDecisionReason: result.reason ?? `Denied by handler: ${handler.name}`,
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return { decision: 'block', reason: result.reason ?? `Denied by handler: ${handler.name}` };
|
|
134
151
|
}
|
|
135
|
-
|
|
136
|
-
|
|
152
|
+
|
|
153
|
+
// Collect hookSpecificOutput (additionalContext, etc.)
|
|
154
|
+
if (result.hookSpecificOutput?.additionalContext) {
|
|
155
|
+
contextMessages.push(result.hookSpecificOutput.additionalContext);
|
|
137
156
|
}
|
|
138
|
-
|
|
139
|
-
|
|
157
|
+
|
|
158
|
+
// Collect updatedInput from either format
|
|
159
|
+
const inputUpdate = result.hookSpecificOutput?.updatedInput ?? result.updatedInput;
|
|
160
|
+
if (inputUpdate) {
|
|
161
|
+
currentInput = { ...currentInput, ...inputUpdate };
|
|
140
162
|
}
|
|
141
163
|
}
|
|
142
164
|
|
|
143
|
-
|
|
144
|
-
|
|
165
|
+
// Build response
|
|
166
|
+
const response: Record<string, unknown> = {};
|
|
167
|
+
|
|
168
|
+
const hasContext = contextMessages.length > 0;
|
|
169
|
+
const hasInputChange =
|
|
170
|
+
currentInput && payload.tool_input && JSON.stringify(currentInput) !== JSON.stringify(payload.tool_input);
|
|
171
|
+
|
|
172
|
+
// updatedInput at top level (backward compat, used by tests + older CC versions)
|
|
173
|
+
if (hasInputChange) {
|
|
145
174
|
response.updatedInput = currentInput;
|
|
146
175
|
}
|
|
147
|
-
|
|
148
|
-
|
|
176
|
+
|
|
177
|
+
// For PreToolUse, ALSO emit hookSpecificOutput (the correct CC format)
|
|
178
|
+
if (hookEventName === 'PreToolUse' && (hasContext || hasInputChange)) {
|
|
179
|
+
const output: Record<string, unknown> = { hookEventName: 'PreToolUse' };
|
|
180
|
+
if (hasContext) output.additionalContext = contextMessages.join('\n');
|
|
181
|
+
if (hasInputChange) {
|
|
182
|
+
output.permissionDecision = 'allow';
|
|
183
|
+
output.updatedInput = currentInput;
|
|
184
|
+
}
|
|
185
|
+
response.hookSpecificOutput = output;
|
|
149
186
|
}
|
|
150
187
|
|
|
151
188
|
return response;
|
|
@@ -186,14 +223,12 @@ export async function dispatch(stdin: string): Promise<string> {
|
|
|
186
223
|
const matched = resolveHandlers(event, toolName);
|
|
187
224
|
|
|
188
225
|
if (matched.length === 0) {
|
|
189
|
-
// No handlers — implicit allow (empty stdout)
|
|
190
226
|
return '';
|
|
191
227
|
}
|
|
192
228
|
|
|
193
229
|
if (isBlockingEvent(event)) {
|
|
194
230
|
const result = await executeBlockingChain(matched, payload);
|
|
195
|
-
|
|
196
|
-
if (result.decision || result.updatedInput || result.systemMessage) {
|
|
231
|
+
if (Object.keys(result).length > 0) {
|
|
197
232
|
return JSON.stringify(result);
|
|
198
233
|
}
|
|
199
234
|
return '';
|
package/src/hooks/types.ts
CHANGED
|
@@ -43,11 +43,18 @@ export interface HookPayload {
|
|
|
43
43
|
|
|
44
44
|
/** Decision a PreToolUse handler can return. */
|
|
45
45
|
export interface HookDecision {
|
|
46
|
+
/** @deprecated Use hookSpecificOutput.permissionDecision instead for PreToolUse */
|
|
46
47
|
decision?: 'allow' | 'deny' | 'ask';
|
|
47
48
|
reason?: string;
|
|
48
49
|
updatedInput?: Record<string, unknown>;
|
|
49
|
-
/**
|
|
50
|
-
|
|
50
|
+
/** CC hookSpecificOutput — the correct format for PreToolUse decisions. */
|
|
51
|
+
hookSpecificOutput?: {
|
|
52
|
+
hookEventName: string;
|
|
53
|
+
permissionDecision?: 'allow' | 'deny' | 'ask';
|
|
54
|
+
permissionDecisionReason?: string;
|
|
55
|
+
additionalContext?: string;
|
|
56
|
+
updatedInput?: Record<string, unknown>;
|
|
57
|
+
};
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
/** Result from a handler — either a decision or void (implicit allow). */
|
package/src/lib/task-service.ts
CHANGED
|
@@ -524,15 +524,11 @@ async function resolveColumnId(sql: Sql, boardId: string, stageName: string): Pr
|
|
|
524
524
|
return match?.id ?? null;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
/**
|
|
528
|
-
function
|
|
527
|
+
/** Nullable fields with null default. */
|
|
528
|
+
function taskNullables(input: TaskInput) {
|
|
529
529
|
return {
|
|
530
530
|
desc: input.description ?? null,
|
|
531
531
|
ac: input.acceptanceCriteria ?? null,
|
|
532
|
-
type: input.typeId ?? 'software',
|
|
533
|
-
stage: input.stage ?? 'draft',
|
|
534
|
-
status: input.status ?? 'ready',
|
|
535
|
-
priority: input.priority ?? 'normal',
|
|
536
532
|
parent: input.parentId ?? null,
|
|
537
533
|
wish: input.wishFile ?? null,
|
|
538
534
|
group: input.groupName ?? null,
|
|
@@ -546,6 +542,17 @@ function buildTaskVals(input: TaskInput) {
|
|
|
546
542
|
};
|
|
547
543
|
}
|
|
548
544
|
|
|
545
|
+
/** Extract and normalize task input fields with defaults. */
|
|
546
|
+
function buildTaskVals(input: TaskInput) {
|
|
547
|
+
return {
|
|
548
|
+
...taskNullables(input),
|
|
549
|
+
type: input.typeId ?? 'software',
|
|
550
|
+
stage: input.stage ?? 'draft',
|
|
551
|
+
status: input.status ?? 'ready',
|
|
552
|
+
priority: input.priority ?? 'normal',
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
549
556
|
export async function createTask(input: TaskInput, repoPath?: string, projectId?: string): Promise<TaskRow> {
|
|
550
557
|
const sql = await getConnection();
|
|
551
558
|
const repo = repoPath ?? getRepoPath();
|
package/src/tui/app.tsx
CHANGED
|
@@ -10,9 +10,11 @@ export function App({ rightPane }: { rightPane?: string }) {
|
|
|
10
10
|
const renderer = useRenderer();
|
|
11
11
|
|
|
12
12
|
useKeyboard((key) => {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// Ctrl+Q or Ctrl+C: kill the entire TUI (both panes)
|
|
14
|
+
if ((key.ctrl && key.name === 'q') || (key.ctrl && key.name === 'c')) {
|
|
15
|
+
// Kill tmux session FIRST (kills both panes), then destroy renderer
|
|
15
16
|
cleanup();
|
|
17
|
+
renderer.destroy();
|
|
16
18
|
}
|
|
17
19
|
});
|
|
18
20
|
|
package/src/tui/session-tree.ts
CHANGED
|
@@ -7,6 +7,9 @@ import type { DiagnosticSnapshot, TmuxPane, TmuxSession, TmuxWindow } from './di
|
|
|
7
7
|
import type { TreeNode, TuiExecutor } from './types.js';
|
|
8
8
|
|
|
9
9
|
/** Build a TreeNode tree from tmux sessions, enriched with executor state. */
|
|
10
|
+
/** The TUI's own session name — filtered from the tree to prevent self-attach loops */
|
|
11
|
+
const TUI_SESSION = 'genie-tui';
|
|
12
|
+
|
|
10
13
|
export function buildSessionTree(snapshot: DiagnosticSnapshot): TreeNode[] {
|
|
11
14
|
const executorByPaneId = new Map<string, TuiExecutor>();
|
|
12
15
|
for (const exec of snapshot.executors) {
|
|
@@ -15,7 +18,9 @@ export function buildSessionTree(snapshot: DiagnosticSnapshot): TreeNode[] {
|
|
|
15
18
|
}
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
return snapshot.sessions
|
|
21
|
+
return snapshot.sessions
|
|
22
|
+
.filter((s) => s.name !== TUI_SESSION)
|
|
23
|
+
.map((session) => sessionToNode(session, executorByPaneId));
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
function sessionToNode(session: TmuxSession, executorMap: Map<string, TuiExecutor>): TreeNode {
|
package/src/tui/tmux.ts
CHANGED
|
@@ -84,6 +84,8 @@ function ensureSession(sessionName: string): void {
|
|
|
84
84
|
|
|
85
85
|
/** Switch right pane to a specific session window */
|
|
86
86
|
export function attachProjectWindow(rightPane: string, targetSession: string, windowIndex?: number): void {
|
|
87
|
+
// Guard: never attach the TUI session to itself (causes infinite loop)
|
|
88
|
+
if (targetSession === SESSION_NAME) return;
|
|
87
89
|
const pane = resolveRightPane(rightPane);
|
|
88
90
|
ensureSession(targetSession);
|
|
89
91
|
if (windowIndex !== undefined) {
|