@automagik/genie 4.260329.13 → 4.260329.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "genie",
13
- "version": "4.260329.13",
13
+ "version": "4.260329.15",
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
@@ -1432,7 +1432,7 @@ ${answer}
1432
1432
  ORDER BY seq
1433
1433
  `).map((r)=>({id:r.id,seq:r.seq,title:r.title,stage:r.stage,repoPath:r.repo_path}))}function getRepoPathSafe(){try{return execSync12("git rev-parse --show-toplevel",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return process.cwd()}}var init_task_close_merged=()=>{};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 execSync13,spawnSync as spawnSync2}from"child_process";function hasTmux(){try{return execSync13("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{execSync13(`tmux kill-session -t ${SESSION_NAME} 2>/dev/null`,{stdio:"ignore"})}catch{}execSync13(`tmux new-session -d -s ${SESSION_NAME} -x ${cols} -y ${rows} -e GENIE_TUI_PANE=left`,{stdio:"ignore"}),execSync13(`tmux split-window -h -t ${SESSION_NAME}:0 -l ${cols-NAV_WIDTH-1}`,{stdio:"ignore"});let panes=execSync13(`tmux list-panes -t ${SESSION_NAME}:0 -F '#{pane_id}'`,{encoding:"utf-8"}).trim().split(`
1434
1434
  `),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 execSync13(`tmux display-message -t ${rightPane} -p ''`,{stdio:"ignore"}),rightPane}catch{try{let panes=execSync13(`tmux list-panes -t ${SESSION_NAME}:0 -F '#{pane_id}'`,{encoding:"utf-8"}).trim().split(`
1435
- `);return panes[1]||panes[0]}catch{return rightPane}}}function ensureSession3(sessionName){try{execSync13(`tmux has-session -t '${sessionName}' 2>/dev/null`,{stdio:"ignore"})}catch{try{execSync13(`tmux new-session -d -s '${sessionName}'`,{stdio:"ignore"})}catch{}}}function attachProject(rightPane,targetSession){let pane=resolveRightPane(rightPane);ensureSession3(targetSession);try{execSync13(`tmux respawn-pane -k -t ${pane} "TMUX='' tmux attach-session -t '${targetSession}'"`,{stdio:"ignore"})}catch{}}function switchRightPane(rightPane,targetSession){attachProject(rightPane,targetSession)}function setupKeybindings(session){try{execSync13(`tmux bind-key -T ${KEY_TABLE} Tab select-pane -t ${session}:0.+ \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync13(`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"}),execSync13(`tmux bind-key -T ${KEY_TABLE} C-t send-keys -t ${session}:0.1 C-b c \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync13(`tmux bind-key -T ${KEY_TABLE} 'C-\\' run-shell "tmux kill-session -t ${session}"`,{stdio:"ignore"}),execSync13(`tmux set-hook -t ${session} client-session-changed "switch-client -T ${KEY_TABLE}"`,{stdio:"ignore"}),execSync13(`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)execSync13(`tmux ${cmd}`,{stdio:"ignore"})}catch{}}function cleanup(session=SESSION_NAME){try{execSync13(`tmux unbind-key -T ${KEY_TABLE} Tab 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux unbind-key -T ${KEY_TABLE} C-b 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux unbind-key -T ${KEY_TABLE} C-t 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux unbind-key -T ${KEY_TABLE} 'C-\\' 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux set-hook -u -t ${session} client-session-changed 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux kill-session -t ${session} 2>/dev/null`,{stdio:"ignore"})}catch{}}function attachTuiSession(){spawnSync2("tmux",["attach-session","-t",SESSION_NAME],{stdio:"inherit"})}function listSessionWindows(session){try{let output=execSync13(`tmux list-windows -t ${session} -F '#{window_name}:#{window_index}:#{?window_active,1,0}' 2>/dev/null`,{encoding:"utf-8"}).trim();if(!output)return[];return output.split(`
1435
+ `);return panes[1]||panes[0]}catch{return rightPane}}}function ensureSession3(sessionName){try{execSync13(`tmux has-session -t '${sessionName}' 2>/dev/null`,{stdio:"ignore"})}catch{try{execSync13(`tmux new-session -d -s '${sessionName}'`,{stdio:"ignore"})}catch{}}}function attachProject(rightPane,targetSession){let pane=resolveRightPane(rightPane);ensureSession3(targetSession);try{execSync13(`tmux respawn-pane -k -t ${pane} "TMUX='' tmux attach-session -t '${targetSession}'"`,{stdio:"ignore"})}catch{}}function switchRightPane(rightPane,targetSession){attachProject(rightPane,targetSession)}function attachProjectWindow(rightPane,targetSession,windowIndex){let pane=resolveRightPane(rightPane);if(ensureSession3(targetSession),windowIndex!==void 0)try{execSync13(`tmux select-window -t '${targetSession}:${windowIndex}'`,{stdio:"ignore"})}catch{}try{execSync13(`tmux respawn-pane -k -t ${pane} "TMUX='' tmux attach-session -t '${targetSession}'"`,{stdio:"ignore"})}catch{}}function setupKeybindings(session){try{execSync13(`tmux bind-key -T ${KEY_TABLE} Tab select-pane -t :.+ \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync13(`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"}),execSync13(`tmux bind-key -T ${KEY_TABLE} C-t send-keys -t ${session}:0.1 C-b c \\; switch-client -T ${KEY_TABLE}`,{stdio:"ignore"}),execSync13(`tmux bind-key -T ${KEY_TABLE} 'C-\\' run-shell "tmux kill-session -t ${session}"`,{stdio:"ignore"}),execSync13(`tmux set-hook -t ${session} client-session-changed "switch-client -T ${KEY_TABLE}"`,{stdio:"ignore"}),execSync13(`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)execSync13(`tmux ${cmd}`,{stdio:"ignore"})}catch{}}function cleanup(session=SESSION_NAME){try{execSync13(`tmux unbind-key -T ${KEY_TABLE} Tab 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux unbind-key -T ${KEY_TABLE} C-b 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux unbind-key -T ${KEY_TABLE} C-t 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux unbind-key -T ${KEY_TABLE} 'C-\\' 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux set-hook -u -t ${session} client-session-changed 2>/dev/null`,{stdio:"ignore"}),execSync13(`tmux kill-session -t ${session} 2>/dev/null`,{stdio:"ignore"})}catch{}}function attachTuiSession(){spawnSync2("tmux",["attach-session","-t",SESSION_NAME],{stdio:"inherit"})}function listSessionWindows(session){try{let output=execSync13(`tmux list-windows -t ${session} -F '#{window_name}:#{window_index}:#{?window_active,1,0}' 2>/dev/null`,{encoding:"utf-8"}).trim();if(!output)return[];return output.split(`
1436
1436
  `).map((line)=>{let[name,idx,active]=line.split(":");return{name,index:Number.parseInt(idx,10),active:active==="1"}})}catch{return[]}}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 resolve7,dirname as dirname9}from"path";import{fileURLToPath as fileURLToPath2}from"url";import{resolve as resolve22,isAbsolute,parse as parse2}from"path";import{existsSync as existsSync33}from"fs";import{basename as basename7,join as join44}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 mkdir9,readFile as readFile13,writeFile as writeFile6}from"fs/promises";import*as path3 from"path";import{readdir as readdir7}from"fs/promises";import{dlopen,toArrayBuffer as toArrayBuffer4,JSCallback,ptr as ptr4}from"bun:ffi";import{existsSync as existsSync210,writeFileSync as writeFileSync11}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:
1437
1437
  `+treeLines.join(`
1438
1438
  `))}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 cp3=parseInt(cpStr,10);if(!isNaN(cp3)&&cp3>0&&cp3<=1114111)text+=String.fromCodePoint(cp3)}}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(`
@@ -1881,7 +1881,7 @@ To install use this command:
1881
1881
 
1882
1882
  $ bun add react-devtools-core@7 -d
1883
1883
  `.trim()+`
1884
- `);else throw error2}reconciler.injectIntoDevTools();_r=reconciler,flushSync=_r.flushSyncFromReconciler??_r.flushSync});function useEffectEvent(handler){let handlerRef=import_react6.useRef(handler);return import_react6.useLayoutEffect(()=>{handlerRef.current=handler}),import_react6.useCallback((...args)=>{let fn=handlerRef.current;return fn(...args)},[])}function renderPluginFailurePlaceholder(registry,pluginFailurePlaceholder,failure,pluginId,slot){if(!pluginFailurePlaceholder)return null;try{return pluginFailurePlaceholder(failure)}catch(error2){return registry.reportPluginError({pluginId,slot,phase:"error_placeholder",source:"react",error:error2}),null}}var import_react5,import_react6,import_react7,import_react8,import_react9,import_react10,import_react11,import_react12,useKeyboard=(handler,options={release:!1})=>{let{keyHandler}=useAppContext(),stableHandler=useEffectEvent(handler);import_react5.useEffect(()=>{if(keyHandler?.on("keypress",stableHandler),options?.release)keyHandler?.on("keyrelease",stableHandler);return()=>{if(keyHandler?.off("keypress",stableHandler),options?.release)keyHandler?.off("keyrelease",stableHandler)}},[keyHandler,options.release])},useRenderer=()=>{let{renderer}=useAppContext();if(!renderer)throw Error("Renderer not found.");return renderer},PluginErrorBoundary;var init_react=__esm(async()=>{init_chunk_2mx7fq49();await __promiseAll([init_chunk_kxd698dk(),init_core(),init_core(),init_core()]);import_react5=__toESM(require_react_development(),1),import_react6=__toESM(require_react_development(),1),import_react7=__toESM(require_react_development(),1),import_react8=__toESM(require_react_development(),1),import_react9=__toESM(require_react_development(),1),import_react10=__toESM(require_react_development(),1),import_react11=__toESM(require_react_development(),1),import_react12=__toESM(require_react_development(),1);PluginErrorBoundary=class PluginErrorBoundary extends import_react10.default.Component{constructor(props){super(props);this.state={failure:null}}componentDidCatch(error2){let failure=this.props.registry.reportPluginError({pluginId:this.props.pluginId,slot:this.props.slotName,phase:"render",source:"react",error:error2});this.setState({failure})}componentDidUpdate(previousProps){if(previousProps.resetToken!==this.props.resetToken&&this.state.failure)this.setState({failure:null})}render(){if(this.state.failure){let placeholder=renderPluginFailurePlaceholder(this.props.registry,this.props.pluginFailurePlaceholder,this.state.failure,this.props.pluginId,this.props.slotName);if(placeholder===null||placeholder===void 0||placeholder===!1)return this.props.fallbackOnFailure??null;return placeholder}return this.props.children}};extend3({"time-to-first-draw":TimeToFirstDrawRenderable})});function toAgentState(state){switch(state){case"idle":case"running":return"idle";case"working":case"spawning":return"working";case"permission":case"question":return"permission";case"error":return"error";default:return}}function statePriority(state){switch(state){case"working":return 3;case"permission":return 2;case"error":return 1;case"idle":return 0}}function groupByTask(executors,assignments){let executorById=new Map(executors.map((e)=>[e.id,e])),result=new Map;for(let a of assignments){if(!a.taskId)continue;let exec3=executorById.get(a.executorId);if(!exec3)continue;let list2=result.get(a.taskId)||[];list2.push(exec3),result.set(a.taskId,list2)}return result}function bestExecutorState(execs){let best;for(let exec3 of execs){let state=toAgentState(exec3.state);if(state&&(!best||statePriority(state)>statePriority(best)))best=state}return best}function getExecutorActivity(executors,assignments){let result=new Map;for(let[taskId,execs]of groupByTask(executors,assignments))result.set(taskId,{panes:execs.length,state:bestExecutorState(execs)});return result}function getActiveWork(session){return listSessionWindows(session).filter((w2)=>w2.name!=="bash"&&w2.name!=="zsh").map((w2)=>({windowName:w2.name,index:w2.index,active:w2.active}))}function matchWorkToTasks(windows,tasks){let result=new Map;for(let win of windows){let slug=win.windowName.toLowerCase().replace(/[^a-z0-9-]/g,""),matched=tasks.find((t2)=>{let taskSlug=t2.title.toLowerCase().replace(/[^a-z0-9-]/g,"");return taskSlug.includes(slug)||slug.includes(taskSlug)||slug.includes(`${t2.seq}`)});if(matched){let existing=result.get(matched.id)||{panes:0};result.set(matched.id,{panes:existing.panes+1,state:existing.state})}}return result}var init_activity=__esm(()=>{init_tmux2()});var exports_db2={};__export(exports_db2,{subscribe:()=>subscribe,loadExecutors:()=>loadExecutors,loadAssignments:()=>loadAssignments,loadAll:()=>loadAll2});async function loadAll2(){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2(),[orgRows,projRows,boardRows,taskRows,apRows]=await Promise.all([sql`SELECT id, name, slug, description, leader_agent FROM organizations ORDER BY name`,sql`SELECT id, name, description, org_id, leader_agent, tmux_session, repo_path FROM projects ORDER BY name`,sql`SELECT id, name, project_id, description, columns FROM boards ORDER BY name`,sql`SELECT id, seq, title, status, stage, priority, project_id, board_id, column_id, description
1884
+ `);else throw error2}reconciler.injectIntoDevTools();_r=reconciler,flushSync=_r.flushSyncFromReconciler??_r.flushSync});function useEffectEvent(handler){let handlerRef=import_react6.useRef(handler);return import_react6.useLayoutEffect(()=>{handlerRef.current=handler}),import_react6.useCallback((...args)=>{let fn=handlerRef.current;return fn(...args)},[])}function renderPluginFailurePlaceholder(registry,pluginFailurePlaceholder,failure,pluginId,slot){if(!pluginFailurePlaceholder)return null;try{return pluginFailurePlaceholder(failure)}catch(error2){return registry.reportPluginError({pluginId,slot,phase:"error_placeholder",source:"react",error:error2}),null}}var import_react5,import_react6,import_react7,import_react8,import_react9,import_react10,import_react11,import_react12,useKeyboard=(handler,options={release:!1})=>{let{keyHandler}=useAppContext(),stableHandler=useEffectEvent(handler);import_react5.useEffect(()=>{if(keyHandler?.on("keypress",stableHandler),options?.release)keyHandler?.on("keyrelease",stableHandler);return()=>{if(keyHandler?.off("keypress",stableHandler),options?.release)keyHandler?.off("keyrelease",stableHandler)}},[keyHandler,options.release])},useRenderer=()=>{let{renderer}=useAppContext();if(!renderer)throw Error("Renderer not found.");return renderer},PluginErrorBoundary;var init_react=__esm(async()=>{init_chunk_2mx7fq49();await __promiseAll([init_chunk_kxd698dk(),init_core(),init_core(),init_core()]);import_react5=__toESM(require_react_development(),1),import_react6=__toESM(require_react_development(),1),import_react7=__toESM(require_react_development(),1),import_react8=__toESM(require_react_development(),1),import_react9=__toESM(require_react_development(),1),import_react10=__toESM(require_react_development(),1),import_react11=__toESM(require_react_development(),1),import_react12=__toESM(require_react_development(),1);PluginErrorBoundary=class PluginErrorBoundary extends import_react10.default.Component{constructor(props){super(props);this.state={failure:null}}componentDidCatch(error2){let failure=this.props.registry.reportPluginError({pluginId:this.props.pluginId,slot:this.props.slotName,phase:"render",source:"react",error:error2});this.setState({failure})}componentDidUpdate(previousProps){if(previousProps.resetToken!==this.props.resetToken&&this.state.failure)this.setState({failure:null})}render(){if(this.state.failure){let placeholder=renderPluginFailurePlaceholder(this.props.registry,this.props.pluginFailurePlaceholder,this.state.failure,this.props.pluginId,this.props.slotName);if(placeholder===null||placeholder===void 0||placeholder===!1)return this.props.fallbackOnFailure??null;return placeholder}return this.props.children}};extend3({"time-to-first-draw":TimeToFirstDrawRenderable})});function toAgentState(state){switch(state){case"idle":case"running":return"idle";case"working":case"spawning":return"working";case"permission":case"question":return"permission";case"error":return"error";default:return}}function statePriority(state){switch(state){case"working":return 3;case"permission":return 2;case"error":return 1;case"idle":return 0}}function groupByTask(executors,assignments){let executorById=new Map(executors.map((e)=>[e.id,e])),result=new Map;for(let a of assignments){if(!a.taskId)continue;let exec3=executorById.get(a.executorId);if(!exec3)continue;let list2=result.get(a.taskId)||[];list2.push(exec3),result.set(a.taskId,list2)}return result}function bestExecutorState(execs){let best;for(let exec3 of execs){let state=toAgentState(exec3.state);if(state&&(!best||statePriority(state)>statePriority(best)))best=state}return best}function getExecutorActivity(executors,assignments){let result=new Map;for(let[taskId,execs]of groupByTask(executors,assignments))result.set(taskId,{panes:execs.length,state:bestExecutorState(execs)});return result}function getActiveWork(session){return listSessionWindows(session).filter((w2)=>w2.name!=="bash"&&w2.name!=="zsh").map((w2)=>({windowName:w2.name,index:w2.index,active:w2.active}))}function matchWorkToTasks(windows,tasks){let result=new Map;for(let win of windows){let slug=win.windowName.toLowerCase().replace(/[^a-z0-9-]/g,""),matched=tasks.find((t2)=>{let taskSlug=t2.title.toLowerCase().replace(/[^a-z0-9-]/g,"");return taskSlug.includes(slug)||slug.includes(taskSlug)||slug.includes(`${t2.seq}`)});if(matched){let existing=result.get(matched.id)||{panes:0};result.set(matched.id,{panes:existing.panes+1,state:existing.state})}}return result}var init_activity=__esm(()=>{init_tmux2()});var exports_db2={};__export(exports_db2,{subscribe:()=>subscribe,loadTmuxTree:()=>loadTmuxTree,loadExecutors:()=>loadExecutors,loadAssignments:()=>loadAssignments,loadAll:()=>loadAll2});import{execSync as execSync14}from"child_process";async function loadAll2(){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db)),sql=await getConnection2(),[orgRows,projRows,boardRows,taskRows,apRows]=await Promise.all([sql`SELECT id, name, slug, description, leader_agent FROM organizations ORDER BY name`,sql`SELECT id, name, description, org_id, leader_agent, tmux_session, repo_path FROM projects ORDER BY name`,sql`SELECT id, name, project_id, description, columns FROM boards ORDER BY name`,sql`SELECT id, seq, title, status, stage, priority, project_id, board_id, column_id, description
1885
1885
  FROM tasks WHERE status NOT IN ('cancelled')
