codingbuddy 4.2.0 → 4.3.0

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.
Files changed (124) hide show
  1. package/dist/src/cli/cli.d.ts +2 -2
  2. package/dist/src/cli/cli.js +4 -2
  3. package/dist/src/cli/cli.js.map +1 -1
  4. package/dist/src/cli/cli.types.d.ts +3 -0
  5. package/dist/src/cli/init/prompts/agent-prompt.d.ts +1 -1
  6. package/dist/src/cli/restart-tui.d.ts +9 -0
  7. package/dist/src/cli/restart-tui.js +30 -0
  8. package/dist/src/cli/restart-tui.js.map +1 -0
  9. package/dist/src/cli/run-tui.d.ts +3 -1
  10. package/dist/src/cli/run-tui.js +13 -1
  11. package/dist/src/cli/run-tui.js.map +1 -1
  12. package/dist/src/keyword/keyword.types.d.ts +3 -3
  13. package/dist/src/keyword/keyword.types.js +31 -1
  14. package/dist/src/keyword/keyword.types.js.map +1 -1
  15. package/dist/src/keyword/patterns/backend.patterns.js +10 -0
  16. package/dist/src/keyword/patterns/backend.patterns.js.map +1 -1
  17. package/dist/src/keyword/patterns/context.patterns.js +1 -1
  18. package/dist/src/keyword/patterns/data-science.patterns.d.ts +2 -0
  19. package/dist/src/keyword/patterns/data-science.patterns.js +71 -0
  20. package/dist/src/keyword/patterns/data-science.patterns.js.map +1 -0
  21. package/dist/src/keyword/patterns/index.d.ts +3 -0
  22. package/dist/src/keyword/patterns/index.js +7 -1
  23. package/dist/src/keyword/patterns/index.js.map +1 -1
  24. package/dist/src/keyword/patterns/intent-pattern-checks.js +24 -0
  25. package/dist/src/keyword/patterns/intent-pattern-checks.js.map +1 -1
  26. package/dist/src/keyword/patterns/security.patterns.d.ts +2 -0
  27. package/dist/src/keyword/patterns/security.patterns.js +106 -0
  28. package/dist/src/keyword/patterns/security.patterns.js.map +1 -0
  29. package/dist/src/keyword/patterns/systems.patterns.d.ts +2 -0
  30. package/dist/src/keyword/patterns/systems.patterns.js +101 -0
  31. package/dist/src/keyword/patterns/systems.patterns.js.map +1 -0
  32. package/dist/src/keyword/patterns/test.patterns.d.ts +2 -0
  33. package/dist/src/keyword/patterns/test.patterns.js +71 -0
  34. package/dist/src/keyword/patterns/test.patterns.js.map +1 -0
  35. package/dist/src/keyword/strategies/__tests__/strategy-test.utils.d.ts +1 -1
  36. package/dist/src/keyword/strategies/__tests__/strategy-test.utils.js +4 -0
  37. package/dist/src/keyword/strategies/__tests__/strategy-test.utils.js.map +1 -1
  38. package/dist/src/keyword/strategies/act-agent.strategy.js +2 -2
  39. package/dist/src/keyword/strategies/act-agent.strategy.js.map +1 -1
  40. package/dist/src/mcp/handlers/index.d.ts +1 -0
  41. package/dist/src/mcp/handlers/index.js +3 -1
  42. package/dist/src/mcp/handlers/index.js.map +1 -1
  43. package/dist/src/mcp/handlers/mode.handler.js +15 -1
  44. package/dist/src/mcp/handlers/mode.handler.js.map +1 -1
  45. package/dist/src/mcp/handlers/tui.handler.d.ts +8 -0
  46. package/dist/src/mcp/handlers/tui.handler.js +50 -0
  47. package/dist/src/mcp/handlers/tui.handler.js.map +1 -0
  48. package/dist/src/mcp/mcp.module.js +1 -0
  49. package/dist/src/mcp/mcp.module.js.map +1 -1
  50. package/dist/src/shared/version.d.ts +1 -0
  51. package/dist/src/shared/version.js +5 -0
  52. package/dist/src/shared/version.js.map +1 -0
  53. package/dist/src/shared/version.utils.js +2 -14
  54. package/dist/src/shared/version.utils.js.map +1 -1
  55. package/dist/src/tui/__perf__/rendering-performance.spec.js +4 -4
  56. package/dist/src/tui/__perf__/rendering-performance.spec.js.map +1 -1
  57. package/dist/src/tui/components/ActivityVisualizer.d.ts +7 -3
  58. package/dist/src/tui/components/ActivityVisualizer.js +8 -9
  59. package/dist/src/tui/components/ActivityVisualizer.js.map +1 -1
  60. package/dist/src/tui/components/ActivityVisualizer.spec.js +73 -27
  61. package/dist/src/tui/components/ActivityVisualizer.spec.js.map +1 -1
  62. package/dist/src/tui/components/ChecklistPanel.d.ts +10 -0
  63. package/dist/src/tui/components/ChecklistPanel.js +19 -0
  64. package/dist/src/tui/components/ChecklistPanel.js.map +1 -0
  65. package/dist/src/tui/components/ChecklistPanel.spec.d.ts +1 -0
  66. package/dist/src/tui/components/ChecklistPanel.spec.js +45 -0
  67. package/dist/src/tui/components/ChecklistPanel.spec.js.map +1 -0
  68. package/dist/src/tui/components/FocusedAgentPanel.d.ts +2 -8
  69. package/dist/src/tui/components/FocusedAgentPanel.js +1 -14
  70. package/dist/src/tui/components/FocusedAgentPanel.js.map +1 -1
  71. package/dist/src/tui/components/FocusedAgentPanel.spec.js +20 -51
  72. package/dist/src/tui/components/FocusedAgentPanel.spec.js.map +1 -1
  73. package/dist/src/tui/components/HeaderBar.d.ts +1 -2
  74. package/dist/src/tui/components/HeaderBar.js +3 -7
  75. package/dist/src/tui/components/HeaderBar.js.map +1 -1
  76. package/dist/src/tui/components/HeaderBar.spec.js +13 -13
  77. package/dist/src/tui/components/HeaderBar.spec.js.map +1 -1
  78. package/dist/src/tui/components/StageHealthBar.d.ts +3 -1
  79. package/dist/src/tui/components/StageHealthBar.js +10 -4
  80. package/dist/src/tui/components/StageHealthBar.js.map +1 -1
  81. package/dist/src/tui/components/StageHealthBar.spec.js +21 -7
  82. package/dist/src/tui/components/StageHealthBar.spec.js.map +1 -1
  83. package/dist/src/tui/components/activity-visualizer.pure.d.ts +3 -8
  84. package/dist/src/tui/components/activity-visualizer.pure.js +113 -50
  85. package/dist/src/tui/components/activity-visualizer.pure.js.map +1 -1
  86. package/dist/src/tui/components/checklist-panel.pure.d.ts +2 -0
  87. package/dist/src/tui/components/checklist-panel.pure.js +18 -0
  88. package/dist/src/tui/components/checklist-panel.pure.js.map +1 -0
  89. package/dist/src/tui/components/flow-map.pure.d.ts +2 -2
  90. package/dist/src/tui/components/flow-map.pure.js +120 -29
  91. package/dist/src/tui/components/flow-map.pure.js.map +1 -1
  92. package/dist/src/tui/components/focused-agent.pure.d.ts +0 -14
  93. package/dist/src/tui/components/focused-agent.pure.js +0 -45
  94. package/dist/src/tui/components/focused-agent.pure.js.map +1 -1
  95. package/dist/src/tui/components/grid-layout.pure.js +26 -4
  96. package/dist/src/tui/components/grid-layout.pure.js.map +1 -1
  97. package/dist/src/tui/components/index.d.ts +4 -2
  98. package/dist/src/tui/components/index.js +7 -7
  99. package/dist/src/tui/components/index.js.map +1 -1
  100. package/dist/src/tui/components/stage-health.pure.d.ts +1 -0
  101. package/dist/src/tui/components/stage-health.pure.js +4 -0
  102. package/dist/src/tui/components/stage-health.pure.js.map +1 -1
  103. package/dist/src/tui/dashboard-app.d.ts +2 -1
  104. package/dist/src/tui/dashboard-app.js +12 -15
  105. package/dist/src/tui/dashboard-app.js.map +1 -1
  106. package/dist/src/tui/dashboard-app.spec.js +11 -3
  107. package/dist/src/tui/dashboard-app.spec.js.map +1 -1
  108. package/dist/src/tui/dashboard-types.d.ts +6 -0
  109. package/dist/src/tui/dashboard-types.js +1 -0
  110. package/dist/src/tui/dashboard-types.js.map +1 -1
  111. package/dist/src/tui/eventbus-ui.integration.spec.js +1 -0
  112. package/dist/src/tui/eventbus-ui.integration.spec.js.map +1 -1
  113. package/dist/src/tui/events/parse-agent.js +0 -26
  114. package/dist/src/tui/events/parse-agent.js.map +1 -1
  115. package/dist/src/tui/events/response-event-extractor.js +33 -7
  116. package/dist/src/tui/events/response-event-extractor.js.map +1 -1
  117. package/dist/src/tui/hooks/use-dashboard-state.d.ts +6 -0
  118. package/dist/src/tui/hooks/use-dashboard-state.js +68 -18
  119. package/dist/src/tui/hooks/use-dashboard-state.js.map +1 -1
  120. package/dist/src/tui/multi-session-app.js +6 -1
  121. package/dist/src/tui/multi-session-app.js.map +1 -1
  122. package/dist/src/tui-bundle.mjs +615 -333
  123. package/dist/tsconfig.build.tsbuildinfo +1 -1
  124. package/package.json +3 -3
@@ -1371,12 +1371,12 @@ var require_eventemitter2 = __commonJS({
1371
1371
  });
1372
1372
 
1373
1373
  // src/tui/index.tsx
1374
- import React10 from "react";
1374
+ import React11 from "react";
1375
1375
  import { render } from "ink";
1376
1376
 
1377
1377
  // src/tui/dashboard-app.tsx
1378
- import React7, { useMemo as useMemo3 } from "react";
1379
- import { Box as Box7 } from "ink";
1378
+ import React8, { useMemo as useMemo3 } from "react";
1379
+ import { Box as Box8 } from "ink";
1380
1380
 
1381
1381
  // src/tui/hooks/use-terminal-size.ts
1382
1382
  import { useState, useEffect } from "react";
@@ -1400,6 +1400,7 @@ function createDefaultDashboardNode(params) {
1400
1400
  status: "idle",
1401
1401
  isPrimary: false,
1402
1402
  progress: 0,
1403
+ isParallel: false,
1403
1404
  ...params
1404
1405
  };
1405
1406
  }
@@ -1492,23 +1493,6 @@ var MODE_KEYWORD_TO_AGENT = {
1492
1493
  EVAL: "eval-mode",
1493
1494
  AUTO: "auto-mode"
1494
1495
  };
