@kolisachint/hoocode-agent 0.4.76 → 0.4.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.77] - 2026-06-21
4
+
3
5
  ## [0.4.76] - 2026-06-21
4
6
 
5
7
  ### Fixed
@@ -1 +1 @@
1
- {"version":3,"file":"task-panel.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAiC1E;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;AAse3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,kBAAmB,YAAW,SAAS,EAAE,SAAS;IAC9D,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAa;IAChC,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,IAAI,CAAyB;IACrC,OAAO,CAAC,QAAQ,CAAS;IAKzB,OAAO,UAAS;IAChB,OAAO,CAAC,YAAY,CAAqB;IACzC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,wDAAwD;IACxD,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IAEzB,YAAY,EAAE,CAAC,EAAE,GAAG,EAEnB;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,OAAO,CAAC,UAAU;IAIlB,2EAA2E;IAC3E,WAAW,IAAI,MAAM,GAAG,SAAS,CAKhC;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAyB9B;IAED,OAAO,IAAI,aAAa,CAEvB;IAED,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAGjC;IAED;;;;OAIG;IACH,SAAS,IAAI,aAAa,CAMzB;IAED,6EAA6E;IAC7E,OAAO,CAAC,eAAe;IAsBvB,gDAAgD;IAChD,OAAO,IAAI,IAAI,CAMd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAoI9B;CACD","sourcesContent":["import type { Component, Focusable, TUI } from \"@kolisachint/hoocode-tui\";\nimport { getKeybindings, matchesKey, truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type { Task, TaskAgent, TaskAgentKind, TaskAgentState, TaskStatus } from \"../../../core/task-store.js\";\nimport { taskOwnerId, taskStore } from \"../../../core/task-store.js\";\nimport type { ThemeColor } from \"../theme/theme.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/**\n * Single-cell marker for MCP-sourced rows, which have no owning agent. Every\n * other row derives its marker from the owner's kind via AGENT_GLYPH (◆ main /\n * ◇ subagent / ▸ team role), so the flat lens attributes a row exactly the way\n * the grouped lenses do. The row also carries a text origin tag before the\n * title (see formatTaskLine).\n */\nconst MCP_SOURCE_GLYPH = \"⧉\";\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/**\n * How the same task list is presented:\n * - flat → one ungrouped list (default)\n * - subagents → grouped by owning agent (◆ main orchestrator + ◇ workers)\n * - teams → grouped by named role-agent (▸), with handoff arrows\n */\nexport type TaskPanelView = \"flat\" | \"subagents\" | \"teams\";\n\nconst VIEW_LABEL: Record<TaskPanelView, string> = { flat: \"tasks\", subagents: \"subagents\", teams: \"teams\" };\n\n/**\n * A top-level task the main agent owns directly: its own TodoWrite plan. Source\n * is unset (not a subagent/MCP delegation) and it sits at the root of the forest\n * (not a child merged in from a subagent's subtree). These are exactly the rows\n * the flat (\"tasks\") lens shows.\n */\nfunction isMainTask(task: Task): boolean {\n\t// Source-unset (not a subagent/MCP delegation — note taskOwnerId folds MCP into\n\t// \"main\", so check source directly), not owned by a role agent, and a forest\n\t// root (not a child merged in from a subagent's subtree).\n\treturn task.source === undefined && task.agent === undefined && task.parentTaskId === undefined;\n}\n\n/**\n * Lenses that currently have content, split by ownership:\n * - flat (\"tasks\") → only when the main agent has its own TodoWrite plan.\n * - subagents → when delegated work exists (a registered subagent, or any\n * subagent/MCP-sourced task).\n * - teams → when role agents are registered (hooteams `--team`).\n * The cycle key and the header switcher both skip empty lenses, and an empty\n * flat lens falls through to subagents (see render), so a session that only\n * delegated work opens straight on the task tree with no empty \"tasks\" view.\n */\nfunction availableViews(tasks: readonly Task[], agents: readonly TaskAgent[]): TaskPanelView[] {\n\tconst views: TaskPanelView[] = [];\n\tif (tasks.some(isMainTask)) views.push(\"flat\");\n\tconst hasSubagentWork =\n\t\tagents.some((a) => a.kind === \"subagent\") || tasks.some((t) => t.source === \"subagent\" || t.source === \"mcp\");\n\tif (hasSubagentWork) views.push(\"subagents\");\n\tif (agents.some((a) => a.kind === \"role\")) views.push(\"teams\");\n\treturn views;\n}\n\n/** Owner glyphs: main agent a filled diamond, spawned subagents the hollow counterpart, team roles a triangle. */\nconst AGENT_GLYPH: Record<TaskAgentKind, string> = { main: \"◆\", subagent: \"◇\", role: \"▸\" };\nconst AGENT_GLYPH_COLOR: Record<TaskAgentKind, ThemeColor> = {\n\tmain: \"accent\",\n\tsubagent: \"accent\",\n\trole: \"borderAccent\",\n};\n\n/** Color for an agent's lifecycle `[state]` tag (mirrors the design's .ast-* classes). */\nconst AGENT_STATE_COLOR: Record<TaskAgentState, ThemeColor> = {\n\tactive: \"warning\",\n\trunning: \"warning\",\n\tdone: \"success\",\n\tqueued: \"dim\",\n\tidle: \"dim\",\n\twaiting: \"mdLink\",\n\tfailed: \"error\",\n};\n\n/** Two-cell indent under a group header, with a faint vertical guide. */\nconst GROUP_INDENT_PLAIN = \"│ \";\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * View switcher rendered at the right edge of the ledger header: the labels\n * of the lenses that have content joined by `·`, the active one in bold\n * accent. Hidden entirely when only one lens is available. Purely an\n * indicator in the TUI — the bound key cycles it (see app.tasks.cycleView).\n */\nfunction formatViewSwitcher(\n\tview: TaskPanelView,\n\tavailable: readonly TaskPanelView[],\n): { plain: string; styled: string } {\n\tif (available.length < 2) return { plain: \"\", styled: \"\" };\n\tconst plain = available.map((v) => VIEW_LABEL[v]).join(\" · \");\n\tconst styled = available\n\t\t.map((v) => (v === view ? theme.bold(theme.fg(\"accent\", VIEW_LABEL[v])) : theme.fg(\"dim\", VIEW_LABEL[v])))\n\t\t.join(theme.fg(\"dim\", \" · \"));\n\treturn { plain, styled };\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) plus the view\n * switcher on the right.\n */\nfunction formatHeader(\n\ttasks: readonly Task[],\n\twidth: number,\n\tstate: PanelState,\n\ttotalSecs: number,\n\tview: TaskPanelView,\n\tavailable: readonly TaskPanelView[],\n): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\t// Right cluster: turn delta, then the view switcher at the far edge. The\n\t// switcher is the first thing dropped when the terminal narrows; the turn\n\t// delta next; the stamp/count survive to the end. Either piece may be\n\t// absent (no usage reported / only one lens available).\n\tconst switcher = formatViewSwitcher(view, available);\n\tconst rightVariants: Array<{ plain: string; styled: string }> = [];\n\tif (turnPlain && switcher.plain) {\n\t\trightVariants.push({\n\t\t\tplain: `${turnPlain} ${switcher.plain}`,\n\t\t\tstyled: `${turnText} ${switcher.styled}`,\n\t\t});\n\t}\n\tif (turnPlain) rightVariants.push({ plain: turnPlain, styled: turnText });\n\telse if (switcher.plain) rightVariants.push(switcher);\n\n\tfor (const right of rightVariants) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(right.plain));\n\t\t\treturn leftFull + \" \".repeat(pad) + right.styled;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(right.plain));\n\t\t\treturn leftMin + \" \".repeat(pad) + right.styled;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) {\n\t\treturn leftFull + \" \".repeat(width - visibleWidth(leftFullPlain));\n\t}\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(\n\ttask: Task,\n\twidth: number,\n\tframe: number,\n\toptions: { grouped?: boolean; owner?: TaskAgent; treePrefix?: string } = {},\n): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\t// In grouped views the group header already carries the row's origin, so the\n\t// owner glyph and tag are suppressed; the rows sit on a faint indent guide.\n\tconst grouped = options.grouped === true;\n\tconst indent = grouped ? theme.fg(\"borderMuted\", GROUP_INDENT_PLAIN) : \"\";\n\n\t// Owner marker between the status icon and the id, derived from the owning\n\t// agent's kind so the flat lens attributes rows the same way the grouped\n\t// lenses do (a roster-less owner falls back on the task's source). MCP rows\n\t// have no owning agent and keep their own ⧉ marker. Every row carries one\n\t// cell, so the id column stays aligned.\n\tconst isMcp = task.source === \"mcp\";\n\tconst ownerKind = options.owner?.kind ?? (task.source === \"subagent\" ? \"subagent\" : \"main\");\n\tconst sourceGlyph = isMcp ? MCP_SOURCE_GLYPH : AGENT_GLYPH[ownerKind];\n\tconst styledSource = grouped ? \"\" : theme.fg(\"dim\", sourceGlyph);\n\n\t// Origin tag prefixed to the title, naming who runs the row: the subagent\n\t// type (\"[explore]\"), the team role's name (\"[planner]\"), or the MCP server\n\t// (\"[github]\"; \"[MCP]\" when no server label was recorded). Drawn in accent,\n\t// parallel to the chat's `Agent [explore]` / `MCP [server › tool]`. Grouped\n\t// rows drop it — the group header carries the origin — except MCP rows,\n\t// which group under main without being main's own work.\n\tlet tag = \"\";\n\tif (isMcp) tag = `[${task.subagentMode ?? \"MCP\"}]`;\n\telse if (!grouped) {\n\t\tif (task.subagentMode) tag = `[${task.subagentMode}]`;\n\t\telse if (ownerKind === \"role\" && options.owner) tag = `[${options.owner.name}]`;\n\t}\n\tconst styledTag = tag ? `${theme.fg(\"accent\", tag)} ` : \"\";\n\tconst title = task.title;\n\t// The title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their token usage; the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tif (tokenText) {\n\t\t\trightPlain = tokenText;\n\t\t\trightStyled = theme.fg(\"muted\", tokenText);\n\t\t}\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\t// A warning note (e.g. inherited-model fallback, exhaustion skip) takes over the\n\t// right column as a ⚠ cue, replacing the usage/status stamp for that row.\n\tif (task.note) {\n\t\trightPlain = `⚠ ${task.note}`;\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\t// truncateToWidth measures visible width (ANSI-aware), so the styled left can be\n\t// truncated against the full left budget directly. Subtracting the prefix here\n\t// (as a prior version did) truncated titles early and unevenly per id width.\n\t// In the subagents tree, a depth-first connector prefix (└─/├─/│) sits before\n\t// the row's glyph; roots pass an empty prefix and read exactly like flat rows.\n\tconst treePrefix = options.treePrefix ? theme.fg(\"borderMuted\", options.treePrefix) : \"\";\n\tconst leftBody = grouped\n\t\t? `${indent}${icon} ${styledTag}${styledTitle}`\n\t\t: `${treePrefix}${icon} ${styledSource} ${styledTag}${styledTitle}`;\n\tconst left = truncateToWidth(leftBody, leftWidth, \"…\");\n\n\tif (!rightPlain) return left;\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Filter tasks and agents to the teams lens: only role agents and the tasks they\n * own. The flat and subagents lenses do their own ownership filtering inline (by\n * source and parentTaskId), so this is teams-specific.\n */\nfunction filterTasksForView(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n\t_view: TaskPanelView,\n): { filteredTasks: readonly Task[]; filteredAgents: readonly TaskAgent[] } {\n\tconst roleIds = new Set(agents.filter((a) => a.kind === \"role\").map((a) => a.id));\n\treturn {\n\t\tfilteredAgents: agents.filter((a) => a.kind === \"role\"),\n\t\tfilteredTasks: tasks.filter((t) => roleIds.has(taskOwnerId(t))),\n\t};\n}\n\n/**\n * Scope the full task list to the tasks visible in the given lens. The header,\n * state stamp, and done/total count are derived from this subset so they match\n * exactly what the user sees in the current view.\n */\nfunction filterTasksForLens(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n\tview: TaskPanelView,\n): readonly Task[] {\n\tswitch (view) {\n\t\tcase \"flat\":\n\t\t\treturn tasks.filter(isMainTask);\n\t\tcase \"subagents\":\n\t\t\treturn tasks.filter((t) => t.source === \"subagent\" || t.source === \"mcp\" || t.parentTaskId !== undefined);\n\t\tcase \"teams\": {\n\t\t\tconst roleIds = new Set<string>();\n\t\t\tfor (const a of agents) {\n\t\t\t\tif (a.kind === \"role\") roleIds.add(a.id);\n\t\t\t}\n\t\t\treturn tasks.filter((t) => roleIds.has(taskOwnerId(t)));\n\t\t}\n\t}\n}\n\n/** Fallback group metadata when a task's owner has no roster entry. */\nfunction defaultAgentMeta(id: string): TaskAgent {\n\treturn id === \"main\"\n\t\t? { id, name: \"main\", role: \"orchestrator\", kind: \"main\" }\n\t\t: { id, name: id, role: \"subagent\", kind: \"subagent\" };\n}\n\n/**\n * Partition the flat task list into owner groups. An explicit task.agent wins;\n * otherwise a subagent-sourced task falls into a generic \"subagent\" group and\n * everything else into \"main\". Group order is deterministic — main first, then\n * roster order, then stragglers — never reordered by status.\n */\nfunction groupTasks(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n): Array<{ id: string; meta: TaskAgent; items: Task[] }> {\n\tconst meta = new Map<string, TaskAgent>(agents.map((a) => [a.id, a]));\n\tconst groups = new Map<string, Task[]>();\n\tfor (const task of tasks) {\n\t\tconst owner = taskOwnerId(task);\n\t\tconst items = groups.get(owner);\n\t\tif (items) items.push(task);\n\t\telse groups.set(owner, [task]);\n\t}\n\tconst order: string[] = [];\n\tif (groups.has(\"main\")) order.push(\"main\");\n\tfor (const agent of agents) {\n\t\tif (groups.has(agent.id) && !order.includes(agent.id)) order.push(agent.id);\n\t}\n\tfor (const id of groups.keys()) {\n\t\tif (!order.includes(id)) order.push(id);\n\t}\n\treturn order.map((id) => ({\n\t\tid,\n\t\tmeta: meta.get(id) ?? defaultAgentMeta(id),\n\t\titems: groups.get(id) ?? [],\n\t}));\n}\n\n/**\n * Group header for the grouped views: owner glyph + bold name + role, the\n * agent's lifecycle `[state]` tag, an optional handoff arrow (teams), then the\n * agent's own token/cost totals + done/total on the right. Mirrors the footer's\n * \"every number accounted for\" stance, but per agent.\n */\nfunction formatGroupHeader(meta: TaskAgent, items: readonly Task[], width: number, selected = false): string {\n\t// A focused role row swaps its ▸ for a filled ▶ in accent — the team-focus\n\t// selection cursor (the owner-kind glyph mapping itself is unchanged).\n\tconst glyph = selected\n\t\t? theme.fg(\"accent\", \"▶\")\n\t\t: theme.fg(AGENT_GLYPH_COLOR[meta.kind], AGENT_GLYPH[meta.kind] ?? AGENT_GLYPH.subagent);\n\tconst name = selected ? theme.bold(theme.fg(\"accent\", meta.name)) : theme.bold(meta.name);\n\t// Roles read as a dim \"· role\" suffix for spawned/team agents; the main\n\t// orchestrator's role sits brighter (muted), matching the design's .grp-role.\n\tconst role = meta.role\n\t\t? meta.kind === \"main\"\n\t\t\t? ` ${theme.fg(\"muted\", meta.role)}`\n\t\t\t: theme.fg(\"dim\", ` · ${meta.role}`)\n\t\t: \"\";\n\tconst state = meta.state ? ` ${theme.fg(AGENT_STATE_COLOR[meta.state] ?? \"dim\", `[${meta.state}]`)}` : \"\";\n\t// Live activity for a running subagent (e.g. the tool it's executing). Empty\n\t// string means idle/between-tools, so it disappears rather than lingering stale.\n\tconst activity = meta.activity ? theme.fg(\"dim\", ` ⋯ ${meta.activity}`) : \"\";\n\tconst handoff = meta.handoff ? ` ${theme.fg(\"dim\", meta.handoff)}` : \"\";\n\n\tconst done = items.filter((t) => t.status === \"done\").length;\n\tconst countPlain = `${done}/${items.length}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${items.length}`);\n\tconst stats = meta.stats;\n\tlet rightPlain = countPlain;\n\tlet rightStyled = count;\n\tif (stats && (stats.input > 0 || stats.output > 0 || stats.cost > 0)) {\n\t\tconst statsPlain = `↑${formatTokens(stats.input)} ↓${formatTokens(stats.output)} · $${stats.cost.toFixed(3)}`;\n\t\trightPlain = `${statsPlain} ${countPlain}`;\n\t\trightStyled = `${theme.fg(\"dim\", statsPlain)} ${count}`;\n\t}\n\n\tconst rightWidth = visibleWidth(rightPlain) + 1;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\tconst left = truncateToWidth(`${glyph} ${name}${role}${state}${activity}${handoff}`, leftWidth, \"…\");\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - A single-cell owner glyph (◆ main / ◇ subagent / ▸ team role / ⧉ MCP) sits\n * before the id, derived from the owning agent's kind, so every row's origin\n * is readable at a glance even in the flat lens. A text origin tag before the\n * title names the owner: the subagent type (\"[explore]\"), the team role\n * (\"[planner]\", fed by `--team <url>`), or the MCP server (\"[github]\").\n * - Three views split by ownership (cycled via app.tasks.cycleView, shown as a\n * `tasks · subagents · teams` switcher in the header):\n * - flat (\"tasks\") — only the main agent's own TodoWrite plan;\n * - subagents — a recursive task tree over delegated work, where a subagent\n * that spawned a subagent shows its nested tasks (roots are the dispatched\n * subagents and direct MCP calls; children link via parentTaskId, merged\n * across the process boundary). Each task is its own node keeping its\n * [subagentMode]/[server] tag, with depth drawn by └─/├─/│ connectors; a\n * run with only top-level tasks reads flat (no extra indent);\n * - teams — grouped by named role-agent with handoff arrows.\n * The cycle is adaptive: empty lenses are skipped and dropped from the\n * switcher, which hides entirely when only one lens has content. An empty flat\n * lens (no main task) falls through to subagents when delegated work exists.\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks — unless a team roster is\n * registered (`--team`), in which case the empty flat lens falls through to\n * teams and every role renders as a placeholder group, so idle roles are\n * visible from startup.\n */\nexport class TaskPanelComponent implements Component, Focusable {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate view: TaskPanelView = \"flat\";\n\tprivate disposed = false;\n\n\t// Team focus mode: when the TUI focuses the panel, role rows become a\n\t// navigable list (↑/↓ select, n nudge, a attach, q/esc back). The selection\n\t// is tracked by role name so a roster reorder doesn't move the cursor.\n\tfocused = false;\n\tprivate selectedRole: string | undefined;\n\t/** Open the inline nudge editor for the selected role. */\n\tonNudge?: (role: string) => void;\n\t/** Open the attach side panel for the selected role. */\n\tonAttach?: (role: string) => void;\n\t/** Leave team focus (focus returns to the main editor). */\n\tonExitFocus?: () => void;\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\tprivate roleAgents(): TaskAgent[] {\n\t\treturn taskStore.agents().filter((a) => a.kind === \"role\");\n\t}\n\n\t/** The role the team-focus cursor sits on (clamped to the live roster). */\n\tfocusedRole(): string | undefined {\n\t\tconst roles = this.roleAgents();\n\t\tif (roles.length === 0) return undefined;\n\t\tconst match = roles.find((a) => a.name === this.selectedRole);\n\t\treturn (match ?? roles[0]).name;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst roles = this.roleAgents();\n\t\tif (roles.length === 0) {\n\t\t\tthis.onExitFocus?.();\n\t\t\treturn;\n\t\t}\n\t\tconst keybindings = getKeybindings();\n\t\tconst index = Math.max(\n\t\t\t0,\n\t\t\troles.findIndex((a) => a.name === this.selectedRole),\n\t\t);\n\t\tif (keybindings.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedRole = roles[Math.max(0, index - 1)].name;\n\t\t} else if (keybindings.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedRole = roles[Math.min(roles.length - 1, index + 1)].name;\n\t\t} else if (matchesKey(data, \"n\")) {\n\t\t\tconst role = this.focusedRole();\n\t\t\tif (role) this.onNudge?.(role);\n\t\t} else if (matchesKey(data, \"a\")) {\n\t\t\tconst role = this.focusedRole();\n\t\t\tif (role) this.onAttach?.(role);\n\t\t} else if (matchesKey(data, \"q\") || keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onExitFocus?.();\n\t\t}\n\t\tthis.ui?.requestRender();\n\t}\n\n\tgetView(): TaskPanelView {\n\t\treturn this.view;\n\t}\n\n\tsetView(view: TaskPanelView): void {\n\t\tthis.view = view;\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Advance to the next view lens with content (flat → subagents → teams →\n\t * flat), skipping empty lenses. With nothing delegated this is a no-op on\n\t * flat; a stale view (its lens emptied since selection) snaps back to flat.\n\t */\n\tcycleView(): TaskPanelView {\n\t\tconst available = availableViews(taskStore.list(), taskStore.agents());\n\t\tconst idx = available.indexOf(this.view);\n\t\tthis.view = available[(idx + 1) % available.length] ?? \"flat\";\n\t\tthis.ui?.requestRender();\n\t\treturn this.view;\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (this.disposed) {\n\t\t\tif (this.animationTimer) {\n\t\t\t\tclearInterval(this.animationTimer);\n\t\t\t\tthis.animationTimer = null;\n\t\t\t\tthis.frame = 0;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t\tthis.disposed = true;\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.disposed) return [];\n\n\t\tconst tasks = taskStore.list();\n\t\tconst allAgents = taskStore.agents();\n\n\t\t// Resolve the lens: keep the stored view while it still has content, else\n\t\t// fall through to the first available lens (flat → subagents → teams). The\n\t\t// stored view is untouched so an explicit setView choice resumes once its\n\t\t// content returns. This is how an empty flat lens (no main/TodoWrite task)\n\t\t// falls through to the subagents tree when only delegated work exists, and\n\t\t// how an empty pane falls through to the teams roster at startup.\n\t\tconst available = availableViews(tasks, allAgents);\n\t\tlet view: TaskPanelView = available.includes(this.view) ? this.view : (available[0] ?? \"flat\");\n\n\t\t// Team focus always operates on the teams lens — the focused role list is\n\t\t// exactly what the lens renders, so the cursor is never invisible.\n\t\tif (this.focused) view = \"teams\";\n\n\t\t// In teams view the roster itself is content: role agents render as\n\t\t// placeholder groups even without tasks (idle roles at startup, queued\n\t\t// upcoming work).\n\t\tconst hasRoleRoster = view === \"teams\" && allAgents.some((a) => a.kind === \"role\");\n\n\t\tif (tasks.length === 0 && !hasRoleRoster) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\t// Scope all visual state to the tasks visible in the current lens so the\n\t\t// animation, rail color, and header count match exactly what the user sees.\n\t\tconst lensTasks = filterTasksForLens(tasks, allAgents, view);\n\t\tconst hasActive = lensTasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(lensTasks);\n\t\tconst totalSecs = lensTasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\tconst lines: string[] = [gutter + formatHeader(lensTasks, inner, state, totalSecs, view, available)];\n\n\t\tif (view === \"flat\") {\n\t\t\t// Only the main agent's own TodoWrite plan — delegated (subagent/MCP) work\n\t\t\t// belongs to the subagents lens. Resolve each row's owner from the roster\n\t\t\t// so the glyph/tag reflect the owning agent's kind, not just the source.\n\t\t\tconst agentById = new Map(allAgents.map((a) => [a.id, a]));\n\t\t\tfor (const task of tasks) {\n\t\t\t\tif (!isMainTask(task)) continue;\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { owner: agentById.get(taskOwnerId(task)) }));\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tif (view === \"subagents\") {\n\t\t\t// A recursive task tree over the delegated forest: roots are the main\n\t\t\t// agent's dispatched subagents (and direct MCP calls), children are the\n\t\t\t// tasks they spawned in turn (linked by parentTaskId, merged across the\n\t\t\t// process boundary), so a subagent that spawned a subagent is visible.\n\t\t\t// Each task is its own node keeping its [subagentMode]/[server] tag; depth\n\t\t\t// is drawn with └─/├─/│ connectors. With only top-level tasks the roots\n\t\t\t// carry an empty prefix and read exactly like flat rows (no extra indent).\n\t\t\tconst childrenByParent = new Map<number, Task[]>();\n\t\t\tfor (const task of tasks) {\n\t\t\t\tif (task.parentTaskId === undefined) continue;\n\t\t\t\tconst siblings = childrenByParent.get(task.parentTaskId);\n\t\t\t\tif (siblings) siblings.push(task);\n\t\t\t\telse childrenByParent.set(task.parentTaskId, [task]);\n\t\t\t}\n\t\t\tconst roots = tasks.filter(\n\t\t\t\t(t) => t.parentTaskId === undefined && (t.source === \"subagent\" || t.source === \"mcp\"),\n\t\t\t);\n\t\t\tconst walk = (task: Task, prefix: string, isLast: boolean, isRoot: boolean): void => {\n\t\t\t\tconst connector = isRoot ? \"\" : `${prefix}${isLast ? \"└─ \" : \"├─ \"}`;\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { treePrefix: connector }));\n\t\t\t\tconst kids = childrenByParent.get(task.id) ?? [];\n\t\t\t\tconst childPrefix = isRoot ? \"\" : `${prefix}${isLast ? \" \" : \"│ \"}`;\n\t\t\t\tfor (let i = 0; i < kids.length; i++) {\n\t\t\t\t\twalk(kids[i] as Task, childPrefix, i === kids.length - 1, false);\n\t\t\t\t}\n\t\t\t};\n\t\t\tfor (const root of roots) walk(root, \"\", true, true);\n\t\t\treturn lines;\n\t\t}\n\n\t\t// teams view: role-agent groups with handoff connectors and queued placeholders.\n\t\tconst { filteredTasks, filteredAgents } = filterTasksForView(tasks, allAgents, view);\n\t\tconst groups = groupTasks(filteredTasks, filteredAgents);\n\t\tconst groupIds = new Set(groups.map((g) => g.id));\n\t\t// Role agents with no tasks still get a group header: idle roles are the\n\t\t// roster at startup, queued ones upcoming work, done/failed ones the\n\t\t// state they settled in after reset() dropped their tasks.\n\t\tfor (const agent of filteredAgents) {\n\t\t\tif (!groupIds.has(agent.id)) {\n\t\t\t\tgroups.push({ id: agent.id, meta: agent, items: [] });\n\t\t\t}\n\t\t}\n\t\tconst cursorRole = this.focused ? this.focusedRole() : undefined;\n\t\tfor (const group of groups) {\n\t\t\tconst selected = cursorRole !== undefined && group.meta.kind === \"role\" && group.meta.name === cursorRole;\n\t\t\tlines.push(gutter + formatGroupHeader(group.meta, group.items, inner, selected));\n\t\t\tfor (const task of group.items) {\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { grouped: true }));\n\t\t\t}\n\t\t\t// Forward-handoff connector: emit \"└──→ name\" only for \"→ name\" arrows\n\t\t\t// (not back-references \"← name\"), and only when the target exists in the\n\t\t\t// visible role roster.\n\t\t\tconst { handoff } = group.meta;\n\t\t\tif (handoff) {\n\t\t\t\tconst arrowIdx = handoff.indexOf(\"→ \");\n\t\t\t\tif (arrowIdx !== -1) {\n\t\t\t\t\tconst nextName = handoff.slice(arrowIdx + 2).trim();\n\t\t\t\t\tif (filteredAgents.some((a) => a.name === nextName)) {\n\t\t\t\t\t\tconst connectorPrefix = `${GROUP_INDENT_PLAIN} └──→ `;\n\t\t\t\t\t\tconst connectorPad = Math.max(0, inner - visibleWidth(connectorPrefix) - visibleWidth(nextName));\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\tgutter +\n\t\t\t\t\t\t\t\ttheme.fg(\"borderMuted\", connectorPrefix) +\n\t\t\t\t\t\t\t\ttheme.fg(\"dim\", nextName) +\n\t\t\t\t\t\t\t\t\" \".repeat(connectorPad),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (this.focused) {\n\t\t\tlines.push(\n\t\t\t\tgutter + truncateToWidth(theme.fg(\"dim\", \"↑/↓ select · n nudge · a attach · q/esc back\"), inner, \"…\"),\n\t\t\t);\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
1
+ {"version":3,"file":"task-panel.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAiC1E;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;AA2e3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,kBAAmB,YAAW,SAAS,EAAE,SAAS;IAC9D,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAa;IAChC,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,IAAI,CAAyB;IACrC,OAAO,CAAC,QAAQ,CAAS;IAKzB,OAAO,UAAS;IAChB,OAAO,CAAC,YAAY,CAAqB;IACzC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,wDAAwD;IACxD,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IAEzB,YAAY,EAAE,CAAC,EAAE,GAAG,EAEnB;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,OAAO,CAAC,UAAU;IAIlB,2EAA2E;IAC3E,WAAW,IAAI,MAAM,GAAG,SAAS,CAKhC;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAyB9B;IAED,OAAO,IAAI,aAAa,CAEvB;IAED,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAGjC;IAED;;;;OAIG;IACH,SAAS,IAAI,aAAa,CAMzB;IAED,6EAA6E;IAC7E,OAAO,CAAC,eAAe;IAsBvB,gDAAgD;IAChD,OAAO,IAAI,IAAI,CAMd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAoI9B;CACD","sourcesContent":["import type { Component, Focusable, TUI } from \"@kolisachint/hoocode-tui\";\nimport { getKeybindings, matchesKey, truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type { Task, TaskAgent, TaskAgentKind, TaskAgentState, TaskStatus } from \"../../../core/task-store.js\";\nimport { taskOwnerId, taskStore } from \"../../../core/task-store.js\";\nimport type { ThemeColor } from \"../theme/theme.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/**\n * Single-cell marker for MCP-sourced rows, which have no owning agent. Every\n * other row derives its marker from the owner's kind via AGENT_GLYPH (◆ main /\n * ◇ subagent / ▸ team role), so the flat lens attributes a row exactly the way\n * the grouped lenses do. The row also carries a text origin tag before the\n * title (see formatTaskLine).\n */\nconst MCP_SOURCE_GLYPH = \"⧉\";\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/**\n * How the same task list is presented:\n * - flat → one ungrouped list (default)\n * - subagents → grouped by owning agent (◆ main orchestrator + ◇ workers)\n * - teams → grouped by named role-agent (▸), with handoff arrows\n */\nexport type TaskPanelView = \"flat\" | \"subagents\" | \"teams\";\n\nconst VIEW_LABEL: Record<TaskPanelView, string> = { flat: \"tasks\", subagents: \"subagents\", teams: \"teams\" };\n\n/**\n * A top-level task the main agent owns directly: its own TodoWrite plan. Source\n * is unset (not a subagent/MCP delegation) and it sits at the root of the forest\n * (not a child merged in from a subagent's subtree). These are exactly the rows\n * the flat (\"tasks\") lens shows.\n */\nfunction isMainTask(task: Task): boolean {\n\t// Source-unset (not a subagent/MCP delegation — note taskOwnerId folds MCP into\n\t// \"main\", so check source directly), not owned by a role agent, and a forest\n\t// root (not a child merged in from a subagent's subtree).\n\treturn task.source === undefined && task.agent === undefined && task.parentTaskId === undefined;\n}\n\n/**\n * Lenses that currently have content, split by ownership:\n * - flat (\"tasks\") → only when the main agent has its own TodoWrite plan.\n * - subagents → when delegated work exists (a registered subagent, or any\n * subagent/MCP-sourced task).\n * - teams → when role agents are registered (hooteams `--team`).\n * The cycle key and the header switcher both skip empty lenses, and an empty\n * flat lens falls through to subagents (see render), so a session that only\n * delegated work opens straight on the task tree with no empty \"tasks\" view.\n */\nfunction availableViews(tasks: readonly Task[], agents: readonly TaskAgent[]): TaskPanelView[] {\n\tconst views: TaskPanelView[] = [];\n\tif (tasks.some(isMainTask)) views.push(\"flat\");\n\tconst hasSubagentWork =\n\t\tagents.some((a) => a.kind === \"subagent\") || tasks.some((t) => t.source === \"subagent\" || t.source === \"mcp\");\n\tif (hasSubagentWork) views.push(\"subagents\");\n\tif (agents.some((a) => a.kind === \"role\")) views.push(\"teams\");\n\treturn views;\n}\n\n/** Owner glyphs: main agent a filled diamond, spawned subagents the hollow counterpart, team roles a triangle. */\nconst AGENT_GLYPH: Record<TaskAgentKind, string> = { main: \"◆\", subagent: \"◇\", role: \"▸\" };\nconst AGENT_GLYPH_COLOR: Record<TaskAgentKind, ThemeColor> = {\n\tmain: \"accent\",\n\tsubagent: \"accent\",\n\trole: \"borderAccent\",\n};\n\n/** Color for an agent's lifecycle `[state]` tag (mirrors the design's .ast-* classes). */\nconst AGENT_STATE_COLOR: Record<TaskAgentState, ThemeColor> = {\n\tactive: \"warning\",\n\trunning: \"warning\",\n\tdone: \"success\",\n\tqueued: \"dim\",\n\tidle: \"dim\",\n\twaiting: \"mdLink\",\n\tfailed: \"error\",\n};\n\n/** Two-cell indent under a group header, with a faint vertical guide. */\nconst GROUP_INDENT_PLAIN = \"│ \";\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * View switcher rendered at the right edge of the ledger header: the labels\n * of the lenses that have content joined by `·`, the active one in bold\n * accent. Hidden entirely when only one lens is available. Purely an\n * indicator in the TUI — the bound key cycles it (see app.tasks.cycleView).\n */\nfunction formatViewSwitcher(\n\tview: TaskPanelView,\n\tavailable: readonly TaskPanelView[],\n): { plain: string; styled: string } {\n\tif (available.length < 2) return { plain: \"\", styled: \"\" };\n\tconst plain = available.map((v) => VIEW_LABEL[v]).join(\" · \");\n\tconst styled = available\n\t\t.map((v) => (v === view ? theme.bold(theme.fg(\"accent\", VIEW_LABEL[v])) : theme.fg(\"dim\", VIEW_LABEL[v])))\n\t\t.join(theme.fg(\"dim\", \" · \"));\n\treturn { plain, styled };\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) plus the view\n * switcher on the right.\n */\nfunction formatHeader(\n\ttasks: readonly Task[],\n\twidth: number,\n\tstate: PanelState,\n\ttotalSecs: number,\n\tview: TaskPanelView,\n\tavailable: readonly TaskPanelView[],\n): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\t// Right cluster: turn delta, then the view switcher at the far edge. The\n\t// switcher is the first thing dropped when the terminal narrows; the turn\n\t// delta next; the stamp/count survive to the end. Either piece may be\n\t// absent (no usage reported / only one lens available).\n\tconst switcher = formatViewSwitcher(view, available);\n\tconst rightVariants: Array<{ plain: string; styled: string }> = [];\n\tif (turnPlain && switcher.plain) {\n\t\trightVariants.push({\n\t\t\tplain: `${turnPlain} ${switcher.plain}`,\n\t\t\tstyled: `${turnText} ${switcher.styled}`,\n\t\t});\n\t}\n\tif (turnPlain) rightVariants.push({ plain: turnPlain, styled: turnText });\n\telse if (switcher.plain) rightVariants.push(switcher);\n\n\tfor (const right of rightVariants) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(right.plain));\n\t\t\treturn leftFull + \" \".repeat(pad) + right.styled;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(right.plain));\n\t\t\treturn leftMin + \" \".repeat(pad) + right.styled;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) {\n\t\treturn leftFull + \" \".repeat(width - visibleWidth(leftFullPlain));\n\t}\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(\n\ttask: Task,\n\twidth: number,\n\tframe: number,\n\toptions: { grouped?: boolean; owner?: TaskAgent; treePrefix?: string } = {},\n): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\t// In grouped views the group header already carries the row's origin, so the\n\t// owner glyph and tag are suppressed; the rows sit on a faint indent guide.\n\tconst grouped = options.grouped === true;\n\tconst indent = grouped ? theme.fg(\"borderMuted\", GROUP_INDENT_PLAIN) : \"\";\n\n\t// Owner marker between the status icon and the id, derived from the owning\n\t// agent's kind so the flat lens attributes rows the same way the grouped\n\t// lenses do (a roster-less owner falls back on the task's source). MCP rows\n\t// have no owning agent and keep their own ⧉ marker. Every row carries one\n\t// cell, so the id column stays aligned.\n\tconst isMcp = task.source === \"mcp\";\n\tconst ownerKind = options.owner?.kind ?? (task.source === \"subagent\" ? \"subagent\" : \"main\");\n\tconst sourceGlyph = isMcp ? MCP_SOURCE_GLYPH : AGENT_GLYPH[ownerKind];\n\tconst styledSource = grouped ? \"\" : theme.fg(\"dim\", sourceGlyph);\n\n\t// Origin tag prefixed to the title, naming who runs the row: the subagent\n\t// type (\"[explore]\"), the team role's name (\"[planner]\"), or the MCP server\n\t// (\"[github]\"; \"[MCP]\" when no server label was recorded). Drawn in accent,\n\t// parallel to the chat's `Agent [explore]` / `MCP [server › tool]`. Grouped\n\t// rows drop it — the group header carries the origin — except MCP rows,\n\t// which group under main without being main's own work.\n\tlet tag = \"\";\n\tif (isMcp) tag = `[${task.subagentMode ?? \"MCP\"}]`;\n\telse if (!grouped) {\n\t\tif (task.subagentMode) tag = `[${task.subagentMode}]`;\n\t\telse if (ownerKind === \"role\" && options.owner) tag = `[${options.owner.name}]`;\n\t}\n\tconst styledTag = tag ? `${theme.fg(\"accent\", tag)} ` : \"\";\n\tconst title = task.title;\n\t// The title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their token usage; the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tif (tokenText) {\n\t\t\trightPlain = tokenText;\n\t\t\trightStyled = theme.fg(\"muted\", tokenText);\n\t\t}\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\t// A warning note (e.g. inherited-model fallback, exhaustion skip) takes over the\n\t// right column as a ⚠ cue, replacing the usage/status stamp for that row.\n\tif (task.note) {\n\t\trightPlain = `⚠ ${task.note}`;\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\t// truncateToWidth measures visible width (ANSI-aware), so the styled left can be\n\t// truncated against the full left budget directly. Subtracting the prefix here\n\t// (as a prior version did) truncated titles early and unevenly per id width.\n\t// In the subagents tree, a depth-first connector prefix (└─/├─/│) sits before\n\t// the row's glyph; roots pass an empty prefix and read exactly like flat rows.\n\tconst treePrefix = options.treePrefix ? theme.fg(\"borderMuted\", options.treePrefix) : \"\";\n\tconst leftBody = grouped\n\t\t? `${indent}${icon} ${styledTag}${styledTitle}`\n\t\t: `${treePrefix}${icon} ${styledSource} ${styledTag}${styledTitle}`;\n\tconst left = truncateToWidth(leftBody, leftWidth, \"…\");\n\n\t// Pad every row to the full pane width so rows align regardless of whether they\n\t// carry a right column (token usage / running… / queued).\n\tif (!rightPlain) {\n\t\tconst pad = Math.max(0, width - visibleWidth(left));\n\t\treturn left + \" \".repeat(pad);\n\t}\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Filter tasks and agents to the teams lens: only role agents and the tasks they\n * own. The flat and subagents lenses do their own ownership filtering inline (by\n * source and parentTaskId), so this is teams-specific.\n */\nfunction filterTasksForView(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n\t_view: TaskPanelView,\n): { filteredTasks: readonly Task[]; filteredAgents: readonly TaskAgent[] } {\n\tconst roleIds = new Set(agents.filter((a) => a.kind === \"role\").map((a) => a.id));\n\treturn {\n\t\tfilteredAgents: agents.filter((a) => a.kind === \"role\"),\n\t\tfilteredTasks: tasks.filter((t) => roleIds.has(taskOwnerId(t))),\n\t};\n}\n\n/**\n * Scope the full task list to the tasks visible in the given lens. The header,\n * state stamp, and done/total count are derived from this subset so they match\n * exactly what the user sees in the current view.\n */\nfunction filterTasksForLens(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n\tview: TaskPanelView,\n): readonly Task[] {\n\tswitch (view) {\n\t\tcase \"flat\":\n\t\t\treturn tasks.filter(isMainTask);\n\t\tcase \"subagents\":\n\t\t\treturn tasks.filter((t) => t.source === \"subagent\" || t.source === \"mcp\" || t.parentTaskId !== undefined);\n\t\tcase \"teams\": {\n\t\t\tconst roleIds = new Set<string>();\n\t\t\tfor (const a of agents) {\n\t\t\t\tif (a.kind === \"role\") roleIds.add(a.id);\n\t\t\t}\n\t\t\treturn tasks.filter((t) => roleIds.has(taskOwnerId(t)));\n\t\t}\n\t}\n}\n\n/** Fallback group metadata when a task's owner has no roster entry. */\nfunction defaultAgentMeta(id: string): TaskAgent {\n\treturn id === \"main\"\n\t\t? { id, name: \"main\", role: \"orchestrator\", kind: \"main\" }\n\t\t: { id, name: id, role: \"subagent\", kind: \"subagent\" };\n}\n\n/**\n * Partition the flat task list into owner groups. An explicit task.agent wins;\n * otherwise a subagent-sourced task falls into a generic \"subagent\" group and\n * everything else into \"main\". Group order is deterministic — main first, then\n * roster order, then stragglers — never reordered by status.\n */\nfunction groupTasks(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n): Array<{ id: string; meta: TaskAgent; items: Task[] }> {\n\tconst meta = new Map<string, TaskAgent>(agents.map((a) => [a.id, a]));\n\tconst groups = new Map<string, Task[]>();\n\tfor (const task of tasks) {\n\t\tconst owner = taskOwnerId(task);\n\t\tconst items = groups.get(owner);\n\t\tif (items) items.push(task);\n\t\telse groups.set(owner, [task]);\n\t}\n\tconst order: string[] = [];\n\tif (groups.has(\"main\")) order.push(\"main\");\n\tfor (const agent of agents) {\n\t\tif (groups.has(agent.id) && !order.includes(agent.id)) order.push(agent.id);\n\t}\n\tfor (const id of groups.keys()) {\n\t\tif (!order.includes(id)) order.push(id);\n\t}\n\treturn order.map((id) => ({\n\t\tid,\n\t\tmeta: meta.get(id) ?? defaultAgentMeta(id),\n\t\titems: groups.get(id) ?? [],\n\t}));\n}\n\n/**\n * Group header for the grouped views: owner glyph + bold name + role, the\n * agent's lifecycle `[state]` tag, an optional handoff arrow (teams), then the\n * agent's own token/cost totals + done/total on the right. Mirrors the footer's\n * \"every number accounted for\" stance, but per agent.\n */\nfunction formatGroupHeader(meta: TaskAgent, items: readonly Task[], width: number, selected = false): string {\n\t// A focused role row swaps its ▸ for a filled ▶ in accent — the team-focus\n\t// selection cursor (the owner-kind glyph mapping itself is unchanged).\n\tconst glyph = selected\n\t\t? theme.fg(\"accent\", \"▶\")\n\t\t: theme.fg(AGENT_GLYPH_COLOR[meta.kind], AGENT_GLYPH[meta.kind] ?? AGENT_GLYPH.subagent);\n\tconst name = selected ? theme.bold(theme.fg(\"accent\", meta.name)) : theme.bold(meta.name);\n\t// Roles read as a dim \"· role\" suffix for spawned/team agents; the main\n\t// orchestrator's role sits brighter (muted), matching the design's .grp-role.\n\tconst role = meta.role\n\t\t? meta.kind === \"main\"\n\t\t\t? ` ${theme.fg(\"muted\", meta.role)}`\n\t\t\t: theme.fg(\"dim\", ` · ${meta.role}`)\n\t\t: \"\";\n\tconst state = meta.state ? ` ${theme.fg(AGENT_STATE_COLOR[meta.state] ?? \"dim\", `[${meta.state}]`)}` : \"\";\n\t// Live activity for a running subagent (e.g. the tool it's executing). Empty\n\t// string means idle/between-tools, so it disappears rather than lingering stale.\n\tconst activity = meta.activity ? theme.fg(\"dim\", ` ⋯ ${meta.activity}`) : \"\";\n\tconst handoff = meta.handoff ? ` ${theme.fg(\"dim\", meta.handoff)}` : \"\";\n\n\tconst done = items.filter((t) => t.status === \"done\").length;\n\tconst countPlain = `${done}/${items.length}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${items.length}`);\n\tconst stats = meta.stats;\n\tlet rightPlain = countPlain;\n\tlet rightStyled = count;\n\tif (stats && (stats.input > 0 || stats.output > 0 || stats.cost > 0)) {\n\t\tconst statsPlain = `↑${formatTokens(stats.input)} ↓${formatTokens(stats.output)} · $${stats.cost.toFixed(3)}`;\n\t\trightPlain = `${statsPlain} ${countPlain}`;\n\t\trightStyled = `${theme.fg(\"dim\", statsPlain)} ${count}`;\n\t}\n\n\tconst rightWidth = visibleWidth(rightPlain) + 1;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\tconst left = truncateToWidth(`${glyph} ${name}${role}${state}${activity}${handoff}`, leftWidth, \"…\");\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - A single-cell owner glyph (◆ main / ◇ subagent / ▸ team role / ⧉ MCP) sits\n * before the id, derived from the owning agent's kind, so every row's origin\n * is readable at a glance even in the flat lens. A text origin tag before the\n * title names the owner: the subagent type (\"[explore]\"), the team role\n * (\"[planner]\", fed by `--team <url>`), or the MCP server (\"[github]\").\n * - Three views split by ownership (cycled via app.tasks.cycleView, shown as a\n * `tasks · subagents · teams` switcher in the header):\n * - flat (\"tasks\") — only the main agent's own TodoWrite plan;\n * - subagents — a recursive task tree over delegated work, where a subagent\n * that spawned a subagent shows its nested tasks (roots are the dispatched\n * subagents and direct MCP calls; children link via parentTaskId, merged\n * across the process boundary). Each task is its own node keeping its\n * [subagentMode]/[server] tag, with depth drawn by └─/├─/│ connectors; a\n * run with only top-level tasks reads flat (no extra indent);\n * - teams — grouped by named role-agent with handoff arrows.\n * The cycle is adaptive: empty lenses are skipped and dropped from the\n * switcher, which hides entirely when only one lens has content. An empty flat\n * lens (no main task) falls through to subagents when delegated work exists.\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks — unless a team roster is\n * registered (`--team`), in which case the empty flat lens falls through to\n * teams and every role renders as a placeholder group, so idle roles are\n * visible from startup.\n */\nexport class TaskPanelComponent implements Component, Focusable {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate view: TaskPanelView = \"flat\";\n\tprivate disposed = false;\n\n\t// Team focus mode: when the TUI focuses the panel, role rows become a\n\t// navigable list (↑/↓ select, n nudge, a attach, q/esc back). The selection\n\t// is tracked by role name so a roster reorder doesn't move the cursor.\n\tfocused = false;\n\tprivate selectedRole: string | undefined;\n\t/** Open the inline nudge editor for the selected role. */\n\tonNudge?: (role: string) => void;\n\t/** Open the attach side panel for the selected role. */\n\tonAttach?: (role: string) => void;\n\t/** Leave team focus (focus returns to the main editor). */\n\tonExitFocus?: () => void;\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\tprivate roleAgents(): TaskAgent[] {\n\t\treturn taskStore.agents().filter((a) => a.kind === \"role\");\n\t}\n\n\t/** The role the team-focus cursor sits on (clamped to the live roster). */\n\tfocusedRole(): string | undefined {\n\t\tconst roles = this.roleAgents();\n\t\tif (roles.length === 0) return undefined;\n\t\tconst match = roles.find((a) => a.name === this.selectedRole);\n\t\treturn (match ?? roles[0]).name;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst roles = this.roleAgents();\n\t\tif (roles.length === 0) {\n\t\t\tthis.onExitFocus?.();\n\t\t\treturn;\n\t\t}\n\t\tconst keybindings = getKeybindings();\n\t\tconst index = Math.max(\n\t\t\t0,\n\t\t\troles.findIndex((a) => a.name === this.selectedRole),\n\t\t);\n\t\tif (keybindings.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedRole = roles[Math.max(0, index - 1)].name;\n\t\t} else if (keybindings.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedRole = roles[Math.min(roles.length - 1, index + 1)].name;\n\t\t} else if (matchesKey(data, \"n\")) {\n\t\t\tconst role = this.focusedRole();\n\t\t\tif (role) this.onNudge?.(role);\n\t\t} else if (matchesKey(data, \"a\")) {\n\t\t\tconst role = this.focusedRole();\n\t\t\tif (role) this.onAttach?.(role);\n\t\t} else if (matchesKey(data, \"q\") || keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onExitFocus?.();\n\t\t}\n\t\tthis.ui?.requestRender();\n\t}\n\n\tgetView(): TaskPanelView {\n\t\treturn this.view;\n\t}\n\n\tsetView(view: TaskPanelView): void {\n\t\tthis.view = view;\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Advance to the next view lens with content (flat → subagents → teams →\n\t * flat), skipping empty lenses. With nothing delegated this is a no-op on\n\t * flat; a stale view (its lens emptied since selection) snaps back to flat.\n\t */\n\tcycleView(): TaskPanelView {\n\t\tconst available = availableViews(taskStore.list(), taskStore.agents());\n\t\tconst idx = available.indexOf(this.view);\n\t\tthis.view = available[(idx + 1) % available.length] ?? \"flat\";\n\t\tthis.ui?.requestRender();\n\t\treturn this.view;\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (this.disposed) {\n\t\t\tif (this.animationTimer) {\n\t\t\t\tclearInterval(this.animationTimer);\n\t\t\t\tthis.animationTimer = null;\n\t\t\t\tthis.frame = 0;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t\tthis.disposed = true;\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.disposed) return [];\n\n\t\tconst tasks = taskStore.list();\n\t\tconst allAgents = taskStore.agents();\n\n\t\t// Resolve the lens: keep the stored view while it still has content, else\n\t\t// fall through to the first available lens (flat → subagents → teams). The\n\t\t// stored view is untouched so an explicit setView choice resumes once its\n\t\t// content returns. This is how an empty flat lens (no main/TodoWrite task)\n\t\t// falls through to the subagents tree when only delegated work exists, and\n\t\t// how an empty pane falls through to the teams roster at startup.\n\t\tconst available = availableViews(tasks, allAgents);\n\t\tlet view: TaskPanelView = available.includes(this.view) ? this.view : (available[0] ?? \"flat\");\n\n\t\t// Team focus always operates on the teams lens — the focused role list is\n\t\t// exactly what the lens renders, so the cursor is never invisible.\n\t\tif (this.focused) view = \"teams\";\n\n\t\t// In teams view the roster itself is content: role agents render as\n\t\t// placeholder groups even without tasks (idle roles at startup, queued\n\t\t// upcoming work).\n\t\tconst hasRoleRoster = view === \"teams\" && allAgents.some((a) => a.kind === \"role\");\n\n\t\tif (tasks.length === 0 && !hasRoleRoster) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\t// Scope all visual state to the tasks visible in the current lens so the\n\t\t// animation, rail color, and header count match exactly what the user sees.\n\t\tconst lensTasks = filterTasksForLens(tasks, allAgents, view);\n\t\tconst hasActive = lensTasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(lensTasks);\n\t\tconst totalSecs = lensTasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\tconst lines: string[] = [gutter + formatHeader(lensTasks, inner, state, totalSecs, view, available)];\n\n\t\tif (view === \"flat\") {\n\t\t\t// Only the main agent's own TodoWrite plan — delegated (subagent/MCP) work\n\t\t\t// belongs to the subagents lens. Resolve each row's owner from the roster\n\t\t\t// so the glyph/tag reflect the owning agent's kind, not just the source.\n\t\t\tconst agentById = new Map(allAgents.map((a) => [a.id, a]));\n\t\t\tfor (const task of tasks) {\n\t\t\t\tif (!isMainTask(task)) continue;\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { owner: agentById.get(taskOwnerId(task)) }));\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tif (view === \"subagents\") {\n\t\t\t// A recursive task tree over the delegated forest: roots are the main\n\t\t\t// agent's dispatched subagents (and direct MCP calls), children are the\n\t\t\t// tasks they spawned in turn (linked by parentTaskId, merged across the\n\t\t\t// process boundary), so a subagent that spawned a subagent is visible.\n\t\t\t// Each task is its own node keeping its [subagentMode]/[server] tag; depth\n\t\t\t// is drawn with └─/├─/│ connectors. With only top-level tasks the roots\n\t\t\t// carry an empty prefix and read exactly like flat rows (no extra indent).\n\t\t\tconst childrenByParent = new Map<number, Task[]>();\n\t\t\tfor (const task of tasks) {\n\t\t\t\tif (task.parentTaskId === undefined) continue;\n\t\t\t\tconst siblings = childrenByParent.get(task.parentTaskId);\n\t\t\t\tif (siblings) siblings.push(task);\n\t\t\t\telse childrenByParent.set(task.parentTaskId, [task]);\n\t\t\t}\n\t\t\tconst roots = tasks.filter(\n\t\t\t\t(t) => t.parentTaskId === undefined && (t.source === \"subagent\" || t.source === \"mcp\"),\n\t\t\t);\n\t\t\tconst walk = (task: Task, prefix: string, isLast: boolean, isRoot: boolean): void => {\n\t\t\t\tconst connector = isRoot ? \"\" : `${prefix}${isLast ? \"└─ \" : \"├─ \"}`;\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { treePrefix: connector }));\n\t\t\t\tconst kids = childrenByParent.get(task.id) ?? [];\n\t\t\t\tconst childPrefix = isRoot ? \"\" : `${prefix}${isLast ? \" \" : \"│ \"}`;\n\t\t\t\tfor (let i = 0; i < kids.length; i++) {\n\t\t\t\t\twalk(kids[i] as Task, childPrefix, i === kids.length - 1, false);\n\t\t\t\t}\n\t\t\t};\n\t\t\tfor (const root of roots) walk(root, \"\", true, true);\n\t\t\treturn lines;\n\t\t}\n\n\t\t// teams view: role-agent groups with handoff connectors and queued placeholders.\n\t\tconst { filteredTasks, filteredAgents } = filterTasksForView(tasks, allAgents, view);\n\t\tconst groups = groupTasks(filteredTasks, filteredAgents);\n\t\tconst groupIds = new Set(groups.map((g) => g.id));\n\t\t// Role agents with no tasks still get a group header: idle roles are the\n\t\t// roster at startup, queued ones upcoming work, done/failed ones the\n\t\t// state they settled in after reset() dropped their tasks.\n\t\tfor (const agent of filteredAgents) {\n\t\t\tif (!groupIds.has(agent.id)) {\n\t\t\t\tgroups.push({ id: agent.id, meta: agent, items: [] });\n\t\t\t}\n\t\t}\n\t\tconst cursorRole = this.focused ? this.focusedRole() : undefined;\n\t\tfor (const group of groups) {\n\t\t\tconst selected = cursorRole !== undefined && group.meta.kind === \"role\" && group.meta.name === cursorRole;\n\t\t\tlines.push(gutter + formatGroupHeader(group.meta, group.items, inner, selected));\n\t\t\tfor (const task of group.items) {\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { grouped: true }));\n\t\t\t}\n\t\t\t// Forward-handoff connector: emit \"└──→ name\" only for \"→ name\" arrows\n\t\t\t// (not back-references \"← name\"), and only when the target exists in the\n\t\t\t// visible role roster.\n\t\t\tconst { handoff } = group.meta;\n\t\t\tif (handoff) {\n\t\t\t\tconst arrowIdx = handoff.indexOf(\"→ \");\n\t\t\t\tif (arrowIdx !== -1) {\n\t\t\t\t\tconst nextName = handoff.slice(arrowIdx + 2).trim();\n\t\t\t\t\tif (filteredAgents.some((a) => a.name === nextName)) {\n\t\t\t\t\t\tconst connectorPrefix = `${GROUP_INDENT_PLAIN} └──→ `;\n\t\t\t\t\t\tconst connectorPad = Math.max(0, inner - visibleWidth(connectorPrefix) - visibleWidth(nextName));\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\tgutter +\n\t\t\t\t\t\t\t\ttheme.fg(\"borderMuted\", connectorPrefix) +\n\t\t\t\t\t\t\t\ttheme.fg(\"dim\", nextName) +\n\t\t\t\t\t\t\t\t\" \".repeat(connectorPad),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (this.focused) {\n\t\t\tlines.push(\n\t\t\t\tgutter + truncateToWidth(theme.fg(\"dim\", \"↑/↓ select · n nudge · a attach · q/esc back\"), inner, \"…\"),\n\t\t\t);\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
@@ -338,8 +338,12 @@ function formatTaskLine(task, width, frame, options = {}) {
338
338
  ? `${indent}${icon} ${styledTag}${styledTitle}`
339
339
  : `${treePrefix}${icon} ${styledSource} ${styledTag}${styledTitle}`;
340
340
  const left = truncateToWidth(leftBody, leftWidth, "…");
341
- if (!rightPlain)
342
- return left;
341
+ // Pad every row to the full pane width so rows align regardless of whether they
342
+ // carry a right column (token usage / running… / queued).
343
+ if (!rightPlain) {
344
+ const pad = Math.max(0, width - visibleWidth(left));
345
+ return left + " ".repeat(pad);
346
+ }
343
347
  const pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));
344
348
  return left + " ".repeat(pad) + rightStyled;
345
349
  }
@@ -1 +1 @@
1
- {"version":3,"file":"task-panel.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component, Focusable, TUI } from \"@kolisachint/hoocode-tui\";\nimport { getKeybindings, matchesKey, truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type { Task, TaskAgent, TaskAgentKind, TaskAgentState, TaskStatus } from \"../../../core/task-store.js\";\nimport { taskOwnerId, taskStore } from \"../../../core/task-store.js\";\nimport type { ThemeColor } from \"../theme/theme.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/**\n * Single-cell marker for MCP-sourced rows, which have no owning agent. Every\n * other row derives its marker from the owner's kind via AGENT_GLYPH (◆ main /\n * ◇ subagent / ▸ team role), so the flat lens attributes a row exactly the way\n * the grouped lenses do. The row also carries a text origin tag before the\n * title (see formatTaskLine).\n */\nconst MCP_SOURCE_GLYPH = \"⧉\";\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/**\n * How the same task list is presented:\n * - flat → one ungrouped list (default)\n * - subagents → grouped by owning agent (◆ main orchestrator + ◇ workers)\n * - teams → grouped by named role-agent (▸), with handoff arrows\n */\nexport type TaskPanelView = \"flat\" | \"subagents\" | \"teams\";\n\nconst VIEW_LABEL: Record<TaskPanelView, string> = { flat: \"tasks\", subagents: \"subagents\", teams: \"teams\" };\n\n/**\n * A top-level task the main agent owns directly: its own TodoWrite plan. Source\n * is unset (not a subagent/MCP delegation) and it sits at the root of the forest\n * (not a child merged in from a subagent's subtree). These are exactly the rows\n * the flat (\"tasks\") lens shows.\n */\nfunction isMainTask(task: Task): boolean {\n\t// Source-unset (not a subagent/MCP delegation — note taskOwnerId folds MCP into\n\t// \"main\", so check source directly), not owned by a role agent, and a forest\n\t// root (not a child merged in from a subagent's subtree).\n\treturn task.source === undefined && task.agent === undefined && task.parentTaskId === undefined;\n}\n\n/**\n * Lenses that currently have content, split by ownership:\n * - flat (\"tasks\") → only when the main agent has its own TodoWrite plan.\n * - subagents → when delegated work exists (a registered subagent, or any\n * subagent/MCP-sourced task).\n * - teams → when role agents are registered (hooteams `--team`).\n * The cycle key and the header switcher both skip empty lenses, and an empty\n * flat lens falls through to subagents (see render), so a session that only\n * delegated work opens straight on the task tree with no empty \"tasks\" view.\n */\nfunction availableViews(tasks: readonly Task[], agents: readonly TaskAgent[]): TaskPanelView[] {\n\tconst views: TaskPanelView[] = [];\n\tif (tasks.some(isMainTask)) views.push(\"flat\");\n\tconst hasSubagentWork =\n\t\tagents.some((a) => a.kind === \"subagent\") || tasks.some((t) => t.source === \"subagent\" || t.source === \"mcp\");\n\tif (hasSubagentWork) views.push(\"subagents\");\n\tif (agents.some((a) => a.kind === \"role\")) views.push(\"teams\");\n\treturn views;\n}\n\n/** Owner glyphs: main agent a filled diamond, spawned subagents the hollow counterpart, team roles a triangle. */\nconst AGENT_GLYPH: Record<TaskAgentKind, string> = { main: \"◆\", subagent: \"◇\", role: \"▸\" };\nconst AGENT_GLYPH_COLOR: Record<TaskAgentKind, ThemeColor> = {\n\tmain: \"accent\",\n\tsubagent: \"accent\",\n\trole: \"borderAccent\",\n};\n\n/** Color for an agent's lifecycle `[state]` tag (mirrors the design's .ast-* classes). */\nconst AGENT_STATE_COLOR: Record<TaskAgentState, ThemeColor> = {\n\tactive: \"warning\",\n\trunning: \"warning\",\n\tdone: \"success\",\n\tqueued: \"dim\",\n\tidle: \"dim\",\n\twaiting: \"mdLink\",\n\tfailed: \"error\",\n};\n\n/** Two-cell indent under a group header, with a faint vertical guide. */\nconst GROUP_INDENT_PLAIN = \"│ \";\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * View switcher rendered at the right edge of the ledger header: the labels\n * of the lenses that have content joined by `·`, the active one in bold\n * accent. Hidden entirely when only one lens is available. Purely an\n * indicator in the TUI — the bound key cycles it (see app.tasks.cycleView).\n */\nfunction formatViewSwitcher(\n\tview: TaskPanelView,\n\tavailable: readonly TaskPanelView[],\n): { plain: string; styled: string } {\n\tif (available.length < 2) return { plain: \"\", styled: \"\" };\n\tconst plain = available.map((v) => VIEW_LABEL[v]).join(\" · \");\n\tconst styled = available\n\t\t.map((v) => (v === view ? theme.bold(theme.fg(\"accent\", VIEW_LABEL[v])) : theme.fg(\"dim\", VIEW_LABEL[v])))\n\t\t.join(theme.fg(\"dim\", \" · \"));\n\treturn { plain, styled };\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) plus the view\n * switcher on the right.\n */\nfunction formatHeader(\n\ttasks: readonly Task[],\n\twidth: number,\n\tstate: PanelState,\n\ttotalSecs: number,\n\tview: TaskPanelView,\n\tavailable: readonly TaskPanelView[],\n): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\t// Right cluster: turn delta, then the view switcher at the far edge. The\n\t// switcher is the first thing dropped when the terminal narrows; the turn\n\t// delta next; the stamp/count survive to the end. Either piece may be\n\t// absent (no usage reported / only one lens available).\n\tconst switcher = formatViewSwitcher(view, available);\n\tconst rightVariants: Array<{ plain: string; styled: string }> = [];\n\tif (turnPlain && switcher.plain) {\n\t\trightVariants.push({\n\t\t\tplain: `${turnPlain} ${switcher.plain}`,\n\t\t\tstyled: `${turnText} ${switcher.styled}`,\n\t\t});\n\t}\n\tif (turnPlain) rightVariants.push({ plain: turnPlain, styled: turnText });\n\telse if (switcher.plain) rightVariants.push(switcher);\n\n\tfor (const right of rightVariants) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(right.plain));\n\t\t\treturn leftFull + \" \".repeat(pad) + right.styled;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(right.plain));\n\t\t\treturn leftMin + \" \".repeat(pad) + right.styled;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) {\n\t\treturn leftFull + \" \".repeat(width - visibleWidth(leftFullPlain));\n\t}\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(\n\ttask: Task,\n\twidth: number,\n\tframe: number,\n\toptions: { grouped?: boolean; owner?: TaskAgent; treePrefix?: string } = {},\n): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\t// In grouped views the group header already carries the row's origin, so the\n\t// owner glyph and tag are suppressed; the rows sit on a faint indent guide.\n\tconst grouped = options.grouped === true;\n\tconst indent = grouped ? theme.fg(\"borderMuted\", GROUP_INDENT_PLAIN) : \"\";\n\n\t// Owner marker between the status icon and the id, derived from the owning\n\t// agent's kind so the flat lens attributes rows the same way the grouped\n\t// lenses do (a roster-less owner falls back on the task's source). MCP rows\n\t// have no owning agent and keep their own ⧉ marker. Every row carries one\n\t// cell, so the id column stays aligned.\n\tconst isMcp = task.source === \"mcp\";\n\tconst ownerKind = options.owner?.kind ?? (task.source === \"subagent\" ? \"subagent\" : \"main\");\n\tconst sourceGlyph = isMcp ? MCP_SOURCE_GLYPH : AGENT_GLYPH[ownerKind];\n\tconst styledSource = grouped ? \"\" : theme.fg(\"dim\", sourceGlyph);\n\n\t// Origin tag prefixed to the title, naming who runs the row: the subagent\n\t// type (\"[explore]\"), the team role's name (\"[planner]\"), or the MCP server\n\t// (\"[github]\"; \"[MCP]\" when no server label was recorded). Drawn in accent,\n\t// parallel to the chat's `Agent [explore]` / `MCP [server › tool]`. Grouped\n\t// rows drop it — the group header carries the origin — except MCP rows,\n\t// which group under main without being main's own work.\n\tlet tag = \"\";\n\tif (isMcp) tag = `[${task.subagentMode ?? \"MCP\"}]`;\n\telse if (!grouped) {\n\t\tif (task.subagentMode) tag = `[${task.subagentMode}]`;\n\t\telse if (ownerKind === \"role\" && options.owner) tag = `[${options.owner.name}]`;\n\t}\n\tconst styledTag = tag ? `${theme.fg(\"accent\", tag)} ` : \"\";\n\tconst title = task.title;\n\t// The title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their token usage; the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tif (tokenText) {\n\t\t\trightPlain = tokenText;\n\t\t\trightStyled = theme.fg(\"muted\", tokenText);\n\t\t}\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\t// A warning note (e.g. inherited-model fallback, exhaustion skip) takes over the\n\t// right column as a ⚠ cue, replacing the usage/status stamp for that row.\n\tif (task.note) {\n\t\trightPlain = `⚠ ${task.note}`;\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\t// truncateToWidth measures visible width (ANSI-aware), so the styled left can be\n\t// truncated against the full left budget directly. Subtracting the prefix here\n\t// (as a prior version did) truncated titles early and unevenly per id width.\n\t// In the subagents tree, a depth-first connector prefix (└─/├─/│) sits before\n\t// the row's glyph; roots pass an empty prefix and read exactly like flat rows.\n\tconst treePrefix = options.treePrefix ? theme.fg(\"borderMuted\", options.treePrefix) : \"\";\n\tconst leftBody = grouped\n\t\t? `${indent}${icon} ${styledTag}${styledTitle}`\n\t\t: `${treePrefix}${icon} ${styledSource} ${styledTag}${styledTitle}`;\n\tconst left = truncateToWidth(leftBody, leftWidth, \"…\");\n\n\tif (!rightPlain) return left;\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Filter tasks and agents to the teams lens: only role agents and the tasks they\n * own. The flat and subagents lenses do their own ownership filtering inline (by\n * source and parentTaskId), so this is teams-specific.\n */\nfunction filterTasksForView(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n\t_view: TaskPanelView,\n): { filteredTasks: readonly Task[]; filteredAgents: readonly TaskAgent[] } {\n\tconst roleIds = new Set(agents.filter((a) => a.kind === \"role\").map((a) => a.id));\n\treturn {\n\t\tfilteredAgents: agents.filter((a) => a.kind === \"role\"),\n\t\tfilteredTasks: tasks.filter((t) => roleIds.has(taskOwnerId(t))),\n\t};\n}\n\n/**\n * Scope the full task list to the tasks visible in the given lens. The header,\n * state stamp, and done/total count are derived from this subset so they match\n * exactly what the user sees in the current view.\n */\nfunction filterTasksForLens(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n\tview: TaskPanelView,\n): readonly Task[] {\n\tswitch (view) {\n\t\tcase \"flat\":\n\t\t\treturn tasks.filter(isMainTask);\n\t\tcase \"subagents\":\n\t\t\treturn tasks.filter((t) => t.source === \"subagent\" || t.source === \"mcp\" || t.parentTaskId !== undefined);\n\t\tcase \"teams\": {\n\t\t\tconst roleIds = new Set<string>();\n\t\t\tfor (const a of agents) {\n\t\t\t\tif (a.kind === \"role\") roleIds.add(a.id);\n\t\t\t}\n\t\t\treturn tasks.filter((t) => roleIds.has(taskOwnerId(t)));\n\t\t}\n\t}\n}\n\n/** Fallback group metadata when a task's owner has no roster entry. */\nfunction defaultAgentMeta(id: string): TaskAgent {\n\treturn id === \"main\"\n\t\t? { id, name: \"main\", role: \"orchestrator\", kind: \"main\" }\n\t\t: { id, name: id, role: \"subagent\", kind: \"subagent\" };\n}\n\n/**\n * Partition the flat task list into owner groups. An explicit task.agent wins;\n * otherwise a subagent-sourced task falls into a generic \"subagent\" group and\n * everything else into \"main\". Group order is deterministic — main first, then\n * roster order, then stragglers — never reordered by status.\n */\nfunction groupTasks(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n): Array<{ id: string; meta: TaskAgent; items: Task[] }> {\n\tconst meta = new Map<string, TaskAgent>(agents.map((a) => [a.id, a]));\n\tconst groups = new Map<string, Task[]>();\n\tfor (const task of tasks) {\n\t\tconst owner = taskOwnerId(task);\n\t\tconst items = groups.get(owner);\n\t\tif (items) items.push(task);\n\t\telse groups.set(owner, [task]);\n\t}\n\tconst order: string[] = [];\n\tif (groups.has(\"main\")) order.push(\"main\");\n\tfor (const agent of agents) {\n\t\tif (groups.has(agent.id) && !order.includes(agent.id)) order.push(agent.id);\n\t}\n\tfor (const id of groups.keys()) {\n\t\tif (!order.includes(id)) order.push(id);\n\t}\n\treturn order.map((id) => ({\n\t\tid,\n\t\tmeta: meta.get(id) ?? defaultAgentMeta(id),\n\t\titems: groups.get(id) ?? [],\n\t}));\n}\n\n/**\n * Group header for the grouped views: owner glyph + bold name + role, the\n * agent's lifecycle `[state]` tag, an optional handoff arrow (teams), then the\n * agent's own token/cost totals + done/total on the right. Mirrors the footer's\n * \"every number accounted for\" stance, but per agent.\n */\nfunction formatGroupHeader(meta: TaskAgent, items: readonly Task[], width: number, selected = false): string {\n\t// A focused role row swaps its ▸ for a filled ▶ in accent — the team-focus\n\t// selection cursor (the owner-kind glyph mapping itself is unchanged).\n\tconst glyph = selected\n\t\t? theme.fg(\"accent\", \"▶\")\n\t\t: theme.fg(AGENT_GLYPH_COLOR[meta.kind], AGENT_GLYPH[meta.kind] ?? AGENT_GLYPH.subagent);\n\tconst name = selected ? theme.bold(theme.fg(\"accent\", meta.name)) : theme.bold(meta.name);\n\t// Roles read as a dim \"· role\" suffix for spawned/team agents; the main\n\t// orchestrator's role sits brighter (muted), matching the design's .grp-role.\n\tconst role = meta.role\n\t\t? meta.kind === \"main\"\n\t\t\t? ` ${theme.fg(\"muted\", meta.role)}`\n\t\t\t: theme.fg(\"dim\", ` · ${meta.role}`)\n\t\t: \"\";\n\tconst state = meta.state ? ` ${theme.fg(AGENT_STATE_COLOR[meta.state] ?? \"dim\", `[${meta.state}]`)}` : \"\";\n\t// Live activity for a running subagent (e.g. the tool it's executing). Empty\n\t// string means idle/between-tools, so it disappears rather than lingering stale.\n\tconst activity = meta.activity ? theme.fg(\"dim\", ` ⋯ ${meta.activity}`) : \"\";\n\tconst handoff = meta.handoff ? ` ${theme.fg(\"dim\", meta.handoff)}` : \"\";\n\n\tconst done = items.filter((t) => t.status === \"done\").length;\n\tconst countPlain = `${done}/${items.length}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${items.length}`);\n\tconst stats = meta.stats;\n\tlet rightPlain = countPlain;\n\tlet rightStyled = count;\n\tif (stats && (stats.input > 0 || stats.output > 0 || stats.cost > 0)) {\n\t\tconst statsPlain = `↑${formatTokens(stats.input)} ↓${formatTokens(stats.output)} · $${stats.cost.toFixed(3)}`;\n\t\trightPlain = `${statsPlain} ${countPlain}`;\n\t\trightStyled = `${theme.fg(\"dim\", statsPlain)} ${count}`;\n\t}\n\n\tconst rightWidth = visibleWidth(rightPlain) + 1;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\tconst left = truncateToWidth(`${glyph} ${name}${role}${state}${activity}${handoff}`, leftWidth, \"…\");\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - A single-cell owner glyph (◆ main / ◇ subagent / ▸ team role / ⧉ MCP) sits\n * before the id, derived from the owning agent's kind, so every row's origin\n * is readable at a glance even in the flat lens. A text origin tag before the\n * title names the owner: the subagent type (\"[explore]\"), the team role\n * (\"[planner]\", fed by `--team <url>`), or the MCP server (\"[github]\").\n * - Three views split by ownership (cycled via app.tasks.cycleView, shown as a\n * `tasks · subagents · teams` switcher in the header):\n * - flat (\"tasks\") — only the main agent's own TodoWrite plan;\n * - subagents — a recursive task tree over delegated work, where a subagent\n * that spawned a subagent shows its nested tasks (roots are the dispatched\n * subagents and direct MCP calls; children link via parentTaskId, merged\n * across the process boundary). Each task is its own node keeping its\n * [subagentMode]/[server] tag, with depth drawn by └─/├─/│ connectors; a\n * run with only top-level tasks reads flat (no extra indent);\n * - teams — grouped by named role-agent with handoff arrows.\n * The cycle is adaptive: empty lenses are skipped and dropped from the\n * switcher, which hides entirely when only one lens has content. An empty flat\n * lens (no main task) falls through to subagents when delegated work exists.\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks — unless a team roster is\n * registered (`--team`), in which case the empty flat lens falls through to\n * teams and every role renders as a placeholder group, so idle roles are\n * visible from startup.\n */\nexport class TaskPanelComponent implements Component, Focusable {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate view: TaskPanelView = \"flat\";\n\tprivate disposed = false;\n\n\t// Team focus mode: when the TUI focuses the panel, role rows become a\n\t// navigable list (↑/↓ select, n nudge, a attach, q/esc back). The selection\n\t// is tracked by role name so a roster reorder doesn't move the cursor.\n\tfocused = false;\n\tprivate selectedRole: string | undefined;\n\t/** Open the inline nudge editor for the selected role. */\n\tonNudge?: (role: string) => void;\n\t/** Open the attach side panel for the selected role. */\n\tonAttach?: (role: string) => void;\n\t/** Leave team focus (focus returns to the main editor). */\n\tonExitFocus?: () => void;\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\tprivate roleAgents(): TaskAgent[] {\n\t\treturn taskStore.agents().filter((a) => a.kind === \"role\");\n\t}\n\n\t/** The role the team-focus cursor sits on (clamped to the live roster). */\n\tfocusedRole(): string | undefined {\n\t\tconst roles = this.roleAgents();\n\t\tif (roles.length === 0) return undefined;\n\t\tconst match = roles.find((a) => a.name === this.selectedRole);\n\t\treturn (match ?? roles[0]).name;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst roles = this.roleAgents();\n\t\tif (roles.length === 0) {\n\t\t\tthis.onExitFocus?.();\n\t\t\treturn;\n\t\t}\n\t\tconst keybindings = getKeybindings();\n\t\tconst index = Math.max(\n\t\t\t0,\n\t\t\troles.findIndex((a) => a.name === this.selectedRole),\n\t\t);\n\t\tif (keybindings.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedRole = roles[Math.max(0, index - 1)].name;\n\t\t} else if (keybindings.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedRole = roles[Math.min(roles.length - 1, index + 1)].name;\n\t\t} else if (matchesKey(data, \"n\")) {\n\t\t\tconst role = this.focusedRole();\n\t\t\tif (role) this.onNudge?.(role);\n\t\t} else if (matchesKey(data, \"a\")) {\n\t\t\tconst role = this.focusedRole();\n\t\t\tif (role) this.onAttach?.(role);\n\t\t} else if (matchesKey(data, \"q\") || keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onExitFocus?.();\n\t\t}\n\t\tthis.ui?.requestRender();\n\t}\n\n\tgetView(): TaskPanelView {\n\t\treturn this.view;\n\t}\n\n\tsetView(view: TaskPanelView): void {\n\t\tthis.view = view;\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Advance to the next view lens with content (flat → subagents → teams →\n\t * flat), skipping empty lenses. With nothing delegated this is a no-op on\n\t * flat; a stale view (its lens emptied since selection) snaps back to flat.\n\t */\n\tcycleView(): TaskPanelView {\n\t\tconst available = availableViews(taskStore.list(), taskStore.agents());\n\t\tconst idx = available.indexOf(this.view);\n\t\tthis.view = available[(idx + 1) % available.length] ?? \"flat\";\n\t\tthis.ui?.requestRender();\n\t\treturn this.view;\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (this.disposed) {\n\t\t\tif (this.animationTimer) {\n\t\t\t\tclearInterval(this.animationTimer);\n\t\t\t\tthis.animationTimer = null;\n\t\t\t\tthis.frame = 0;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t\tthis.disposed = true;\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.disposed) return [];\n\n\t\tconst tasks = taskStore.list();\n\t\tconst allAgents = taskStore.agents();\n\n\t\t// Resolve the lens: keep the stored view while it still has content, else\n\t\t// fall through to the first available lens (flat → subagents → teams). The\n\t\t// stored view is untouched so an explicit setView choice resumes once its\n\t\t// content returns. This is how an empty flat lens (no main/TodoWrite task)\n\t\t// falls through to the subagents tree when only delegated work exists, and\n\t\t// how an empty pane falls through to the teams roster at startup.\n\t\tconst available = availableViews(tasks, allAgents);\n\t\tlet view: TaskPanelView = available.includes(this.view) ? this.view : (available[0] ?? \"flat\");\n\n\t\t// Team focus always operates on the teams lens — the focused role list is\n\t\t// exactly what the lens renders, so the cursor is never invisible.\n\t\tif (this.focused) view = \"teams\";\n\n\t\t// In teams view the roster itself is content: role agents render as\n\t\t// placeholder groups even without tasks (idle roles at startup, queued\n\t\t// upcoming work).\n\t\tconst hasRoleRoster = view === \"teams\" && allAgents.some((a) => a.kind === \"role\");\n\n\t\tif (tasks.length === 0 && !hasRoleRoster) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\t// Scope all visual state to the tasks visible in the current lens so the\n\t\t// animation, rail color, and header count match exactly what the user sees.\n\t\tconst lensTasks = filterTasksForLens(tasks, allAgents, view);\n\t\tconst hasActive = lensTasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(lensTasks);\n\t\tconst totalSecs = lensTasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\tconst lines: string[] = [gutter + formatHeader(lensTasks, inner, state, totalSecs, view, available)];\n\n\t\tif (view === \"flat\") {\n\t\t\t// Only the main agent's own TodoWrite plan — delegated (subagent/MCP) work\n\t\t\t// belongs to the subagents lens. Resolve each row's owner from the roster\n\t\t\t// so the glyph/tag reflect the owning agent's kind, not just the source.\n\t\t\tconst agentById = new Map(allAgents.map((a) => [a.id, a]));\n\t\t\tfor (const task of tasks) {\n\t\t\t\tif (!isMainTask(task)) continue;\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { owner: agentById.get(taskOwnerId(task)) }));\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tif (view === \"subagents\") {\n\t\t\t// A recursive task tree over the delegated forest: roots are the main\n\t\t\t// agent's dispatched subagents (and direct MCP calls), children are the\n\t\t\t// tasks they spawned in turn (linked by parentTaskId, merged across the\n\t\t\t// process boundary), so a subagent that spawned a subagent is visible.\n\t\t\t// Each task is its own node keeping its [subagentMode]/[server] tag; depth\n\t\t\t// is drawn with └─/├─/│ connectors. With only top-level tasks the roots\n\t\t\t// carry an empty prefix and read exactly like flat rows (no extra indent).\n\t\t\tconst childrenByParent = new Map<number, Task[]>();\n\t\t\tfor (const task of tasks) {\n\t\t\t\tif (task.parentTaskId === undefined) continue;\n\t\t\t\tconst siblings = childrenByParent.get(task.parentTaskId);\n\t\t\t\tif (siblings) siblings.push(task);\n\t\t\t\telse childrenByParent.set(task.parentTaskId, [task]);\n\t\t\t}\n\t\t\tconst roots = tasks.filter(\n\t\t\t\t(t) => t.parentTaskId === undefined && (t.source === \"subagent\" || t.source === \"mcp\"),\n\t\t\t);\n\t\t\tconst walk = (task: Task, prefix: string, isLast: boolean, isRoot: boolean): void => {\n\t\t\t\tconst connector = isRoot ? \"\" : `${prefix}${isLast ? \"└─ \" : \"├─ \"}`;\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { treePrefix: connector }));\n\t\t\t\tconst kids = childrenByParent.get(task.id) ?? [];\n\t\t\t\tconst childPrefix = isRoot ? \"\" : `${prefix}${isLast ? \" \" : \"│ \"}`;\n\t\t\t\tfor (let i = 0; i < kids.length; i++) {\n\t\t\t\t\twalk(kids[i] as Task, childPrefix, i === kids.length - 1, false);\n\t\t\t\t}\n\t\t\t};\n\t\t\tfor (const root of roots) walk(root, \"\", true, true);\n\t\t\treturn lines;\n\t\t}\n\n\t\t// teams view: role-agent groups with handoff connectors and queued placeholders.\n\t\tconst { filteredTasks, filteredAgents } = filterTasksForView(tasks, allAgents, view);\n\t\tconst groups = groupTasks(filteredTasks, filteredAgents);\n\t\tconst groupIds = new Set(groups.map((g) => g.id));\n\t\t// Role agents with no tasks still get a group header: idle roles are the\n\t\t// roster at startup, queued ones upcoming work, done/failed ones the\n\t\t// state they settled in after reset() dropped their tasks.\n\t\tfor (const agent of filteredAgents) {\n\t\t\tif (!groupIds.has(agent.id)) {\n\t\t\t\tgroups.push({ id: agent.id, meta: agent, items: [] });\n\t\t\t}\n\t\t}\n\t\tconst cursorRole = this.focused ? this.focusedRole() : undefined;\n\t\tfor (const group of groups) {\n\t\t\tconst selected = cursorRole !== undefined && group.meta.kind === \"role\" && group.meta.name === cursorRole;\n\t\t\tlines.push(gutter + formatGroupHeader(group.meta, group.items, inner, selected));\n\t\t\tfor (const task of group.items) {\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { grouped: true }));\n\t\t\t}\n\t\t\t// Forward-handoff connector: emit \"└──→ name\" only for \"→ name\" arrows\n\t\t\t// (not back-references \"← name\"), and only when the target exists in the\n\t\t\t// visible role roster.\n\t\t\tconst { handoff } = group.meta;\n\t\t\tif (handoff) {\n\t\t\t\tconst arrowIdx = handoff.indexOf(\"→ \");\n\t\t\t\tif (arrowIdx !== -1) {\n\t\t\t\t\tconst nextName = handoff.slice(arrowIdx + 2).trim();\n\t\t\t\t\tif (filteredAgents.some((a) => a.name === nextName)) {\n\t\t\t\t\t\tconst connectorPrefix = `${GROUP_INDENT_PLAIN} └──→ `;\n\t\t\t\t\t\tconst connectorPad = Math.max(0, inner - visibleWidth(connectorPrefix) - visibleWidth(nextName));\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\tgutter +\n\t\t\t\t\t\t\t\ttheme.fg(\"borderMuted\", connectorPrefix) +\n\t\t\t\t\t\t\t\ttheme.fg(\"dim\", nextName) +\n\t\t\t\t\t\t\t\t\" \".repeat(connectorPad),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (this.focused) {\n\t\t\tlines.push(\n\t\t\t\tgutter + truncateToWidth(theme.fg(\"dim\", \"↑/↓ select · n nudge · a attach · q/esc back\"), inner, \"…\"),\n\t\t\t);\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
1
+ {"version":3,"file":"task-panel.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component, Focusable, TUI } from \"@kolisachint/hoocode-tui\";\nimport { getKeybindings, matchesKey, truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type { Task, TaskAgent, TaskAgentKind, TaskAgentState, TaskStatus } from \"../../../core/task-store.js\";\nimport { taskOwnerId, taskStore } from \"../../../core/task-store.js\";\nimport type { ThemeColor } from \"../theme/theme.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/**\n * Single-cell marker for MCP-sourced rows, which have no owning agent. Every\n * other row derives its marker from the owner's kind via AGENT_GLYPH (◆ main /\n * ◇ subagent / ▸ team role), so the flat lens attributes a row exactly the way\n * the grouped lenses do. The row also carries a text origin tag before the\n * title (see formatTaskLine).\n */\nconst MCP_SOURCE_GLYPH = \"⧉\";\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/**\n * How the same task list is presented:\n * - flat → one ungrouped list (default)\n * - subagents → grouped by owning agent (◆ main orchestrator + ◇ workers)\n * - teams → grouped by named role-agent (▸), with handoff arrows\n */\nexport type TaskPanelView = \"flat\" | \"subagents\" | \"teams\";\n\nconst VIEW_LABEL: Record<TaskPanelView, string> = { flat: \"tasks\", subagents: \"subagents\", teams: \"teams\" };\n\n/**\n * A top-level task the main agent owns directly: its own TodoWrite plan. Source\n * is unset (not a subagent/MCP delegation) and it sits at the root of the forest\n * (not a child merged in from a subagent's subtree). These are exactly the rows\n * the flat (\"tasks\") lens shows.\n */\nfunction isMainTask(task: Task): boolean {\n\t// Source-unset (not a subagent/MCP delegation — note taskOwnerId folds MCP into\n\t// \"main\", so check source directly), not owned by a role agent, and a forest\n\t// root (not a child merged in from a subagent's subtree).\n\treturn task.source === undefined && task.agent === undefined && task.parentTaskId === undefined;\n}\n\n/**\n * Lenses that currently have content, split by ownership:\n * - flat (\"tasks\") → only when the main agent has its own TodoWrite plan.\n * - subagents → when delegated work exists (a registered subagent, or any\n * subagent/MCP-sourced task).\n * - teams → when role agents are registered (hooteams `--team`).\n * The cycle key and the header switcher both skip empty lenses, and an empty\n * flat lens falls through to subagents (see render), so a session that only\n * delegated work opens straight on the task tree with no empty \"tasks\" view.\n */\nfunction availableViews(tasks: readonly Task[], agents: readonly TaskAgent[]): TaskPanelView[] {\n\tconst views: TaskPanelView[] = [];\n\tif (tasks.some(isMainTask)) views.push(\"flat\");\n\tconst hasSubagentWork =\n\t\tagents.some((a) => a.kind === \"subagent\") || tasks.some((t) => t.source === \"subagent\" || t.source === \"mcp\");\n\tif (hasSubagentWork) views.push(\"subagents\");\n\tif (agents.some((a) => a.kind === \"role\")) views.push(\"teams\");\n\treturn views;\n}\n\n/** Owner glyphs: main agent a filled diamond, spawned subagents the hollow counterpart, team roles a triangle. */\nconst AGENT_GLYPH: Record<TaskAgentKind, string> = { main: \"◆\", subagent: \"◇\", role: \"▸\" };\nconst AGENT_GLYPH_COLOR: Record<TaskAgentKind, ThemeColor> = {\n\tmain: \"accent\",\n\tsubagent: \"accent\",\n\trole: \"borderAccent\",\n};\n\n/** Color for an agent's lifecycle `[state]` tag (mirrors the design's .ast-* classes). */\nconst AGENT_STATE_COLOR: Record<TaskAgentState, ThemeColor> = {\n\tactive: \"warning\",\n\trunning: \"warning\",\n\tdone: \"success\",\n\tqueued: \"dim\",\n\tidle: \"dim\",\n\twaiting: \"mdLink\",\n\tfailed: \"error\",\n};\n\n/** Two-cell indent under a group header, with a faint vertical guide. */\nconst GROUP_INDENT_PLAIN = \"│ \";\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * View switcher rendered at the right edge of the ledger header: the labels\n * of the lenses that have content joined by `·`, the active one in bold\n * accent. Hidden entirely when only one lens is available. Purely an\n * indicator in the TUI — the bound key cycles it (see app.tasks.cycleView).\n */\nfunction formatViewSwitcher(\n\tview: TaskPanelView,\n\tavailable: readonly TaskPanelView[],\n): { plain: string; styled: string } {\n\tif (available.length < 2) return { plain: \"\", styled: \"\" };\n\tconst plain = available.map((v) => VIEW_LABEL[v]).join(\" · \");\n\tconst styled = available\n\t\t.map((v) => (v === view ? theme.bold(theme.fg(\"accent\", VIEW_LABEL[v])) : theme.fg(\"dim\", VIEW_LABEL[v])))\n\t\t.join(theme.fg(\"dim\", \" · \"));\n\treturn { plain, styled };\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) plus the view\n * switcher on the right.\n */\nfunction formatHeader(\n\ttasks: readonly Task[],\n\twidth: number,\n\tstate: PanelState,\n\ttotalSecs: number,\n\tview: TaskPanelView,\n\tavailable: readonly TaskPanelView[],\n): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\t// Right cluster: turn delta, then the view switcher at the far edge. The\n\t// switcher is the first thing dropped when the terminal narrows; the turn\n\t// delta next; the stamp/count survive to the end. Either piece may be\n\t// absent (no usage reported / only one lens available).\n\tconst switcher = formatViewSwitcher(view, available);\n\tconst rightVariants: Array<{ plain: string; styled: string }> = [];\n\tif (turnPlain && switcher.plain) {\n\t\trightVariants.push({\n\t\t\tplain: `${turnPlain} ${switcher.plain}`,\n\t\t\tstyled: `${turnText} ${switcher.styled}`,\n\t\t});\n\t}\n\tif (turnPlain) rightVariants.push({ plain: turnPlain, styled: turnText });\n\telse if (switcher.plain) rightVariants.push(switcher);\n\n\tfor (const right of rightVariants) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(right.plain));\n\t\t\treturn leftFull + \" \".repeat(pad) + right.styled;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(right.plain));\n\t\t\treturn leftMin + \" \".repeat(pad) + right.styled;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) {\n\t\treturn leftFull + \" \".repeat(width - visibleWidth(leftFullPlain));\n\t}\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(\n\ttask: Task,\n\twidth: number,\n\tframe: number,\n\toptions: { grouped?: boolean; owner?: TaskAgent; treePrefix?: string } = {},\n): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\t// In grouped views the group header already carries the row's origin, so the\n\t// owner glyph and tag are suppressed; the rows sit on a faint indent guide.\n\tconst grouped = options.grouped === true;\n\tconst indent = grouped ? theme.fg(\"borderMuted\", GROUP_INDENT_PLAIN) : \"\";\n\n\t// Owner marker between the status icon and the id, derived from the owning\n\t// agent's kind so the flat lens attributes rows the same way the grouped\n\t// lenses do (a roster-less owner falls back on the task's source). MCP rows\n\t// have no owning agent and keep their own ⧉ marker. Every row carries one\n\t// cell, so the id column stays aligned.\n\tconst isMcp = task.source === \"mcp\";\n\tconst ownerKind = options.owner?.kind ?? (task.source === \"subagent\" ? \"subagent\" : \"main\");\n\tconst sourceGlyph = isMcp ? MCP_SOURCE_GLYPH : AGENT_GLYPH[ownerKind];\n\tconst styledSource = grouped ? \"\" : theme.fg(\"dim\", sourceGlyph);\n\n\t// Origin tag prefixed to the title, naming who runs the row: the subagent\n\t// type (\"[explore]\"), the team role's name (\"[planner]\"), or the MCP server\n\t// (\"[github]\"; \"[MCP]\" when no server label was recorded). Drawn in accent,\n\t// parallel to the chat's `Agent [explore]` / `MCP [server › tool]`. Grouped\n\t// rows drop it — the group header carries the origin — except MCP rows,\n\t// which group under main without being main's own work.\n\tlet tag = \"\";\n\tif (isMcp) tag = `[${task.subagentMode ?? \"MCP\"}]`;\n\telse if (!grouped) {\n\t\tif (task.subagentMode) tag = `[${task.subagentMode}]`;\n\t\telse if (ownerKind === \"role\" && options.owner) tag = `[${options.owner.name}]`;\n\t}\n\tconst styledTag = tag ? `${theme.fg(\"accent\", tag)} ` : \"\";\n\tconst title = task.title;\n\t// The title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their token usage; the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tif (tokenText) {\n\t\t\trightPlain = tokenText;\n\t\t\trightStyled = theme.fg(\"muted\", tokenText);\n\t\t}\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\t// A warning note (e.g. inherited-model fallback, exhaustion skip) takes over the\n\t// right column as a ⚠ cue, replacing the usage/status stamp for that row.\n\tif (task.note) {\n\t\trightPlain = `⚠ ${task.note}`;\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\t// truncateToWidth measures visible width (ANSI-aware), so the styled left can be\n\t// truncated against the full left budget directly. Subtracting the prefix here\n\t// (as a prior version did) truncated titles early and unevenly per id width.\n\t// In the subagents tree, a depth-first connector prefix (└─/├─/│) sits before\n\t// the row's glyph; roots pass an empty prefix and read exactly like flat rows.\n\tconst treePrefix = options.treePrefix ? theme.fg(\"borderMuted\", options.treePrefix) : \"\";\n\tconst leftBody = grouped\n\t\t? `${indent}${icon} ${styledTag}${styledTitle}`\n\t\t: `${treePrefix}${icon} ${styledSource} ${styledTag}${styledTitle}`;\n\tconst left = truncateToWidth(leftBody, leftWidth, \"…\");\n\n\t// Pad every row to the full pane width so rows align regardless of whether they\n\t// carry a right column (token usage / running… / queued).\n\tif (!rightPlain) {\n\t\tconst pad = Math.max(0, width - visibleWidth(left));\n\t\treturn left + \" \".repeat(pad);\n\t}\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Filter tasks and agents to the teams lens: only role agents and the tasks they\n * own. The flat and subagents lenses do their own ownership filtering inline (by\n * source and parentTaskId), so this is teams-specific.\n */\nfunction filterTasksForView(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n\t_view: TaskPanelView,\n): { filteredTasks: readonly Task[]; filteredAgents: readonly TaskAgent[] } {\n\tconst roleIds = new Set(agents.filter((a) => a.kind === \"role\").map((a) => a.id));\n\treturn {\n\t\tfilteredAgents: agents.filter((a) => a.kind === \"role\"),\n\t\tfilteredTasks: tasks.filter((t) => roleIds.has(taskOwnerId(t))),\n\t};\n}\n\n/**\n * Scope the full task list to the tasks visible in the given lens. The header,\n * state stamp, and done/total count are derived from this subset so they match\n * exactly what the user sees in the current view.\n */\nfunction filterTasksForLens(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n\tview: TaskPanelView,\n): readonly Task[] {\n\tswitch (view) {\n\t\tcase \"flat\":\n\t\t\treturn tasks.filter(isMainTask);\n\t\tcase \"subagents\":\n\t\t\treturn tasks.filter((t) => t.source === \"subagent\" || t.source === \"mcp\" || t.parentTaskId !== undefined);\n\t\tcase \"teams\": {\n\t\t\tconst roleIds = new Set<string>();\n\t\t\tfor (const a of agents) {\n\t\t\t\tif (a.kind === \"role\") roleIds.add(a.id);\n\t\t\t}\n\t\t\treturn tasks.filter((t) => roleIds.has(taskOwnerId(t)));\n\t\t}\n\t}\n}\n\n/** Fallback group metadata when a task's owner has no roster entry. */\nfunction defaultAgentMeta(id: string): TaskAgent {\n\treturn id === \"main\"\n\t\t? { id, name: \"main\", role: \"orchestrator\", kind: \"main\" }\n\t\t: { id, name: id, role: \"subagent\", kind: \"subagent\" };\n}\n\n/**\n * Partition the flat task list into owner groups. An explicit task.agent wins;\n * otherwise a subagent-sourced task falls into a generic \"subagent\" group and\n * everything else into \"main\". Group order is deterministic — main first, then\n * roster order, then stragglers — never reordered by status.\n */\nfunction groupTasks(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n): Array<{ id: string; meta: TaskAgent; items: Task[] }> {\n\tconst meta = new Map<string, TaskAgent>(agents.map((a) => [a.id, a]));\n\tconst groups = new Map<string, Task[]>();\n\tfor (const task of tasks) {\n\t\tconst owner = taskOwnerId(task);\n\t\tconst items = groups.get(owner);\n\t\tif (items) items.push(task);\n\t\telse groups.set(owner, [task]);\n\t}\n\tconst order: string[] = [];\n\tif (groups.has(\"main\")) order.push(\"main\");\n\tfor (const agent of agents) {\n\t\tif (groups.has(agent.id) && !order.includes(agent.id)) order.push(agent.id);\n\t}\n\tfor (const id of groups.keys()) {\n\t\tif (!order.includes(id)) order.push(id);\n\t}\n\treturn order.map((id) => ({\n\t\tid,\n\t\tmeta: meta.get(id) ?? defaultAgentMeta(id),\n\t\titems: groups.get(id) ?? [],\n\t}));\n}\n\n/**\n * Group header for the grouped views: owner glyph + bold name + role, the\n * agent's lifecycle `[state]` tag, an optional handoff arrow (teams), then the\n * agent's own token/cost totals + done/total on the right. Mirrors the footer's\n * \"every number accounted for\" stance, but per agent.\n */\nfunction formatGroupHeader(meta: TaskAgent, items: readonly Task[], width: number, selected = false): string {\n\t// A focused role row swaps its ▸ for a filled ▶ in accent — the team-focus\n\t// selection cursor (the owner-kind glyph mapping itself is unchanged).\n\tconst glyph = selected\n\t\t? theme.fg(\"accent\", \"▶\")\n\t\t: theme.fg(AGENT_GLYPH_COLOR[meta.kind], AGENT_GLYPH[meta.kind] ?? AGENT_GLYPH.subagent);\n\tconst name = selected ? theme.bold(theme.fg(\"accent\", meta.name)) : theme.bold(meta.name);\n\t// Roles read as a dim \"· role\" suffix for spawned/team agents; the main\n\t// orchestrator's role sits brighter (muted), matching the design's .grp-role.\n\tconst role = meta.role\n\t\t? meta.kind === \"main\"\n\t\t\t? ` ${theme.fg(\"muted\", meta.role)}`\n\t\t\t: theme.fg(\"dim\", ` · ${meta.role}`)\n\t\t: \"\";\n\tconst state = meta.state ? ` ${theme.fg(AGENT_STATE_COLOR[meta.state] ?? \"dim\", `[${meta.state}]`)}` : \"\";\n\t// Live activity for a running subagent (e.g. the tool it's executing). Empty\n\t// string means idle/between-tools, so it disappears rather than lingering stale.\n\tconst activity = meta.activity ? theme.fg(\"dim\", ` ⋯ ${meta.activity}`) : \"\";\n\tconst handoff = meta.handoff ? ` ${theme.fg(\"dim\", meta.handoff)}` : \"\";\n\n\tconst done = items.filter((t) => t.status === \"done\").length;\n\tconst countPlain = `${done}/${items.length}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${items.length}`);\n\tconst stats = meta.stats;\n\tlet rightPlain = countPlain;\n\tlet rightStyled = count;\n\tif (stats && (stats.input > 0 || stats.output > 0 || stats.cost > 0)) {\n\t\tconst statsPlain = `↑${formatTokens(stats.input)} ↓${formatTokens(stats.output)} · $${stats.cost.toFixed(3)}`;\n\t\trightPlain = `${statsPlain} ${countPlain}`;\n\t\trightStyled = `${theme.fg(\"dim\", statsPlain)} ${count}`;\n\t}\n\n\tconst rightWidth = visibleWidth(rightPlain) + 1;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\tconst left = truncateToWidth(`${glyph} ${name}${role}${state}${activity}${handoff}`, leftWidth, \"…\");\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - A single-cell owner glyph (◆ main / ◇ subagent / ▸ team role / ⧉ MCP) sits\n * before the id, derived from the owning agent's kind, so every row's origin\n * is readable at a glance even in the flat lens. A text origin tag before the\n * title names the owner: the subagent type (\"[explore]\"), the team role\n * (\"[planner]\", fed by `--team <url>`), or the MCP server (\"[github]\").\n * - Three views split by ownership (cycled via app.tasks.cycleView, shown as a\n * `tasks · subagents · teams` switcher in the header):\n * - flat (\"tasks\") — only the main agent's own TodoWrite plan;\n * - subagents — a recursive task tree over delegated work, where a subagent\n * that spawned a subagent shows its nested tasks (roots are the dispatched\n * subagents and direct MCP calls; children link via parentTaskId, merged\n * across the process boundary). Each task is its own node keeping its\n * [subagentMode]/[server] tag, with depth drawn by └─/├─/│ connectors; a\n * run with only top-level tasks reads flat (no extra indent);\n * - teams — grouped by named role-agent with handoff arrows.\n * The cycle is adaptive: empty lenses are skipped and dropped from the\n * switcher, which hides entirely when only one lens has content. An empty flat\n * lens (no main task) falls through to subagents when delegated work exists.\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks — unless a team roster is\n * registered (`--team`), in which case the empty flat lens falls through to\n * teams and every role renders as a placeholder group, so idle roles are\n * visible from startup.\n */\nexport class TaskPanelComponent implements Component, Focusable {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate view: TaskPanelView = \"flat\";\n\tprivate disposed = false;\n\n\t// Team focus mode: when the TUI focuses the panel, role rows become a\n\t// navigable list (↑/↓ select, n nudge, a attach, q/esc back). The selection\n\t// is tracked by role name so a roster reorder doesn't move the cursor.\n\tfocused = false;\n\tprivate selectedRole: string | undefined;\n\t/** Open the inline nudge editor for the selected role. */\n\tonNudge?: (role: string) => void;\n\t/** Open the attach side panel for the selected role. */\n\tonAttach?: (role: string) => void;\n\t/** Leave team focus (focus returns to the main editor). */\n\tonExitFocus?: () => void;\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\tprivate roleAgents(): TaskAgent[] {\n\t\treturn taskStore.agents().filter((a) => a.kind === \"role\");\n\t}\n\n\t/** The role the team-focus cursor sits on (clamped to the live roster). */\n\tfocusedRole(): string | undefined {\n\t\tconst roles = this.roleAgents();\n\t\tif (roles.length === 0) return undefined;\n\t\tconst match = roles.find((a) => a.name === this.selectedRole);\n\t\treturn (match ?? roles[0]).name;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst roles = this.roleAgents();\n\t\tif (roles.length === 0) {\n\t\t\tthis.onExitFocus?.();\n\t\t\treturn;\n\t\t}\n\t\tconst keybindings = getKeybindings();\n\t\tconst index = Math.max(\n\t\t\t0,\n\t\t\troles.findIndex((a) => a.name === this.selectedRole),\n\t\t);\n\t\tif (keybindings.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedRole = roles[Math.max(0, index - 1)].name;\n\t\t} else if (keybindings.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedRole = roles[Math.min(roles.length - 1, index + 1)].name;\n\t\t} else if (matchesKey(data, \"n\")) {\n\t\t\tconst role = this.focusedRole();\n\t\t\tif (role) this.onNudge?.(role);\n\t\t} else if (matchesKey(data, \"a\")) {\n\t\t\tconst role = this.focusedRole();\n\t\t\tif (role) this.onAttach?.(role);\n\t\t} else if (matchesKey(data, \"q\") || keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onExitFocus?.();\n\t\t}\n\t\tthis.ui?.requestRender();\n\t}\n\n\tgetView(): TaskPanelView {\n\t\treturn this.view;\n\t}\n\n\tsetView(view: TaskPanelView): void {\n\t\tthis.view = view;\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Advance to the next view lens with content (flat → subagents → teams →\n\t * flat), skipping empty lenses. With nothing delegated this is a no-op on\n\t * flat; a stale view (its lens emptied since selection) snaps back to flat.\n\t */\n\tcycleView(): TaskPanelView {\n\t\tconst available = availableViews(taskStore.list(), taskStore.agents());\n\t\tconst idx = available.indexOf(this.view);\n\t\tthis.view = available[(idx + 1) % available.length] ?? \"flat\";\n\t\tthis.ui?.requestRender();\n\t\treturn this.view;\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (this.disposed) {\n\t\t\tif (this.animationTimer) {\n\t\t\t\tclearInterval(this.animationTimer);\n\t\t\t\tthis.animationTimer = null;\n\t\t\t\tthis.frame = 0;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t\tthis.disposed = true;\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.disposed) return [];\n\n\t\tconst tasks = taskStore.list();\n\t\tconst allAgents = taskStore.agents();\n\n\t\t// Resolve the lens: keep the stored view while it still has content, else\n\t\t// fall through to the first available lens (flat → subagents → teams). The\n\t\t// stored view is untouched so an explicit setView choice resumes once its\n\t\t// content returns. This is how an empty flat lens (no main/TodoWrite task)\n\t\t// falls through to the subagents tree when only delegated work exists, and\n\t\t// how an empty pane falls through to the teams roster at startup.\n\t\tconst available = availableViews(tasks, allAgents);\n\t\tlet view: TaskPanelView = available.includes(this.view) ? this.view : (available[0] ?? \"flat\");\n\n\t\t// Team focus always operates on the teams lens — the focused role list is\n\t\t// exactly what the lens renders, so the cursor is never invisible.\n\t\tif (this.focused) view = \"teams\";\n\n\t\t// In teams view the roster itself is content: role agents render as\n\t\t// placeholder groups even without tasks (idle roles at startup, queued\n\t\t// upcoming work).\n\t\tconst hasRoleRoster = view === \"teams\" && allAgents.some((a) => a.kind === \"role\");\n\n\t\tif (tasks.length === 0 && !hasRoleRoster) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\t// Scope all visual state to the tasks visible in the current lens so the\n\t\t// animation, rail color, and header count match exactly what the user sees.\n\t\tconst lensTasks = filterTasksForLens(tasks, allAgents, view);\n\t\tconst hasActive = lensTasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(lensTasks);\n\t\tconst totalSecs = lensTasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\tconst lines: string[] = [gutter + formatHeader(lensTasks, inner, state, totalSecs, view, available)];\n\n\t\tif (view === \"flat\") {\n\t\t\t// Only the main agent's own TodoWrite plan — delegated (subagent/MCP) work\n\t\t\t// belongs to the subagents lens. Resolve each row's owner from the roster\n\t\t\t// so the glyph/tag reflect the owning agent's kind, not just the source.\n\t\t\tconst agentById = new Map(allAgents.map((a) => [a.id, a]));\n\t\t\tfor (const task of tasks) {\n\t\t\t\tif (!isMainTask(task)) continue;\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { owner: agentById.get(taskOwnerId(task)) }));\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tif (view === \"subagents\") {\n\t\t\t// A recursive task tree over the delegated forest: roots are the main\n\t\t\t// agent's dispatched subagents (and direct MCP calls), children are the\n\t\t\t// tasks they spawned in turn (linked by parentTaskId, merged across the\n\t\t\t// process boundary), so a subagent that spawned a subagent is visible.\n\t\t\t// Each task is its own node keeping its [subagentMode]/[server] tag; depth\n\t\t\t// is drawn with └─/├─/│ connectors. With only top-level tasks the roots\n\t\t\t// carry an empty prefix and read exactly like flat rows (no extra indent).\n\t\t\tconst childrenByParent = new Map<number, Task[]>();\n\t\t\tfor (const task of tasks) {\n\t\t\t\tif (task.parentTaskId === undefined) continue;\n\t\t\t\tconst siblings = childrenByParent.get(task.parentTaskId);\n\t\t\t\tif (siblings) siblings.push(task);\n\t\t\t\telse childrenByParent.set(task.parentTaskId, [task]);\n\t\t\t}\n\t\t\tconst roots = tasks.filter(\n\t\t\t\t(t) => t.parentTaskId === undefined && (t.source === \"subagent\" || t.source === \"mcp\"),\n\t\t\t);\n\t\t\tconst walk = (task: Task, prefix: string, isLast: boolean, isRoot: boolean): void => {\n\t\t\t\tconst connector = isRoot ? \"\" : `${prefix}${isLast ? \"└─ \" : \"├─ \"}`;\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { treePrefix: connector }));\n\t\t\t\tconst kids = childrenByParent.get(task.id) ?? [];\n\t\t\t\tconst childPrefix = isRoot ? \"\" : `${prefix}${isLast ? \" \" : \"│ \"}`;\n\t\t\t\tfor (let i = 0; i < kids.length; i++) {\n\t\t\t\t\twalk(kids[i] as Task, childPrefix, i === kids.length - 1, false);\n\t\t\t\t}\n\t\t\t};\n\t\t\tfor (const root of roots) walk(root, \"\", true, true);\n\t\t\treturn lines;\n\t\t}\n\n\t\t// teams view: role-agent groups with handoff connectors and queued placeholders.\n\t\tconst { filteredTasks, filteredAgents } = filterTasksForView(tasks, allAgents, view);\n\t\tconst groups = groupTasks(filteredTasks, filteredAgents);\n\t\tconst groupIds = new Set(groups.map((g) => g.id));\n\t\t// Role agents with no tasks still get a group header: idle roles are the\n\t\t// roster at startup, queued ones upcoming work, done/failed ones the\n\t\t// state they settled in after reset() dropped their tasks.\n\t\tfor (const agent of filteredAgents) {\n\t\t\tif (!groupIds.has(agent.id)) {\n\t\t\t\tgroups.push({ id: agent.id, meta: agent, items: [] });\n\t\t\t}\n\t\t}\n\t\tconst cursorRole = this.focused ? this.focusedRole() : undefined;\n\t\tfor (const group of groups) {\n\t\t\tconst selected = cursorRole !== undefined && group.meta.kind === \"role\" && group.meta.name === cursorRole;\n\t\t\tlines.push(gutter + formatGroupHeader(group.meta, group.items, inner, selected));\n\t\t\tfor (const task of group.items) {\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, { grouped: true }));\n\t\t\t}\n\t\t\t// Forward-handoff connector: emit \"└──→ name\" only for \"→ name\" arrows\n\t\t\t// (not back-references \"← name\"), and only when the target exists in the\n\t\t\t// visible role roster.\n\t\t\tconst { handoff } = group.meta;\n\t\t\tif (handoff) {\n\t\t\t\tconst arrowIdx = handoff.indexOf(\"→ \");\n\t\t\t\tif (arrowIdx !== -1) {\n\t\t\t\t\tconst nextName = handoff.slice(arrowIdx + 2).trim();\n\t\t\t\t\tif (filteredAgents.some((a) => a.name === nextName)) {\n\t\t\t\t\t\tconst connectorPrefix = `${GROUP_INDENT_PLAIN} └──→ `;\n\t\t\t\t\t\tconst connectorPad = Math.max(0, inner - visibleWidth(connectorPrefix) - visibleWidth(nextName));\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\tgutter +\n\t\t\t\t\t\t\t\ttheme.fg(\"borderMuted\", connectorPrefix) +\n\t\t\t\t\t\t\t\ttheme.fg(\"dim\", nextName) +\n\t\t\t\t\t\t\t\t\" \".repeat(connectorPad),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (this.focused) {\n\t\t\tlines.push(\n\t\t\t\tgutter + truncateToWidth(theme.fg(\"dim\", \"↑/↓ select · n nudge · a attach · q/esc back\"), inner, \"…\"),\n\t\t\t);\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.2.73",
4
+ "version": "0.2.74",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "bun": ">=1.0.0"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.2.73",
4
+ "version": "0.2.74",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "bun": ">=1.0.0"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-extension-sandbox",
3
3
  "private": true,
4
- "version": "0.2.73",
4
+ "version": "0.2.74",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "bun": ">=1.0.0"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-extension-with-deps",
3
3
  "private": true,
4
- "version": "0.2.73",
4
+ "version": "0.2.74",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "bun": ">=1.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-agent",
3
- "version": "0.4.76",
3
+ "version": "0.4.77",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "hoocodeConfig": {
@@ -50,9 +50,9 @@
50
50
  "prepublishOnly": "npm run clean && npm run build"
51
51
  },
52
52
  "dependencies": {
53
- "@kolisachint/hoocode-agent-core": "^0.4.76",
54
- "@kolisachint/hoocode-ai": "^0.4.76",
55
- "@kolisachint/hoocode-tui": "^0.4.76",
53
+ "@kolisachint/hoocode-agent-core": "^0.4.77",
54
+ "@kolisachint/hoocode-ai": "^0.4.77",
55
+ "@kolisachint/hoocode-tui": "^0.4.77",
56
56
  "@silvia-odwyer/photon-node": "^0.3.4",
57
57
  "chalk": "^5.5.0",
58
58
  "cli-highlight": "^2.1.11",