1886
1886
  ORDER BY priority DESC, seq ASC`,sql`SELECT agent_name, project_id, role FROM agent_projects`]);return{orgs:orgRows.map(mapOrg),projects:projRows.map(mapProject2),boards:boardRows.map(mapBoard2),tasks:taskRows.map(mapTask2),agentProjects:apRows.map(mapAgentProject)}}async function subscribe(onEvent){let{followRuntimeEvents:followRuntimeEvents2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events)),handle=await followRuntimeEvents2({},(event)=>{onEvent(event.kind,{id:event.id,agent:event.agent,team:event.team,text:event.text,subject:event.subject,data:event.data})});return{stop:()=>handle.stop()}}async function loadExecutors(){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db));return(await(await getConnection2())`
1887
1887
  SELECT e.id, e.agent_id, e.provider, e.transport, e.pid, e.tmux_session,
@@ -1899,8 +1899,9 @@ $ bun add react-devtools-core@7 -d
1899
1899
  WHERE a.executor_id = ANY(${executorIds})
1900
1900
  AND a.ended_at IS NULL
1901
1901
  ORDER BY a.started_at DESC
1902
- `).map(mapAssignment)}function mapOrg(row){return{id:String(row.id),name:String(row.name),slug:String(row.slug),description:row.description?String(row.description):null,leaderAgent:row.leader_agent?String(row.leader_agent):null}}function mapProject2(row){return{id:String(row.id),name:String(row.name),description:row.description?String(row.description):null,orgId:row.org_id?String(row.org_id):null,leaderAgent:row.leader_agent?String(row.leader_agent):null,tmuxSession:row.tmux_session?String(row.tmux_session):null,repoPath:row.repo_path?String(row.repo_path):null}}function mapBoard2(row){let rawCols=row.columns,columns=Array.isArray(rawCols)?rawCols.map((c,i2)=>({id:String(c.id??`col-${i2}`),name:String(c.name??""),label:String(c.label??c.name??""),color:String(c.color??"#94a3b8"),position:typeof c.position==="number"?c.position:i2})):[];return{id:String(row.id),name:String(row.name),projectId:row.project_id?String(row.project_id):null,description:row.description?String(row.description):null,columns}}function mapTask2(row){return{id:String(row.id),seq:Number(row.seq),title:String(row.title),status:String(row.status),stage:String(row.stage),priority:String(row.priority),projectId:row.project_id?String(row.project_id):null,boardId:row.board_id?String(row.board_id):null,columnId:row.column_id?String(row.column_id):null,description:row.description?String(row.description):null}}function mapAgentProject(row){return{agentName:String(row.agent_name),projectId:String(row.project_id),role:String(row.role)}}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 execSync14}from"child_process";function execQuiet(cmd){try{return execSync14(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]=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}}}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}'");if(!paneOutput)return[];let sessionMap=new Map,windowMap=new Map;for(let line of paneOutput.split(`
1903
- `)){if(!line)continue;let parts=line.split("|");if(parts.length<14)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;return{deadPidExecutors,orphanPanes,linkedCount,totalExecutors:executors.length,totalClaudePanes:claudePanes.length}}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 buildTree(data){let orgNodes=[];for(let org of data.orgs){let projectNodes=data.projects.filter((p)=>p.orgId===org.id).map((proj)=>{let boardNodes=data.boards.filter((b3)=>b3.projectId===proj.id).map((board)=>{let columnNodes=(board.columns||[]).sort((a,b3)=>a.position-b3.position).map((col)=>{let colTasks=data.tasks.filter((t2)=>t2.boardId===board.id&&t2.columnId===col.id),taskNodes=colTasks.map((task)=>({id:task.id,type:"task",label:`#${task.seq} ${task.title}`,depth:4,expanded:!1,children:[],data:task,activePanes:0}));return{id:col.id,type:"column",label:`${col.label} (${colTasks.length})`,depth:3,expanded:!1,children:taskNodes,data:col,activePanes:0}});return{id:board.id,type:"board",label:board.name,depth:2,expanded:!1,children:columnNodes,data:board,activePanes:0}});return{id:proj.id,type:"project",label:proj.name,depth:1,expanded:!1,children:boardNodes,data:proj,activePanes:0}});orgNodes.push({id:org.id,type:"org",label:org.name,depth:0,expanded:!0,children:projectNodes,data:org,activePanes:0})}return orgNodes}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)}})}function applyActivity(nodes,activity){return nodes.map((node)=>{let act=activity.get(node.id),updated={...node,activePanes:act?.panes??0,agentState:act?.state,children:applyActivity(node.children,activity)};if(updated.children.some((c)=>c.activePanes>0)&&updated.activePanes===0)updated.activePanes=updated.children.reduce((sum,c)=>sum+c.activePanes,0);return updated})}var import_jsx_dev_runtime2;var init_jsx_dev_runtime=__esm(()=>{import_jsx_dev_runtime2=__toESM(require_react_jsx_dev_runtime_development(),1)});function stateColor(state){return STATE_COLORS[state]??palette.textMuted}function executorDisplayName(exec3){let name=exec3.agentName?`${exec3.agentName}${exec3.team?`@${exec3.team}`:""}`:exec3.agentId;return exec3.role?`${name} (${exec3.role})`:name}function metaRow(exec3){let hasTmux2=!!exec3.tmuxPaneId,isApi=exec3.transport==="api";return{id:`meta:${exec3.id}`,depth:1,label:hasTmux2?`${exec3.tmuxSession??"?"}:${exec3.tmuxPaneId}`:isApi?"api (no tmux)":"tmux: not linked",color:hasTmux2?palette.emerald:isApi?palette.textDim:palette.error,detail:exec3.pid!=null?`pid:${exec3.pid}`:"",detailColor:palette.textMuted,isOrphan:!hasTmux2&&!isApi}}function assignmentRow(exec3,a){let taskLabel=a.taskTitle??a.taskId??"unknown",wishLabel=a.wishSlug?` [${a.wishSlug}]`:"";return{id:`assign:${exec3.id}`,depth:1,label:`\u2192 ${taskLabel}${wishLabel}`,color:palette.purple,detail:a.groupNumber!=null?`grp:${a.groupNumber}`:"",detailColor:palette.textMuted,isOrphan:!1}}function executorToRows(exec3,assignments,isDead){let rows=[{id:`exec:${exec3.id}`,depth:0,label:executorDisplayName(exec3),color:isDead?palette.error:stateColor(exec3.state),detail:isDead?`DEAD pid:${exec3.pid}`:`${exec3.state} ${exec3.provider}`,detailColor:isDead?palette.error:palette.textMuted,isOrphan:isDead},metaRow(exec3)],active=assignments.find((a)=>a.executorId===exec3.id);if(active)rows.push(assignmentRow(exec3,active));return rows}function flattenExecutors(executors,assignments,gaps){let deadIds=new Set(gaps.deadPidExecutors.map((e)=>e.id)),rows=executors.flatMap((exec3)=>executorToRows(exec3,assignments,deadIds.has(exec3.id)));if(gaps.orphanPanes.length>0){rows.push({id:"orphan-header",depth:0,label:`\u2500\u2500 Orphan Panes (${gaps.orphanPanes.length}) \u2500\u2500`,color:palette.warning,detail:"",detailColor:palette.textMuted,isOrphan:!1});for(let pane of gaps.orphanPanes)rows.push({id:`orphan:${pane.paneId}`,depth:1,label:`${pane.sessionName}:${pane.windowIndex}.${pane.paneIndex}`,color:palette.error,detail:`pid:${pane.pid} [${pane.title}]`,detailColor:palette.textMuted,isOrphan:!0})}return rows}function ClaudeView({executors,assignments,gaps,selectedIndex}){let rows=import_react13.useMemo(()=>flattenExecutors(executors,assignments,gaps),[executors,assignments,gaps]);return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLighter,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.emerald,children:[gaps.linkedCount," linked"]},void 0,!0,void 0,this),gaps.deadPidExecutors.length>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:[" ",gaps.deadPidExecutors.length," dead"]},void 0,!0,void 0,this):null,gaps.orphanPanes.length>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.warning,children:[" ",gaps.orphanPanes.length," unmapped"]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("scrollbox",{focused:!0,height:"100%",style:{scrollbarOptions:{showArrows:!1,trackOptions:{foregroundColor:palette.scrollThumb,backgroundColor:palette.scrollTrack}}},children:rows.map((row,i2)=>{let indent=" ".repeat(row.depth),icon=row.depth===0?row.isOrphan?"\u2718":"\u2713":"\u2514";return import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",backgroundColor:i2===selectedIndex?palette.violet:void 0,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:indent},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:row.color,children:[icon," ",row.label]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:row.detailColor,children:[" ",row.detail]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},row.id,!1,void 0,this)})},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function getClaudeRowCount(executors,assignments,gaps){let count=0;for(let exec3 of executors)if(count+=2,assignments.some((a)=>a.executorId===exec3.id))count++;if(gaps.orphanPanes.length>0)count+=1+gaps.orphanPanes.length;return count}var import_react13,STATE_COLORS;var init_ClaudeView=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react13=__toESM(require_react_development(),1),STATE_COLORS={working:palette.emerald,idle:palette.textDim,running:palette.cyan,spawning:palette.warning,permission:palette.warning,question:palette.warning,error:palette.error}});function TabBar({activeTab,focused,gaps}){let totalGaps=(gaps?.orphanProcesses??0)+(gaps?.orphanPanes??0);return import_jsx_dev_runtime2.jsxDEV("box",{height:1,flexDirection:"row",width:"100%",backgroundColor:palette.bgLight,children:TAB_ORDER.map((tab)=>{let isActive=tab===activeTab,bg2=isActive?palette.violet:palette.bgLight,fg2=isActive?"#ffffff":focused?palette.textDim:palette.textMuted,badge=tab==="claude"&&totalGaps>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:[" ",totalGaps]},void 0,!0,void 0,this):null;return import_jsx_dev_runtime2.jsxDEV("box",{backgroundColor:bg2,paddingX:1,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:fg2,children:[isActive&&focused?">":" ",TAB_LABELS[tab]]},void 0,!0,void 0,this),badge]},void 0,!0,void 0,this)},tab,!1,void 0,this)})},void 0,!1,void 0,this)}var TAB_ORDER,TAB_LABELS;var init_TabBar=__esm(()=>{init_theme2();init_jsx_dev_runtime();TAB_ORDER=["projects","tmux","claude"],TAB_LABELS={projects:"Projects",tmux:"tmux",claude:"Claude"}});function flattenSessions(sessions){return sessions.flatMap((session)=>{let sessionRow={id:`s:${session.name}`,depth:0,label:session.name,color:session.attached?palette.emerald:palette.textDim,detail:`${session.windowCount}w ${session.attached?"(attached)":""}`,detailColor:session.attached?palette.emerald:palette.textMuted,type:"session",sessionName:session.name,isClaude:!1},windowRows=session.windows.flatMap((window2)=>{let winRow={id:`w:${session.name}:${window2.index}`,depth:1,label:`${window2.index}:${window2.name}`,color:window2.active?palette.cyan:palette.text,detail:`${window2.paneCount}p${window2.active?" *":""}`,detailColor:window2.active?palette.cyan:palette.textMuted,type:"window",sessionName:session.name,isClaude:!1},paneRows=window2.panes.map((pane)=>{let isClaude=pane.command==="claude"||pane.title.includes("claude");return{id:`p:${pane.paneId}`,depth:2,label:`${pane.paneId} [${pane.command}]`,color:isClaude?palette.cyan:palette.textDim,detail:`pid:${pane.pid} ${pane.size}`,detailColor:palette.textMuted,type:"pane",sessionName:session.name,isClaude}});return[winRow,...paneRows]});return[sessionRow,...windowRows]})}function TmuxView({sessions,selectedIndex}){let rows=import_react14.useMemo(()=>flattenSessions(sessions),[sessions]),totalPanes=sessions.reduce((sum,s)=>sum+s.windows.reduce((ws,w2)=>ws+w2.panes.length,0),0);return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLighter,children:import_jsx_dev_runtime2.jsxDEV("text",{children:import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[sessions.length,"s ",sessions.reduce((s,x2)=>s+x2.windowCount,0),"w ",totalPanes,"p"]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("scrollbox",{focused:!0,height:"100%",style:{scrollbarOptions:{showArrows:!1,trackOptions:{foregroundColor:palette.scrollThumb,backgroundColor:palette.scrollTrack}}},children:rows.map((row,i2)=>{let indent=" ".repeat(row.depth),icon=row.type==="session"?"\u25C8":row.type==="window"?"\u25A1":"\u2500";return import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",backgroundColor:i2===selectedIndex?palette.violet:void 0,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:indent},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:row.color,children:[icon," ",row.label]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:row.detailColor,children:[" ",row.detail]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},row.id,!1,void 0,this)})},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function getTmuxRowCount(sessions){let count=0;for(let s of sessions){count++;for(let w2 of s.windows)count++,count+=w2.panes.length}return count}var import_react14;var init_TmuxView=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react14=__toESM(require_react_development(),1)});function getNodeIcon(node){switch(node.type){case"org":return icons.org;case"project":return node.expanded?icons.projectOpen:icons.project;case"board":return node.expanded?icons.boardOpen:icons.board;case"column":return icons.column;case"task":if(node.activePanes>0)return icons.taskActive;if(node.data.status==="done")return icons.taskDone;return icons.task;default:return" "}}function getNodeColor(node){switch(node.type){case"org":return palette.purple;case"project":return node.activePanes>0?palette.emerald:palette.textDim;case"board":return palette.violet;case"column":return palette.textMuted;case"task":if(node.activePanes>0)return palette.cyan;if(node.data.status==="done")return palette.emerald;return palette.text;default:return palette.text}}var import_react15,TreeNodeRow;var init_TreeNode=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react15=__toESM(require_react_development(),1),TreeNodeRow=import_react15.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]},void 0,!0,void 0,this)},void 0,!1,void 0,this)})});function Nav({tree,onTreeChange,onProjectSelect}){let[activeTab,setActiveTab]=import_react17.useState("projects"),[tabBarFocused,setTabBarFocused]=import_react17.useState(!1),[diagnostics,setDiagnostics]=import_react17.useState(null),[projectIndex,setProjectIndex]=import_react17.useState(0),[tmuxIndex,setTmuxIndex]=import_react17.useState(0),[claudeIndex,setClaudeIndex]=import_react17.useState(0),flatNodes=import_react17.useMemo(()=>flattenTree(tree),[tree]),diagTimer=import_react17.useRef(null);import_react17.useEffect(()=>{let active=!0;async function refresh(){try{let snap=await collectDiagnostics();if(active)setDiagnostics(snap)}catch(err){console.error("TUI: diagnostics failed:",err)}}return refresh(),diagTimer.current=setInterval(refresh,2000),()=>{if(active=!1,diagTimer.current)clearInterval(diagTimer.current)}},[]);let getRowCount=import_react17.useCallback(()=>{switch(activeTab){case"projects":return flatNodes.length;case"tmux":return diagnostics?getTmuxRowCount(diagnostics.sessions):0;case"claude":return diagnostics?getClaudeRowCount(diagnostics.executors,diagnostics.assignments,diagnostics.gaps):0}},[activeTab,flatNodes,diagnostics]),getSelectedIndex=import_react17.useCallback(()=>{switch(activeTab){case"projects":return projectIndex;case"tmux":return tmuxIndex;case"claude":return claudeIndex}},[activeTab,projectIndex,tmuxIndex,claudeIndex]),setSelectedIndex=import_react17.useCallback((idx)=>{switch(activeTab){case"projects":setProjectIndex(idx);break;case"tmux":setTmuxIndex(idx);break;case"claude":setClaudeIndex(idx);break}},[activeTab]);import_react17.useEffect(()=>{if(activeTab!=="projects"||tabBarFocused)return;let current=flatNodes[projectIndex]?.node;if(!current||current.type!=="project")return;let proj=current.data;if(proj.tmuxSession)onProjectSelect(proj.id,proj.tmuxSession)},[activeTab,tabBarFocused,projectIndex,flatNodes,onProjectSelect]);let handleProjectSelect=import_react17.useCallback((id)=>{let idx=flatNodes.findIndex((n)=>n.node.id===id);if(idx>=0)setProjectIndex(idx)},[flatNodes]),handleToggle=import_react17.useCallback((id)=>{onTreeChange(toggleNode(tree,id))},[tree,onTreeChange]),handleEnter=import_react17.useCallback(()=>{if(activeTab!=="projects")return;let current=flatNodes[projectIndex]?.node;if(!current)return;if(current.type==="project"){let proj=current.data;onProjectSelect(proj.id,proj.tmuxSession)}else if(current.children.length>0)handleToggle(current.id)},[activeTab,flatNodes,projectIndex,onProjectSelect,handleToggle]),handleTabBarKey=import_react17.useCallback((keyName2)=>{if(keyName2==="left"||keyName2==="h"){let idx=(TAB_ORDER.indexOf(activeTab)-1+TAB_ORDER.length)%TAB_ORDER.length;return setActiveTab(TAB_ORDER[idx]),!0}if(keyName2==="right"||keyName2==="l"){let idx=(TAB_ORDER.indexOf(activeTab)+1)%TAB_ORDER.length;return setActiveTab(TAB_ORDER[idx]),!0}if(keyName2==="down"||keyName2==="j")return setTabBarFocused(!1),setSelectedIndex(0),!0;return!1},[activeTab,setSelectedIndex]),handleVerticalNav=import_react17.useCallback((keyName2,selectedIdx,rowCount)=>{if(rowCount===0)return;if(keyName2==="up"||keyName2==="k")setSelectedIndex(selectedIdx===0?rowCount-1:selectedIdx-1);else if(keyName2==="down"||keyName2==="j")setSelectedIndex(selectedIdx>=rowCount-1?0:selectedIdx+1)},[setSelectedIndex]),handleTreeExpand=import_react17.useCallback((keyName2,selectedIdx)=>{if(activeTab!=="projects")return;let node=flatNodes[selectedIdx]?.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)},[activeTab,flatNodes,handleToggle]),handleListKey=import_react17.useCallback((keyName2,selectedIdx,rowCount)=>{if(keyName2==="enter"||keyName2==="return")handleEnter();else if(keyName2==="up"||keyName2==="k"||keyName2==="down"||keyName2==="j")handleVerticalNav(keyName2,selectedIdx,rowCount);else handleTreeExpand(keyName2,selectedIdx)},[handleEnter,handleVerticalNav,handleTreeExpand]);useKeyboard((key)=>{if(tabBarFocused){handleTabBarKey(key.name);return}handleListKey(key.name,getSelectedIndex(),getRowCount())});let gapCounts=diagnostics?{orphanProcesses:diagnostics.gaps.deadPidExecutors.length,orphanPanes:diagnostics.gaps.orphanPanes.length}:void 0;return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",backgroundColor:palette.bg,children:[import_jsx_dev_runtime2.jsxDEV(TabBar,{activeTab,focused:tabBarFocused,gaps:gapCounts},void 0,!1,void 0,this),activeTab==="projects"&&import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",flexGrow:1,children: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:!tabBarFocused&&i2===projectIndex,onSelect:handleProjectSelect,onToggle:handleToggle},flat.node.id,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this),activeTab==="tmux"&&diagnostics&&import_jsx_dev_runtime2.jsxDEV(TmuxView,{sessions:diagnostics.sessions,selectedIndex:tabBarFocused?-1:tmuxIndex},void 0,!1,void 0,this),activeTab==="claude"&&diagnostics&&import_jsx_dev_runtime2.jsxDEV(ClaudeView,{executors:diagnostics.executors,assignments:diagnostics.assignments,gaps:diagnostics.gaps,selectedIndex:tabBarFocused?-1:claudeIndex},void 0,!1,void 0,this),(activeTab==="tmux"||activeTab==="claude")&&!diagnostics&&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:tabBarFocused?"\u2190\u2192:tab \u2193:tree":"\u2191\u2193:nav Enter:select"},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var import_react17;var init_Nav=__esm(async()=>{init_diagnostics();init_theme2();init_ClaudeView();init_TabBar();init_TmuxView();init_TreeNode();init_jsx_dev_runtime();await init_react();import_react17=__toESM(require_react_development(),1)});function App({rightPane}){let renderer=useRenderer(),[tree,setTree]=import_react19.useState([]),[loading,setLoading]=import_react19.useState(!0),[error2,setError]=import_react19.useState(null),[currentProject,setCurrentProject]=import_react19.useState(null),dataRef=import_react19.useRef(null),subRef=import_react19.useRef(null);useKeyboard((key)=>{if(key.name==="q"||key.ctrl&&key.name==="c")renderer.destroy(),cleanup()}),import_react19.useEffect(()=>{let cancelled=!1,onEvent=async()=>{try{let freshData=await loadAll2();if(cancelled)return;dataRef.current=freshData,setTree((prev)=>mergeExpandedState(prev,buildTree(freshData)))}catch(err){console.error("TUI: data refresh failed:",err)}};async function boot(){let data=await loadAll2();if(cancelled)return;dataRef.current=data,setTree(buildTree(data)),setLoading(!1);let sub=await subscribe(onEvent);if(cancelled)return void sub.stop();subRef.current=sub}return boot().catch((err)=>{if(cancelled)return;setError(err instanceof Error?err.message:String(err)),setLoading(!1)}),()=>{cancelled=!0,subRef.current?.stop(),subRef.current=null}},[]);let executorsRef=import_react19.useRef([]),assignmentsRef=import_react19.useRef([]);import_react19.useEffect(()=>{let active=!0;async function refreshActivity(){if(!dataRef.current)return;let activity=await fetchMergedActivity(dataRef.current,executorsRef,assignmentsRef);if(active)setTree((prev)=>applyActivity(prev,activity))}let timer2=setInterval(refreshActivity,1000);return()=>{active=!1,clearInterval(timer2)}},[]);let handleTreeChange=import_react19.useCallback((newTree)=>{setTree(newTree)},[]),handleProjectSelect=import_react19.useCallback((projectId,tmuxSession)=>{if(!tmuxSession||!rightPane)return;if(currentProject===projectId)return;if(currentProject)switchRightPane(rightPane,tmuxSession);else attachProject(rightPane,tmuxSession);setCurrentProject(projectId)},[rightPane,currentProject]);if(loading)return import_jsx_dev_runtime2.jsxDEV("box",{width:"100%",height:"100%",backgroundColor:palette.bg,justifyContent:"center",alignItems:"center",children:import_jsx_dev_runtime2.jsxDEV("text",{fg:palette.purple,children:"Loading..."},void 0,!1,void 0,this)},void 0,!1,void 0,this);if(error2)return import_jsx_dev_runtime2.jsxDEV("box",{width:"100%",height:"100%",backgroundColor:palette.bg,justifyContent:"center",alignItems:"center",children:import_jsx_dev_runtime2.jsxDEV("text",{fg:palette.error,children:error2},void 0,!1,void 0,this)},void 0,!1,void 0,this);return import_jsx_dev_runtime2.jsxDEV(Nav,{tree,onTreeChange:handleTreeChange,onProjectSelect:handleProjectSelect},void 0,!1,void 0,this)}function mergeExpandedState(oldTree,newTree){let expandedIds=new Set;function collect(nodes){for(let n of nodes){if(n.expanded)expandedIds.add(n.id);collect(n.children)}}collect(oldTree);function apply(nodes){return nodes.map((n)=>({...n,expanded:expandedIds.has(n.id)||n.expanded,children:apply(n.children)}))}return apply(newTree)}function scanTmuxActivity(data){let result=new Map;for(let proj of data.projects){if(!proj.tmuxSession)continue;let windows=getActiveWork(proj.tmuxSession);for(let[taskId,act]of matchWorkToTasks(windows,data.tasks))result.set(taskId,act)}return result}async function fetchMergedActivity(data,executorsRef,assignmentsRef){try{let execs=await loadExecutors(),execIds=execs.map((e)=>e.id),assigns=execIds.length>0?await loadAssignments(execIds):[];executorsRef.current=execs,assignmentsRef.current=assigns;let activity=getExecutorActivity(execs,assigns);for(let[taskId,act]of scanTmuxActivity(data))if(!activity.has(taskId))activity.set(taskId,act);return activity}catch{return scanTmuxActivity(data)}}var import_react19;var init_app=__esm(async()=>{init_activity();init_theme2();init_tmux2();init_jsx_dev_runtime();await __promiseAll([init_react(),init_Nav()]);import_react19=__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:execSync15}=await import("child_process");if(options.dev)execSync15(`tmux send-keys -t '${leftPane}' "GENIE_TUI_PANE=left GENIE_TUI_RIGHT=${rightPane} bun --watch ${genieBin} tui" Enter`,{stdio:"ignore"});else execSync15(`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()});var import__=__toESM(require_commander(),1),{program,createCommand,createArgument,createOption,CommanderError,InvalidArgumentError,InvalidOptionArgumentError,Command,Argument,Option,Help}=import__.default;import{existsSync as existsSync3,unlinkSync as unlinkSync2}from"fs";import{homedir as homedir3}from"os";import{join as join3}from"path";var{$:$2}=globalThis.Bun;import{existsSync,unlinkSync}from"fs";import{homedir}from"os";import{join}from"path";var CLAUDE_DIR=join(homedir(),".claude"),CLAUDE_HOOKS_DIR=join(CLAUDE_DIR,"hooks"),CLAUDE_SETTINGS_FILE=join(CLAUDE_DIR,"settings.json"),GENIE_HOOK_SCRIPT_NAME="genie-bash-hook.sh";function getClaudeSettingsPath(){return CLAUDE_SETTINGS_FILE}function getGenieHookScriptPath(){return join(CLAUDE_HOOKS_DIR,GENIE_HOOK_SCRIPT_NAME)}function hookScriptExists(){return existsSync(getGenieHookScriptPath())}function removeHookScript(){let scriptPath=getGenieHookScriptPath();if(existsSync(scriptPath))unlinkSync(scriptPath)}function contractClaudePath(path){let home=homedir();if(path.startsWith(`${home}/`))return`~${path.slice(home.length)}`;if(path===home)return"~";return path}init_genie_config2();var{$}=globalThis.Bun;async function checkCommand(cmd){try{let cmdPath=(await $`which ${cmd}`.quiet().text()).trim();if(!cmdPath)return{exists:!1};let version;try{let firstLine=(await $`${cmd} --version`.quiet().text()).split(`
1902
+ `).map(mapAssignment)}function mapOrg(row){return{id:String(row.id),name:String(row.name),slug:String(row.slug),description:row.description?String(row.description):null,leaderAgent:row.leader_agent?String(row.leader_agent):null}}function mapProject2(row){return{id:String(row.id),name:String(row.name),description:row.description?String(row.description):null,orgId:row.org_id?String(row.org_id):null,leaderAgent:row.leader_agent?String(row.leader_agent):null,tmuxSession:row.tmux_session?String(row.tmux_session):null,repoPath:row.repo_path?String(row.repo_path):null}}function mapBoard2(row){let rawCols=row.columns,columns=Array.isArray(rawCols)?rawCols.map((c,i2)=>({id:String(c.id??`col-${i2}`),name:String(c.name??""),label:String(c.label??c.name??""),color:String(c.color??"#94a3b8"),position:typeof c.position==="number"?c.position:i2})):[];return{id:String(row.id),name:String(row.name),projectId:row.project_id?String(row.project_id):null,description:row.description?String(row.description):null,columns}}function mapTask2(row){return{id:String(row.id),seq:Number(row.seq),title:String(row.title),status:String(row.status),stage:String(row.stage),priority:String(row.priority),projectId:row.project_id?String(row.project_id):null,boardId:row.board_id?String(row.board_id):null,columnId:row.column_id?String(row.column_id):null,description:row.description?String(row.description):null}}function mapAgentProject(row){return{agentName:String(row.agent_name),projectId:String(row.project_id),role:String(row.role)}}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)}}function loadTmuxTree(){let output;try{output=execSync14("tmux list-windows -a -F '#{session_name}|#{window_index}|#{window_name}|#{window_active}|#{window_panes}|#{session_attached}|#{session_windows}'",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return[]}if(!output)return[];let sessionMap=new Map;for(let line of output.split(`
1903
+ `)){if(!line)continue;let[sessName,winIdx,winName,winActive,winPanes,sessAttached,sessWindows]=line.split("|");if(!sessionMap.has(sessName))sessionMap.set(sessName,{name:sessName,attached:sessAttached==="1",windowCount:Number.parseInt(sessWindows,10)||0,windows:[]});sessionMap.get(sessName)?.windows.push({sessionName:sessName,index:Number.parseInt(winIdx,10)||0,name:winName,active:winActive==="1",paneCount:Number.parseInt(winPanes,10)||0})}return Array.from(sessionMap.values()).sort((a,b3)=>a.name.localeCompare(b3.name))}var init_db2=()=>{};import{execSync as execSync15}from"child_process";function execQuiet(cmd){try{return execSync15(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(`
1904
+ `)){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(() => (init_db2(),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 buildTree(data){let orgNodes=[];for(let org of data.orgs){let projectNodes=data.projects.filter((p)=>p.orgId===org.id).map((proj)=>{let boardNodes=data.boards.filter((b3)=>b3.projectId===proj.id).map((board)=>{let columnNodes=(board.columns||[]).sort((a,b3)=>a.position-b3.position).map((col)=>{let colTasks=data.tasks.filter((t2)=>t2.boardId===board.id&&t2.columnId===col.id),taskNodes=colTasks.map((task)=>({id:task.id,type:"task",label:`#${task.seq} ${task.title}`,depth:4,expanded:!1,children:[],data:task,activePanes:0}));return{id:col.id,type:"column",label:`${col.label} (${colTasks.length})`,depth:3,expanded:!1,children:taskNodes,data:col,activePanes:0}});return{id:board.id,type:"board",label:board.name,depth:2,expanded:!1,children:columnNodes,data:board,activePanes:0}});return{id:proj.id,type:"project",label:proj.name,depth:1,expanded:!1,children:boardNodes,data:proj,activePanes:0}});orgNodes.push({id:org.id,type:"org",label:org.name,depth:0,expanded:!0,children:projectNodes,data:org,activePanes:0})}return orgNodes}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)}})}function applyActivity(nodes,activity){return nodes.map((node)=>{let act=activity.get(node.id),updated={...node,activePanes:act?.panes??0,agentState:act?.state,children:applyActivity(node.children,activity)};if(updated.children.some((c)=>c.activePanes>0)&&updated.activePanes===0)updated.activePanes=updated.children.reduce((sum,c)=>sum+c.activePanes,0);return updated})}var import_jsx_dev_runtime2;var init_jsx_dev_runtime=__esm(()=>{import_jsx_dev_runtime2=__toESM(require_react_jsx_dev_runtime_development(),1)});function stateColor(state){return STATE_COLORS[state]??palette.textMuted}function executorDisplayName(exec3){let name=exec3.agentName?`${exec3.agentName}${exec3.team?`@${exec3.team}`:""}`:exec3.agentId;return exec3.role?`${name} (${exec3.role})`:name}function metaRow(exec3){let hasTmux2=!!exec3.tmuxPaneId,isApi=exec3.transport==="api";return{id:`meta:${exec3.id}`,depth:1,label:hasTmux2?`${exec3.tmuxSession??"?"}:${exec3.tmuxPaneId}`:isApi?"api (no tmux)":"tmux: not linked",color:hasTmux2?palette.emerald:isApi?palette.textDim:palette.error,detail:exec3.pid!=null?`pid:${exec3.pid}`:"",detailColor:palette.textMuted,isOrphan:!hasTmux2&&!isApi}}function assignmentRow(exec3,a){let taskLabel=a.taskTitle??a.taskId??"unknown",wishLabel=a.wishSlug?` [${a.wishSlug}]`:"";return{id:`assign:${exec3.id}`,depth:1,label:`\u2192 ${taskLabel}${wishLabel}`,color:palette.purple,detail:a.groupNumber!=null?`grp:${a.groupNumber}`:"",detailColor:palette.textMuted,isOrphan:!1}}function executorToRows(exec3,assignments,isDead){let rows=[{id:`exec:${exec3.id}`,depth:0,label:executorDisplayName(exec3),color:isDead?palette.error:stateColor(exec3.state),detail:isDead?`DEAD pid:${exec3.pid}`:`${exec3.state} ${exec3.provider}`,detailColor:isDead?palette.error:palette.textMuted,isOrphan:isDead},metaRow(exec3)],active=assignments.find((a)=>a.executorId===exec3.id);if(active)rows.push(assignmentRow(exec3,active));return rows}function flattenExecutors(executors,assignments,gaps){let deadIds=new Set(gaps.deadPidExecutors.map((e)=>e.id)),rows=executors.flatMap((exec3)=>executorToRows(exec3,assignments,deadIds.has(exec3.id)));if(gaps.orphanPanes.length>0){rows.push({id:"orphan-header",depth:0,label:`\u2500\u2500 Orphan Panes (${gaps.orphanPanes.length}) \u2500\u2500`,color:palette.warning,detail:"",detailColor:palette.textMuted,isOrphan:!1});for(let pane of gaps.orphanPanes)rows.push({id:`orphan:${pane.paneId}`,depth:1,label:`${pane.sessionName}:${pane.windowIndex}.${pane.paneIndex}`,color:palette.error,detail:`pid:${pane.pid} [${pane.title}]`,detailColor:palette.textMuted,isOrphan:!0})}return rows}function ClaudeView({executors,assignments,gaps,selectedIndex}){let rows=import_react13.useMemo(()=>flattenExecutors(executors,assignments,gaps),[executors,assignments,gaps]);return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLighter,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.emerald,children:[gaps.linkedCount," linked"]},void 0,!0,void 0,this),gaps.deadPidExecutors.length>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:[" ",gaps.deadPidExecutors.length," dead"]},void 0,!0,void 0,this):null,gaps.orphanPanes.length>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.warning,children:[" ",gaps.orphanPanes.length," unmapped"]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("scrollbox",{focused:!0,height:"100%",style:{scrollbarOptions:{showArrows:!1,trackOptions:{foregroundColor:palette.scrollThumb,backgroundColor:palette.scrollTrack}}},children:rows.map((row,i2)=>{let indent=" ".repeat(row.depth),icon=row.depth===0?row.isOrphan?"\u2718":"\u2713":"\u2514";return import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",backgroundColor:i2===selectedIndex?palette.violet:void 0,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:indent},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:row.color,children:[icon," ",row.label]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:row.detailColor,children:[" ",row.detail]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},row.id,!1,void 0,this)})},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function getClaudeRowCount(executors,assignments,gaps){let count=0;for(let exec3 of executors)if(count+=2,assignments.some((a)=>a.executorId===exec3.id))count++;if(gaps.orphanPanes.length>0)count+=1+gaps.orphanPanes.length;return count}var import_react13,STATE_COLORS;var init_ClaudeView=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react13=__toESM(require_react_development(),1),STATE_COLORS={working:palette.emerald,idle:palette.textDim,running:palette.cyan,spawning:palette.warning,permission:palette.warning,question:palette.warning,error:palette.error}});function tabBadge(tab,gaps){if(tab==="claude"){let total=(gaps?.orphanProcesses??0)+(gaps?.orphanPanes??0);return total>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:[" ",total]},void 0,!0,void 0,this):null}if(tab==="tmux"){let dead=gaps?.deadPanes??0;return dead>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:[" ",dead,"\\u2620"]},void 0,!0,void 0,this):null}return null}function TabBar({activeTab,focused,gaps}){return import_jsx_dev_runtime2.jsxDEV("box",{height:1,flexDirection:"row",width:"100%",backgroundColor:palette.bgLight,children:TAB_ORDER.map((tab)=>{let isActive=tab===activeTab,bg2=isActive?palette.violet:palette.bgLight,fg2=isActive?"#ffffff":focused?palette.textDim:palette.textMuted;return import_jsx_dev_runtime2.jsxDEV("box",{backgroundColor:bg2,paddingX:1,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:fg2,children:[isActive&&focused?">":" ",TAB_LABELS[tab]]},void 0,!0,void 0,this),tabBadge(tab,gaps)]},void 0,!0,void 0,this)},tab,!1,void 0,this)})},void 0,!1,void 0,this)}var TAB_ORDER,TAB_LABELS;var init_TabBar=__esm(()=>{init_theme2();init_jsx_dev_runtime();TAB_ORDER=["projects","tmux","claude"],TAB_LABELS={projects:"Projects",tmux:"tmux",claude:"Claude"}});function toPaneRow(pane,sessionName,windowIndex){let isClaude=pane.command==="claude"||pane.title.includes("claude"),color=pane.isDead?palette.textMuted:isClaude?palette.cyan:palette.textDim;return{id:`p:${pane.paneId}`,depth:2,label:pane.isDead?`${pane.paneId} [DEAD]`:`${pane.paneId} [${pane.command}]`,color,detail:`pid:${pane.pid} ${pane.size}`,detailColor:palette.textMuted,type:"pane",sessionName,windowIndex,isClaude,isDead:pane.isDead}}function flattenSessions(sessions){return sessions.flatMap((session)=>{let sessionRow={id:`s:${session.name}`,depth:0,label:session.name,color:session.attached?palette.emerald:palette.textDim,detail:`${session.windowCount}w ${session.attached?"(attached)":""}`,detailColor:session.attached?palette.emerald:palette.textMuted,type:"session",sessionName:session.name,isClaude:!1,isDead:!1},windowRows=session.windows.flatMap((window2)=>{let deadInWindow=window2.panes.filter((p)=>p.isDead).length,winRow={id:`w:${session.name}:${window2.index}`,depth:1,label:`${window2.index}:${window2.name}`,color:window2.active?palette.cyan:palette.text,detail:`${window2.paneCount}p${deadInWindow>0?` ${deadInWindow}\u2620`:""}${window2.active?" *":""}`,detailColor:window2.active?palette.cyan:palette.textMuted,type:"window",sessionName:session.name,windowIndex:window2.index,isClaude:!1,isDead:!1},paneRows=window2.panes.map((pane)=>toPaneRow(pane,session.name,window2.index));return[winRow,...paneRows]});return[sessionRow,...windowRows]})}function TmuxView({sessions,selectedIndex}){let rows=import_react14.useMemo(()=>flattenSessions(sessions),[sessions]),totalPanes=sessions.reduce((sum,s)=>sum+s.windows.reduce((ws,w2)=>ws+w2.panes.length,0),0),deadPanes=sessions.reduce((sum,s)=>sum+s.windows.reduce((ws,w2)=>ws+w2.panes.filter((p)=>p.isDead).length,0),0);return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",children:[import_jsx_dev_runtime2.jsxDEV("box",{height:1,paddingX:1,backgroundColor:palette.bgLighter,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textDim,children:[sessions.length,"s ",sessions.reduce((s,x2)=>s+x2.windowCount,0),"w ",totalPanes,"p"]},void 0,!0,void 0,this),deadPanes>0?import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.error,children:[" ",deadPanes," dead"]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("scrollbox",{focused:!0,height:"100%",style:{scrollbarOptions:{showArrows:!1,trackOptions:{foregroundColor:palette.scrollThumb,backgroundColor:palette.scrollTrack}}},children:rows.map((row,i2)=>{let indent=" ".repeat(row.depth),icon=row.type==="session"?"\u25C8":row.type==="window"?"\u25A1":row.isDead?"\u2718":"\u2500";return import_jsx_dev_runtime2.jsxDEV("box",{height:1,width:"100%",backgroundColor:i2===selectedIndex?palette.violet:void 0,children:import_jsx_dev_runtime2.jsxDEV("text",{children:[import_jsx_dev_runtime2.jsxDEV("span",{fg:palette.textMuted,children:indent},void 0,!1,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:row.color,children:[icon," ",row.label]},void 0,!0,void 0,this),import_jsx_dev_runtime2.jsxDEV("span",{fg:row.detailColor,children:[" ",row.detail]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},row.id,!1,void 0,this)})},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function getTmuxRowCount(sessions){let count=0;for(let s of sessions){count++;for(let w2 of s.windows)count++,count+=w2.panes.length}return count}function getTmuxRowTarget(sessions,index){let row=flattenSessions(sessions)[index];if(!row)return null;return{sessionName:row.sessionName,windowIndex:row.windowIndex}}var import_react14;var init_TmuxView=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react14=__toESM(require_react_development(),1)});function getNodeIcon(node){switch(node.type){case"org":return icons.org;case"project":return node.expanded?icons.projectOpen:icons.project;case"board":return node.expanded?icons.boardOpen:icons.board;case"column":return icons.column;case"task":if(node.activePanes>0)return icons.taskActive;if(node.data.status==="done")return icons.taskDone;return icons.task;default:return" "}}function getNodeColor(node){switch(node.type){case"org":return palette.purple;case"project":return node.activePanes>0?palette.emerald:palette.textDim;case"board":return palette.violet;case"column":return palette.textMuted;case"task":if(node.activePanes>0)return palette.cyan;if(node.data.status==="done")return palette.emerald;return palette.text;default:return palette.text}}var import_react15,TreeNodeRow;var init_TreeNode=__esm(()=>{init_theme2();init_jsx_dev_runtime();import_react15=__toESM(require_react_development(),1),TreeNodeRow=import_react15.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]},void 0,!0,void 0,this)},void 0,!1,void 0,this)})});function Nav({tree,onTreeChange,onProjectSelect,onTmuxSessionSelect}){let[activeTab,setActiveTab]=import_react17.useState("projects"),[tabBarFocused,setTabBarFocused]=import_react17.useState(!1),[diagnostics,setDiagnostics]=import_react17.useState(null),[projectIndex,setProjectIndex]=import_react17.useState(0),[tmuxIndex,setTmuxIndex]=import_react17.useState(0),[claudeIndex,setClaudeIndex]=import_react17.useState(0),flatNodes=import_react17.useMemo(()=>flattenTree(tree),[tree]),diagTimer=import_react17.useRef(null);import_react17.useEffect(()=>{let active=!0;async function refresh(){try{let snap=await collectDiagnostics();if(active)setDiagnostics(snap)}catch(err){console.error("TUI: diagnostics failed:",err)}}return refresh(),diagTimer.current=setInterval(refresh,2000),()=>{if(active=!1,diagTimer.current)clearInterval(diagTimer.current)}},[]);let getRowCount=import_react17.useCallback(()=>{switch(activeTab){case"projects":return flatNodes.length;case"tmux":return diagnostics?getTmuxRowCount(diagnostics.sessions):0;case"claude":return diagnostics?getClaudeRowCount(diagnostics.executors,diagnostics.assignments,diagnostics.gaps):0}},[activeTab,flatNodes,diagnostics]),getSelectedIndex=import_react17.useCallback(()=>{switch(activeTab){case"projects":return projectIndex;case"tmux":return tmuxIndex;case"claude":return claudeIndex}},[activeTab,projectIndex,tmuxIndex,claudeIndex]),setSelectedIndex=import_react17.useCallback((idx)=>{switch(activeTab){case"projects":setProjectIndex(idx);break;case"tmux":setTmuxIndex(idx);break;case"claude":setClaudeIndex(idx);break}},[activeTab]);import_react17.useEffect(()=>{if(activeTab!=="projects"||tabBarFocused)return;let current=flatNodes[projectIndex]?.node;if(!current||current.type!=="project")return;let proj=current.data;if(proj.tmuxSession)onProjectSelect(proj.id,proj.tmuxSession)},[activeTab,tabBarFocused,projectIndex,flatNodes,onProjectSelect]);let handleProjectSelect=import_react17.useCallback((id)=>{let idx=flatNodes.findIndex((n)=>n.node.id===id);if(idx>=0)setProjectIndex(idx)},[flatNodes]),handleToggle=import_react17.useCallback((id)=>{onTreeChange(toggleNode(tree,id))},[tree,onTreeChange]),handleEnter=import_react17.useCallback(()=>{if(activeTab==="projects"){let current=flatNodes[projectIndex]?.node;if(!current)return;if(current.type==="project"){let proj=current.data;onProjectSelect(proj.id,proj.tmuxSession)}else if(current.children.length>0)handleToggle(current.id)}else if(activeTab==="tmux"&&diagnostics&&onTmuxSessionSelect){let target=getTmuxRowTarget(diagnostics.sessions,tmuxIndex);if(target)onTmuxSessionSelect(target.sessionName,target.windowIndex)}},[activeTab,flatNodes,projectIndex,onProjectSelect,handleToggle,diagnostics,tmuxIndex,onTmuxSessionSelect]),handleTabBarKey=import_react17.useCallback((keyName2)=>{if(keyName2==="left"||keyName2==="h"){let idx=(TAB_ORDER.indexOf(activeTab)-1+TAB_ORDER.length)%TAB_ORDER.length;return setActiveTab(TAB_ORDER[idx]),!0}if(keyName2==="right"||keyName2==="l"){let idx=(TAB_ORDER.indexOf(activeTab)+1)%TAB_ORDER.length;return setActiveTab(TAB_ORDER[idx]),!0}if(keyName2==="down"||keyName2==="j")return setTabBarFocused(!1),setSelectedIndex(0),!0;return!1},[activeTab,setSelectedIndex]),handleVerticalNav=import_react17.useCallback((keyName2,selectedIdx,rowCount)=>{if(rowCount===0)return;if(keyName2==="up"||keyName2==="k")setSelectedIndex(selectedIdx===0?rowCount-1:selectedIdx-1);else if(keyName2==="down"||keyName2==="j")setSelectedIndex(selectedIdx>=rowCount-1?0:selectedIdx+1)},[setSelectedIndex]),handleTreeExpand=import_react17.useCallback((keyName2,selectedIdx)=>{if(activeTab!=="projects")return;let node=flatNodes[selectedIdx]?.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)},[activeTab,flatNodes,handleToggle]),handleListKey=import_react17.useCallback((keyName2,selectedIdx,rowCount)=>{if(keyName2==="enter"||keyName2==="return")handleEnter();else if(keyName2==="up"||keyName2==="k"||keyName2==="down"||keyName2==="j")handleVerticalNav(keyName2,selectedIdx,rowCount);else handleTreeExpand(keyName2,selectedIdx)},[handleEnter,handleVerticalNav,handleTreeExpand]);useKeyboard((key)=>{if(tabBarFocused){handleTabBarKey(key.name);return}handleListKey(key.name,getSelectedIndex(),getRowCount())});let gapCounts=diagnostics?{orphanProcesses:diagnostics.gaps.deadPidExecutors.length,orphanPanes:diagnostics.gaps.orphanPanes.length,deadPanes:diagnostics.gaps.deadPaneCount}:void 0;return import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",width:"100%",height:"100%",backgroundColor:palette.bg,children:[import_jsx_dev_runtime2.jsxDEV(TabBar,{activeTab,focused:tabBarFocused,gaps:gapCounts},void 0,!1,void 0,this),activeTab==="projects"&&import_jsx_dev_runtime2.jsxDEV("box",{flexDirection:"column",flexGrow:1,children: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:!tabBarFocused&&i2===projectIndex,onSelect:handleProjectSelect,onToggle:handleToggle},flat.node.id,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this),activeTab==="tmux"&&diagnostics&&import_jsx_dev_runtime2.jsxDEV(TmuxView,{sessions:diagnostics.sessions,selectedIndex:tabBarFocused?-1:tmuxIndex},void 0,!1,void 0,this),activeTab==="claude"&&diagnostics&&import_jsx_dev_runtime2.jsxDEV(ClaudeView,{executors:diagnostics.executors,assignments:diagnostics.assignments,gaps:diagnostics.gaps,selectedIndex:tabBarFocused?-1:claudeIndex},void 0,!1,void 0,this),(activeTab==="tmux"||activeTab==="claude")&&!diagnostics&&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:tabBarFocused?"\u2190\u2192:tab \u2193:tree":"\u2191\u2193:nav Enter:select"},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var import_react17;var init_Nav=__esm(async()=>{init_diagnostics();init_theme2();init_ClaudeView();init_TabBar();init_TmuxView();init_TreeNode();init_jsx_dev_runtime();await init_react();import_react17=__toESM(require_react_development(),1)});function App({rightPane}){let renderer=useRenderer(),[tree,setTree]=import_react19.useState([]),[loading,setLoading]=import_react19.useState(!0),[error2,setError]=import_react19.useState(null),[currentProject,setCurrentProject]=import_react19.useState(null),dataRef=import_react19.useRef(null),subRef=import_react19.useRef(null);useKeyboard((key)=>{if(key.name==="q"||key.ctrl&&key.name==="c")renderer.destroy(),cleanup()}),import_react19.useEffect(()=>{let cancelled=!1,onEvent=async()=>{try{let freshData=await loadAll2();if(cancelled)return;dataRef.current=freshData,setTree((prev)=>mergeExpandedState(prev,buildTree(freshData)))}catch(err){console.error("TUI: data refresh failed:",err)}};async function boot(){let data=await loadAll2();if(cancelled)return;dataRef.current=data,setTree(buildTree(data)),setLoading(!1);let sub=await subscribe(onEvent);if(cancelled)return void sub.stop();subRef.current=sub}return boot().catch((err)=>{if(cancelled)return;setError(err instanceof Error?err.message:String(err)),setLoading(!1)}),()=>{cancelled=!0,subRef.current?.stop(),subRef.current=null}},[]);let executorsRef=import_react19.useRef([]),assignmentsRef=import_react19.useRef([]);import_react19.useEffect(()=>{let active=!0;async function refreshActivity(){if(!dataRef.current)return;let activity=await fetchMergedActivity(dataRef.current,executorsRef,assignmentsRef);if(active)setTree((prev)=>applyActivity(prev,activity))}let timer2=setInterval(refreshActivity,1000);return()=>{active=!1,clearInterval(timer2)}},[]);let handleTreeChange=import_react19.useCallback((newTree)=>{setTree(newTree)},[]),handleProjectSelect=import_react19.useCallback((projectId,tmuxSession)=>{if(!tmuxSession||!rightPane)return;if(currentProject===projectId)return;if(currentProject)switchRightPane(rightPane,tmuxSession);else attachProject(rightPane,tmuxSession);setCurrentProject(projectId)},[rightPane,currentProject]),handleTmuxSessionSelect=import_react19.useCallback((sessionName,windowIndex)=>{if(!rightPane)return;attachProjectWindow(rightPane,sessionName,windowIndex)},[rightPane]);if(loading)return import_jsx_dev_runtime2.jsxDEV("box",{width:"100%",height:"100%",backgroundColor:palette.bg,justifyContent:"center",alignItems:"center",children:import_jsx_dev_runtime2.jsxDEV("text",{fg:palette.purple,children:"Loading..."},void 0,!1,void 0,this)},void 0,!1,void 0,this);if(error2)return import_jsx_dev_runtime2.jsxDEV("box",{width:"100%",height:"100%",backgroundColor:palette.bg,justifyContent:"center",alignItems:"center",children:import_jsx_dev_runtime2.jsxDEV("text",{fg:palette.error,children:error2},void 0,!1,void 0,this)},void 0,!1,void 0,this);return import_jsx_dev_runtime2.jsxDEV(Nav,{tree,onTreeChange:handleTreeChange,onProjectSelect:handleProjectSelect,onTmuxSessionSelect:handleTmuxSessionSelect},void 0,!1,void 0,this)}function mergeExpandedState(oldTree,newTree){let expandedIds=new Set;function collect(nodes){for(let n of nodes){if(n.expanded)expandedIds.add(n.id);collect(n.children)}}collect(oldTree);function apply(nodes){return nodes.map((n)=>({...n,expanded:expandedIds.has(n.id)||n.expanded,children:apply(n.children)}))}return apply(newTree)}function scanTmuxActivity(data){let result=new Map;for(let proj of data.projects){if(!proj.tmuxSession)continue;let windows=getActiveWork(proj.tmuxSession);for(let[taskId,act]of matchWorkToTasks(windows,data.tasks))result.set(taskId,act)}return result}async function fetchMergedActivity(data,executorsRef,assignmentsRef){try{let execs=await loadExecutors(),execIds=execs.map((e)=>e.id),assigns=execIds.length>0?await loadAssignments(execIds):[];executorsRef.current=execs,assignmentsRef.current=assigns;let activity=getExecutorActivity(execs,assigns);for(let[taskId,act]of scanTmuxActivity(data))if(!activity.has(taskId))activity.set(taskId,act);return activity}catch{return scanTmuxActivity(data)}}var import_react19;var init_app=__esm(async()=>{init_activity();init_db2();init_theme2();init_tmux2();init_jsx_dev_runtime();await __promiseAll([init_react(),init_Nav()]);import_react19=__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:execSync16}=await import("child_process");if(options.dev)execSync16(`tmux send-keys -t '${leftPane}' "GENIE_TUI_PANE=left GENIE_TUI_RIGHT=${rightPane} bun --watch ${genieBin} tui" Enter`,{stdio:"ignore"});else execSync16(`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()});var import__=__toESM(require_commander(),1),{program,createCommand,createArgument,createOption,CommanderError,InvalidArgumentError,InvalidOptionArgumentError,Command,Argument,Option,Help}=import__.default;import{existsSync as existsSync3,unlinkSync as unlinkSync2}from"fs";import{homedir as homedir3}from"os";import{join as join3}from"path";var{$:$2}=globalThis.Bun;import{existsSync,unlinkSync}from"fs";import{homedir}from"os";import{join}from"path";var CLAUDE_DIR=join(homedir(),".claude"),CLAUDE_HOOKS_DIR=join(CLAUDE_DIR,"hooks"),CLAUDE_SETTINGS_FILE=join(CLAUDE_DIR,"settings.json"),GENIE_HOOK_SCRIPT_NAME="genie-bash-hook.sh";function getClaudeSettingsPath(){return CLAUDE_SETTINGS_FILE}function getGenieHookScriptPath(){return join(CLAUDE_HOOKS_DIR,GENIE_HOOK_SCRIPT_NAME)}function hookScriptExists(){return existsSync(getGenieHookScriptPath())}function removeHookScript(){let scriptPath=getGenieHookScriptPath();if(existsSync(scriptPath))unlinkSync(scriptPath)}function contractClaudePath(path){let home=homedir();if(path.startsWith(`${home}/`))return`~${path.slice(home.length)}`;if(path===home)return"~";return path}init_genie_config2();var{$}=globalThis.Bun;async function checkCommand(cmd){try{let cmdPath=(await $`which ${cmd}`.quiet().text()).trim();if(!cmdPath)return{exists:!1};let version;try{let firstLine=(await $`${cmd} --version`.quiet().text()).split(`
1904
1905
  `)[0].trim(),versionMatch=firstLine.match(/(\d+\.[\d.]+[a-z0-9-]*)/i);version=versionMatch?versionMatch[1]:firstLine.slice(0,50)}catch{try{let firstLine=(await $`${cmd} -v`.quiet().text()).split(`