1495
- var TOOL_AGENT_MAP = {
1496
- search_rules: "query",
1497
- get_agent_details: "query",
1498
- get_project_config: "config",
1499
- set_project_root: "config",
1500
- suggest_config_updates: "config",
1501
- recommend_skills: "skill",
1502
- get_skill: "skill",
1503
- list_skills: "skill",
1504
- analyze_task: "analysis",
1505
- generate_checklist: "checklist",
1506
- read_context: "context",
1507
- update_context: "context",
1508
- cleanup_context: "context",
1509
- get_code_conventions: "conventions",
1510
- dispatch_agents: "orchestrator"
1511
- };
1512
1496
  function parseAgentFromToolName(toolName, args) {
1513
1497
  if (toolName === "get_agent_system_prompt") {
1514
1498
  return parseGetAgentSystemPrompt(args);
@@ -1519,15 +1503,6 @@ function parseAgentFromToolName(toolName, args) {
1519
1503
  if (toolName === "prepare_parallel_agents") {
1520
1504
  return parseParallelAgents(args);
1521
1505
  }
1522
- const role = TOOL_AGENT_MAP[toolName];
1523
- if (role) {
1524
- return {
1525
- agentId: toolName,
1526
- name: toolName,
1527
- role,
1528
- isPrimary: false
1529
- };
1530
- }
1531
1506
  return null;
1532
1507
  }
1533
1508
  function parseGetAgentSystemPrompt(args) {
@@ -1664,13 +1639,24 @@ function extractFromParseMode(json) {
1664
1639
  if (Array.isArray(par.specialists) && par.specialists.length > 0) {
1665
1640
  const specialists = par.specialists.filter((s) => typeof s === "string");
1666
1641
  if (specialists.length > 0) {
1642
+ const parallelMode = typeof json.mode === "string" && VALID_MODES.has(json.mode) ? json.mode : "PLAN";
1667
1643
  events.push({
1668
1644
  event: TUI_EVENTS.PARALLEL_STARTED,
1669
- payload: {
1670
- specialists,
1671
- mode: typeof json.mode === "string" && VALID_MODES.has(json.mode) ? json.mode : "PLAN"
1672
- }
1645
+ payload: { specialists, mode: parallelMode }
1673
1646
  });
1647
+ if (delegateName) {
1648
+ for (const name of specialists) {
1649
+ events.push({
1650
+ event: TUI_EVENTS.AGENT_RELATIONSHIP,
1651
+ payload: {
1652
+ from: `primary:${delegateName}`,
1653
+ to: `specialist:${name}`,
1654
+ label: "parallel",
1655
+ type: "delegation"
1656
+ }
1657
+ });
1658
+ }
1659
+ }
1674
1660
  }
1675
1661
  }
1676
1662
  }
@@ -1694,12 +1680,27 @@ function extractFromPrepareParallelAgents(json) {
1694
1680
  }).filter((name) => name !== null);
1695
1681
  if (specialists.length === 0) return [];
1696
1682
  const mode = typeof json.mode === "string" && VALID_MODES.has(json.mode) ? json.mode : "PLAN";
1697
- return [
1683
+ const primaryAgentId = typeof json.primaryAgentId === "string" ? json.primaryAgentId : null;
1684
+ const events = [
1698
1685
  {
1699
1686
  event: TUI_EVENTS.PARALLEL_STARTED,
1700
1687
  payload: { specialists, mode }
1701
1688
  }
1702
1689
  ];
1690
+ if (primaryAgentId) {
1691
+ for (const name of specialists) {
1692
+ events.push({
1693
+ event: TUI_EVENTS.AGENT_RELATIONSHIP,
1694
+ payload: {
1695
+ from: primaryAgentId,
1696
+ to: `specialist:${name}`,
1697
+ label: "parallel",
1698
+ type: "delegation"
1699
+ }
1700
+ });
1701
+ }
1702
+ }
1703
+ return events;
1703
1704
  }
1704
1705
  function extractFromDispatchAgents(json) {
1705
1706
  const events = [];
@@ -2086,12 +2087,24 @@ var ACT_PRIMARY_AGENTS = [
2086
2087
  // IaC, Kubernetes, multi-cloud specialist - high priority for infra tasks
2087
2088
  "data-engineer",
2088
2089
  // Database/schema specialist - high priority for data tasks
2090
+ "data-scientist",
2091
+ // EDA, statistical analysis, ML modeling, visualization, Jupyter notebooks
2092
+ "ai-ml-engineer",
2093
+ // ML frameworks, LLM integration, embeddings, fine-tuning
2089
2094
  "mobile-developer",
2090
2095
  // Mobile app specialist - detected by project files
2091
2096
  "frontend-developer",
2092
2097
  "backend-developer",
2093
2098
  "devops-engineer",
2094
- "agent-architect"
2099
+ "agent-architect",
2100
+ "test-engineer",
2101
+ // TDD, unit/integration/e2e test specialist
2102
+ "security-engineer",
2103
+ // Security features implementation & vulnerability remediation (intent priority: 5th)
2104
+ "systems-developer",
2105
+ // Rust, C, C++, FFI, WASM, embedded, low-level optimization (intent priority: 6th)
2106
+ "software-engineer"
2107
+ // fallback default — no intent patterns, language-agnostic generalist
2095
2108
  ];
2096
2109
  var EVAL_PRIMARY_AGENT = "code-reviewer";
2097
2110
  var ALL_PRIMARY_AGENTS = [
@@ -3933,6 +3946,7 @@ function selectFocusedAgent(agents, currentFocusId) {
3933
3946
  var EVENT_LOG_MAX = 100;
3934
3947
  var EDGE_MAX = 200;
3935
3948
  var TOOL_CALLS_MAX = 200;
3949
+ var EXPECTED_AGENT_DURATION_MS = 12e4;
3936
3950
  function createInitialDashboardState() {
3937
3951
  return {
3938
3952
  workspace: process.cwd(),
@@ -3948,6 +3962,8 @@ function createInitialDashboardState() {
3948
3962
  objectives: [],
3949
3963
  activeSkills: [],
3950
3964
  toolInvokeCount: 0,
3965
+ agentActivateCount: 0,
3966
+ skillInvokeCount: 0,
3951
3967
  outputStats: { files: 0, commits: 0 },
3952
3968
  contextDecisions: [],
3953
3969
  contextNotes: [],
@@ -3969,11 +3985,20 @@ function dashboardReducer(state, action) {
3969
3985
  stage: state.currentMode ?? "PLAN",
3970
3986
  status: "running",
3971
3987
  isPrimary,
3972
- progress: 0
3988
+ progress: 0,
3989
+ isParallel: false,
3990
+ // AGENT_ACTIVATED는 항상 단일 에이전트
3991
+ startedAt: Date.now()
3973
3992
  });
3974
3993
  const globalState = "RUNNING";
3975
3994
  const focusedAgentId = selectFocusedAgent(agents, state.focusedAgentId);
3976
- return { ...state, agents, globalState, focusedAgentId };
3995
+ return {
3996
+ ...state,
3997
+ agents,
3998
+ globalState,
3999
+ focusedAgentId,
4000
+ agentActivateCount: state.agentActivateCount + 1
4001
+ };
3977
4002
  }
3978
4003
  case "AGENT_DEACTIVATED": {
3979
4004
  const { agentId, reason } = action.payload;
@@ -3983,7 +4008,8 @@ function dashboardReducer(state, action) {
3983
4008
  agents.set(agentId, {
3984
4009
  ...existing,
3985
4010
  status: reason === "error" ? "error" : "done",
3986
- progress: reason === "error" ? existing.progress : 100
4011
+ progress: reason === "error" ? existing.progress : 100,
4012
+ completedAt: Date.now()
3987
4013
  });
3988
4014
  }
3989
4015
  let anyRunning = false;
@@ -3998,24 +4024,19 @@ function dashboardReducer(state, action) {
3998
4024
  return { ...state, agents, globalState, focusedAgentId };
3999
4025
  }
4000
4026
  case "MODE_CHANGED": {
4001
- const newMode = action.payload.to;
4002
- let agents = state.agents;
4003
- let needsClone = false;
4004
- for (const a of state.agents.values()) {
4005
- if (a.status === "running") {
4006
- needsClone = true;
4007
- break;
4027
+ const { from, to: newMode } = action.payload;
4028
+ const agents = cloneAgents(state.agents);
4029
+ for (const [id, a] of agents) {
4030
+ if (from !== null && a.stage === from && (a.status === "done" || a.status === "idle" && a.isParallel)) {
4031
+ agents.delete(id);
4032
+ continue;
4008
4033
  }
4009
- }
4010
- if (needsClone) {
4011
- agents = cloneAgents(state.agents);
4012
- for (const [id, a] of agents) {
4013
- if (a.status === "running") {
4014
- agents.set(id, { ...a, stage: newMode });
4015
- }
4034
+ if (a.status === "running") {
4035
+ agents.set(id, { ...a, stage: newMode });
4016
4036
  }
4017
4037
  }
4018
- return { ...state, currentMode: newMode, agents, activeSkills: [] };
4038
+ const focusedAgentId = selectFocusedAgent(agents, state.focusedAgentId);
4039
+ return { ...state, currentMode: newMode, agents, activeSkills: [], focusedAgentId };
4019
4040
  }
4020
4041
  case "AGENT_RELATIONSHIP": {
4021
4042
  const edge = {
@@ -4049,10 +4070,15 @@ function dashboardReducer(state, action) {
4049
4070
  targetAgent = state.agents.get(state.focusedAgentId) ?? null;
4050
4071
  }
4051
4072
  if (targetAgent && targetAgent.status === "running") {
4073
+ const now = Date.now();
4074
+ const elapsed = targetAgent.startedAt !== void 0 ? now - targetAgent.startedAt : 0;
4075
+ const timeBased = Math.min(90, elapsed / EXPECTED_AGENT_DURATION_MS * 100);
4076
+ const toolBased = Math.min(95, targetAgent.progress + 3);
4077
+ const newProgress = Math.round(Math.max(timeBased, toolBased));
4052
4078
  agents = cloneAgents(state.agents);
4053
4079
  agents.set(targetAgent.id, {
4054
4080
  ...targetAgent,
4055
- progress: Math.min(95, targetAgent.progress + 5)
4081
+ progress: newProgress
4056
4082
  });
4057
4083
  }
4058
4084
  const toolCall = {
@@ -4075,7 +4101,11 @@ function dashboardReducer(state, action) {
4075
4101
  case "SKILL_RECOMMENDED": {
4076
4102
  const { skillName } = action.payload;
4077
4103
  if (state.activeSkills.includes(skillName)) return state;
4078
- return { ...state, activeSkills: [...state.activeSkills, skillName] };
4104
+ return {
4105
+ ...state,
4106
+ activeSkills: [...state.activeSkills, skillName],
4107
+ skillInvokeCount: state.skillInvokeCount + 1
4108
+ };
4079
4109
  }
4080
4110
  case "PARALLEL_STARTED": {
4081
4111
  const { specialists, mode } = action.payload;
@@ -4089,7 +4119,10 @@ function dashboardReducer(state, action) {
4089
4119
  stage: mode,
4090
4120
  status: "idle",
4091
4121
  isPrimary: false,
4092
- progress: 0
4122
+ progress: 0,
4123
+ isParallel: true,
4124
+ // parallel dispatch로 등록된 에이전트
4125
+ startedAt: Date.now()
4093
4126
  });
4094
4127
  }
4095
4128
  return { ...state, agents };
@@ -4106,6 +4139,21 @@ function dashboardReducer(state, action) {
4106
4139
  }
4107
4140
  case "SESSION_RESET":
4108
4141
  return createInitialDashboardState();
4142
+ case "CLEANUP_STALE_AGENTS": {
4143
+ const { now, ttlMs } = action.payload;
4144
+ let changed = false;
4145
+ const agents = cloneAgents(state.agents);
4146
+ for (const [id, a] of agents) {
4147
+ if (a.status === "running") continue;
4148
+ if (a.status === "done" && a.completedAt !== void 0 && now - a.completedAt > ttlMs || a.status === "error" && a.completedAt !== void 0 && now - a.completedAt > ttlMs * 2 || a.status === "idle" && a.isParallel && a.startedAt !== void 0 && now - a.startedAt > ttlMs) {
4149
+ agents.delete(id);
4150
+ changed = true;
4151
+ }
4152
+ }
4153
+ if (!changed) return state;
4154
+ const focusedAgentId = selectFocusedAgent(agents, state.focusedAgentId);
4155
+ return { ...state, agents, focusedAgentId };
4156
+ }
4109
4157
  // Reserved: no emitter currently produces PARALLEL_COMPLETED. Subscription kept for forward compatibility.
4110
4158
  case "PARALLEL_COMPLETED":
4111
4159
  case "AGENTS_LOADED":
@@ -4162,6 +4210,12 @@ function useDashboardState(eventBus) {
4162
4210
  eventBus.off(TUI_EVENTS.CONTEXT_UPDATED, onContextUpdated);
4163
4211
  };
4164
4212
  }, [eventBus]);
4213
+ useEffect2(() => {
4214
+ const cleanupInterval = setInterval(() => {
4215
+ dispatch({ type: "CLEANUP_STALE_AGENTS", payload: { now: Date.now(), ttlMs: 3e4 } });
4216
+ }, 1e4);
4217
+ return () => clearInterval(cleanupInterval);
4218
+ }, []);
4165
4219
  return state;
4166
4220
  }
4167
4221
 
@@ -4296,7 +4350,6 @@ function StateIndicator({ globalState }) {
4296
4350
  }
4297
4351
  function HeaderBar({
4298
4352
  workspace,
4299
- sessionId,
4300
4353
  currentMode,
4301
4354
  globalState,
4302
4355
  layoutMode,
@@ -4319,7 +4372,6 @@ function HeaderBar({
4319
4372
  /* @__PURE__ */ React.createElement(StateIndicator, { globalState })
4320
4373
  );
4321
4374
  }
4322
- const sessDisplay = sessionId.length > 8 ? sessionId.slice(0, 8) : sessionId;
4323
4375
  return /* @__PURE__ */ React.createElement(
4324
4376
  Box,
4325
4377
  {
@@ -4329,9 +4381,9 @@ function HeaderBar({
4329
4381
  overflowX: "hidden",
4330
4382
  flexDirection: "row"
4331
4383
  },
4332
- /* @__PURE__ */ React.createElement(Box, { gap: 2, flexShrink: 0 }, /* @__PURE__ */ React.createElement(Text, { color: "cyan", bold: true }, "\u27E8\u27E9 CODINGBUDDY AGENT DASHBOARD"), /* @__PURE__ */ React.createElement(ModeFlow, { currentMode }), /* @__PURE__ */ React.createElement(StateIndicator, { globalState })),
4384
+ /* @__PURE__ */ React.createElement(Box, { gap: 2, flexShrink: 1, minWidth: 0 }, /* @__PURE__ */ React.createElement(Text, { color: "cyan", bold: true }, "\u27E8\u27E9 CODINGBUDDY AGENT DASHBOARD"), /* @__PURE__ */ React.createElement(ModeFlow, { currentMode }), /* @__PURE__ */ React.createElement(StateIndicator, { globalState })),
4333
4385
  /* @__PURE__ */ React.createElement(Box, { flexGrow: 1 }),
4334
- /* @__PURE__ */ React.createElement(Box, { flexShrink: 1, overflowX: "hidden" }, /* @__PURE__ */ React.createElement(Text, { dimColor: true, wrap: "truncate" }, workspace, " sess:", sessDisplay))
4386
+ /* @__PURE__ */ React.createElement(Box, { flexShrink: 1, overflowX: "hidden" }, /* @__PURE__ */ React.createElement(Text, { dimColor: true, wrap: "truncate" }, workspace))
4335
4387
  );
4336
4388
  }
4337
4389
 
@@ -4491,9 +4543,16 @@ function computeLabelPosition(path9, label) {
4491
4543
  // src/tui/components/flow-map.pure.ts
4492
4544
  var NODE_WIDTH = 17;
4493
4545
  var NODE_HEIGHT = 4;
4546
+ var PARALLEL_STYLES = {
4547
+ parallel: { fg: "cyan" }
4548
+ // 파랑 — 병렬 실행
4549
+ };
4550
+ var TREE_CONNECTOR_STYLE = { fg: "cyan", dim: true };
4494
4551
  var STAGE_ORDER = ["PLAN", "ACT", "EVAL", "AUTO"];
4495
- var STAGE_SLOT_WIDTH = 8;
4552
+ var STAGE_SLOT_WIDTH = 18;
4496
4553
  var PIPELINE_RIGHT_MARGIN = 10;
4554
+ var DIMMED_STYLE = { dim: true };
4555
+ var WAITING_STYLE = { fg: "gray", dim: true };
4497
4556
  function groupByStage(agents) {
4498
4557
  const byStage = /* @__PURE__ */ new Map();
4499
4558
  for (const agent of agents.values()) {
@@ -4503,9 +4562,15 @@ function groupByStage(agents) {
4503
4562
  }
4504
4563
  return byStage;
4505
4564
  }
4565
+ function isBoxAgent(agent) {
4566
+ return agent.isPrimary || agent.isParallel;
4567
+ }
4506
4568
  function sortAgents(agents) {
4507
4569
  return [...agents].sort((a, b) => {
4508
4570
  if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;
4571
+ if (!a.isPrimary && !b.isPrimary) {
4572
+ if (a.isParallel !== b.isParallel) return a.isParallel ? -1 : 1;
4573
+ }
4509
4574
  return a.name.localeCompare(b.name);
4510
4575
  });
4511
4576
  }
@@ -4539,14 +4604,18 @@ function layoutAgentNodes(agents, columns) {
4539
4604
  if (!col) continue;
4540
4605
  const sorted = sortAgents(stageAgents);
4541
4606
  const startY = 3;
4542
- sorted.forEach((agent, idx) => {
4607
+ let currentY = startY;
4608
+ for (const agent of sorted) {
4609
+ const boxAgent = isBoxAgent(agent);
4610
+ const height = boxAgent ? NODE_HEIGHT : 1;
4543
4611
  positions.set(agent.id, {
4544
4612
  x: col.startX + Math.floor((col.width - NODE_WIDTH) / 2),
4545
- y: startY + idx * (NODE_HEIGHT + 1),
4613
+ y: currentY,
4546
4614
  width: NODE_WIDTH,
4547
- height: NODE_HEIGHT
4615
+ height
4548
4616
  });
4549
- });
4617
+ currentY += boxAgent ? height + 1 : height;
4618
+ }
4550
4619
  }
4551
4620
  return positions;
4552
4621
  }
@@ -4554,8 +4623,27 @@ function getNodeTextStyle(status) {
4554
4623
  if (status === "running") return { fg: "white", bold: true };
4555
4624
  return { dim: true };
4556
4625
  }
4557
- function drawAgentNode(buf, x, y, boxW, agent) {
4558
- const borderStyle = getStatusStyle(agent.status);
4626
+ function drawTreeConnector(buf, x, y, agent, isLast, dimmed = false) {
4627
+ const connector = isLast ? "\u2514\u2500" : "\u251C\u2500";
4628
+ const icon = STATUS_ICONS[agent.status];
4629
+ const maxNameLen = Math.max(1, NODE_WIDTH - connector.length - icon.length - 3);
4630
+ const nameStr = agent.name.length > maxNameLen ? agent.name.slice(0, maxNameLen - 3) + "..." : agent.name;
4631
+ buf.writeText(x, y, connector, dimmed ? DIMMED_STYLE : TREE_CONNECTOR_STYLE);
4632
+ buf.writeText(
4633
+ x + connector.length + 1,
4634
+ y,
4635
+ icon,
4636
+ dimmed ? DIMMED_STYLE : STATUS_STYLES[agent.status]
4637
+ );
4638
+ buf.writeText(
4639
+ x + connector.length + 1 + icon.length + 1,
4640
+ y,
4641
+ nameStr,
4642
+ dimmed ? DIMMED_STYLE : getNodeTextStyle(agent.status)
4643
+ );
4644
+ }
4645
+ function drawAgentNode(buf, x, y, boxW, agent, dimmed = false) {
4646
+ const borderStyle = dimmed ? DIMMED_STYLE : getStatusStyle(agent.status);
4559
4647
  if (agent.isPrimary) {
4560
4648
  buf.drawDoubleBox(x, y, boxW, NODE_HEIGHT, borderStyle);
4561
4649
  } else {
@@ -4564,15 +4652,26 @@ function drawAgentNode(buf, x, y, boxW, agent) {
4564
4652
  const icon = STATUS_ICONS[agent.status];
4565
4653
  const maxNameLen = Math.max(1, boxW - 5);
4566
4654
  const nameStr = agent.name.length > maxNameLen ? agent.name.slice(0, maxNameLen - 3) + "..." : agent.name;
4567
- buf.writeText(x + 2, y + 1, nameStr, getNodeTextStyle(agent.status));
4568
- buf.writeText(x + 2 + nameStr.length + 1, y + 1, icon, STATUS_STYLES[agent.status]);
4569
- const barWidth = boxW - 4;
4570
- const bar = buildProgressBar(agent.progress, barWidth);
4571
- const filledStr = PROGRESS_BAR_CHARS.filled.repeat(bar.filled);
4572
- const emptyStr = PROGRESS_BAR_CHARS.empty.repeat(bar.empty);
4573
- buf.writeText(x + 2, y + 2, filledStr, PROGRESS_BAR_STYLES.filled);
4574
- if (bar.empty > 0) {
4575
- buf.writeText(x + 2 + bar.filled, y + 2, emptyStr, PROGRESS_BAR_STYLES.empty);
4655
+ const textStyle = dimmed ? DIMMED_STYLE : getNodeTextStyle(agent.status);
4656
+ const iconStyle = dimmed ? DIMMED_STYLE : STATUS_STYLES[agent.status];
4657
+ buf.writeText(x + 2, y + 1, nameStr, textStyle);
4658
+ buf.writeText(x + 2 + nameStr.length + 1, y + 1, icon, iconStyle);
4659
+ if (agent.isPrimary) {
4660
+ const barWidth = Math.max(1, boxW - 8);
4661
+ const bar = buildProgressBar(agent.progress, barWidth);
4662
+ const filledStr = PROGRESS_BAR_CHARS.filled.repeat(bar.filled);
4663
+ const emptyStr = PROGRESS_BAR_CHARS.empty.repeat(bar.empty);
4664
+ const filledStyle = dimmed ? DIMMED_STYLE : PROGRESS_BAR_STYLES.filled;
4665
+ const emptyStyle = dimmed ? DIMMED_STYLE : PROGRESS_BAR_STYLES.empty;
4666
+ buf.writeText(x + 2, y + 2, filledStr, filledStyle);
4667
+ if (bar.empty > 0) {
4668
+ buf.writeText(x + 2 + bar.filled, y + 2, emptyStr, emptyStyle);
4669
+ }
4670
+ buf.writeText(x + 2 + barWidth + 1, y + 2, bar.label, dimmed ? DIMMED_STYLE : WAITING_STYLE);
4671
+ } else if (agent.status === "idle") {
4672
+ buf.writeText(x + 2, y + 2, "\u231B waiting...", dimmed ? DIMMED_STYLE : WAITING_STYLE);
4673
+ } else if (agent.isParallel) {
4674
+ buf.writeText(x + 2, y + 2, "\u2AF8 parallel", dimmed ? DIMMED_STYLE : PARALLEL_STYLES.parallel);
4576
4675
  }
4577
4676
  }
4578
4677
  function buildProgressBar(value, barWidth) {
@@ -4607,7 +4706,17 @@ function countByStatus(agents) {
4607
4706
  }
4608
4707
  return counts;
4609
4708
  }
4610
- function renderPipelineHeader(buf, width, activeStage, hasAutoAgents = false) {
4709
+ function countStageRunningDone(agents, stage) {
4710
+ let running = 0;
4711
+ let done = 0;
4712
+ for (const agent of agents.values()) {
4713
+ if (agent.stage !== stage) continue;
4714
+ if (agent.status === "running") running++;
4715
+ else if (agent.status === "done") done++;
4716
+ }
4717
+ return { running, done };
4718
+ }
4719
+ function renderPipelineHeader(buf, width, activeStage, hasAutoAgents = false, agents) {
4611
4720
  const stages = hasAutoAgents ? STAGE_ORDER : STAGE_ORDER.filter((s) => s !== "AUTO");
4612
4721
  let x = 1;
4613
4722
  for (let i = 0; i < stages.length; i++) {
@@ -4618,6 +4727,17 @@ function renderPipelineHeader(buf, width, activeStage, hasAutoAgents = false) {
4618
4727
  x += 2;
4619
4728
  buf.writeText(x, 0, stage, stageStyle);
4620
4729
  x += stage.length;
4730
+ if (agents && agents.size > 0) {
4731
+ const { running, done } = countStageRunningDone(agents, stage);
4732
+ if (running > 0 || done > 0) {
4733
+ const parts = [];
4734
+ if (running > 0) parts.push(`${running}\u2191`);
4735
+ if (done > 0) parts.push(`${done}\u2713`);
4736
+ const statsStr = ` (${parts.join(" ")})`;
4737
+ buf.writeText(x, 0, statsStr, { fg: "gray", dim: !isActive });
4738
+ x += statsStr.length;
4739
+ }
4740
+ }
4621
4741
  if (i < stages.length - 1) {
4622
4742
  x += 1;
4623
4743
  const connLen = Math.max(
@@ -4644,20 +4764,36 @@ function renderFlowMap(agents, edges, width, height, activeStage = null) {
4644
4764
  const buf = new ColorBuffer(width, height);
4645
4765
  const columns = layoutStageColumns(width, hasAutoAgents);
4646
4766
  const positions = layoutAgentNodes(agents, columns);
4647
- renderPipelineHeader(buf, width, activeStage, hasAutoAgents);
4767
+ renderPipelineHeader(buf, width, activeStage, hasAutoAgents, agents);
4768
+ const inactiveIds = activeStage !== null ? new Set([...agents.values()].filter((a) => a.stage !== activeStage).map((a) => a.id)) : /* @__PURE__ */ new Set();
4648
4769
  for (const [id, pos] of positions) {
4649
4770
  const agent = agents.get(id);
4650
4771
  if (!agent) continue;
4651
- if (agent.status === "running" && agent.isPrimary) {
4772
+ if (agent.status === "running" && agent.isPrimary && !inactiveIds.has(id)) {
4652
4773
  drawGlow(buf, pos.x, pos.y, pos.width, pos.height, GLOW_STYLE);
4653
4774
  }
4654
4775
  }
4655
- for (const [id, pos] of positions) {
4656
- const agent = agents.get(id);
4657
- if (!agent) continue;
4658
- drawAgentNode(buf, pos.x, pos.y, pos.width, agent);
4776
+ const byStage = groupByStage(agents);
4777
+ for (const [, stageAgents] of byStage) {
4778
+ const sorted = sortAgents(stageAgents);
4779
+ const connectorAgents = sorted.filter((a) => !isBoxAgent(a));
4780
+ const connectorLastIdx = connectorAgents.length - 1;
4781
+ const connectorIndexMap = new Map(connectorAgents.map((a, i) => [a.id, i]));
4782
+ for (const agent of sorted) {
4783
+ const pos = positions.get(agent.id);
4784
+ if (!pos) continue;
4785
+ if (isBoxAgent(agent)) {
4786
+ drawAgentNode(buf, pos.x, pos.y, pos.width, agent, inactiveIds.has(agent.id));
4787
+ } else {
4788
+ const isLast = connectorIndexMap.get(agent.id) === connectorLastIdx;
4789
+ drawTreeConnector(buf, pos.x, pos.y, agent, isLast, inactiveIds.has(agent.id));
4790
+ }
4791
+ }
4659
4792
  }
4660
4793
  for (const edge of edges) {
4794
+ const fromAgent = agents.get(edge.from);
4795
+ const toAgent = agents.get(edge.to);
4796
+ if (fromAgent && toAgent && fromAgent.stage === toAgent.stage) continue;
4661
4797
  const fromPos = positions.get(edge.from);
4662
4798
  const toPos = positions.get(edge.to);
4663
4799
  if (!fromPos || !toPos) continue;
@@ -4670,13 +4806,20 @@ function renderFlowMap(agents, edges, width, height, activeStage = null) {
4670
4806
  y: toPos.y + Math.floor(toPos.height / 2)
4671
4807
  };
4672
4808
  const path9 = computeEdgePath(fromPoint, toPoint);
4809
+ const edgeDimmed = inactiveIds.has(edge.from) && inactiveIds.has(edge.to);
4673
4810
  for (const seg of path9) {
4674
4811
  const isArrow = seg.char === "\u25B8" || seg.char === "\u25C2" || seg.char === "\u25BE" || seg.char === "\u25B4";
4675
- buf.setChar(seg.x, seg.y, seg.char, isArrow ? EDGE_STYLES.arrow : EDGE_STYLES.path);
4812
+ const segStyle = edgeDimmed ? DIMMED_STYLE : isArrow ? EDGE_STYLES.arrow : EDGE_STYLES.path;
4813
+ buf.setChar(seg.x, seg.y, seg.char, segStyle);
4676
4814
  }
4677
4815
  const labelPos = computeLabelPosition(path9, edge.label);
4678
4816
  if (labelPos) {
4679
- buf.writeText(labelPos.x, labelPos.y - 1, edge.label, EDGE_STYLES.label);
4817
+ buf.writeText(
4818
+ labelPos.x,
4819
+ labelPos.y - 1,
4820
+ edge.label,
4821
+ edgeDimmed ? DIMMED_STYLE : EDGE_STYLES.label
4822
+ );
4680
4823
  }
4681
4824
  }
4682
4825
  const counts = countByStatus(agents);
@@ -4695,20 +4838,22 @@ function renderFlowMap(agents, edges, width, height, activeStage = null) {
4695
4838
  }
4696
4839
  return buf;
4697
4840
  }
4698
- function renderFlowMapSimplified(agents, width, height) {
4841
+ function renderFlowMapSimplified(agents, width, height, activeStage = null) {
4699
4842
  const buf = new ColorBuffer(width, height);
4700
4843
  const byStage = groupByStage(agents);
4701
4844
  let y = 0;
4702
4845
  for (const stage of STAGE_ORDER) {
4703
4846
  const stageAgents = byStage.get(stage);
4704
4847
  if (!stageAgents || stageAgents.length === 0) continue;
4705
- buf.writeText(1, y, stage, STAGE_LABEL_STYLES[stage]);
4848
+ const isInactiveStage = activeStage !== null && stage !== activeStage;
4849
+ const stageLabelStyle = isInactiveStage ? { fg: STAGE_LABEL_STYLES[stage].fg, dim: true } : STAGE_LABEL_STYLES[stage];
4850
+ buf.writeText(1, y, stage, stageLabelStyle);
4706
4851
  y++;
4707
4852
  const sorted = sortAgents(stageAgents);
4708
4853
  for (const agent of sorted) {
4709
4854
  if (y >= height - 1) break;
4710
4855
  const boxW = Math.min(width - 2, NODE_WIDTH);
4711
- drawAgentNode(buf, 1, y, boxW, agent);
4856
+ drawAgentNode(buf, 1, y, boxW, agent, isInactiveStage);
4712
4857
  y += NODE_HEIGHT + 1;
4713
4858
  }
4714
4859
  }
@@ -4821,15 +4966,6 @@ function formatObjective(objectives, maxLines = 3) {
4821
4966
  }
4822
4967
  return lines.join("\n");
4823
4968
  }
4824
- function formatToolIO(tools, inputs, outputs) {
4825
- const toolLine = `Tools: ${tools.join(" / ") || "none"}`;
4826
- const inLine = `IN : ${inputs.join(", ") || "none"}`;
4827
- const outParts = Object.entries(outputs).filter(([, v]) => v !== void 0 && v > 0).map(([k, v]) => `${k}(${v})`);
4828
- const outLine = `OUT: ${outParts.join(" / ") || "none"}`;
4829
- return `${toolLine}
4830
- ${inLine}
4831
- ${outLine}`;
4832
- }
4833
4969
  function formatLogTail(events, maxLines = 10) {
4834
4970
  const tail = events.slice(-maxLines);
4835
4971
  return tail.map((e) => `${e.timestamp} ${e.message}`).join("\n");
@@ -4849,20 +4985,6 @@ function formatEnhancedProgressBar(progress, width = 30) {
4849
4985
  label: `${clamped}%`
4850
4986
  };
4851
4987
  }
4852
- var SPARK_CHARS = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
4853
- function formatActivitySparkline(toolCalls, bucketCount = 20, windowMs = 6e4, now) {
4854
- const currentTime = now ?? Date.now();
4855
- const bucketSize = windowMs / bucketCount;
4856
- const buckets = new Array(bucketCount).fill(0);
4857
- for (const call of toolCalls) {
4858
- const age = currentTime - call.timestamp;
4859
- if (age < 0 || age >= windowMs) continue;
4860
- const idx = Math.min(bucketCount - 1, Math.floor(age / bucketSize));
4861
- buckets[bucketCount - 1 - idx]++;
4862
- }
4863
- const max = Math.max(1, ...buckets);
4864
- return buckets.map((b) => SPARK_CHARS[Math.round(b / max * (SPARK_CHARS.length - 1))]).join("");
4865
- }
4866
4988
  function formatEnhancedChecklist(tasks, maxItems = 6) {
4867
4989
  const visible = tasks.slice(0, maxItems);
4868
4990
  const lines = visible.map((t) => t.completed ? ` \u2714 ${t.subject}` : ` \u25FB ${t.subject}`);
@@ -4931,12 +5053,7 @@ function FocusedAgentPanel({
4931
5053
  agent,
4932
5054
  objectives,
4933
5055
  activeSkills,
4934
- tasks,
4935
- tools,
4936
- inputs,
4937
- outputs,
4938
5056
  eventLog,
4939
- toolCalls,
4940
5057
  contextDecisions = [],
4941
5058
  contextNotes = [],
4942
5059
  width,
@@ -4961,11 +5078,7 @@ function FocusedAgentPanel({
4961
5078
  const statusLabel = agent.status.toUpperCase();
4962
5079
  const progressBar = formatEnhancedProgressBar(agent.progress);
4963
5080
  const objective = formatObjective(objectives);
4964
- const checklist = formatEnhancedChecklist(tasks);
4965
- const toolIO = formatToolIO(tools, inputs, outputs);
4966
5081
  const logs = formatLogTail(eventLog);
4967
- const agentToolCalls = toolCalls.filter((tc) => tc.agentId === agent.id);
4968
- const sparkline = formatActivitySparkline(agentToolCalls);
4969
5082
  return /* @__PURE__ */ React4.createElement(
4970
5083
  Box4,
4971
5084
  {
@@ -4981,15 +5094,6 @@ function FocusedAgentPanel({
4981
5094
  objective ? /* @__PURE__ */ React4.createElement(Text4, null, objective) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No objectives"),
4982
5095
  /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Skills" }),
4983
5096
  activeSkills.length > 0 ? activeSkills.map((s, i) => /* @__PURE__ */ React4.createElement(Text4, { key: i }, " ", s)) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No skills"),
4984
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Checklist" }),
4985
- checklist ? checklist.split("\n").map((line, i) => {
4986
- const isCompleted = line.includes("\u2714");
4987
- return /* @__PURE__ */ React4.createElement(Text4, { key: i, color: isCompleted ? "green" : void 0 }, line);
4988
- }) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No tasks"),
4989
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Activity" }),
4990
- /* @__PURE__ */ React4.createElement(Text4, null, sparkline),
4991
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Tools / IO" }),
4992
- /* @__PURE__ */ React4.createElement(Text4, null, toolIO),
4993
5097
  /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Context" }),
4994
5098
  contextDecisions.length > 0 || contextNotes.length > 0 ? /* @__PURE__ */ React4.createElement(ContextSection, { decisions: contextDecisions, notes: contextNotes, width }) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No context"),
4995
5099
  /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Event Log" }),
@@ -4997,9 +5101,115 @@ function FocusedAgentPanel({
4997
5101
  );
4998
5102
  }
4999
5103
 
5000
- // src/tui/components/StageHealthBar.tsx
5104
+ // src/tui/components/ChecklistPanel.tsx
5001
5105
  import React5 from "react";
5002
5106
  import { Box as Box5, Text as Text5 } from "ink";
5107
+
5108
+ // src/tui/components/checklist-panel.pure.ts
5109
+ function resolveChecklistTasks(tasks, contextDecisions, contextNotes) {
5110
+ if (tasks.length > 0) {
5111
+ return tasks;
5112
+ }
5113
+ if (contextDecisions.length > 0) {
5114
+ return [
5115
+ { id: "fallback-decision", subject: `Review: ${contextDecisions[0]}`, completed: false }
5116
+ ];
5117
+ }
5118
+ if (contextNotes.length > 0) {
5119
+ return [{ id: "fallback-note", subject: `Check: ${contextNotes[0]}`, completed: false }];
5120
+ }
5121
+ return [{ id: "fallback-default", subject: "Review current task objectives", completed: false }];
5122
+ }
5123
+
5124
+ // src/tui/components/ChecklistPanel.tsx
5125
+ function ChecklistPanel({
5126
+ tasks,
5127
+ contextDecisions = [],
5128
+ contextNotes = [],
5129
+ width,
5130
+ height
5131
+ }) {
5132
+ const resolvedTasks = resolveChecklistTasks(tasks, contextDecisions, contextNotes);
5133
+ const checklist = formatEnhancedChecklist(resolvedTasks);
5134
+ return /* @__PURE__ */ React5.createElement(
5135
+ Box5,
5136
+ {
5137
+ borderStyle: "single",
5138
+ borderColor: BORDER_COLORS.panel,
5139
+ flexDirection: "column",
5140
+ width,
5141
+ height
5142
+ },
5143
+ /* @__PURE__ */ React5.createElement(Text5, { bold: true, dimColor: true }, "\u2500\u2500\u2500 Checklist"),
5144
+ checklist.split("\n").map((line, i) => {
5145
+ const isCompleted = line.includes("\u2714");
5146
+ return /* @__PURE__ */ React5.createElement(Text5, { key: i, color: isCompleted ? "green" : void 0 }, line);
5147
+ })
5148
+ );
5149
+ }
5150
+
5151
+ // src/tui/components/StageHealthBar.tsx
5152
+ import React6 from "react";
5153
+ import { Box as Box6, Text as Text6 } from "ink";
5154
+
5155
+ // src/tui/components/stage-health.pure.ts
5156
+ function formatCount(n) {
5157
+ return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
5158
+ }
5159
+ function computeStageHealth(agents) {
5160
+ const health = {
5161
+ PLAN: createEmptyStageStats(),
5162
+ ACT: createEmptyStageStats(),
5163
+ EVAL: createEmptyStageStats(),
5164
+ AUTO: createEmptyStageStats()
5165
+ };
5166
+ for (const agent of agents.values()) {
5167
+ const stage = health[agent.stage];
5168
+ if (!stage) continue;
5169
+ switch (agent.status) {
5170
+ case "running":
5171
+ stage.running++;
5172
+ break;
5173
+ case "blocked":
5174
+ stage.blocked++;
5175
+ break;
5176
+ case "idle":
5177
+ stage.waiting++;
5178
+ break;
5179
+ case "done":
5180
+ stage.done++;
5181
+ break;
5182
+ case "error":
5183
+ stage.error++;
5184
+ break;
5185
+ }
5186
+ }
5187
+ return health;
5188
+ }
5189
+ function detectBottlenecks(eventLog) {
5190
+ const bottlenecks = /* @__PURE__ */ new Set();
5191
+ for (const entry of eventLog) {
5192
+ if (entry.level === "error") {
5193
+ const testMatch = entry.message.match(/(?:test_run|tests?):\s*FAIL\s*\((\d+)\)/i);
5194
+ if (testMatch) {
5195
+ bottlenecks.add(`tests failing(${testMatch[1]})`);
5196
+ continue;
5197
+ }
5198
+ const lintMatch = entry.message.match(/lint\(([^)]+)\)/i);
5199
+ if (lintMatch) {
5200
+ bottlenecks.add(`lint(${lintMatch[1]})`);
5201
+ continue;
5202
+ }
5203
+ const envMatch = entry.message.match(/missing\s+env\(([^)]+)\)/i);
5204
+ if (envMatch) {
5205
+ bottlenecks.add(`missing env(${envMatch[1]})`);
5206
+ }
5207
+ }
5208
+ }
5209
+ return [...bottlenecks];
5210
+ }
5211
+
5212
+ // src/tui/components/StageHealthBar.tsx
5003
5213
  function StageStatDisplay({
5004
5214
  mode,
5005
5215
  stats
@@ -5008,52 +5218,53 @@ function StageStatDisplay({
5008
5218
  const parts = [];
5009
5219
  if (stats.running > 0)
5010
5220
  parts.push(
5011
- /* @__PURE__ */ React5.createElement(Text5, { key: "r", color: "green" }, " ", "\u25CF ", stats.running, " running")
5221
+ /* @__PURE__ */ React6.createElement(Text6, { key: "r", color: "green" }, " ", "\u25CF ", stats.running, " running")
5012
5222
  );
5013
5223
  if (stats.blocked > 0)
5014
5224
  parts.push(
5015
- /* @__PURE__ */ React5.createElement(Text5, { key: "b", color: "yellow" }, " ", "\u23F8 ", stats.blocked, " blocked")
5225
+ /* @__PURE__ */ React6.createElement(Text6, { key: "b", color: "yellow" }, " ", "\u23F8 ", stats.blocked, " blocked")
5016
5226
  );
5017
5227
  if (stats.waiting > 0)
5018
5228
  parts.push(
5019
- /* @__PURE__ */ React5.createElement(Text5, { key: "w", dimColor: true }, " ", "\u25CB ", stats.waiting, " waiting")
5229
+ /* @__PURE__ */ React6.createElement(Text6, { key: "w", dimColor: true }, " ", "\u25CB ", stats.waiting, " waiting")
5020
5230
  );
5021
5231
  if (stats.done > 0)
5022
5232
  parts.push(
5023
- /* @__PURE__ */ React5.createElement(Text5, { key: "d", color: "green" }, " ", "\u2713 ", stats.done)
5233
+ /* @__PURE__ */ React6.createElement(Text6, { key: "d", color: "green" }, " ", "\u2713 ", stats.done)
5024
5234
  );
5025
5235
  if (stats.error > 0)
5026
5236
  parts.push(
5027
- /* @__PURE__ */ React5.createElement(Text5, { key: "e", color: "red", bold: true }, " ", "! ", stats.error, " err")
5237
+ /* @__PURE__ */ React6.createElement(Text6, { key: "e", color: "red", bold: true }, " ", "! ", stats.error, " err")
5028
5238
  );
5029
5239
  if (parts.length === 0)
5030
5240
  parts.push(
5031
- /* @__PURE__ */ React5.createElement(Text5, { key: "idle", dimColor: true }, " ", "idle")
5241
+ /* @__PURE__ */ React6.createElement(Text6, { key: "idle", dimColor: true }, " ", "idle")
5032
5242
  );
5033
- return /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color, bold: true }, mode, ":"), parts);
5243
+ return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color, bold: true }, mode, ":"), parts);
5034
5244
  }
5035
5245
  function StageHealthBar({
5036
5246
  stageHealth,
5037
5247
  bottlenecks,
5038
5248
  toolCount,
5249
+ agentCount,
5250
+ skillCount,
5039
5251
  width
5040
5252
  }) {
5041
- const countStr = toolCount >= 1e3 ? `${Math.round(toolCount / 1e3)}k` : String(toolCount);
5042
- return /* @__PURE__ */ React5.createElement(
5043
- Box5,
5253
+ return /* @__PURE__ */ React6.createElement(
5254
+ Box6,
5044
5255
  {
5045
5256
  borderStyle: "double",
5046
5257
  borderColor: BORDER_COLORS.panel,
5047
5258
  width,
5048
5259
  flexDirection: "column"
5049
5260
  },
5050
- /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Box5, { gap: 2 }, ["PLAN", "ACT", "EVAL"].map((mode) => /* @__PURE__ */ React5.createElement(StageStatDisplay, { key: mode, mode, stats: stageHealth[mode] }))), /* @__PURE__ */ React5.createElement(Box5, { flexGrow: 1 }), /* @__PURE__ */ React5.createElement(Box5, { gap: 2 }, bottlenecks.length > 0 && /* @__PURE__ */ React5.createElement(Text5, { color: "red", bold: true }, "\u26A1 Bottlenecks: ", bottlenecks.join(" / ")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "tools: ", countStr)))
5261
+ /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Box6, { gap: 2 }, ["PLAN", "ACT", "EVAL"].map((mode) => /* @__PURE__ */ React6.createElement(StageStatDisplay, { key: mode, mode, stats: stageHealth[mode] }))), /* @__PURE__ */ React6.createElement(Box6, { flexGrow: 1 }), /* @__PURE__ */ React6.createElement(Box6, { gap: 2 }, bottlenecks.length > 0 && /* @__PURE__ */ React6.createElement(Text6, { color: "red", bold: true }, "\u26A1 Bottlenecks: ", bottlenecks.join(" / ")), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u{1F916} ", formatCount(agentCount)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2699 ", formatCount(skillCount)), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u{1F527} ", formatCount(toolCount))))
5051
5262
  );
5052
5263
  }
5053
5264
 
5054
5265
  // src/tui/components/ActivityVisualizer.tsx
5055
- import React6, { useMemo as useMemo2 } from "react";
5056
- import { Box as Box6, Text as Text6 } from "ink";
5266
+ import React7, { useMemo as useMemo2 } from "react";
5267
+ import { Box as Box7, Text as Text7 } from "ink";
5057
5268
 
5058
5269
  // src/tui/utils/display-width.ts
5059
5270
  function isWide(code) {
@@ -5087,180 +5298,202 @@ function truncateToDisplayWidth(str, maxWidth) {
5087
5298
  }
5088
5299
  return str.slice(0, codeUnitIndex);
5089
5300
  }
5090
- function padEndDisplayWidth(str, targetWidth) {
5091
- const currentWidth = estimateDisplayWidth(str);
5092
- if (currentWidth >= targetWidth) return str;
5093
- return str + " ".repeat(targetWidth - currentWidth);
5094
- }
5095
5301
 
5096
5302
  // src/tui/components/activity-visualizer.pure.ts
5097
- function aggregateForBarChart(calls, maxTools = 10) {
5098
- if (calls.length === 0) return [];
5099
- const toolCounts = /* @__PURE__ */ new Map();
5100
- for (const call of calls) {
5101
- toolCounts.set(call.toolName, (toolCounts.get(call.toolName) ?? 0) + 1);
5102
- }
5103
- return [...toolCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, maxTools).map(([tool, count]) => ({ tool, count }));
5104
- }
5105
- var TOOL_NAME_WIDTH = 12;
5106
- var BAR_FIXED_OVERHEAD = TOOL_NAME_WIDTH + 2 + 2 + 4;
5107
- function renderBarChart(data, width, height) {
5108
- if (data.length === 0 || height <= 0 || width <= 0) return [];
5109
- const lines = [truncateToDisplayWidth("\u{1F4CA} Activity", width)];
5110
- const barWidth = Math.max(1, width - BAR_FIXED_OVERHEAD);
5111
- const maxCount = data[0]?.count || 1;
5112
- for (const item of data) {
5303
+ var ACTIVITY_STATUS_ICONS = Object.freeze({
5304
+ running: "\u25CF",
5305
+ idle: "\u25CB",
5306
+ blocked: "\u23F8",
5307
+ error: "!",
5308
+ done: "\u25CB"
5309
+ });
5310
+ function renderSubtree(nodeId, agents, childrenOf, visited, prefix, isLast, lines, height, width) {
5311
+ if (lines.length >= height) return;
5312
+ if (visited.has(nodeId)) return;
5313
+ visited.add(nodeId);
5314
+ const node = agents.get(nodeId);
5315
+ if (!node) return;
5316
+ const connector = isLast ? "\u2514" : "\u251C";
5317
+ const icon = ACTIVITY_STATUS_ICONS[node.status] ?? "?";
5318
+ lines.push(truncateToDisplayWidth(`${prefix}${connector} ${icon} ${node.name}`, width));
5319
+ const childIds = (childrenOf.get(nodeId) ?? []).filter((id) => agents.has(id));
5320
+ const nextPrefix = prefix + (isLast ? " " : "\u2502 ");
5321
+ for (let i = 0; i < childIds.length; i++) {
5113
5322
  if (lines.length >= height) break;
5114
- const name = padEndDisplayWidth(
5115
- truncateToDisplayWidth(item.tool, TOOL_NAME_WIDTH),
5116
- TOOL_NAME_WIDTH
5323
+ renderSubtree(
5324
+ childIds[i],
5325
+ agents,
5326
+ childrenOf,
5327
+ visited,
5328
+ nextPrefix,
5329
+ i === childIds.length - 1,
5330
+ lines,
5331
+ height,
5332
+ width
5117
5333
  );
5118
- const filled = Math.round(item.count / maxCount * barWidth);
5119
- const empty = barWidth - filled;
5120
- const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
5121
- const countStr = String(item.count).padStart(4);
5122
- lines.push(truncateToDisplayWidth(`${name} [${bar}] ${countStr}`, width));
5334
+ }
5335
+ }
5336
+ function renderAgentTree(agents, edges, activeSkills, width, height) {
5337
+ if (height <= 0 || width <= 0) return [];
5338
+ const lines = [truncateToDisplayWidth("\u{1F4CA} Activity", width)];
5339
+ if (height <= 1) return lines;
5340
+ if (agents.size === 0) {
5341
+ lines.push(truncateToDisplayWidth(" No agents", width));
5342
+ return lines.slice(0, height);
5343
+ }
5344
+ const childrenOf = /* @__PURE__ */ new Map();
5345
+ for (const edge of edges) {
5346
+ if (!childrenOf.has(edge.from)) childrenOf.set(edge.from, []);
5347
+ childrenOf.get(edge.from).push(edge.to);
5348
+ }
5349
+ const root = [...agents.values()].find((a) => a.isPrimary) ?? [...agents.values()][0];
5350
+ const rootIcon = ACTIVITY_STATUS_ICONS[root.status] ?? "?";
5351
+ lines.push(truncateToDisplayWidth(`${rootIcon} ${root.name}`, width));
5352
+ const visited = /* @__PURE__ */ new Set([root.id]);
5353
+ const rootChildIds = (childrenOf.get(root.id) ?? []).filter((id) => agents.has(id));
5354
+ const totalChildren = rootChildIds.length + activeSkills.length;
5355
+ for (let i = 0; i < rootChildIds.length; i++) {
5356
+ if (lines.length >= height) break;
5357
+ const isLast = i === totalChildren - 1;
5358
+ renderSubtree(rootChildIds[i], agents, childrenOf, visited, " ", isLast, lines, height, width);
5359
+ }
5360
+ for (let i = 0; i < activeSkills.length; i++) {
5361
+ if (lines.length >= height) break;
5362
+ const isLast = rootChildIds.length + i === totalChildren - 1;
5363
+ const connector = isLast ? "\u2514" : "\u251C";
5364
+ lines.push(truncateToDisplayWidth(` ${connector} \u25C9 ${activeSkills[i]} (skill)`, width));
5123
5365
  }
5124
5366
  return lines.slice(0, height);
5125
5367
  }
5126
- var LIVE_AGENT_NAME_WIDTH = 15;
5127
- var LIVE_HOT_WINDOW_MS = 5e3;
5128
- var LIVE_RECENT_WINDOW_MS = 3e4;
5129
- var LIVE_HOT_SEC = LIVE_HOT_WINDOW_MS / 1e3;
5130
- var LIVE_RECENT_SEC = LIVE_RECENT_WINDOW_MS / 1e3;
5131
- function renderLiveContext(calls, currentMode, width, height, now) {
5132
- const currentTime = now ?? Date.now();
5368
+ function wordWrap(text, width) {
5369
+ if (width <= 0) return [];
5370
+ const words = text.split(" ");
5371
+ const result = [];
5372
+ let current = "";
5373
+ let currentWidth = 0;
5374
+ for (const word of words) {
5375
+ const wordWidth = estimateDisplayWidth(word);
5376
+ if (current.length === 0) {
5377
+ current = wordWidth <= width ? word : truncateToDisplayWidth(word, width);
5378
+ currentWidth = estimateDisplayWidth(current);
5379
+ } else if (currentWidth + 1 + wordWidth <= width) {
5380
+ current += " " + word;
5381
+ currentWidth += 1 + wordWidth;
5382
+ } else {
5383
+ result.push(current);
5384
+ current = wordWidth <= width ? word : truncateToDisplayWidth(word, width);
5385
+ currentWidth = estimateDisplayWidth(current);
5386
+ }
5387
+ }
5388
+ if (current.length > 0) result.push(current);
5389
+ return result;
5390
+ }
5391
+ function renderAgentStatusCard(focusedAgent, currentMode, objectives, activeSkills, width, height) {
5133
5392
  if (height <= 0 || width <= 0) return [];
5134
5393
  const lines = [truncateToDisplayWidth("\u{1F4AC} Live", width)];
5135
- if (currentMode) {
5136
- lines.push(truncateToDisplayWidth(`\u27EB Mode: ${currentMode}`, width));
5137
- }
5138
- const maxItems = height - lines.length;
5139
- const seen = /* @__PURE__ */ new Set();
5140
- for (let i = calls.length - 1; i >= 0 && seen.size < maxItems; i--) {
5141
- const call = calls[i];
5142
- if (call.status === "error") continue;
5143
- if (seen.has(call.toolName)) continue;
5144
- seen.add(call.toolName);
5145
- const ageSec = Math.floor((currentTime - call.timestamp) / 1e3);
5146
- const ageIndicator = ageSec < LIVE_HOT_SEC ? "\u25C9" : ageSec < LIVE_RECENT_SEC ? "\u25CB" : "\xB7";
5147
- const agent = call.agentId !== "unknown" ? truncateToDisplayWidth(call.agentId, LIVE_AGENT_NAME_WIDTH) + " " : "";
5148
- lines.push(truncateToDisplayWidth(`${ageIndicator} ${agent}${call.toolName}`, width));
5394
+ if (height <= 1) return lines;
5395
+ lines.push(truncateToDisplayWidth(`\u27EB Mode: ${currentMode ?? "\u2014"}`, width));
5396
+ if (!focusedAgent) {
5397
+ lines.push(truncateToDisplayWidth(" No agent", width));
5398
+ return lines.slice(0, height);
5399
+ }
5400
+ lines.push(truncateToDisplayWidth(`\u{1F916} ${focusedAgent.name}`, width));
5401
+ const statusIcon = ACTIVITY_STATUS_ICONS[focusedAgent.status] ?? "?";
5402
+ lines.push(truncateToDisplayWidth(` ${statusIcon} ${focusedAgent.status}`, width));
5403
+ const separator = truncateToDisplayWidth("\u2500".repeat(width), width);
5404
+ if (lines.length < height) lines.push(separator);
5405
+ let objectivesAdded = false;
5406
+ if (objectives.length > 0 && lines.length < height) {
5407
+ const wrapped = wordWrap(objectives[0], width);
5408
+ for (const wline of wrapped) {
5409
+ if (lines.length >= height) break;
5410
+ lines.push(wline);
5411
+ objectivesAdded = true;
5412
+ }
5413
+ }
5414
+ if (objectivesAdded && lines.length < height) lines.push(separator);
5415
+ for (const skill of activeSkills) {
5416
+ if (lines.length >= height) break;
5417
+ lines.push(truncateToDisplayWidth(`\u2699 ${skill}`, width));
5149
5418
  }
5150
5419
  return lines.slice(0, height);
5151
5420
  }
5152
5421
 
5153
5422
  // src/tui/components/ActivityVisualizer.tsx
5154
5423
  function ActivityVisualizer({
5155
- toolCalls,
5156
5424
  currentMode,
5425
+ focusedAgent,
5426
+ agents,
5427
+ edges,
5428
+ activeSkills,
5429
+ objectives,
5157
5430
  width,
5158
5431
  height
5159
5432
  }) {
5160
- if (width <= 0 || height <= 0) {
5161
- return /* @__PURE__ */ React6.createElement(Box6, null);
5162
- }
5163
- const barChartWidth = Math.floor(width * 0.6);
5164
- const livePanelWidth = width - barChartWidth;
5433
+ const treeWidth = Math.floor(width * 0.6);
5434
+ const cardWidth = width - treeWidth;
5165
5435
  const contentHeight = Math.max(1, height - 2);
5166
- const barChartData = useMemo2(() => aggregateForBarChart(toolCalls), [toolCalls]);
5167
- const barChartLines = useMemo2(
5168
- () => renderBarChart(barChartData, Math.max(1, barChartWidth - 2), contentHeight),
5169
- [barChartData, barChartWidth, contentHeight]
5436
+ const treeLines = useMemo2(
5437
+ () => renderAgentTree(agents, edges, activeSkills, Math.max(1, treeWidth - 2), contentHeight),
5438
+ [agents, edges, activeSkills, treeWidth, contentHeight]
5170
5439
  );
5171
- const liveLines = useMemo2(
5172
- () => renderLiveContext(toolCalls, currentMode, Math.max(1, livePanelWidth - 2), contentHeight),
5173
- [toolCalls, currentMode, livePanelWidth, contentHeight]
5440
+ const cardLines = useMemo2(
5441
+ () => renderAgentStatusCard(
5442
+ focusedAgent,
5443
+ currentMode,
5444
+ objectives,
5445
+ activeSkills,
5446
+ Math.max(1, cardWidth - 2),
5447
+ contentHeight
5448
+ ),
5449
+ [focusedAgent, currentMode, objectives, activeSkills, cardWidth, contentHeight]
5174
5450
  );
5175
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "row", width, height }, /* @__PURE__ */ React6.createElement(
5176
- Box6,
5451
+ if (width <= 0 || height <= 0) {
5452
+ return /* @__PURE__ */ React7.createElement(Box7, null);
5453
+ }
5454
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "row", width, height }, /* @__PURE__ */ React7.createElement(
5455
+ Box7,
5177
5456
  {
5178
5457
  borderStyle: "single",
5179
5458
  borderColor: BORDER_COLORS.panel,
5180
5459
  flexDirection: "column",
5181
- width: barChartWidth,
5460
+ width: treeWidth,
5182
5461
  height
5183
5462
  },
5184
- barChartLines.map((line, i) => /* @__PURE__ */ React6.createElement(Text6, { key: i }, line))
5185
- ), /* @__PURE__ */ React6.createElement(
5186
- Box6,
5463
+ treeLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: i }, line))
5464
+ ), /* @__PURE__ */ React7.createElement(
5465
+ Box7,
5187
5466
  {
5188
5467
  borderStyle: "single",
5189
5468
  borderColor: BORDER_COLORS.panel,
5190
5469
  flexDirection: "column",
5191
- width: livePanelWidth,
5470
+ width: cardWidth,
5192
5471
  height
5193
5472
  },
5194
- liveLines.map((line, i) => /* @__PURE__ */ React6.createElement(Text6, { key: i }, line))
5473
+ cardLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: i }, line))
5195
5474
  ));
5196
5475
  }
5197
5476
 
5198
- // src/tui/components/stage-health.pure.ts
5199
- function computeStageHealth(agents) {
5200
- const health = {
5201
- PLAN: createEmptyStageStats(),
5202
- ACT: createEmptyStageStats(),
5203
- EVAL: createEmptyStageStats(),
5204
- AUTO: createEmptyStageStats()
5205
- };
5206
- for (const agent of agents.values()) {
5207
- const stage = health[agent.stage];
5208
- if (!stage) continue;
5209
- switch (agent.status) {
5210
- case "running":
5211
- stage.running++;
5212
- break;
5213
- case "blocked":
5214
- stage.blocked++;
5215
- break;
5216
- case "idle":
5217
- stage.waiting++;
5218
- break;
5219
- case "done":
5220
- stage.done++;
5221
- break;
5222
- case "error":
5223
- stage.error++;
5224
- break;
5225
- }
5226
- }
5227
- return health;
5228
- }
5229
- function detectBottlenecks(eventLog) {
5230
- const bottlenecks = /* @__PURE__ */ new Set();
5231
- for (const entry of eventLog) {
5232
- if (entry.level === "error") {
5233
- const testMatch = entry.message.match(/(?:test_run|tests?):\s*FAIL\s*\((\d+)\)/i);
5234
- if (testMatch) {
5235
- bottlenecks.add(`tests failing(${testMatch[1]})`);
5236
- continue;
5237
- }
5238
- const lintMatch = entry.message.match(/lint\(([^)]+)\)/i);
5239
- if (lintMatch) {
5240
- bottlenecks.add(`lint(${lintMatch[1]})`);
5241
- continue;
5242
- }
5243
- const envMatch = entry.message.match(/missing\s+env\(([^)]+)\)/i);
5244
- if (envMatch) {
5245
- bottlenecks.add(`missing env(${envMatch[1]})`);
5246
- }
5247
- }
5248
- }
5249
- return [...bottlenecks];
5250
- }
5251
-
5252
5477
  // src/tui/components/grid-layout.pure.ts
5253
5478
  var HEADER_HEIGHT = 3;
5254
5479
  var STAGE_HEALTH_HEIGHT = 3;
5255
5480
  var NARROW_FLOW_MAP_HEIGHT = 5;
5256
5481
  var MIN_ROWS = HEADER_HEIGHT + STAGE_HEALTH_HEIGHT + 2;
5257
5482
  var MIN_COLUMNS = 20;
5483
+ var CHECKLIST_HEIGHT_MIN = 6;
5484
+ var CHECKLIST_HEIGHT_MAX = 10;
5258
5485
  var FOCUSED_AGENT_WIDTH = {
5259
5486
  wide: 63,
5260
5487
  // was 70 → -10% (FlowMap/Activity gains +7 cols)
5261
5488
  medium: 58
5262
5489
  // was 64 → -9.4% (FlowMap/Activity gains +6 cols)
5263
5490
  };
5491
+ function computeChecklistHeight(mainHeight) {
5492
+ return Math.max(
5493
+ CHECKLIST_HEIGHT_MIN,
5494
+ Math.min(CHECKLIST_HEIGHT_MAX, Math.ceil(mainHeight * 0.3))
5495
+ );
5496
+ }
5264
5497
  function computeGridLayout(columns, rows, layoutMode) {
5265
5498
  const cols = Math.max(MIN_COLUMNS, columns);
5266
5499
  const r = Math.max(MIN_ROWS, rows);
@@ -5275,11 +5508,20 @@ function computeGridLayout(columns, rows, layoutMode) {
5275
5508
  const mainHeight = r - HEADER_HEIGHT - STAGE_HEALTH_HEIGHT;
5276
5509
  if (layoutMode === "narrow") {
5277
5510
  const flowMapHeight2 = Math.min(NARROW_FLOW_MAP_HEIGHT, mainHeight - 1);
5278
- const focusedHeight = mainHeight - flowMapHeight2;
5511
+ const availableForChecklist = mainHeight - flowMapHeight2;
5512
+ const rawChecklistHeight = computeChecklistHeight(mainHeight);
5513
+ const checklistHeight2 = Math.min(rawChecklistHeight, Math.max(0, availableForChecklist - 1));
5514
+ const focusedHeight = availableForChecklist - checklistHeight2;
5279
5515
  return {
5280
5516
  header,
5281
- focusedAgent: { x: 0, y: mainY, width: cols, height: focusedHeight },
5282
- flowMap: { x: 0, y: mainY + focusedHeight, width: cols, height: flowMapHeight2 },
5517
+ checklistPanel: { x: 0, y: mainY, width: cols, height: checklistHeight2 },
5518
+ focusedAgent: { x: 0, y: mainY + checklistHeight2, width: cols, height: focusedHeight },
5519
+ flowMap: {
5520
+ x: 0,
5521
+ y: mainY + checklistHeight2 + focusedHeight,
5522
+ width: cols,
5523
+ height: flowMapHeight2
5524
+ },
5283
5525
  monitorPanel: { x: 0, y: 0, width: 0, height: 0 },
5284
5526
  stageHealth,
5285
5527
  total: { width: cols, height: r }
@@ -5289,119 +5531,148 @@ function computeGridLayout(columns, rows, layoutMode) {
5289
5531
  const flowMapWidth = cols - focusedWidth;
5290
5532
  const flowMapHeight = Math.ceil(mainHeight / 2);
5291
5533
  const monitorHeight = mainHeight - flowMapHeight;
5534
+ const checklistHeight = computeChecklistHeight(mainHeight);
5535
+ const focusedAgentHeight = mainHeight - checklistHeight;
5292
5536
  return {
5293
5537
  header,
5538
+ checklistPanel: { x: flowMapWidth, y: mainY, width: focusedWidth, height: checklistHeight },
5294
5539
  flowMap: { x: 0, y: mainY, width: flowMapWidth, height: flowMapHeight },
5295
5540
  monitorPanel: { x: 0, y: mainY + flowMapHeight, width: flowMapWidth, height: monitorHeight },
5296
- focusedAgent: { x: flowMapWidth, y: mainY, width: focusedWidth, height: mainHeight },
5541
+ focusedAgent: {
5542
+ x: flowMapWidth,
5543
+ y: mainY + checklistHeight,
5544
+ width: focusedWidth,
5545
+ height: focusedAgentHeight
5546
+ },
5297
5547
  stageHealth,
5298
5548
  total: { width: cols, height: r }
5299
5549
  };
5300
5550
  }
5301
5551
 
5302
5552
  // src/tui/dashboard-app.tsx
5303
- function DashboardApp({ eventBus, externalState }) {
5553
+ function DashboardApp({
5554
+ eventBus,
5555
+ externalState,
5556
+ workspace: workspaceProp
5557
+ }) {
5304
5558
  const { columns, rows, layoutMode } = useTerminalSize();
5305
5559
  const internalState = useDashboardState(externalState ? void 0 : eventBus);
5306
5560
  const state = externalState ?? internalState;
5307
5561
  const focusedAgent = state.focusedAgentId ? state.agents.get(state.focusedAgentId) ?? null : null;
5308
5562
  const stageHealth = useMemo3(() => computeStageHealth(state.agents), [state.agents]);
5309
- const tools = useMemo3(() => {
5310
- const seen = /* @__PURE__ */ new Set();
5311
- for (const e of state.eventLog) {
5312
- seen.add(e.message.split(" [")[0]);
5313
- }
5314
- return [...seen];
5315
- }, [state.eventLog]);
5316
5563
  const bottlenecks = useMemo3(() => detectBottlenecks(state.eventLog), [state.eventLog]);
5317
5564
  const grid = useMemo3(
5318
5565
  () => computeGridLayout(columns, rows, layoutMode),
5319
5566
  [columns, rows, layoutMode]
5320
5567
  );
5321
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", width: grid.total.width, height: grid.total.height }, /* @__PURE__ */ React7.createElement(
5568
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: grid.total.width, height: grid.total.height }, /* @__PURE__ */ React8.createElement(
5322
5569
  HeaderBar,
5323
5570
  {
5324
- workspace: state.workspace,
5325
- sessionId: state.sessionId,
5571
+ workspace: workspaceProp ?? state.workspace,
5326
5572
  currentMode: state.currentMode,
5327
5573
  globalState: state.globalState,
5328
5574
  layoutMode,
5329
5575
  width: grid.header.width
5330
5576
  }
5331
- ), layoutMode === "narrow" ? /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(
5577
+ ), layoutMode === "narrow" ? /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, grid.checklistPanel.height > 0 && /* @__PURE__ */ React8.createElement(
5578
+ ChecklistPanel,
5579
+ {
5580
+ tasks: state.tasks,
5581
+ contextDecisions: state.contextDecisions,
5582
+ contextNotes: state.contextNotes,
5583
+ width: grid.checklistPanel.width,
5584
+ height: grid.checklistPanel.height
5585
+ }
5586
+ ), /* @__PURE__ */ React8.createElement(
5332
5587
  FocusedAgentPanel,
5333
5588
  {
5334
5589
  agent: focusedAgent,
5335
5590
  objectives: state.objectives,
5336
5591
  activeSkills: state.activeSkills,
5337
- tasks: state.tasks,
5338
- tools,
5339
- inputs: tools,
5340
- outputs: state.outputStats,
5341
5592
  eventLog: state.eventLog,
5342
- toolCalls: state.toolCalls,
5343
5593
  contextDecisions: state.contextDecisions,
5344
5594
  contextNotes: state.contextNotes,
5345
5595
  width: grid.focusedAgent.width,
5346
5596
  height: grid.focusedAgent.height
5347
5597
  }
5348
- ), /* @__PURE__ */ React7.createElement(
5349
- FlowMap,
5350
- {
5351
- agents: state.agents,
5352
- edges: state.edges,
5353
- layoutMode,
5354
- width: grid.flowMap.width,
5355
- height: grid.flowMap.height
5356
- }
5357
- )) : /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "row", width: grid.total.width, height: grid.focusedAgent.height }, /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", width: grid.flowMap.width }, /* @__PURE__ */ React7.createElement(
5598
+ ), /* @__PURE__ */ React8.createElement(
5358
5599
  FlowMap,
5359
5600
  {
5360
5601
  agents: state.agents,
5361
5602
  edges: state.edges,
5362
5603
  layoutMode,
5363
5604
  width: grid.flowMap.width,
5364
- height: grid.flowMap.height
5605
+ height: grid.flowMap.height,
5606
+ activeStage: state.currentMode
5365
5607
  }
5366
- ), /* @__PURE__ */ React7.createElement(
5367
- ActivityVisualizer,
5608
+ )) : /* @__PURE__ */ React8.createElement(
5609
+ Box8,
5368
5610
  {
5369
- toolCalls: state.toolCalls,
5370
- currentMode: state.currentMode,
5371
- width: grid.monitorPanel.width,
5372
- height: grid.monitorPanel.height
5373
- }
5374
- )), /* @__PURE__ */ React7.createElement(
5375
- FocusedAgentPanel,
5376
- {
5377
- agent: focusedAgent,
5378
- objectives: state.objectives,
5379
- activeSkills: state.activeSkills,
5380
- tasks: state.tasks,
5381
- tools,
5382
- inputs: tools,
5383
- outputs: state.outputStats,
5384
- eventLog: state.eventLog,
5385
- toolCalls: state.toolCalls,
5386
- contextDecisions: state.contextDecisions,
5387
- contextNotes: state.contextNotes,
5388
- width: grid.focusedAgent.width,
5389
- height: grid.focusedAgent.height
5390
- }
5391
- )), /* @__PURE__ */ React7.createElement(
5611
+ flexDirection: "row",
5612
+ width: grid.total.width,
5613
+ height: grid.checklistPanel.height + grid.focusedAgent.height
5614
+ },
5615
+ /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: grid.flowMap.width }, /* @__PURE__ */ React8.createElement(
5616
+ FlowMap,
5617
+ {
5618
+ agents: state.agents,
5619
+ edges: state.edges,
5620
+ layoutMode,
5621
+ width: grid.flowMap.width,
5622
+ height: grid.flowMap.height,
5623
+ activeStage: state.currentMode
5624
+ }
5625
+ ), /* @__PURE__ */ React8.createElement(
5626
+ ActivityVisualizer,
5627
+ {
5628
+ currentMode: state.currentMode,
5629
+ focusedAgent,
5630
+ agents: state.agents,
5631
+ edges: state.edges,
5632
+ activeSkills: state.activeSkills,
5633
+ objectives: state.objectives,
5634
+ width: grid.monitorPanel.width,
5635
+ height: grid.monitorPanel.height
5636
+ }
5637
+ )),
5638
+ /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: grid.checklistPanel.width }, /* @__PURE__ */ React8.createElement(
5639
+ ChecklistPanel,
5640
+ {
5641
+ tasks: state.tasks,
5642
+ contextDecisions: state.contextDecisions,
5643
+ contextNotes: state.contextNotes,
5644
+ width: grid.checklistPanel.width,
5645
+ height: grid.checklistPanel.height
5646
+ }
5647
+ ), /* @__PURE__ */ React8.createElement(
5648
+ FocusedAgentPanel,
5649
+ {
5650
+ agent: focusedAgent,
5651
+ objectives: state.objectives,
5652
+ activeSkills: state.activeSkills,
5653
+ eventLog: state.eventLog,
5654
+ contextDecisions: state.contextDecisions,
5655
+ contextNotes: state.contextNotes,
5656
+ width: grid.focusedAgent.width,
5657
+ height: grid.focusedAgent.height
5658
+ }
5659
+ ))
5660
+ ), /* @__PURE__ */ React8.createElement(
5392
5661
  StageHealthBar,
5393
5662
  {
5394
5663
  stageHealth,
5395
5664
  bottlenecks,
5396
5665
  toolCount: state.toolInvokeCount,
5666
+ agentCount: state.agentActivateCount,
5667
+ skillCount: state.skillInvokeCount,
5397
5668
  width: grid.stageHealth.width
5398
5669
  }
5399
5670
  ));
5400
5671
  }
5401
5672
 
5402
5673
  // src/tui/multi-session-app.tsx
5403
- import React9, { useMemo as useMemo4, useCallback as useCallback2 } from "react";
5404
- import { Box as Box9, useInput } from "ink";
5674
+ import React10, { useMemo as useMemo4, useCallback as useCallback2 } from "react";
5675
+ import { Box as Box10, useInput } from "ink";
5405
5676
  import * as path8 from "path";
5406
5677
 
5407
5678
  // src/tui/hooks/use-multi-session-state.ts
@@ -5557,8 +5828,8 @@ function useMultiSessionState(manager) {
5557
5828
  }
5558
5829
 
5559
5830
  // src/tui/components/SessionTabBar.tsx
5560
- import React8 from "react";
5561
- import { Box as Box8, Text as Text7 } from "ink";
5831
+ import React9 from "react";
5832
+ import { Box as Box9, Text as Text8 } from "ink";
5562
5833
 
5563
5834
  // src/tui/components/session-tab-bar.pure.ts
5564
5835
  var STATUS_ICONS2 = {
@@ -5612,7 +5883,7 @@ function SessionTabBar({
5612
5883
  if (formatted === "") {
5613
5884
  return null;
5614
5885
  }
5615
- return /* @__PURE__ */ React8.createElement(Box8, { width, height: 1 }, /* @__PURE__ */ React8.createElement(Text7, null, formatted));
5886
+ return /* @__PURE__ */ React9.createElement(Box9, { width, height: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, formatted));
5616
5887
  }
5617
5888
 
5618
5889
  // src/tui/multi-session-app.tsx
@@ -5640,6 +5911,10 @@ function MultiSessionApp({ manager }) {
5640
5911
  const found = managedSessions.find((s) => s.instance.pid === activeSessionPid);
5641
5912
  return found?.eventBus;
5642
5913
  }, [manager, activeSessionPid]);
5914
+ const activeProjectRoot = useMemo4(() => {
5915
+ if (activeSessionPid === null) return void 0;
5916
+ return sessions.get(activeSessionPid)?.projectRoot;
5917
+ }, [sessions, activeSessionPid]);
5643
5918
  const handleInput = useCallback2(
5644
5919
  (input, key) => {
5645
5920
  if (key.rightArrow) {
@@ -5658,7 +5933,14 @@ function MultiSessionApp({ manager }) {
5658
5933
  [switchNext, switchPrev, switchByIndex]
5659
5934
  );
5660
5935
  useInput(handleInput);
5661
- return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(SessionTabBar, { sessions: tabs, width: columns, layoutMode }), /* @__PURE__ */ React9.createElement(DashboardApp, { key: activeSessionPid ?? "none", eventBus: activeEventBus }));
5936
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(SessionTabBar, { sessions: tabs, width: columns, layoutMode }), /* @__PURE__ */ React10.createElement(
5937
+ DashboardApp,
5938
+ {
5939
+ key: activeSessionPid ?? "none",
5940
+ eventBus: activeEventBus,
5941
+ workspace: activeProjectRoot
5942
+ }
5943
+ ));
5662
5944
  }
5663
5945
 
5664
5946
  // src/tui/types.ts
@@ -5721,10 +6003,10 @@ function useClock() {
5721
6003
  // src/tui/index.tsx
5722
6004
  function startTui(options) {
5723
6005
  const renderOptions = options.stdout ? { stdout: options.stdout } : {};
5724
- return render(/* @__PURE__ */ React10.createElement(DashboardApp, { eventBus: options.eventBus }), renderOptions);
6006
+ return render(/* @__PURE__ */ React11.createElement(DashboardApp, { eventBus: options.eventBus }), renderOptions);
5725
6007
  }
5726
6008
  function renderMultiSession(options) {
5727
- return render(/* @__PURE__ */ React10.createElement(MultiSessionApp, { manager: options.manager }));
6009
+ return render(/* @__PURE__ */ React11.createElement(MultiSessionApp, { manager: options.manager }));
5728
6010
  }
5729
6011
  export {
5730
6012
  AGENT_STATUSES,