1905
1906
  `)[0].trim(),versionMatch=firstLine.match(/(\d+\.[\d.]+[a-z0-9-]*)/i);version=versionMatch?versionMatch[1]:firstLine.slice(0,50)}catch{}}return{exists:!0,version,path:cmdPath}}catch{return{exists:!1}}}function printSectionHeader(title){console.log(),console.log(`\x1B[1m${title}:\x1B[0m`)}function printCheckResult(result){let icon={pass:"\x1B[32m\u2713\x1B[0m",fail:"\x1B[31m\u2717\x1B[0m",warn:"\x1B[33m!\x1B[0m"}[result.status],message=result.message?` ${result.message}`:"";if(console.log(` ${icon} ${result.name}${message}`),result.suggestion)console.log(` \x1B[2m${result.suggestion}\x1B[0m`)}async function checkPrerequisites(){let results=[],tmuxCheck=await checkCommand("tmux");if(tmuxCheck.exists)results.push({name:"tmux",status:"pass",message:tmuxCheck.version||""});else results.push({name:"tmux",status:"fail",suggestion:"Install with: brew install tmux (or apt install tmux)"});let jqCheck=await checkCommand("jq");if(jqCheck.exists)results.push({name:"jq",status:"pass",message:jqCheck.version||""});else results.push({name:"jq",status:"fail",suggestion:"Install with: brew install jq (or apt install jq)"});let bunCheck=await checkCommand("bun");if(bunCheck.exists)results.push({name:"bun",status:"pass",message:bunCheck.version||""});else results.push({name:"bun",status:"fail",suggestion:"Install with: curl -fsSL https://bun.sh/install | bash"});let claudeCheck=await checkCommand("claude");if(claudeCheck.exists)results.push({name:"Claude Code",status:"pass",message:claudeCheck.version||""});else results.push({name:"Claude Code",status:"warn",suggestion:"Install with: npm install -g @anthropic-ai/claude-code"});return results}async function checkConfiguration(){let results=[];if(genieConfigExists())results.push({name:"Genie config exists",status:"pass",message:contractClaudePath(getGenieConfigPath())});else results.push({name:"Genie config exists",status:"warn",message:"not found",suggestion:"Run: genie setup"});if(isSetupComplete())results.push({name:"Setup complete",status:"pass"});else results.push({name:"Setup complete",status:"warn",message:"not completed",suggestion:"Run: genie setup"});let claudeSettingsPath=getClaudeSettingsPath();if(existsSync3(claudeSettingsPath))results.push({name:"Claude settings exists",status:"pass",message:contractClaudePath(claudeSettingsPath)});else results.push({name:"Claude settings exists",status:"warn",message:"not found",suggestion:"Claude Code creates this on first run"});return results}async function checkTmux(){let results=[];try{if((await $2`tmux list-sessions 2>/dev/null`.quiet()).exitCode===0)results.push({name:"Server running",status:"pass"});else return results.push({name:"Server running",status:"warn",message:"no sessions",suggestion:"Start with: tmux new-session -d -s genie"}),results}catch{return results.push({name:"Server running",status:"warn",message:"could not check"}),results}let sessionName=(await loadGenieConfig()).session.name;try{if((await $2`tmux has-session -t ${sessionName} 2>/dev/null`.quiet()).exitCode===0)results.push({name:`Session '${sessionName}' exists`,status:"pass"});else results.push({name:`Session '${sessionName}' exists`,status:"warn",suggestion:`Start with: tmux new-session -d -s ${sessionName}`})}catch{results.push({name:`Session '${sessionName}' exists`,status:"warn",message:"could not check"})}return results}async function checkWorkerProfiles(){let results=[];if(!genieConfigExists())return results.push({name:"Worker profiles",status:"warn",message:"no genie config",suggestion:"Run: genie setup"}),results;let config=await loadGenieConfig(),profiles=config.workerProfiles;if(!profiles||Object.keys(profiles).length===0)return results.push({name:"Worker profiles",status:"pass",message:"none configured (using defaults)"}),results;let totalProfiles=Object.keys(profiles).length;results.push({name:"Profiles configured",status:"pass",message:`${totalProfiles} profile${totalProfiles===1?"":"s"}`});for(let name of Object.keys(profiles))results.push({name:`Profile '${name}'`,status:"pass",message:"claude (direct)"});if(config.defaultWorkerProfile)if(profiles[config.defaultWorkerProfile])results.push({name:"Default profile",status:"pass",message:config.defaultWorkerProfile});else results.push({name:"Default profile",status:"warn",message:`'${config.defaultWorkerProfile}' not found`,suggestion:"Run: genie profiles default <profile>"});return results}function runCheckSection(label,results,counts){printSectionHeader(label);for(let result of results){if(printCheckResult(result),result.status==="fail")counts.errors=!0;if(result.status==="warn")counts.warnings=!0}}async function doctorCommand(options){if(options?.fix){await doctorFix();return}console.log(),console.log("\x1B[1mGenie Doctor\x1B[0m"),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`);let counts={errors:!1,warnings:!1};if(runCheckSection("Prerequisites",await checkPrerequisites(),counts),runCheckSection("Configuration",await checkConfiguration(),counts),runCheckSection("Tmux",await checkTmux(),counts),runCheckSection("Worker Profiles",await checkWorkerProfiles(),counts),console.log(),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m`),counts.errors)console.log("\x1B[31mSome checks failed.\x1B[0m Run \x1B[36mgenie setup\x1B[0m to fix.");else if(counts.warnings)console.log("\x1B[33mSome warnings detected.\x1B[0m Everything should still work.");else console.log("\x1B[32mAll checks passed!\x1B[0m");if(console.log(),counts.errors)process.exit(1)}async function doctorFix(){console.log(`
1906
1907
  \x1B[1mGenie Doctor \u2014 Auto Fix\x1B[0m`),console.log(`\x1B[2m${"\u2500".repeat(40)}\x1B[0m
@@ -2459,4 +2460,4 @@ Valid columns for board "${board.name}": ${validCols}`),process.exit(1)}catch{}}
2459
2460
  Board: ${board.name} (${board.id})`),console.log("\u2550".repeat(40));let columns=[...board.columns].sort((a,b2)=>a.position-b2.position);for(let col of columns)printColumnTasks(col.label,tasks.filter((t)=>t.columnId===col.id));let columnIds=new Set(columns.map((c)=>c.id)),orphaned=tasks.filter((t)=>t.columnId&&!columnIds.has(t.columnId));if(orphaned.length>0)printColumnTasks("Orphaned",orphaned,!1);console.log("")}function parseGhRef(gh){let match=gh.match(/^([^#]+)#(\d+)$/);if(!match)console.error(`Error: Invalid --gh format. Expected owner/repo#N, got: ${gh}`),process.exit(1);let[,ownerRepo,num]=match;return{externalId:`${ownerRepo}#${num}`,externalUrl:`https://github.com/${ownerRepo}/issues/${num}`}}async function handleTaskCreate(title,options){let ts3=await getTaskService8(),actor=currentActor2(),repoPath,projectId;if(options.project){let project=await ts3.getProjectByName(options.project);if(!project)project=await ts3.createProject({name:options.project});projectId=project.id,repoPath=project.repoPath??void 0}let parentId;if(options.parent){if(parentId=await ts3.resolveTaskId(options.parent,repoPath)??void 0,!parentId)console.error(`Error: Parent task not found: ${options.parent}`),process.exit(1)}let boardId=await resolveBoardOption(options.board),externalId=options.externalId,externalUrl=options.externalUrl;if(options.gh){let parsed=parseGhRef(options.gh);externalId=parsed.externalId,externalUrl=parsed.externalUrl}let task=await ts3.createTask({title,typeId:options.type,priority:options.priority,dueDate:options.due,startDate:options.start,parentId,description:options.description,estimatedEffort:options.effort,boardId,externalId,externalUrl},repoPath,projectId);if(await ts3.assignTask(task.id,actor,"creator",{},task.repoPath),options.assign)await ts3.assignTask(task.id,localActor2(options.assign),"assignee",{},task.repoPath);if(options.tags){let tagIds=options.tags.split(",").map((t)=>t.trim());await ts3.tagTask(task.id,tagIds,actor,task.repoPath)}if(options.comment)await ts3.commentOnTask(task.id,actor,options.comment,task.repoPath);if(console.log(`Created task #${task.seq}: ${task.title}`),console.log(` ID: ${task.id}`),console.log(` Stage: ${task.stage} | Priority: ${task.priority}`),options.due)console.log(` Due: ${options.due}`)}async function handleCloseMerged(options){let result=await(await getCloseMergedService()).closeMergedTasks({since:options.since,dryRun:options.dryRun,repo:options.repo});if(options.dryRun)console.log("[dry-run] Would close:");for(let d of result.details){let prefix=options.dryRun?" [dry-run]":" \u2713";console.log(`${prefix} #${d.taskSeq} "${d.taskTitle}" \u2190 PR #${d.prNumber} (${d.slug})`)}let mode=options.dryRun?"[dry-run] ":"";console.log(`
2460
2461
  ${mode}Closed ${result.closed} task${result.closed===1?"":"s"} from ${result.prsScanned} merged PR${result.prsScanned===1?"":"s"} (${result.alreadyShipped} already shipped)`)}function registerTaskCommands(program2){let task=program2.command("task").description("Task lifecycle management");task.command("create <title>").description("Create a new task").option("--type <type>","Task type","software").option("--priority <priority>","Priority: urgent, high, normal, low","normal").option("--due <date>","Due date (YYYY-MM-DD)").option("--start <date>","Start date (YYYY-MM-DD)").option("--tags <tags>","Comma-separated tag IDs").option("--parent <id>","Parent task ID or #seq").option("--assign <name>","Assign to local actor").option("--description <text>","Task description").option("--effort <effort>",'Estimated effort (e.g., "2h", "3 points")').option("--comment <msg>","Initial comment on the task").option("--project <name>","Create task in a specific project (overrides CWD)").option("--board <name>","Board name to assign task to").option("--gh <owner/repo#N>","Link to GitHub issue (sets external_id + external_url)").option("--external-id <id>","External tracker ID (e.g., JIRA-123)").option("--external-url <url>","External tracker URL").action(async(title,options)=>{try{await handleTaskCreate(title,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("list").description("List tasks with filters").option("--stage <stage>","Filter by stage").option("--type <type>","Filter by type").option("--status <status>","Filter by status").option("--priority <priority>","Filter by priority").option("--release <release>","Filter by release").option("--due-before <date>","Filter by due date").option("--mine","Show only tasks assigned to me").option("--project <name>","Show tasks for a specific project").option("--board <name>","Filter by board name").option("--gh <owner/repo#N>","Filter by GitHub issue link").option("--by-column","Group tasks by board column (kanban view)").option("--include-done","Include done tasks in kanban view (hidden by default)").option("--all","Show tasks from ALL projects").option("--limit <n>","Max number of tasks to return","100").option("--offset <n>","Skip first N tasks (for pagination)","0").option("--json","Output as JSON").action(async(options)=>{try{let ts3=await getTaskService8(),filters={stage:options.stage,typeId:options.type,status:options.status,priority:options.priority,releaseId:options.release,dueBefore:options.dueBefore,projectName:options.project,boardName:options.board,externalId:options.gh?parseGhRef(options.gh).externalId:void 0,allProjects:options.all,limit:Number(options.limit)||100,offset:Number(options.offset)||0,...options.all?{limit:1e4}:{}},tasks;if(options.mine)tasks=await ts3.listTasksForActor(currentActor2(),filters);else tasks=await ts3.listTasks(filters);if(options.byColumn){if(!options.board)console.error("Error: --by-column requires --board"),process.exit(1);if(!options.includeDone)tasks=tasks.filter((t)=>t.status!=="done");await printByColumn(tasks,options.board);return}if(options.json){console.log(JSON.stringify(tasks,null,2));return}printTaskList(tasks,options.all)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("show <id>").description("Show task detail (accepts task-id or #seq)").option("--json","Output as JSON").action(async(id,options)=>{try{let t=await(await getTaskService8()).getTask(id);if(!t)console.error(`Error: Task not found: ${id}`),process.exit(1);if(options.json){console.log(JSON.stringify(t,null,2));return}await printTaskDetail(t)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("link <id>").description("Link task to an external tracker (GitHub, Jira, etc.)").option("--gh <owner/repo#N>","Link to GitHub issue").option("--external-id <id>","External tracker ID").option("--external-url <url>","External tracker URL").action(async(id,options)=>{try{let ts3=await getTaskService8(),externalId,externalUrl;if(options.gh){let parsed=parseGhRef(options.gh);externalId=parsed.externalId,externalUrl=parsed.externalUrl}else if(options.externalId&&options.externalUrl)externalId=options.externalId,externalUrl=options.externalUrl;else console.error("Error: Provide --gh or both --external-id and --external-url."),process.exit(1);let t=await ts3.linkTask(id,externalId,externalUrl);if(!t)console.error(`Error: Task not found: ${id}`),process.exit(1);if(console.log(`Linked task #${t.seq} to ${externalId}`),t.externalUrl)console.log(` URL: ${t.externalUrl}`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("move <id>").description("Move task to a new stage").requiredOption("--to <stage>","Target stage").option("--comment <msg>","Comment on the move").action(async(id,options)=>{try{let ts3=await getTaskService8(),actor=currentActor2(),t=await ts3.moveTask(id,options.to,actor,options.comment);console.log(`Moved task #${t.seq} to stage "${t.stage}".`)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);if(message.includes("Invalid stage"))await handleInvalidStageError(id,message);console.error(`Error: ${message}`),process.exit(1)}}),task.command("assign <id>").description("Assign an actor to a task").requiredOption("--to <name>","Actor name").option("--role <role>","Actor role","assignee").option("--comment <msg>","Comment on the assignment").action(async(id,options)=>{try{let ts3=await getTaskService8(),actor=currentActor2();if(await ts3.assignTask(id,localActor2(options.to),options.role,{}),options.comment)await ts3.commentOnTask(id,actor,options.comment);console.log(`Assigned "${options.to}" as ${options.role??"assignee"} on task ${id}.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("tag <id> <tags...>").description("Add tags to a task").action(async(id,tags)=>{try{await(await getTaskService8()).tagTask(id,tags,currentActor2()),console.log(`Tagged task ${id} with: ${tags.join(", ")}`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("comment <id> <message>").description("Add a comment to a task").option("--reply-to <msgId>","Reply to a specific message ID").action(async(id,message,options)=>{try{let ts3=await getTaskService8(),replyTo=options.replyTo?Number(options.replyTo):void 0,msg=await ts3.commentOnTask(id,currentActor2(),message,void 0,replyTo);console.log(`Comment #${msg.id} added to task ${id}.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("block <id>").description("Mark task as blocked").requiredOption("--reason <reason>","Reason for blocking").option("--comment <msg>","Additional comment").action(async(id,options)=>{try{let ts3=await getTaskService8(),actor=currentActor2(),t=await ts3.blockTask(id,options.reason,actor,options.comment);console.log(`Task #${t.seq} blocked: ${options.reason}`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("unblock <id>").description("Unblock a task").option("--comment <msg>","Comment on unblock").action(async(id,options)=>{try{let ts3=await getTaskService8(),actor=currentActor2(),t=await ts3.unblockTask(id,actor,options.comment);console.log(`Task #${t.seq} unblocked.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("done <id>").description("Mark task as done").option("--comment <msg>","Comment on completion").action(async(id,options)=>{try{let ts3=await getTaskService8(),actor=currentActor2(),t=await ts3.markDone(id,actor,options.comment);console.log(`Task #${t.seq} marked as done.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("checkout <id>").description("Atomically claim a task for execution").action(async(id)=>{try{let ts3=await getTaskService8(),runId=getRunId(),t=await ts3.checkoutTask(id,runId);console.log(`Checked out task #${t.seq} for run: ${runId}`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("release <id>").description("Release task checkout claim").action(async(id)=>{try{let ts3=await getTaskService8(),runId=getRunId(),t=await ts3.releaseTask(id,runId);console.log(`Released task #${t.seq} from run: ${runId}`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("unlock <id>").description("Force-release a stale checkout (admin override)").action(async(id)=>{try{let t=await(await getTaskService8()).forceUnlockTask(id);console.log(`Force-unlocked task #${t.seq}.`)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("close-merged").description("Auto-close tasks whose wish slugs match recently merged PRs").option("--since <duration>",'Time window for merged PRs (e.g., "24h", "7d")',"24h").option("--dry-run","Show what would be closed without acting").option("--repo <owner/repo>","Override GitHub remote detection").action(async(options)=>{try{await handleCloseMerged(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),task.command("dep <id>").description("Manage task dependencies").option("--depends-on <id2>","This task depends on id2").option("--blocks <id2>","This task blocks id2").option("--relates-to <id2>","This task relates to id2").option("--remove <id2>","Remove dependency on id2").action(async(id,options)=>{try{let ts3=await getTaskService8();if(options.remove){if(await ts3.removeDependency(id,options.remove))console.log(`Removed dependency between ${id} and ${options.remove}.`);else console.log("No dependency found to remove.");return}if(options.dependsOn)await ts3.addDependency(id,options.dependsOn,"depends_on"),console.log(`${id} now depends on ${options.dependsOn}.`);if(options.blocks)await ts3.addDependency(id,options.blocks,"blocks"),console.log(`${id} now blocks ${options.blocks}.`);if(options.relatesTo)await ts3.addDependency(id,options.relatesTo,"relates_to"),console.log(`${id} now relates to ${options.relatesTo}.`);if(!options.dependsOn&&!options.blocks&&!options.relatesTo)console.error("Error: Specify --depends-on, --blocks, --relates-to, or --remove."),process.exit(1)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_team_manager();import{existsSync as existsSync32}from"fs";import{copyFile as copyFile2,cp as cp2,mkdir as mkdir8}from"fs/promises";import{join as join43,resolve as resolve6}from"path";function registerTeamNamespace(program2){let team=program2.command("team").description("Team lifecycle management");team.command("create <name>").description("Create a new team with a git worktree").requiredOption("--repo <path>","Path to the git repository").option("--branch <branch>","Base branch to create from","dev").option("--wish <slug>","Wish slug \u2014 auto-spawns a task leader with wish context").option("--session <name>","Tmux session name (avoids session explosion on parallel creates)").option("--no-spawn","Create team and copy wish without spawning the leader (useful for testing)").action(async(name,options)=>{try{await handleTeamCreate(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}),team.command("hire <agent>").description('Add an agent to a team ("council" hires all 10 council members)').option("--team <name>","Team name (auto-detects from leader context if omitted)").action(async(agent,options)=>{try{let teamName=options.team??await autoDetectTeam();if(!teamName)console.error("Error: Could not detect team. Use --team <name> to specify."),process.exit(1);let added=await hireAgent(teamName,agent);if(added.length===0)console.log(`Agent "${agent}" is already a member of "${teamName}".`);else if(agent==="council"){console.log(`Hired ${added.length} council members to "${teamName}":`);for(let name of added)console.log(` + ${name}`)}else console.log(`Hired "${agent}" to team "${teamName}".`)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}),team.command("fire <agent>").description("Remove an agent from a team").option("--team <name>","Team name (auto-detects from leader context if omitted)").action(async(agent,options)=>{try{let teamName=options.team??await autoDetectTeam();if(!teamName)console.error("Error: Could not detect team. Use --team <name> to specify."),process.exit(1);if(await fireAgent(teamName,agent))console.log(`Fired "${agent}" from team "${teamName}".`);else console.error(`Agent "${agent}" is not a member of "${teamName}".`),process.exit(1)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}),team.command("ls [name]").alias("list").description("List teams or members of a team").option("--json","Output as JSON").action(async(name,options)=>{try{if(name)await printMembers(name,options.json);else await printTeams(options.json)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}),team.command("disband <name>").description("Disband a team: kill members, remove worktree, delete config").action(async(name)=>{try{if(await disbandTeam(name))console.log(`Team "${name}" disbanded.`);else console.error(`Team "${name}" not found.`),process.exit(1)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}),team.command("done <name>").description("Mark a team as done and kill all members").action(async(name)=>{try{await setTeamStatus(name,"done"),await killTeamMembers(name),console.log(`Team "${name}" marked as done. All members killed.`)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}),team.command("blocked <name>").description("Mark a team as blocked and kill all members").action(async(name)=>{try{await setTeamStatus(name,"blocked"),await killTeamMembers(name),console.log(`Team "${name}" marked as blocked. All members killed.`)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}async function handleTeamCreate(name,options){if(options.wish){let resolvedRepo=resolve6(options.repo),wishPath=join43(resolvedRepo,".genie","wishes",options.wish,"WISH.md");if(!existsSync32(wishPath)){let cwdWishDir=join43(process.cwd(),".genie","wishes",options.wish),cwdWishPath=join43(cwdWishDir,"WISH.md");if(existsSync32(cwdWishPath)){let destDir=join43(resolvedRepo,".genie","wishes",options.wish);await mkdir8(destDir,{recursive:!0}),await cp2(cwdWishDir,destDir,{recursive:!0}),console.log(`Wish: copied ${options.wish}/WISH.md to repo`)}else console.error(`Error: Wish not found at ${wishPath}`),process.exit(1)}}let config=await createTeam(name,options.repo,options.branch),needsUpdate=!1;if(options.wish)config.wishSlug=options.wish,needsUpdate=!0;if(options.session)config.tmuxSessionName=options.session,needsUpdate=!0;if(needsUpdate)await updateTeamConfig(name,config);if(console.log(`Team "${config.name}" created.`),console.log(` Worktree: ${config.worktreePath}`),console.log(` Branch: ${config.name} (from ${config.baseBranch})`),config.tmuxSessionName)console.log(` Session: ${config.tmuxSessionName}`);if(config.nativeTeamsEnabled)console.log(" Native teams: enabled");if(options.wish&&options.spawn!==!1)await spawnLeaderWithWish(config,options.wish,options.repo,options.session)}async function spawnLeaderWithWish(config,slug,repoPath,sessionOverride){let{handleWorkerSpawn:handleWorkerSpawn2}=await Promise.resolve().then(() => (init_agents(),exports_agents)),{getCurrentSessionName:getCurrentSessionName2}=await Promise.resolve().then(() => (init_tmux(),exports_tmux)),resolvedRepo=resolve6(repoPath),tmuxSession=sessionOverride??await getCurrentSessionName2(config.name)??config.name;config.tmuxSessionName=tmuxSession,await updateTeamConfig(config.name,config);let sourceWishPath=join43(resolvedRepo,".genie","wishes",slug,"WISH.md");if(!existsSync32(sourceWishPath))console.error(`Error: Wish not found at ${sourceWishPath}`),process.exit(1);let destWishDir=join43(config.worktreePath,".genie","wishes",slug);await mkdir8(destWishDir,{recursive:!0});let destWishPath=join43(destWishDir,"WISH.md");await copyFile2(sourceWishPath,destWishPath),console.log(` Wish: copied ${slug}/WISH.md into worktree`);let standardTeam=["team-lead","engineer","reviewer","qa","fix"];for(let role of standardTeam)await hireAgent(config.name,role);console.log(` Team: hired ${standardTeam.join(", ")}`);let members=standardTeam.filter((r)=>r!=="team-lead").join(", "),kickoffPrompt=`Your team is "${config.name}". Repo: ${config.repo}. Branch: ${config.name}. Worktree: ${config.worktreePath}. Wish slug: ${slug}. Your team members are: ${members} (already hired \u2014 genie work will spawn them automatically). Read the wish at .genie/wishes/${slug}/WISH.md and execute the full lifecycle autonomously.`;await handleWorkerSpawn2("team-lead",{provider:"claude",team:config.name,cwd:config.worktreePath,session:tmuxSession,initialPrompt:kickoffPrompt});let result=await(await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router))).sendMessage(config.worktreePath,"cli","team-lead",kickoffPrompt);if(!result.delivered)console.warn(`\u26A0 Backup delivery to team-lead failed: ${result.reason??"unknown"}`);console.log(" Leader: spawned and working")}async function autoDetectTeam(){let envTeam=process.env.GENIE_TEAM;if(envTeam)return envTeam;let teams=await listTeams2();if(teams.length===1)return teams[0].name;return null}async function printMembers(name,json2){let members=await listMembers(name);if(members===null)console.error(`Team "${name}" not found.`),process.exit(1);if(json2){console.log(JSON.stringify(members,null,2));return}if(members.length===0){console.log(`Team "${name}" has no members. Hire agents with: genie team hire <agent> --team ${name}`);return}console.log(""),console.log(`MEMBERS of "${name}"`),console.log("-".repeat(60));for(let m of members)console.log(` ${m}`);console.log("")}async function printTeams(json2){let teams=await listTeams2();if(json2){console.log(JSON.stringify(teams,null,2));return}if(teams.length===0){console.log("No teams found. Create one with: genie team create <name> --repo <path>");return}console.log(""),console.log("TEAMS"),console.log("-".repeat(60));for(let t of teams)printTeamSummary(t);console.log("")}function printTeamSummary(t){let status=t.status??"in_progress";console.log(` ${t.name} [${status}]`),console.log(` Repo: ${t.repo}`),console.log(` Branch: ${t.name} (from ${t.baseBranch})`),console.log(` Worktree: ${t.worktreePath}`),console.log(` Members: ${t.members.length}`)}function registerTemplateCommands(program2){let tmpl=program2.command("template").description("Board template management");tmpl.command("list",{isDefault:!0}).description("List available templates").option("--json","Output as JSON").action(async(options)=>{let{listTemplates:listTemplates3}=await Promise.resolve().then(() => (init_template_service(),exports_template_service)),templates=await listTemplates3();if(options.json){console.log(JSON.stringify(templates,null,2));return}if(templates.length===0){console.log("No templates found.");return}let maxName=Math.max(...templates.map((t)=>t.name.length),4);for(let t of templates){let cols=t.columns?.length??0,tag=t.isBuiltin?" (builtin)":"";console.log(` ${padRight(t.name,maxName)} ${cols} columns${tag} ${t.id}`)}console.log(`
2461
2462
  ${templates.length} template${templates.length===1?"":"s"}`)}),tmpl.command("show <name>").description("Show template details").option("--json","Output as JSON").action(async(name,options)=>{let{getTemplate:getTemplate2}=await Promise.resolve().then(() => (init_template_service(),exports_template_service)),t=await getTemplate2(name);if(!t)console.error(`Template "${name}" not found.`),process.exit(1);if(options.json){console.log(JSON.stringify(t,null,2));return}if(console.log(`
2462
- Name: ${t.name}`),console.log(`ID: ${t.id}`),console.log(`Builtin: ${t.isBuiltin}`),console.log(`Description: ${t.description??"(none)"}`),t.columns&&t.columns.length>0)console.log(`Columns: ${t.columns.map((c)=>c.name).join(", ")}`);console.log("")}),tmpl.command("delete <name>").description("Delete a template").action(async(name)=>{let{deleteTemplate:deleteTemplate2,getTemplate:getTemplate2}=await Promise.resolve().then(() => (init_template_service(),exports_template_service)),t=await getTemplate2(name);if(!t)console.error(`Template "${name}" not found.`),process.exit(1);let ok=await deleteTemplate2(t.id);console.log(ok?`Deleted template "${t.name}".`:"Delete failed.")})}try{let{execSync:execSyncStartup}=__require("child_process");if(execSyncStartup("git config core.bare",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()==="true")execSyncStartup("git config core.bare false",{stdio:["pipe","pipe","pipe"]})}catch{}var program2=new Command;program2.name("genie").description("Genie CLI - AI-assisted development").version(VERSION);async function startNamedSession(name){let{buildTeamLeadCommand:buildTeamLeadCommand2,sessionExists:sessionExists2}=await Promise.resolve().then(() => (init_team_lead_command(),exports_team_lead_command)),{getAgentsFilePath:getAgentsFilePath2}=await Promise.resolve().then(() => (init_session(),exports_session)),systemPromptFile=getAgentsFilePath2(),hasPriorSession=sessionExists2(name),cmd=buildTeamLeadCommand2(name,{systemPromptFile:systemPromptFile??void 0,continueName:hasPriorSession?name:void 0});console.log(hasPriorSession?`Resuming session: ${name}`:`Starting new session: ${name}`);let{spawnSync:spawnSync3}=await import("child_process"),result=spawnSync3("sh",["-c",cmd],{stdio:"inherit"});if(result.status)process.exit(result.status)}program2.command("setup").description("Configure genie settings").option("--quick","Accept all defaults").option("--shortcuts","Only configure keyboard shortcuts").option("--codex","Only configure Codex integration").option("--terminal","Only configure terminal defaults").option("--session","Only configure session settings").option("--reset","Reset configuration to defaults").option("--show","Show current configuration").action(async(options)=>{await setupCommand(options)});program2.command("doctor").description("Run diagnostic checks on genie installation").option("--fix","Auto-fix: kill zombie postgres, clean shared memory, restart daemon").action(doctorCommand);program2.command("update").description("Update Genie CLI to the latest version").option("--next","Switch to dev builds (npm @next tag)").option("--stable","Switch to stable releases (npm @latest tag)").action(updateCommand);program2.command("uninstall").description("Remove Genie CLI and clean up hooks").action(uninstallCommand);var shortcuts=program2.command("shortcuts").description("Manage tmux keyboard shortcuts");shortcuts.action(shortcutsShowCommand);shortcuts.command("show").description("Show available shortcuts and installation status").action(shortcutsShowCommand);shortcuts.command("install").description("Install shortcuts to config files (~/.tmux.conf, shell rc)").action(shortcutsInstallCommand);shortcuts.command("uninstall").description("Remove shortcuts from config files").action(shortcutsUninstallCommand);registerAgentCommands(program2);registerTaskCommands(program2);extendTaskCommands(program2);registerTeamNamespace(program2);registerExecCommands(program2);program2.command("tui").description("Launch interactive terminal UI (OpenTUI nav + tmux Claude Code)").option("--dev","Development mode with auto-reload on file changes").action(async(options)=>{let{launchTui:launchTui2}=await Promise.resolve().then(() => (init_tui(),exports_tui));await launchTui2({dev:options.dev})});registerDispatchCommands(program2);registerHookNamespace(program2);registerDbCommands(program2);registerScheduleCommands(program2);registerDaemonCommands(program2);registerNotifyCommands(program2);registerEventsCommands(program2);registerSessionsCommands(program2);registerMetricsCommands(program2);registerExportCommands(program2);registerImportCommands(program2);registerTemplateCommands(program2);registerInstallCommand(program2);registerPublishCommand(program2);var itemCmd=program2.command("item").description("Item registry management");registerItemUninstallCommand(itemCmd);registerItemUpdateCommand(itemCmd);var auditTimers=new Map;program2.hook("preAction",(_thisCommand,actionCommand)=>{let name=actionCommand.name();auditTimers.set(name,Date.now()),Promise.resolve().then(() => (init_db(),exports_db)).then(({isConnected:isConnected2})=>{if(!isConnected2())return;recordAuditEvent("command",name,"command_start",getActor(),{args:actionCommand.args}).catch(()=>{})}).catch(()=>{})});program2.hook("postAction",(_thisCommand,actionCommand)=>{let name=actionCommand.name(),startMs=auditTimers.get(name),durationMs=startMs?Date.now()-startMs:void 0;auditTimers.delete(name),Promise.resolve().then(() => (init_db(),exports_db)).then(({isConnected:isConnected2})=>{if(!isConnected2())return;recordAuditEvent("command",name,"command_success",getActor(),{args:actionCommand.args,duration_ms:durationMs}).catch(()=>{})}).catch(()=>{})});var qaCmd=program2.command("qa").description("QA \u2014 self-testing system for genie CLI");qaCmd.command("run [target]",{isDefault:!0}).description("Run QA specs (all, a domain, or a single spec)").option("--timeout <seconds>","Max seconds per spec",(v2)=>Number(v2),3600).option("--parallel <n>","Max specs to run in parallel",(v2)=>Number(v2),5).option("--verbose","Show all collected events").option("--ndjson","Machine-readable NDJSON output").action(async(target,options)=>{await qaCommand(target,options)});qaCmd.command("status").description("Show QA dashboard with last results per spec").option("--json","Output as JSON").action(async(options)=>{await qaStatusCommand(options)});qaCmd.command("history").description("Show recent QA runs").action(async()=>{await qaHistoryCommand()});qaCmd.command("check <specFile>").description("Evaluate a QA spec against current team logs and publish qa-report").option("--team <name>","Team name (defaults to GENIE_TEAM)").option("--since <timestamp>","Only consider events after this ISO timestamp").option("--since-file <path>","Read the lower-bound timestamp from a file").action(async(specFile,options)=>{await qaCheckCommand(specFile,options)});program2.command("qa-report <json>").description("Publish QA result to the PG event log (called by QA team-lead)").action(async(json2)=>{let team=process.env.GENIE_TEAM;if(!team)console.error("Error: GENIE_TEAM not set. This command must be run by a QA team-lead agent."),process.exit(1);try{let data=JSON.parse(json2),{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(process.cwd(),`genie.qa.${team}.result`,{kind:"qa",agent:"qa",team,text:`QA result: ${String(data.result??"unknown")}`,data,source:"hook"}),console.log(`QA result published to PG event log as genie.qa.${team}.result`)}catch(err){console.error(`Failed to publish QA result: ${err}`),process.exit(1)}});function errorRedirect(oldCmd,newCmd){return()=>{let args=process.argv.slice(3).join(" "),suggestion=args?`${newCmd} ${args}`:newCmd;console.error(`Command "${oldCmd}" has moved. Did you mean: genie ${suggestion}?`),process.exit(1)}}program2.command("spawn [args...]").description("(moved) \u2192 genie agent spawn").action(errorRedirect("spawn","agent spawn"));program2.command("kill [args...]").description("(moved) \u2192 genie agent kill").action(errorRedirect("kill","agent kill"));program2.command("stop [args...]").description("(moved) \u2192 genie agent stop").action(errorRedirect("stop","agent stop"));program2.command("resume [args...]").description("(moved) \u2192 genie agent resume").action(errorRedirect("resume","agent resume"));program2.command("ls").description("(moved) \u2192 genie agent list").action(errorRedirect("ls","agent list"));program2.command("read [args...]").description("(moved) \u2192 genie agent log --raw").action(errorRedirect("read","agent log --raw"));program2.command("history [args...]").description("(moved) \u2192 genie agent log --transcript").action(errorRedirect("history","agent log --transcript"));program2.command("log [args...]").description("(moved) \u2192 genie agent log").action(errorRedirect("log","agent log"));program2.command("status [args...]").description("(moved) \u2192 genie task status").action(errorRedirect("status","task status"));program2.command("done [args...]").description("(moved) \u2192 genie task done").action(errorRedirect("done","task done"));program2.command("send [args...]").description("(moved) \u2192 genie agent send").action(errorRedirect("send","agent send"));program2.command("broadcast [args...]").description("(moved) \u2192 genie agent send --broadcast").action(errorRedirect("broadcast","agent send --broadcast"));program2.command("answer [args...]").description("(moved) \u2192 genie agent answer").action(errorRedirect("answer","agent answer"));program2.command("inbox [args...]").description("(moved) \u2192 genie agent inbox").action(errorRedirect("inbox","agent inbox"));program2.command("chat [args...]").description("(moved) \u2192 genie agent log --conversations").action(errorRedirect("chat","agent log --conversations"));program2.command("brief [args...]").description("(moved) \u2192 genie agent brief").action(errorRedirect("brief","agent brief"));program2.command("dir [args...]").description("(moved) \u2192 genie agent directory / genie agent register").action(errorRedirect("dir","agent directory"));program2.command("show [args...]").description("(moved) \u2192 genie task show / genie agent show").action(errorRedirect("show","agent show"));var args=process.argv.slice(2);if(args.length===0||args.every((a)=>a==="--reset")){let{sessionCommand:sessionCommand2}=await Promise.resolve().then(() => (init_session(),exports_session));await sessionCommand2({reset:args.includes("--reset")}),process.exit(0)}var sessionIdx=args.indexOf("--session");if(sessionIdx!==-1&&sessionIdx+1<args.length){let sessionName=args[sessionIdx+1];if(!args.filter((_2,i2)=>i2!==sessionIdx&&i2!==sessionIdx+1).some((a)=>!a.startsWith("-")))try{await startNamedSession(sessionName),process.exit(0)}catch(err){console.error(`Error: ${err instanceof Error?err.message:err}`),process.exit(1)}else try{await program2.parseAsync(process.argv)}finally{stopOtelReceiver(),await shutdown().catch(()=>{})}}else try{await program2.parseAsync(process.argv)}finally{stopOtelReceiver(),await shutdown().catch(()=>{})}
2463
+ Name: ${t.name}`),console.log(`ID: ${t.id}`),console.log(`Builtin: ${t.isBuiltin}`),console.log(`Description: ${t.description??"(none)"}`),t.columns&&t.columns.length>0)console.log(`Columns: ${t.columns.map((c)=>c.name).join(", ")}`);console.log("")}),tmpl.command("delete <name>").description("Delete a template").action(async(name)=>{let{deleteTemplate:deleteTemplate2,getTemplate:getTemplate2}=await Promise.resolve().then(() => (init_template_service(),exports_template_service)),t=await getTemplate2(name);if(!t)console.error(`Template "${name}" not found.`),process.exit(1);let ok=await deleteTemplate2(t.id);console.log(ok?`Deleted template "${t.name}".`:"Delete failed.")})}try{let{execSync:execSyncStartup}=__require("child_process");if(execSyncStartup("git config core.bare",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim()==="true")execSyncStartup("git config core.bare false",{stdio:["pipe","pipe","pipe"]})}catch{}var program2=new Command;program2.name("genie").description("Genie CLI - AI-assisted development").version(VERSION);async function startNamedSession(name){let{buildTeamLeadCommand:buildTeamLeadCommand2,sessionExists:sessionExists2}=await Promise.resolve().then(() => (init_team_lead_command(),exports_team_lead_command)),{getAgentsFilePath:getAgentsFilePath2}=await Promise.resolve().then(() => (init_session(),exports_session)),systemPromptFile=getAgentsFilePath2(),hasPriorSession=sessionExists2(name),cmd=buildTeamLeadCommand2(name,{systemPromptFile:systemPromptFile??void 0,continueName:hasPriorSession?name:void 0});console.log(hasPriorSession?`Resuming session: ${name}`:`Starting new session: ${name}`);let{spawnSync:spawnSync3}=await import("child_process"),result=spawnSync3("sh",["-c",cmd],{stdio:"inherit"});if(result.status)process.exit(result.status)}program2.command("setup").description("Configure genie settings").option("--quick","Accept all defaults").option("--shortcuts","Only configure keyboard shortcuts").option("--codex","Only configure Codex integration").option("--terminal","Only configure terminal defaults").option("--session","Only configure session settings").option("--reset","Reset configuration to defaults").option("--show","Show current configuration").action(async(options)=>{await setupCommand(options)});program2.command("doctor").description("Run diagnostic checks on genie installation").option("--fix","Auto-fix: kill zombie postgres, clean shared memory, restart daemon").action(doctorCommand);program2.command("update").description("Update Genie CLI to the latest version").option("--next","Switch to dev builds (npm @next tag)").option("--stable","Switch to stable releases (npm @latest tag)").action(updateCommand);program2.command("uninstall").description("Remove Genie CLI and clean up hooks").action(uninstallCommand);var shortcuts=program2.command("shortcuts").description("Manage tmux keyboard shortcuts");shortcuts.action(shortcutsShowCommand);shortcuts.command("show").description("Show available shortcuts and installation status").action(shortcutsShowCommand);shortcuts.command("install").description("Install shortcuts to config files (~/.tmux.conf, shell rc)").action(shortcutsInstallCommand);shortcuts.command("uninstall").description("Remove shortcuts from config files").action(shortcutsUninstallCommand);registerAgentCommands(program2);registerTaskCommands(program2);extendTaskCommands(program2);registerTeamNamespace(program2);registerExecCommands(program2);program2.command("tui").description("Launch interactive terminal UI (OpenTUI nav + tmux Claude Code)").option("--dev","Development mode with auto-reload on file changes").action(async(options)=>{let{launchTui:launchTui2}=await Promise.resolve().then(() => (init_tui(),exports_tui));await launchTui2({dev:options.dev})});registerDispatchCommands(program2);registerHookNamespace(program2);registerDbCommands(program2);registerScheduleCommands(program2);registerDaemonCommands(program2);registerNotifyCommands(program2);registerEventsCommands(program2);registerSessionsCommands(program2);registerMetricsCommands(program2);registerExportCommands(program2);registerImportCommands(program2);registerTemplateCommands(program2);registerInstallCommand(program2);registerPublishCommand(program2);var itemCmd=program2.command("item").description("Item registry management");registerItemUninstallCommand(itemCmd);registerItemUpdateCommand(itemCmd);var auditTimers=new Map;program2.hook("preAction",(_thisCommand,actionCommand)=>{let name=actionCommand.name();auditTimers.set(name,Date.now()),Promise.resolve().then(() => (init_db(),exports_db)).then(({isConnected:isConnected2})=>{if(!isConnected2())return;recordAuditEvent("command",name,"command_start",getActor(),{args:actionCommand.args}).catch(()=>{})}).catch(()=>{})});program2.hook("postAction",(_thisCommand,actionCommand)=>{let name=actionCommand.name(),startMs=auditTimers.get(name),durationMs=startMs?Date.now()-startMs:void 0;auditTimers.delete(name),Promise.resolve().then(() => (init_db(),exports_db)).then(({isConnected:isConnected2})=>{if(!isConnected2())return;recordAuditEvent("command",name,"command_success",getActor(),{args:actionCommand.args,duration_ms:durationMs}).catch(()=>{})}).catch(()=>{})});var qaCmd=program2.command("qa").description("QA \u2014 self-testing system for genie CLI");qaCmd.command("run [target]",{isDefault:!0}).description("Run QA specs (all, a domain, or a single spec)").option("--timeout <seconds>","Max seconds per spec",(v2)=>Number(v2),3600).option("--parallel <n>","Max specs to run in parallel",(v2)=>Number(v2),5).option("--verbose","Show all collected events").option("--ndjson","Machine-readable NDJSON output").action(async(target,options)=>{await qaCommand(target,options)});qaCmd.command("status").description("Show QA dashboard with last results per spec").option("--json","Output as JSON").action(async(options)=>{await qaStatusCommand(options)});qaCmd.command("history").description("Show recent QA runs").action(async()=>{await qaHistoryCommand()});qaCmd.command("check <specFile>").description("Evaluate a QA spec against current team logs and publish qa-report").option("--team <name>","Team name (defaults to GENIE_TEAM)").option("--since <timestamp>","Only consider events after this ISO timestamp").option("--since-file <path>","Read the lower-bound timestamp from a file").action(async(specFile,options)=>{await qaCheckCommand(specFile,options)});program2.command("qa-report <json>").description("Publish QA result to the PG event log (called by QA team-lead)").action(async(json2)=>{let team=process.env.GENIE_TEAM;if(!team)console.error("Error: GENIE_TEAM not set. This command must be run by a QA team-lead agent."),process.exit(1);try{let data=JSON.parse(json2),{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(process.cwd(),`genie.qa.${team}.result`,{kind:"qa",agent:"qa",team,text:`QA result: ${String(data.result??"unknown")}`,data,source:"hook"}),console.log(`QA result published to PG event log as genie.qa.${team}.result`)}catch(err){console.error(`Failed to publish QA result: ${err}`),process.exit(1)}});function errorRedirect(oldCmd,newCmd){return()=>{let args=process.argv.slice(3).join(" "),suggestion=args?`${newCmd} ${args}`:newCmd;console.error(`Command "${oldCmd}" has moved. Did you mean: genie ${suggestion}?`),process.exit(1)}}program2.command("spawn [args...]").description("(moved) \u2192 genie agent spawn").action(errorRedirect("spawn","agent spawn"));program2.command("kill [args...]").description("(moved) \u2192 genie agent kill").action(errorRedirect("kill","agent kill"));program2.command("stop [args...]").description("(moved) \u2192 genie agent stop").action(errorRedirect("stop","agent stop"));program2.command("resume [args...]").description("(moved) \u2192 genie agent resume").action(errorRedirect("resume","agent resume"));program2.command("ls").description("(moved) \u2192 genie agent list").action(errorRedirect("ls","agent list"));program2.command("read [args...]").description("(moved) \u2192 genie agent log --raw").action(errorRedirect("read","agent log --raw"));program2.command("history [args...]").description("(moved) \u2192 genie agent log --transcript").action(errorRedirect("history","agent log --transcript"));program2.command("log [args...]").description("(moved) \u2192 genie agent log").action(errorRedirect("log","agent log"));program2.command("status [args...]").description("(moved) \u2192 genie task status").action(errorRedirect("status","task status"));program2.command("done [args...]").description("(moved) \u2192 genie task done").action(errorRedirect("done","task done"));program2.command("reset [args...]").description("(moved) \u2192 genie task reset").action(errorRedirect("reset","task reset"));program2.command("send [args...]").description("(moved) \u2192 genie agent send").action(errorRedirect("send","agent send"));program2.command("broadcast [args...]").description("(moved) \u2192 genie agent send --broadcast").action(errorRedirect("broadcast","agent send --broadcast"));program2.command("answer [args...]").description("(moved) \u2192 genie agent answer").action(errorRedirect("answer","agent answer"));program2.command("inbox [args...]").description("(moved) \u2192 genie agent inbox").action(errorRedirect("inbox","agent inbox"));program2.command("chat [args...]").description("(moved) \u2192 genie agent log --conversations").action(errorRedirect("chat","agent log --conversations"));program2.command("brief [args...]").description("(moved) \u2192 genie agent brief").action(errorRedirect("brief","agent brief"));program2.command("dir [args...]").description("(moved) \u2192 genie agent directory / genie agent register").action(errorRedirect("dir","agent directory"));program2.command("show [args...]").description("(moved) \u2192 genie task show / genie agent show").action(errorRedirect("show","agent show"));var args=process.argv.slice(2);if(args.length===0||args.every((a)=>a==="--reset")){let{sessionCommand:sessionCommand2}=await Promise.resolve().then(() => (init_session(),exports_session));await sessionCommand2({reset:args.includes("--reset")}),process.exit(0)}var sessionIdx=args.indexOf("--session");if(sessionIdx!==-1&&sessionIdx+1<args.length){let sessionName=args[sessionIdx+1];if(!args.filter((_2,i2)=>i2!==sessionIdx&&i2!==sessionIdx+1).some((a)=>!a.startsWith("-")))try{await startNamedSession(sessionName),process.exit(0)}catch(err){console.error(`Error: ${err instanceof Error?err.message:err}`),process.exit(1)}else try{await program2.parseAsync(process.argv)}finally{stopOtelReceiver(),await shutdown().catch(()=>{})}}else try{await program2.parseAsync(process.argv)}finally{stopOtelReceiver(),await shutdown().catch(()=>{})}
@@ -2,7 +2,7 @@
2
2
  "id": "genie",
3
3
  "name": "Genie",
4
4
  "description": "Skills, agents, and hooks for the Genie CLI terminal orchestration toolkit",
5
- "version": "4.260329.13",
5
+ "version": "4.260329.15",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260329.13",
3
+ "version": "4.260329.15",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260329.13",
3
+ "version": "4.260329.15",
4
4
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
5
5
  "author": {
6
6
  "name": "Namastex Labs"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260329.13",
3
+ "version": "4.260329.15",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
package/src/genie.ts CHANGED
@@ -3,14 +3,14 @@
3
3
  /**
4
4
  * genie — Single entrypoint CLI.
5
5
  *
6
- * Top-level commands:
7
- * spawn, kill, stop, ls, history, read, answer, work
8
- *
9
6
  * Namespaces:
10
- * team, dir, send/inbox, state, hook
7
+ * agent — spawn, kill, stop, resume, list, show, log, send, answer, register, directory, inbox, brief
8
+ * task — create, list, status, done, reset, board, project, releases, type
9
+ * team — create, hire, fire, list, disband
10
+ * exec — list, show, terminate (debug)
11
11
  *
12
- * Utilities:
13
- * setup, doctor, update, uninstall, shortcuts
12
+ * Top-level:
13
+ * setup, doctor, update, uninstall, shortcuts, qa, tui, run
14
14
  *
15
15
  * Session:
16
16
  * genie --session <name> — Start or resume a named leader session
@@ -344,6 +344,10 @@ program
344
344
  .description('(moved) → genie task status')
345
345
  .action(errorRedirect('status', 'task status'));
346
346
  program.command('done [args...]').description('(moved) → genie task done').action(errorRedirect('done', 'task done'));
347
+ program
348
+ .command('reset [args...]')
349
+ .description('(moved) → genie task reset')
350
+ .action(errorRedirect('reset', 'task reset'));
347
351
  program.command('send [args...]').description('(moved) → genie agent send').action(errorRedirect('send', 'agent send'));
348
352
  program
349
353
  .command('broadcast [args...]')
package/src/tui/app.tsx CHANGED
@@ -7,7 +7,7 @@ import { getActiveWork, getExecutorActivity, matchWorkToTasks } from './activity
7
7
  import { Nav } from './components/Nav.js';
8
8
  import { loadAll, loadAssignments, loadExecutors, subscribe } from './db.js';
9
9
  import { palette } from './theme.js';
10
- import { attachProject, cleanup, switchRightPane } from './tmux.js';
10
+ import { attachProject, attachProjectWindow, cleanup, switchRightPane } from './tmux.js';
11
11
  import { applyActivity, buildTree } from './tree.js';
12
12
  import type { TreeNode, TuiAssignment, TuiData, TuiExecutor } from './types.js';
13
13
 
@@ -108,6 +108,14 @@ export function App({ rightPane }: { rightPane?: string }) {
108
108
  [rightPane, currentProject],
109
109
  );
110
110
 
111
+ const handleTmuxSessionSelect = useCallback(
112
+ (sessionName: string, windowIndex?: number) => {
113
+ if (!rightPane) return;
114
+ attachProjectWindow(rightPane, sessionName, windowIndex);
115
+ },
116
+ [rightPane],
117
+ );
118
+
111
119
  if (loading) {
112
120
  return (
113
121
  <box width="100%" height="100%" backgroundColor={palette.bg} justifyContent="center" alignItems="center">
@@ -124,7 +132,14 @@ export function App({ rightPane }: { rightPane?: string }) {
124
132
  );
125
133
  }
126
134
 
127
- return <Nav tree={tree} onTreeChange={handleTreeChange} onProjectSelect={handleProjectSelect} />;
135
+ return (
136
+ <Nav
137
+ tree={tree}
138
+ onTreeChange={handleTreeChange}
139
+ onProjectSelect={handleProjectSelect}
140
+ onTmuxSessionSelect={handleTmuxSessionSelect}
141
+ />
142
+ );
128
143
  }
129
144
 
130
145
  /** Merge expanded state from old tree into new tree (preserves user navigation) */
@@ -9,16 +9,17 @@ import { flattenTree, toggleNode } from '../tree.js';
9
9
  import type { TreeNode } from '../types.js';
10
10
  import { ClaudeView, getClaudeRowCount } from './ClaudeView.js';
11
11
  import { TAB_ORDER, TabBar, type TabId } from './TabBar.js';
12
- import { TmuxView, getTmuxRowCount } from './TmuxView.js';
12
+ import { TmuxView, getTmuxRowCount, getTmuxRowTarget } from './TmuxView.js';
13
13
  import { TreeNodeRow } from './TreeNode.js';
14
14
 
15
15
  interface NavProps {
16
16
  tree: TreeNode[];
17
17
  onTreeChange: (tree: TreeNode[]) => void;
18
18
  onProjectSelect: (projectId: string, tmuxSession: string | null) => void;
19
+ onTmuxSessionSelect?: (sessionName: string, windowIndex?: number) => void;
19
20
  }
20
21
 
21
- export function Nav({ tree, onTreeChange, onProjectSelect }: NavProps) {
22
+ export function Nav({ tree, onTreeChange, onProjectSelect, onTmuxSessionSelect }: NavProps) {
22
23
  const [activeTab, setActiveTab] = useState<TabId>('projects');
23
24
  const [tabBarFocused, setTabBarFocused] = useState(false);
24
25
  const [diagnostics, setDiagnostics] = useState<DiagnosticSnapshot | null>(null);
@@ -123,17 +124,23 @@ export function Nav({ tree, onTreeChange, onProjectSelect }: NavProps) {
123
124
  );
124
125
 
125
126
  const handleEnter = useCallback(() => {
126
- if (activeTab !== 'projects') return;
127
- const current = flatNodes[projectIndex]?.node;
128
- if (!current) return;
127
+ if (activeTab === 'projects') {
128
+ const current = flatNodes[projectIndex]?.node;
129
+ if (!current) return;
129
130
 
130
- if (current.type === 'project') {
131
- const proj = current.data as { id: string; tmuxSession: string | null };
132
- onProjectSelect(proj.id, proj.tmuxSession);
133
- } else if (current.children.length > 0) {
134
- handleToggle(current.id);
131
+ if (current.type === 'project') {
132
+ const proj = current.data as { id: string; tmuxSession: string | null };
133
+ onProjectSelect(proj.id, proj.tmuxSession);
134
+ } else if (current.children.length > 0) {
135
+ handleToggle(current.id);
136
+ }
137
+ } else if (activeTab === 'tmux' && diagnostics && onTmuxSessionSelect) {
138
+ const target = getTmuxRowTarget(diagnostics.sessions, tmuxIndex);
139
+ if (target) {
140
+ onTmuxSessionSelect(target.sessionName, target.windowIndex);
141
+ }
135
142
  }
136
- }, [activeTab, flatNodes, projectIndex, onProjectSelect, handleToggle]);
143
+ }, [activeTab, flatNodes, projectIndex, onProjectSelect, handleToggle, diagnostics, tmuxIndex, onTmuxSessionSelect]);
137
144
 
138
145
  const handleTabBarKey = useCallback(
139
146
  (keyName: string): boolean => {
@@ -208,6 +215,7 @@ export function Nav({ tree, onTreeChange, onProjectSelect }: NavProps) {
208
215
  ? {
209
216
  orphanProcesses: diagnostics.gaps.deadPidExecutors.length,
210
217
  orphanPanes: diagnostics.gaps.orphanPanes.length,
218
+ deadPanes: diagnostics.gaps.deadPaneCount,
211
219
  }
212
220
  : undefined;
213
221
 
@@ -16,12 +16,22 @@ const TAB_LABELS: Record<TabId, string> = {
16
16
  interface TabBarProps {
17
17
  activeTab: TabId;
18
18
  focused: boolean;
19
- gaps?: { orphanProcesses: number; orphanPanes: number };
19
+ gaps?: { orphanProcesses: number; orphanPanes: number; deadPanes: number };
20
20
  }
21
21
 
22
- export function TabBar({ activeTab, focused, gaps }: TabBarProps) {
23
- const totalGaps = (gaps?.orphanProcesses ?? 0) + (gaps?.orphanPanes ?? 0);
22
+ function tabBadge(tab: TabId, gaps: TabBarProps['gaps']) {
23
+ if (tab === 'claude') {
24
+ const total = (gaps?.orphanProcesses ?? 0) + (gaps?.orphanPanes ?? 0);
25
+ return total > 0 ? <span fg={palette.error}> {total}</span> : null;
26
+ }
27
+ if (tab === 'tmux') {
28
+ const dead = gaps?.deadPanes ?? 0;
29
+ return dead > 0 ? <span fg={palette.error}> {dead}\u2620</span> : null;
30
+ }
31
+ return null;
32
+ }
24
33
 
34
+ export function TabBar({ activeTab, focused, gaps }: TabBarProps) {
25
35
  return (
26
36
  <box height={1} flexDirection="row" width="100%" backgroundColor={palette.bgLight}>
27
37
  {TAB_ORDER.map((tab) => {
@@ -29,9 +39,6 @@ export function TabBar({ activeTab, focused, gaps }: TabBarProps) {
29
39
  const bg = isActive ? palette.violet : palette.bgLight;
30
40
  const fg = isActive ? '#ffffff' : focused ? palette.textDim : palette.textMuted;
31
41
 
32
- // Show gap count badge on Claude tab
33
- const badge = tab === 'claude' && totalGaps > 0 ? <span fg={palette.error}> {totalGaps}</span> : null;
34
-
35
42
  return (
36
43
  <box key={tab} backgroundColor={bg} paddingX={1}>
37
44
  <text>
@@ -39,7 +46,7 @@ export function TabBar({ activeTab, focused, gaps }: TabBarProps) {
39
46
  {isActive && focused ? '>' : ' '}
40
47
  {TAB_LABELS[tab]}
41
48
  </span>
42
- {badge}
49
+ {tabBadge(tab, gaps)}
43
50
  </text>
44
51
  </box>
45
52
  );
@@ -2,7 +2,7 @@
2
2
  /** tmux inventory: sessions > windows > panes with PID and command info */
3
3
 
4
4
  import { useMemo } from 'react';
5
- import type { TmuxSession } from '../diagnostics.js';
5
+ import type { TmuxPane, TmuxSession } from '../diagnostics.js';
6
6
  import { palette } from '../theme.js';
7
7
 
8
8
  interface TmuxViewProps {
@@ -19,8 +19,29 @@ interface FlatTmuxRow {
19
19
  detailColor: string;
20
20
  type: 'session' | 'window' | 'pane';
21
21
  sessionName: string;
22
+ windowIndex?: number;
22
23
  /** Whether pane is running claude */
23
24
  isClaude: boolean;
25
+ /** Whether pane has exited */
26
+ isDead: boolean;
27
+ }
28
+
29
+ function toPaneRow(pane: TmuxPane, sessionName: string, windowIndex: number): FlatTmuxRow {
30
+ const isClaude = pane.command === 'claude' || pane.title.includes('claude');
31
+ const color = pane.isDead ? palette.textMuted : isClaude ? palette.cyan : palette.textDim;
32
+ return {
33
+ id: `p:${pane.paneId}`,
34
+ depth: 2,
35
+ label: pane.isDead ? `${pane.paneId} [DEAD]` : `${pane.paneId} [${pane.command}]`,
36
+ color,
37
+ detail: `pid:${pane.pid} ${pane.size}`,
38
+ detailColor: palette.textMuted,
39
+ type: 'pane',
40
+ sessionName,
41
+ windowIndex,
42
+ isClaude,
43
+ isDead: pane.isDead,
44
+ };
24
45
  }
25
46
 
26
47
  function flattenSessions(sessions: TmuxSession[]): FlatTmuxRow[] {
@@ -35,33 +56,24 @@ function flattenSessions(sessions: TmuxSession[]): FlatTmuxRow[] {
35
56
  type: 'session',
36
57
  sessionName: session.name,
37
58
  isClaude: false,
59
+ isDead: false,
38
60
  };
39
61
  const windowRows = session.windows.flatMap((window) => {
62
+ const deadInWindow = window.panes.filter((p) => p.isDead).length;
40
63
  const winRow: FlatTmuxRow = {
41
64
  id: `w:${session.name}:${window.index}`,
42
65
  depth: 1,
43
66
  label: `${window.index}:${window.name}`,
44
67
  color: window.active ? palette.cyan : palette.text,
45
- detail: `${window.paneCount}p${window.active ? ' *' : ''}`,
68
+ detail: `${window.paneCount}p${deadInWindow > 0 ? ` ${deadInWindow}\u2620` : ''}${window.active ? ' *' : ''}`,
46
69
  detailColor: window.active ? palette.cyan : palette.textMuted,
47
70
  type: 'window',
48
71
  sessionName: session.name,
72
+ windowIndex: window.index,
49
73
  isClaude: false,
74
+ isDead: false,
50
75
  };
51
- const paneRows: FlatTmuxRow[] = window.panes.map((pane) => {
52
- const isClaude = pane.command === 'claude' || pane.title.includes('claude');
53
- return {
54
- id: `p:${pane.paneId}`,
55
- depth: 2,
56
- label: `${pane.paneId} [${pane.command}]`,
57
- color: isClaude ? palette.cyan : palette.textDim,
58
- detail: `pid:${pane.pid} ${pane.size}`,
59
- detailColor: palette.textMuted,
60
- type: 'pane' as const,
61
- sessionName: session.name,
62
- isClaude,
63
- };
64
- });
76
+ const paneRows: FlatTmuxRow[] = window.panes.map((pane) => toPaneRow(pane, session.name, window.index));
65
77
  return [winRow, ...paneRows];
66
78
  });
67
79
  return [sessionRow, ...windowRows];
@@ -72,6 +84,10 @@ export function TmuxView({ sessions, selectedIndex }: TmuxViewProps) {
72
84
  const rows = useMemo(() => flattenSessions(sessions), [sessions]);
73
85
 
74
86
  const totalPanes = sessions.reduce((sum, s) => sum + s.windows.reduce((ws, w) => ws + w.panes.length, 0), 0);
87
+ const deadPanes = sessions.reduce(
88
+ (sum, s) => sum + s.windows.reduce((ws, w) => ws + w.panes.filter((p) => p.isDead).length, 0),
89
+ 0,
90
+ );
75
91
 
76
92
  return (
77
93
  <box flexDirection="column" width="100%" height="100%">
@@ -81,6 +97,7 @@ export function TmuxView({ sessions, selectedIndex }: TmuxViewProps) {
81
97
  <span fg={palette.textDim}>
82
98
  {sessions.length}s {sessions.reduce((s, x) => s + x.windowCount, 0)}w {totalPanes}p
83
99
  </span>
100
+ {deadPanes > 0 ? <span fg={palette.error}> {deadPanes} dead</span> : null}
84
101
  </text>
85
102
  </box>
86
103
 
@@ -100,7 +117,8 @@ export function TmuxView({ sessions, selectedIndex }: TmuxViewProps) {
100
117
  >
101
118
  {rows.map((row, i) => {
102
119
  const indent = ' '.repeat(row.depth);
103
- const icon = row.type === 'session' ? '\u25c8' : row.type === 'window' ? '\u25a1' : '\u2500'; // ◈ □ ─
120
+ const icon =
121
+ row.type === 'session' ? '\u25c8' : row.type === 'window' ? '\u25a1' : row.isDead ? '\u2718' : '\u2500'; // ◈ □ ✘ ─
104
122
  const selected = i === selectedIndex;
105
123
 
106
124
  return (
@@ -132,3 +150,14 @@ export function getTmuxRowCount(sessions: TmuxSession[]): number {
132
150
  }
133
151
  return count;
134
152
  }
153
+
154
+ /** Look up session target for a given row index (for Enter key navigation). */
155
+ export function getTmuxRowTarget(
156
+ sessions: TmuxSession[],
157
+ index: number,
158
+ ): { sessionName: string; windowIndex?: number } | null {
159
+ const rows = flattenSessions(sessions);
160
+ const row = rows[index];
161
+ if (!row) return null;
162
+ return { sessionName: row.sessionName, windowIndex: row.windowIndex };
163
+ }
package/src/tui/db.ts CHANGED
@@ -1,5 +1,6 @@
1
- /** PG data layer — framework-agnostic, no UI imports */
1
+ /** PG data layer + tmux tree loader — framework-agnostic, no UI imports */
2
2
 
3
+ import { execSync } from 'node:child_process';
3
4
  import type { ExecutorState, TransportType } from '../lib/executor-types.js';
4
5
  import type { ProviderName } from '../lib/provider-adapters.js';
5
6
  import type {
@@ -194,3 +195,62 @@ function mapAssignment(row: Record<string, unknown>): TuiAssignment {
194
195
  startedAt: row.started_at instanceof Date ? row.started_at.toISOString() : String(row.started_at),
195
196
  };
196
197
  }
198
+
199
+ // ── Tmux Tree ────────────────────────────────────────────────────────────────
200
+
201
+ interface TmuxTreeWindow {
202
+ sessionName: string;
203
+ index: number;
204
+ name: string;
205
+ active: boolean;
206
+ paneCount: number;
207
+ }
208
+
209
+ /** @public */
210
+ export interface TmuxTreeSession {
211
+ name: string;
212
+ attached: boolean;
213
+ windowCount: number;
214
+ windows: TmuxTreeWindow[];
215
+ }
216
+
217
+ /** Load tmux session/window tree from shell (lightweight, no pane-level detail). @public */
218
+ export function loadTmuxTree(): TmuxTreeSession[] {
219
+ let output: string;
220
+ try {
221
+ output = execSync(
222
+ "tmux list-windows -a -F '#{session_name}|#{window_index}|#{window_name}|#{window_active}|#{window_panes}|#{session_attached}|#{session_windows}'",
223
+ { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] },
224
+ ).trim();
225
+ } catch {
226
+ return [];
227
+ }
228
+
229
+ if (!output) return [];
230
+
231
+ const sessionMap = new Map<string, TmuxTreeSession>();
232
+
233
+ for (const line of output.split('\n')) {
234
+ if (!line) continue;
235
+ const [sessName, winIdx, winName, winActive, winPanes, sessAttached, sessWindows] = line.split('|');
236
+
237
+ if (!sessionMap.has(sessName)) {
238
+ sessionMap.set(sessName, {
239
+ name: sessName,
240
+ attached: sessAttached === '1',
241
+ windowCount: Number.parseInt(sessWindows, 10) || 0,
242
+ windows: [],
243
+ });
244
+ }
245
+
246
+ sessionMap.get(sessName)?.windows.push({
247
+ sessionName: sessName,
248
+ index: Number.parseInt(winIdx, 10) || 0,
249
+ name: winName,
250
+ active: winActive === '1',
251
+ paneCount: Number.parseInt(winPanes, 10) || 0,
252
+ });
253
+ }
254
+
255
+ return Array.from(sessionMap.values()).sort((a, b) => a.name.localeCompare(b.name));
256
+ }
@@ -21,6 +21,7 @@ export interface TmuxPane {
21
21
  command: string;
22
22
  title: string;
23
23
  size: string;
24
+ isDead: boolean;
24
25
  }
25
26
 
26
27
  export interface TmuxWindow {
@@ -51,6 +52,8 @@ export interface DiagnosticGaps {
51
52
  totalExecutors: number;
52
53
  /** Total panes running claude */
53
54
  totalClaudePanes: number;
55
+ /** Total dead panes (exited) across all sessions */
56
+ deadPaneCount: number;
54
57
  }
55
58
 
56
59
  export interface DiagnosticSnapshot {
@@ -93,6 +96,7 @@ function parsePaneLine(parts: string[]): {
93
96
  sessAttached,
94
97
  sessWindows,
95
98
  sessCreated,
99
+ paneDead,
96
100
  ] = parts;
97
101
  return {
98
102
  sessionName,
@@ -119,6 +123,7 @@ function parsePaneLine(parts: string[]): {
119
123
  command: paneCmd,
120
124
  title: paneTitle,
121
125
  size: paneSize,
126
+ isDead: paneDead === '1',
122
127
  },
123
128
  };
124
129
  }
@@ -126,7 +131,7 @@ function parsePaneLine(parts: string[]): {
126
131
  /** Collect all tmux sessions, windows, and panes into a typed tree. */
127
132
  function getTmuxInventory(): TmuxSession[] {
128
133
  const paneOutput = execQuiet(
129
- "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}'",
134
+ "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}'",
130
135
  );
131
136
 
132
137
  if (!paneOutput) return [];
@@ -137,7 +142,7 @@ function getTmuxInventory(): TmuxSession[] {
137
142
  for (const line of paneOutput.split('\n')) {
138
143
  if (!line) continue;
139
144
  const parts = line.split('|');
140
- if (parts.length < 14) continue;
145
+ if (parts.length < 15) continue;
141
146
 
142
147
  const parsed = parsePaneLine(parts);
143
148
 
@@ -194,12 +199,16 @@ function detectGaps(executors: TuiExecutor[], sessions: TmuxSession[]): Diagnost
194
199
 
195
200
  const linkedCount = executors.filter((e) => e.tmuxPaneId && !deadPidExecutors.some((d) => d.id === e.id)).length;
196
201
 
202
+ const allPanes = sessions.flatMap((s) => s.windows.flatMap((w) => w.panes));
203
+ const deadPaneCount = allPanes.filter((p) => p.isDead).length;
204
+
197
205
  return {
198
206
  deadPidExecutors,
199
207
  orphanPanes,
200
208
  linkedCount,
201
209
  totalExecutors: executors.length,
202
210
  totalClaudePanes: claudePanes.length,
211
+ deadPaneCount,
203
212
  };
204
213
  }
205
214
 
package/src/tui/tmux.ts CHANGED
@@ -100,6 +100,26 @@ export function switchRightPane(rightPane: string, targetSession: string): void
100
100
  attachProject(rightPane, targetSession);
101
101
  }
102
102
 
103
+ /** Switch right pane to a specific session window */
104
+ export function attachProjectWindow(rightPane: string, targetSession: string, windowIndex?: number): void {
105
+ const pane = resolveRightPane(rightPane);
106
+ ensureSession(targetSession);
107
+ if (windowIndex !== undefined) {
108
+ try {
109
+ execSync(`tmux select-window -t '${targetSession}:${windowIndex}'`, { stdio: 'ignore' });
110
+ } catch {
111
+ // window may not exist
112
+ }
113
+ }
114
+ try {
115
+ execSync(`tmux respawn-pane -k -t ${pane} "TMUX='' tmux attach-session -t '${targetSession}'"`, {
116
+ stdio: 'ignore',
117
+ });
118
+ } catch {
119
+ // pane doesn't exist
120
+ }
121
+ }
122
+
103
123
  /**
104
124
  * Set up TUI keybindings in a dedicated key table (not global root).
105
125
  * Uses a custom key table "genie-tui" so bindings only apply inside the TUI session.
@@ -108,7 +128,7 @@ export function switchRightPane(rightPane: string, targetSession: string): void
108
128
  function setupKeybindings(session: string): void {
109
129
  try {
110
130
  // Define bindings in the genie-tui key table (session-scoped, not global)
111
- execSync(`tmux bind-key -T ${KEY_TABLE} Tab select-pane -t ${session}:0.+ \\; switch-client -T ${KEY_TABLE}`, {
131
+ execSync(`tmux bind-key -T ${KEY_TABLE} Tab select-pane -t :.+ \\; switch-client -T ${KEY_TABLE}`, {
112
132
  stdio: 'ignore',
113
133
  });
114
134