codingbuddy 4.5.0 → 5.1.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 (236) hide show
  1. package/dist/src/agent/agent.module.js +3 -1
  2. package/dist/src/agent/agent.module.js.map +1 -1
  3. package/dist/src/agent/agent.service.d.ts +11 -4
  4. package/dist/src/agent/agent.service.js +81 -13
  5. package/dist/src/agent/agent.service.js.map +1 -1
  6. package/dist/src/agent/agent.types.d.ts +41 -1
  7. package/dist/src/agent/agent.types.js.map +1 -1
  8. package/dist/src/collaboration/discussion-engine.d.ts +14 -0
  9. package/dist/src/collaboration/discussion-engine.js +49 -0
  10. package/dist/src/collaboration/discussion-engine.js.map +1 -0
  11. package/dist/src/collaboration/index.d.ts +5 -0
  12. package/dist/src/collaboration/index.js +22 -0
  13. package/dist/src/collaboration/index.js.map +1 -0
  14. package/dist/src/collaboration/opinion-adapter.d.ts +14 -0
  15. package/dist/src/collaboration/opinion-adapter.js +56 -0
  16. package/dist/src/collaboration/opinion-adapter.js.map +1 -0
  17. package/dist/src/collaboration/terminal-formatter.d.ts +5 -0
  18. package/dist/src/collaboration/terminal-formatter.js +57 -0
  19. package/dist/src/collaboration/terminal-formatter.js.map +1 -0
  20. package/dist/src/collaboration/types.d.ts +50 -0
  21. package/dist/src/collaboration/types.js +53 -0
  22. package/dist/src/collaboration/types.js.map +1 -0
  23. package/dist/src/config/config.schema.d.ts +14 -0
  24. package/dist/src/config/config.schema.js +7 -0
  25. package/dist/src/config/config.schema.js.map +1 -1
  26. package/dist/src/context/context-archive.service.d.ts +14 -0
  27. package/dist/src/context/context-archive.service.js +257 -0
  28. package/dist/src/context/context-archive.service.js.map +1 -0
  29. package/dist/src/context/context-archive.types.d.ts +37 -0
  30. package/dist/src/context/context-archive.types.js +47 -0
  31. package/dist/src/context/context-archive.types.js.map +1 -0
  32. package/dist/src/context/context-document.service.d.ts +4 -2
  33. package/dist/src/context/context-document.service.js +24 -4
  34. package/dist/src/context/context-document.service.js.map +1 -1
  35. package/dist/src/context/context-document.types.d.ts +18 -0
  36. package/dist/src/context/context-document.types.js +13 -1
  37. package/dist/src/context/context-document.types.js.map +1 -1
  38. package/dist/src/context/context-parser.utils.js +32 -0
  39. package/dist/src/context/context-parser.utils.js.map +1 -1
  40. package/dist/src/context/context-serializer.utils.d.ts +3 -2
  41. package/dist/src/context/context-serializer.utils.js +25 -1
  42. package/dist/src/context/context-serializer.utils.js.map +1 -1
  43. package/dist/src/context/context.module.js +3 -2
  44. package/dist/src/context/context.module.js.map +1 -1
  45. package/dist/src/context/index.d.ts +1 -0
  46. package/dist/src/context/index.js +1 -0
  47. package/dist/src/context/index.js.map +1 -1
  48. package/dist/src/impact/impact-event.service.d.ts +8 -0
  49. package/dist/src/impact/impact-event.service.js +53 -0
  50. package/dist/src/impact/impact-event.service.js.map +1 -0
  51. package/dist/src/impact/impact-report.service.d.ts +7 -0
  52. package/dist/src/impact/impact-report.service.js +92 -0
  53. package/dist/src/impact/impact-report.service.js.map +1 -0
  54. package/dist/src/impact/impact.module.d.ts +2 -0
  55. package/dist/src/impact/impact.module.js +22 -0
  56. package/dist/src/impact/impact.module.js.map +1 -0
  57. package/dist/src/impact/impact.types.d.ts +32 -0
  58. package/dist/src/impact/impact.types.js +5 -0
  59. package/dist/src/impact/impact.types.js.map +1 -0
  60. package/dist/src/impact/index.d.ts +4 -0
  61. package/dist/src/impact/index.js +25 -0
  62. package/dist/src/impact/index.js.map +1 -0
  63. package/dist/src/keyword/diff-analyzer.d.ts +19 -0
  64. package/dist/src/keyword/diff-analyzer.js +127 -0
  65. package/dist/src/keyword/diff-analyzer.js.map +1 -0
  66. package/dist/src/keyword/explicit-pattern-matcher.d.ts +3 -0
  67. package/dist/src/keyword/explicit-pattern-matcher.js +34 -0
  68. package/dist/src/keyword/explicit-pattern-matcher.js.map +1 -0
  69. package/dist/src/keyword/keyword.module.js +17 -1
  70. package/dist/src/keyword/keyword.module.js.map +1 -1
  71. package/dist/src/keyword/keyword.service.d.ts +3 -0
  72. package/dist/src/keyword/keyword.service.js +33 -3
  73. package/dist/src/keyword/keyword.service.js.map +1 -1
  74. package/dist/src/keyword/keyword.types.d.ts +29 -1
  75. package/dist/src/keyword/keyword.types.js +34 -1
  76. package/dist/src/keyword/keyword.types.js.map +1 -1
  77. package/dist/src/keyword/primary-agent-resolver.d.ts +7 -3
  78. package/dist/src/keyword/primary-agent-resolver.js +24 -2
  79. package/dist/src/keyword/primary-agent-resolver.js.map +1 -1
  80. package/dist/src/keyword/strategies/act-agent.strategy.js +16 -0
  81. package/dist/src/keyword/strategies/act-agent.strategy.js.map +1 -1
  82. package/dist/src/keyword/strategies/index.d.ts +1 -1
  83. package/dist/src/keyword/strategies/index.js.map +1 -1
  84. package/dist/src/keyword/strategies/resolution-strategy.interface.d.ts +5 -0
  85. package/dist/src/keyword/visual-data.builder.d.ts +10 -0
  86. package/dist/src/keyword/visual-data.builder.js +62 -0
  87. package/dist/src/keyword/visual-data.builder.js.map +1 -0
  88. package/dist/src/mcp/handlers/agent.handler.d.ts +4 -1
  89. package/dist/src/mcp/handlers/agent.handler.js +59 -6
  90. package/dist/src/mcp/handlers/agent.handler.js.map +1 -1
  91. package/dist/src/mcp/handlers/checklist-context.handler.d.ts +3 -1
  92. package/dist/src/mcp/handlers/checklist-context.handler.js +13 -2
  93. package/dist/src/mcp/handlers/checklist-context.handler.js.map +1 -1
  94. package/dist/src/mcp/handlers/context-archive.handler.d.ts +14 -0
  95. package/dist/src/mcp/handlers/context-archive.handler.js +136 -0
  96. package/dist/src/mcp/handlers/context-archive.handler.js.map +1 -0
  97. package/dist/src/mcp/handlers/context-document.handler.d.ts +6 -1
  98. package/dist/src/mcp/handlers/context-document.handler.js +113 -12
  99. package/dist/src/mcp/handlers/context-document.handler.js.map +1 -1
  100. package/dist/src/mcp/handlers/discussion.handler.d.ts +14 -0
  101. package/dist/src/mcp/handlers/discussion.handler.js +168 -0
  102. package/dist/src/mcp/handlers/discussion.handler.js.map +1 -0
  103. package/dist/src/mcp/handlers/discussion.types.d.ts +18 -0
  104. package/dist/src/mcp/handlers/discussion.types.js +11 -0
  105. package/dist/src/mcp/handlers/discussion.types.js.map +1 -0
  106. package/dist/src/mcp/handlers/impact.handler.d.ts +11 -0
  107. package/dist/src/mcp/handlers/impact.handler.js +53 -0
  108. package/dist/src/mcp/handlers/impact.handler.js.map +1 -0
  109. package/dist/src/mcp/handlers/index.d.ts +6 -0
  110. package/dist/src/mcp/handlers/index.js +13 -1
  111. package/dist/src/mcp/handlers/index.js.map +1 -1
  112. package/dist/src/mcp/handlers/mode.handler.d.ts +7 -1
  113. package/dist/src/mcp/handlers/mode.handler.js +109 -3
  114. package/dist/src/mcp/handlers/mode.handler.js.map +1 -1
  115. package/dist/src/mcp/handlers/pipeline.handler.d.ts +14 -0
  116. package/dist/src/mcp/handlers/pipeline.handler.js +137 -0
  117. package/dist/src/mcp/handlers/pipeline.handler.js.map +1 -0
  118. package/dist/src/mcp/handlers/rule-insights.handler.d.ts +14 -0
  119. package/dist/src/mcp/handlers/rule-insights.handler.js +78 -0
  120. package/dist/src/mcp/handlers/rule-insights.handler.js.map +1 -0
  121. package/dist/src/mcp/handlers/rules.handler.d.ts +3 -1
  122. package/dist/src/mcp/handlers/rules.handler.js +14 -2
  123. package/dist/src/mcp/handlers/rules.handler.js.map +1 -1
  124. package/dist/src/mcp/mcp.module.js +10 -0
  125. package/dist/src/mcp/mcp.module.js.map +1 -1
  126. package/dist/src/parallel-validation/extract-file-paths.d.ts +1 -0
  127. package/dist/src/parallel-validation/extract-file-paths.js +19 -0
  128. package/dist/src/parallel-validation/extract-file-paths.js.map +1 -0
  129. package/dist/src/parallel-validation/index.d.ts +5 -0
  130. package/dist/src/parallel-validation/index.js +12 -0
  131. package/dist/src/parallel-validation/index.js.map +1 -0
  132. package/dist/src/parallel-validation/overlap-matrix.d.ts +10 -0
  133. package/dist/src/parallel-validation/overlap-matrix.js +23 -0
  134. package/dist/src/parallel-validation/overlap-matrix.js.map +1 -0
  135. package/dist/src/parallel-validation/parallel-validation.handler.d.ts +8 -0
  136. package/dist/src/parallel-validation/parallel-validation.handler.js +84 -0
  137. package/dist/src/parallel-validation/parallel-validation.handler.js.map +1 -0
  138. package/dist/src/parallel-validation/parallel-validation.types.d.ts +12 -0
  139. package/dist/src/parallel-validation/parallel-validation.types.js +3 -0
  140. package/dist/src/parallel-validation/parallel-validation.types.js.map +1 -0
  141. package/dist/src/parallel-validation/wave-splitter.d.ts +2 -0
  142. package/dist/src/parallel-validation/wave-splitter.js +39 -0
  143. package/dist/src/parallel-validation/wave-splitter.js.map +1 -0
  144. package/dist/src/pipeline/index.d.ts +5 -0
  145. package/dist/src/pipeline/index.js +14 -0
  146. package/dist/src/pipeline/index.js.map +1 -0
  147. package/dist/src/pipeline/pipeline.executors.d.ts +2 -0
  148. package/dist/src/pipeline/pipeline.executors.js +79 -0
  149. package/dist/src/pipeline/pipeline.executors.js.map +1 -0
  150. package/dist/src/pipeline/pipeline.module.d.ts +2 -0
  151. package/dist/src/pipeline/pipeline.module.js +21 -0
  152. package/dist/src/pipeline/pipeline.module.js.map +1 -0
  153. package/dist/src/pipeline/pipeline.service.d.ts +8 -0
  154. package/dist/src/pipeline/pipeline.service.js +89 -0
  155. package/dist/src/pipeline/pipeline.service.js.map +1 -0
  156. package/dist/src/pipeline/pipeline.types.d.ts +47 -0
  157. package/dist/src/pipeline/pipeline.types.js +33 -0
  158. package/dist/src/pipeline/pipeline.types.js.map +1 -0
  159. package/dist/src/rules/rule-insights.service.d.ts +31 -0
  160. package/dist/src/rules/rule-insights.service.js +102 -0
  161. package/dist/src/rules/rule-insights.service.js.map +1 -0
  162. package/dist/src/rules/rule-tracker.d.ts +24 -0
  163. package/dist/src/rules/rule-tracker.js +74 -0
  164. package/dist/src/rules/rule-tracker.js.map +1 -0
  165. package/dist/src/rules/rules.module.js +3 -2
  166. package/dist/src/rules/rules.module.js.map +1 -1
  167. package/dist/src/shared/event-bridge-reader.d.ts +17 -0
  168. package/dist/src/shared/event-bridge-reader.js +56 -0
  169. package/dist/src/shared/event-bridge-reader.js.map +1 -0
  170. package/dist/src/shared/version.d.ts +1 -1
  171. package/dist/src/shared/version.js +1 -1
  172. package/dist/src/skill/i18n/keywords.types.d.ts +4 -2
  173. package/dist/src/skill/i18n/keywords.types.js +3 -0
  174. package/dist/src/skill/i18n/keywords.types.js.map +1 -1
  175. package/dist/src/tui/components/ActModeScreen.d.ts +10 -0
  176. package/dist/src/tui/components/ActModeScreen.js +25 -0
  177. package/dist/src/tui/components/ActModeScreen.js.map +1 -0
  178. package/dist/src/tui/components/AgentDiscussionPanel.d.ts +8 -0
  179. package/dist/src/tui/components/AgentDiscussionPanel.js +115 -0
  180. package/dist/src/tui/components/AgentDiscussionPanel.js.map +1 -0
  181. package/dist/src/tui/components/AgentDiscussionPanel.spec.d.ts +1 -0
  182. package/dist/src/tui/components/AgentDiscussionPanel.spec.js +229 -0
  183. package/dist/src/tui/components/AgentDiscussionPanel.spec.js.map +1 -0
  184. package/dist/src/tui/components/EvalModeScreen.d.ts +8 -0
  185. package/dist/src/tui/components/EvalModeScreen.js +21 -0
  186. package/dist/src/tui/components/EvalModeScreen.js.map +1 -0
  187. package/dist/src/tui/components/FlowMap.spec.js.map +1 -1
  188. package/dist/src/tui/components/FocusedAgentPanel.js +1 -1
  189. package/dist/src/tui/components/FocusedAgentPanel.js.map +1 -1
  190. package/dist/src/tui/components/FocusedAgentPanel.spec.js +1 -3
  191. package/dist/src/tui/components/FocusedAgentPanel.spec.js.map +1 -1
  192. package/dist/src/tui/components/HeaderBar.js +3 -3
  193. package/dist/src/tui/components/HeaderBar.js.map +1 -1
  194. package/dist/src/tui/components/ModeScreenRouter.d.ts +13 -0
  195. package/dist/src/tui/components/ModeScreenRouter.js +51 -0
  196. package/dist/src/tui/components/ModeScreenRouter.js.map +1 -0
  197. package/dist/src/tui/components/PlanModeScreen.d.ts +10 -0
  198. package/dist/src/tui/components/PlanModeScreen.js +24 -0
  199. package/dist/src/tui/components/PlanModeScreen.js.map +1 -0
  200. package/dist/src/tui/components/SessionDashboard.d.ts +17 -0
  201. package/dist/src/tui/components/SessionDashboard.js +58 -0
  202. package/dist/src/tui/components/SessionDashboard.js.map +1 -0
  203. package/dist/src/tui/components/act-screen.pure.d.ts +12 -0
  204. package/dist/src/tui/components/act-screen.pure.js +93 -0
  205. package/dist/src/tui/components/act-screen.pure.js.map +1 -0
  206. package/dist/src/tui/components/agent-discussion-panel.pure.d.ts +55 -0
  207. package/dist/src/tui/components/agent-discussion-panel.pure.js +201 -0
  208. package/dist/src/tui/components/agent-discussion-panel.pure.js.map +1 -0
  209. package/dist/src/tui/components/eval-screen.pure.d.ts +13 -0
  210. package/dist/src/tui/components/eval-screen.pure.js +76 -0
  211. package/dist/src/tui/components/eval-screen.pure.js.map +1 -0
  212. package/dist/src/tui/components/index.d.ts +16 -0
  213. package/dist/src/tui/components/index.js +48 -1
  214. package/dist/src/tui/components/index.js.map +1 -1
  215. package/dist/src/tui/components/plan-screen.pure.d.ts +9 -0
  216. package/dist/src/tui/components/plan-screen.pure.js +61 -0
  217. package/dist/src/tui/components/plan-screen.pure.js.map +1 -0
  218. package/dist/src/tui/components/session-dashboard.pure.d.ts +43 -0
  219. package/dist/src/tui/components/session-dashboard.pure.js +182 -0
  220. package/dist/src/tui/components/session-dashboard.pure.js.map +1 -0
  221. package/dist/src/tui/dashboard-app.js +3 -3
  222. package/dist/src/tui/dashboard-app.js.map +1 -1
  223. package/dist/src/tui/dashboard-types.d.ts +49 -0
  224. package/dist/src/tui/dashboard-types.js +20 -1
  225. package/dist/src/tui/dashboard-types.js.map +1 -1
  226. package/dist/src/tui/events/index.d.ts +1 -1
  227. package/dist/src/tui/events/index.js.map +1 -1
  228. package/dist/src/tui/events/types.d.ts +29 -1
  229. package/dist/src/tui/events/types.js +5 -0
  230. package/dist/src/tui/events/types.js.map +1 -1
  231. package/dist/src/tui/hooks/use-dashboard-state.d.ts +16 -1
  232. package/dist/src/tui/hooks/use-dashboard-state.js +70 -1
  233. package/dist/src/tui/hooks/use-dashboard-state.js.map +1 -1
  234. package/dist/src/tui-bundle.mjs +1023 -125
  235. package/dist/tsconfig.build.tsbuildinfo +1 -1
  236. package/package.json +14 -14
@@ -1371,12 +1371,12 @@ var require_eventemitter2 = __commonJS({
1371
1371
  });
1372
1372
 
1373
1373
  // src/tui/index.tsx
1374
- import React11 from "react";
1374
+ import React16 from "react";
1375
1375
  import { render } from "ink";
1376
1376
 
1377
1377
  // src/tui/dashboard-app.tsx
1378
- import React8, { useMemo as useMemo3 } from "react";
1379
- import { Box as Box8 } from "ink";
1378
+ import React13, { useMemo as useMemo7 } from "react";
1379
+ import { Box as Box13 } from "ink";
1380
1380
 
1381
1381
  // src/tui/hooks/use-terminal-size.ts
1382
1382
  import { useState, useEffect } from "react";
@@ -1407,6 +1407,23 @@ function createDefaultDashboardNode(params) {
1407
1407
  function createEmptyStageStats() {
1408
1408
  return { running: 0, blocked: 0, waiting: 0, done: 0, error: 0 };
1409
1409
  }
1410
+ var TDD_PHASES = Object.freeze(["RED", "GREEN", "REFACTOR"]);
1411
+ function createDefaultTddStep(params) {
1412
+ return {
1413
+ agentId: null,
1414
+ status: "pending",
1415
+ ...params
1416
+ };
1417
+ }
1418
+ function createDefaultReviewResult(params) {
1419
+ return {
1420
+ categories: [],
1421
+ totalScore: 0,
1422
+ maxTotalScore: 100,
1423
+ status: "pending",
1424
+ ...params
1425
+ };
1426
+ }
1410
1427
 
1411
1428
  // src/tui/hooks/use-terminal-size.ts
1412
1429
  var DEFAULT_SIZE = { columns: 120, rows: 40 };
@@ -1496,7 +1513,12 @@ var TUI_EVENTS = Object.freeze({
1496
1513
  TOOL_INVOKED: "tool:invoked",
1497
1514
  OBJECTIVE_SET: "objective:set",
1498
1515
  SESSION_RESET: "session:reset",
1499
- CONTEXT_UPDATED: "context:updated"
1516
+ CONTEXT_UPDATED: "context:updated",
1517
+ DISCUSSION_ROUND_ADDED: "discussion:round-added",
1518
+ TDD_PHASE_CHANGED: "tdd:phase-changed",
1519
+ TDD_STEP_UPDATED: "tdd:step-updated",
1520
+ REVIEW_RESULT_ADDED: "review:result-added",
1521
+ CONNECTION_STATUS_CHANGED: "connection:status-changed"
1500
1522
  });
1501
1523
 
1502
1524
  // src/tui/events/parse-agent.ts
@@ -2150,6 +2172,21 @@ var SUPPORTED_LANGUAGES = {
2150
2172
  name: "Spanish",
2151
2173
  nativeName: "Espa\xF1ol",
2152
2174
  description: "AI responses will be in Spanish"
2175
+ },
2176
+ pt: {
2177
+ name: "Portuguese",
2178
+ nativeName: "Portugu\xEAs",
2179
+ description: "AI responses will be in Portuguese"
2180
+ },
2181
+ de: {
2182
+ name: "German",
2183
+ nativeName: "Deutsch",
2184
+ description: "AI responses will be in German"
2185
+ },
2186
+ fr: {
2187
+ name: "French",
2188
+ nativeName: "Fran\xE7ais",
2189
+ description: "AI responses will be in French"
2153
2190
  }
2154
2191
  };
2155
2192
  var SUPPORTED_LANGUAGE_CODES = Object.keys(
@@ -2629,11 +2666,106 @@ RulesService = __decorateClass([
2629
2666
  Injectable4()
2630
2667
  ], RulesService);
2631
2668
 
2669
+ // src/rules/rule-insights.service.ts
2670
+ import { Injectable as Injectable5 } from "@nestjs/common";
2671
+ var WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
2672
+ var MONTH_MS = 30 * 24 * 60 * 60 * 1e3;
2673
+ var TOP_RULES_LIMIT = 10;
2674
+ var EMERGING_THRESHOLD = 3;
2675
+ var SUGGESTION_UNUSED_PREVIEW = 5;
2676
+ var RuleInsightsService = class {
2677
+ generateInsights(stats, allRuleNames, now = Date.now()) {
2678
+ const entries = Object.entries(stats);
2679
+ const totalUsageCount = entries.reduce((sum, [, s]) => sum + s.count, 0);
2680
+ const avgCount = entries.length > 0 ? totalUsageCount / entries.length : 0;
2681
+ const topRules = this.buildTopRules(entries, avgCount);
2682
+ const unusedRules = this.findUnusedRules(stats, allRuleNames, now);
2683
+ const activeRules = entries.filter(([, s]) => now - s.lastUsed <= WEEK_MS).length;
2684
+ const staleRules = entries.filter(([, s]) => now - s.lastUsed > MONTH_MS).length;
2685
+ const trends = this.analyzeTrends(entries, avgCount, now);
2686
+ const suggestions = this.generateSuggestions(
2687
+ topRules,
2688
+ unusedRules,
2689
+ trends.declining,
2690
+ entries.length
2691
+ );
2692
+ return {
2693
+ generatedAt: now,
2694
+ summary: {
2695
+ totalRulesTracked: entries.length,
2696
+ totalUsageCount,
2697
+ activeRules,
2698
+ staleRules
2699
+ },
2700
+ topRules: topRules.slice(0, TOP_RULES_LIMIT),
2701
+ unusedRules,
2702
+ trends,
2703
+ suggestions
2704
+ };
2705
+ }
2706
+ buildTopRules(entries, avgCount) {
2707
+ return entries.map(([name, s]) => ({
2708
+ name,
2709
+ count: s.count,
2710
+ lastUsed: s.lastUsed,
2711
+ classification: this.classifyFrequency(s.count, avgCount)
2712
+ })).sort((a, b) => b.count - a.count);
2713
+ }
2714
+ classifyFrequency(count, avgCount) {
2715
+ if (avgCount === 0) return "low";
2716
+ if (count > avgCount * 2) return "high";
2717
+ if (count > avgCount) return "medium";
2718
+ return "low";
2719
+ }
2720
+ findUnusedRules(stats, allRuleNames, now) {
2721
+ return allRuleNames.filter((name) => {
2722
+ const s = stats[name];
2723
+ return !s || now - s.lastUsed > MONTH_MS;
2724
+ });
2725
+ }
2726
+ analyzeTrends(entries, avgCount, now) {
2727
+ const recentlyActive = entries.filter(([, s]) => now - s.lastUsed <= WEEK_MS).map(([name]) => name);
2728
+ const declining = entries.filter(([, s]) => now - s.lastUsed > MONTH_MS && s.count > avgCount).map(([name]) => name);
2729
+ const emerging = entries.filter(([, s]) => now - s.lastUsed <= WEEK_MS && s.count <= EMERGING_THRESHOLD).map(([name]) => name);
2730
+ return { recentlyActive, declining, emerging };
2731
+ }
2732
+ generateSuggestions(topRules, unusedRules, declining, totalTracked) {
2733
+ const suggestions = [];
2734
+ if (totalTracked === 0) {
2735
+ suggestions.push(
2736
+ "No tracking data available yet \u2014 use parse_mode to start collecting rule usage data"
2737
+ );
2738
+ return suggestions;
2739
+ }
2740
+ const highFreq = topRules.filter((r) => r.classification === "high");
2741
+ if (highFreq.length > 0) {
2742
+ suggestions.push(
2743
+ `High-frequency rules [${highFreq.map((r) => r.name).join(", ")}] may need stricter enforcement or developer education`
2744
+ );
2745
+ }
2746
+ if (unusedRules.length > 0) {
2747
+ const preview = unusedRules.slice(0, SUGGESTION_UNUSED_PREVIEW).join(", ");
2748
+ suggestions.push(
2749
+ `${unusedRules.length} unused/stale rules detected \u2014 consider removing or updating: ${preview}`
2750
+ );
2751
+ }
2752
+ if (declining.length > 0) {
2753
+ suggestions.push(
2754
+ `Rules [${declining.join(", ")}] were once active but have declined \u2014 verify they are still relevant`
2755
+ );
2756
+ }
2757
+ return suggestions;
2758
+ }
2759
+ };
2760
+ RuleInsightsService = __decorateClass([
2761
+ Injectable5()
2762
+ ], RuleInsightsService);
2763
+
2632
2764
  // src/custom/custom.module.ts
2633
2765
  import { Module } from "@nestjs/common";
2634
2766
 
2635
2767
  // src/custom/custom.service.ts
2636
- import { Injectable as Injectable5, Logger as Logger5 } from "@nestjs/common";
2768
+ import { Injectable as Injectable6, Logger as Logger5 } from "@nestjs/common";
2637
2769
  import * as fs from "fs/promises";
2638
2770
  import * as path3 from "path";
2639
2771
 
@@ -2753,7 +2885,7 @@ var CustomService = class {
2753
2885
  }
2754
2886
  };
2755
2887
  CustomService = __decorateClass([
2756
- Injectable5()
2888
+ Injectable6()
2757
2889
  ], CustomService);
2758
2890
 
2759
2891
  // src/custom/custom.module.ts
@@ -2770,7 +2902,7 @@ CustomModule = __decorateClass([
2770
2902
  import { Module as Module2 } from "@nestjs/common";
2771
2903
 
2772
2904
  // src/config/config.service.ts
2773
- import { Injectable as Injectable6, Logger as Logger6 } from "@nestjs/common";
2905
+ import { Injectable as Injectable7, Logger as Logger6 } from "@nestjs/common";
2774
2906
  import { existsSync as existsSync5, statSync } from "fs";
2775
2907
  import { stat, realpath } from "fs/promises";
2776
2908
  import * as path7 from "path";
@@ -2837,6 +2969,22 @@ var TestStrategyConfigSchema = z3.object({
2837
2969
  var AIConfigSchema = z3.object({
2838
2970
  defaultModel: z3.string().optional(),
2839
2971
  primaryAgent: z3.string().optional(),
2972
+ /**
2973
+ * Override default dispatch strength for parallel specialist agents.
2974
+ * - "auto": Always dispatch specialists automatically
2975
+ * - "recommend": Suggest dispatch (default for PLAN/ACT)
2976
+ * - "skip": Do not dispatch specialists
2977
+ *
2978
+ * Default varies by mode: EVAL="auto", PLAN/ACT="recommend"
2979
+ *
2980
+ * @example
2981
+ * ```javascript
2982
+ * ai: {
2983
+ * dispatchStrength: 'auto',
2984
+ * }
2985
+ * ```
2986
+ */
2987
+ dispatchStrength: z3.enum(["auto", "recommend", "skip"]).optional(),
2840
2988
  /**
2841
2989
  * List of agent names to exclude from automatic resolution.
2842
2990
  * Useful for project-specific exclusions (e.g., exclude mobile-developer for backend-only projects).
@@ -2862,7 +3010,27 @@ var AIConfigSchema = z3.object({
2862
3010
  * }
2863
3011
  * ```
2864
3012
  */
2865
- maxIncludedSkills: z3.number().int().min(0).max(10).optional()
3013
+ maxIncludedSkills: z3.number().int().min(0).max(10).optional(),
3014
+ /**
3015
+ * Enable/disable automatic plan-reviewer gate after PLAN completion.
3016
+ * When enabled, parse_mode PLAN response includes a planReviewGate recommendation.
3017
+ * Default: true (enabled)
3018
+ *
3019
+ * @example
3020
+ * ```javascript
3021
+ * ai: {
3022
+ * planReviewGate: false, // disable plan review gate
3023
+ * }
3024
+ * ```
3025
+ */
3026
+ planReviewGate: z3.boolean().optional(),
3027
+ /**
3028
+ * Enable/disable agent discussion integration in EVAL mode.
3029
+ * When enabled, parse_mode EVAL response includes agentDiscussion config
3030
+ * for structuring specialist findings as AgentOpinion protocol.
3031
+ * Default: true (enabled)
3032
+ */
3033
+ agentDiscussion: z3.boolean().optional()
2866
3034
  });
2867
3035
  var AutoConfigSchema = z3.object({
2868
3036
  maxIterations: z3.number().int().min(1).max(10).default(3)
@@ -2879,6 +3047,10 @@ var CodingBuddyConfigSchema = z3.object({
2879
3047
  projectName: z3.string().optional(),
2880
3048
  description: z3.string().optional(),
2881
3049
  repository: z3.string().url().optional(),
3050
+ // Feature Flags
3051
+ eco: z3.boolean().default(true).optional(),
3052
+ tui: z3.boolean().default(true).optional(),
3053
+ tone: z3.enum(["casual", "formal"]).default("casual").optional(),
2882
3054
  // Technical Configuration
2883
3055
  techStack: TechStackConfigSchema.optional(),
2884
3056
  architecture: ArchitectureConfigSchema.optional(),
@@ -2890,6 +3062,8 @@ var CodingBuddyConfigSchema = z3.object({
2890
3062
  auto: AutoConfigSchema.optional(),
2891
3063
  // Context document limits (DoS prevention)
2892
3064
  context: ContextConfigSchema.optional(),
3065
+ // Upstream Repository Mapping (for cross-repo issue creation)
3066
+ upstreamRepos: z3.record(z3.string(), z3.string()).optional(),
2893
3067
  // Additional Context
2894
3068
  keyFiles: z3.array(z3.string()).optional(),
2895
3069
  avoid: z3.array(z3.string()).optional(),
@@ -3738,11 +3912,11 @@ var ConfigService = class {
3738
3912
  }
3739
3913
  };
3740
3914
  ConfigService = __decorateClass([
3741
- Injectable6()
3915
+ Injectable7()
3742
3916
  ], ConfigService);
3743
3917
 
3744
3918
  // src/config/config-diff.service.ts
3745
- import { Injectable as Injectable7 } from "@nestjs/common";
3919
+ import { Injectable as Injectable8 } from "@nestjs/common";
3746
3920
  var ConfigDiffService = class {
3747
3921
  /**
3748
3922
  * Compare project analysis with current config
@@ -3914,7 +4088,7 @@ var ConfigDiffService = class {
3914
4088
  }
3915
4089
  };
3916
4090
  ConfigDiffService = __decorateClass([
3917
- Injectable7()
4091
+ Injectable8()
3918
4092
  ], ConfigDiffService);
3919
4093
 
3920
4094
  // src/config/config.module.ts
@@ -3933,8 +4107,8 @@ var RulesModule = class {
3933
4107
  RulesModule = __decorateClass([
3934
4108
  Module3({
3935
4109
  imports: [CustomModule, CodingBuddyConfigModule],
3936
- providers: [RulesService],
3937
- exports: [RulesService]
4110
+ providers: [RulesService, RuleInsightsService],
4111
+ exports: [RulesService, RuleInsightsService]
3938
4112
  })
3939
4113
  ], RulesModule);
3940
4114
 
@@ -3997,7 +4171,15 @@ function createInitialDashboardState() {
3997
4171
  contextDecisions: [],
3998
4172
  contextNotes: [],
3999
4173
  contextMode: null,
4000
- contextStatus: null
4174
+ contextStatus: null,
4175
+ discussionRounds: [],
4176
+ tddCurrentPhase: null,
4177
+ tddSteps: [],
4178
+ reviewResults: [],
4179
+ connectionStatus: "connected",
4180
+ sessionStartedAt: Date.now(),
4181
+ modeTransitions: [],
4182
+ fileChanges: { created: 0, modified: 0, deleted: 0 }
4001
4183
  };
4002
4184
  }
4003
4185
  function cloneAgents(agents) {
@@ -4065,7 +4247,16 @@ function dashboardReducer(state, action) {
4065
4247
  }
4066
4248
  }
4067
4249
  const focusedAgentId = selectFocusedAgent(agents, state.focusedAgentId);
4068
- return { ...state, currentMode: newMode, agents, activeSkills: [], focusedAgentId };
4250
+ const transition = { from, to: newMode, timestamp: Date.now() };
4251
+ const modeTransitions = [...state.modeTransitions, transition];
4252
+ return {
4253
+ ...state,
4254
+ currentMode: newMode,
4255
+ agents,
4256
+ activeSkills: [],
4257
+ focusedAgentId,
4258
+ modeTransitions
4259
+ };
4069
4260
  }
4070
4261
  case "AGENT_RELATIONSHIP": {
4071
4262
  const edge = {
@@ -4130,13 +4321,21 @@ function dashboardReducer(state, action) {
4130
4321
  } else {
4131
4322
  activityHistory = [...history.slice(-59), { timestamp: sec, toolCalls: 1 }];
4132
4323
  }
4324
+ const toolLower = action.payload.toolName.toLowerCase();
4325
+ let fileChanges = state.fileChanges;
4326
+ if (toolLower === "write" || toolLower === "notebookedit") {
4327
+ fileChanges = { ...fileChanges, created: fileChanges.created + 1 };
4328
+ } else if (toolLower === "edit") {
4329
+ fileChanges = { ...fileChanges, modified: fileChanges.modified + 1 };
4330
+ }
4133
4331
  return {
4134
4332
  ...state,
4135
4333
  agents,
4136
4334
  eventLog: [...base, entry],
4137
4335
  toolCalls: [...toolCallsBase, toolCall],
4138
4336
  activityHistory,
4139
- toolInvokeCount: state.toolInvokeCount + 1
4337
+ toolInvokeCount: state.toolInvokeCount + 1,
4338
+ fileChanges
4140
4339
  };
4141
4340
  }
4142
4341
  case "OBJECTIVE_SET":
@@ -4180,6 +4379,12 @@ function dashboardReducer(state, action) {
4180
4379
  contextStatus: status
4181
4380
  };
4182
4381
  }
4382
+ case "ADD_DISCUSSION_ROUND": {
4383
+ return {
4384
+ ...state,
4385
+ discussionRounds: [...state.discussionRounds, action.payload.round]
4386
+ };
4387
+ }
4183
4388
  case "SESSION_RESET":
4184
4389
  return createInitialDashboardState();
4185
4390
  case "CLEANUP_STALE_AGENTS": {
@@ -4197,6 +4402,24 @@ function dashboardReducer(state, action) {
4197
4402
  const focusedAgentId = selectFocusedAgent(agents, state.focusedAgentId);
4198
4403
  return { ...state, agents, focusedAgentId };
4199
4404
  }
4405
+ case "TDD_PHASE_CHANGED": {
4406
+ return { ...state, tddCurrentPhase: action.payload.phase };
4407
+ }
4408
+ case "TDD_STEP_UPDATED": {
4409
+ const { step } = action.payload;
4410
+ const existing = state.tddSteps.findIndex((s) => s.id === step.id);
4411
+ const tddSteps = existing >= 0 ? state.tddSteps.map((s, i) => i === existing ? step : s) : [...state.tddSteps, step];
4412
+ return { ...state, tddSteps };
4413
+ }
4414
+ case "REVIEW_RESULT_ADDED": {
4415
+ const { result } = action.payload;
4416
+ const idx = state.reviewResults.findIndex((r) => r.agentId === result.agentId);
4417
+ const reviewResults = idx >= 0 ? state.reviewResults.map((r, i) => i === idx ? result : r) : [...state.reviewResults, result];
4418
+ return { ...state, reviewResults };
4419
+ }
4420
+ case "CONNECTION_STATUS_CHANGED": {
4421
+ return { ...state, connectionStatus: action.payload.status };
4422
+ }
4200
4423
  // Reserved: no emitter currently produces PARALLEL_COMPLETED. Subscription kept for forward compatibility.
4201
4424
  case "PARALLEL_COMPLETED":
4202
4425
  case "AGENTS_LOADED":
@@ -4224,6 +4447,11 @@ function useDashboardState(eventBus) {
4224
4447
  const onObjectiveSet = (p) => dispatch({ type: "OBJECTIVE_SET", payload: p });
4225
4448
  const onSessionReset = (p) => dispatch({ type: "SESSION_RESET", payload: p });
4226
4449
  const onContextUpdated = (p) => dispatch({ type: "CONTEXT_UPDATED", payload: p });
4450
+ const onDiscussionRoundAdded = (p) => dispatch({ type: "ADD_DISCUSSION_ROUND", payload: p });
4451
+ const onTddPhaseChanged = (p) => dispatch({ type: "TDD_PHASE_CHANGED", payload: p });
4452
+ const onTddStepUpdated = (p) => dispatch({ type: "TDD_STEP_UPDATED", payload: p });
4453
+ const onReviewResultAdded = (p) => dispatch({ type: "REVIEW_RESULT_ADDED", payload: p });
4454
+ const onConnectionStatusChanged = (p) => dispatch({ type: "CONNECTION_STATUS_CHANGED", payload: p });
4227
4455
  eventBus.on(TUI_EVENTS.AGENT_ACTIVATED, onActivated);
4228
4456
  eventBus.on(TUI_EVENTS.AGENT_DEACTIVATED, onDeactivated);
4229
4457
  eventBus.on(TUI_EVENTS.MODE_CHANGED, onModeChanged);
@@ -4237,6 +4465,11 @@ function useDashboardState(eventBus) {
4237
4465
  eventBus.on(TUI_EVENTS.OBJECTIVE_SET, onObjectiveSet);
4238
4466
  eventBus.on(TUI_EVENTS.SESSION_RESET, onSessionReset);
4239
4467
  eventBus.on(TUI_EVENTS.CONTEXT_UPDATED, onContextUpdated);
4468
+ eventBus.on(TUI_EVENTS.DISCUSSION_ROUND_ADDED, onDiscussionRoundAdded);
4469
+ eventBus.on(TUI_EVENTS.TDD_PHASE_CHANGED, onTddPhaseChanged);
4470
+ eventBus.on(TUI_EVENTS.TDD_STEP_UPDATED, onTddStepUpdated);
4471
+ eventBus.on(TUI_EVENTS.REVIEW_RESULT_ADDED, onReviewResultAdded);
4472
+ eventBus.on(TUI_EVENTS.CONNECTION_STATUS_CHANGED, onConnectionStatusChanged);
4240
4473
  return () => {
4241
4474
  eventBus.off(TUI_EVENTS.AGENT_ACTIVATED, onActivated);
4242
4475
  eventBus.off(TUI_EVENTS.AGENT_DEACTIVATED, onDeactivated);
@@ -4251,6 +4484,11 @@ function useDashboardState(eventBus) {
4251
4484
  eventBus.off(TUI_EVENTS.OBJECTIVE_SET, onObjectiveSet);
4252
4485
  eventBus.off(TUI_EVENTS.SESSION_RESET, onSessionReset);
4253
4486
  eventBus.off(TUI_EVENTS.CONTEXT_UPDATED, onContextUpdated);
4487
+ eventBus.off(TUI_EVENTS.DISCUSSION_ROUND_ADDED, onDiscussionRoundAdded);
4488
+ eventBus.off(TUI_EVENTS.TDD_PHASE_CHANGED, onTddPhaseChanged);
4489
+ eventBus.off(TUI_EVENTS.TDD_STEP_UPDATED, onTddStepUpdated);
4490
+ eventBus.off(TUI_EVENTS.REVIEW_RESULT_ADDED, onReviewResultAdded);
4491
+ eventBus.off(TUI_EVENTS.CONNECTION_STATUS_CHANGED, onConnectionStatusChanged);
4254
4492
  };
4255
4493
  }, [eventBus]);
4256
4494
  useEffect3(() => {
@@ -4490,10 +4728,596 @@ function HeaderBar({
4490
4728
  );
4491
4729
  }
4492
4730
 
4493
- // src/tui/components/FlowMap.tsx
4731
+ // src/tui/components/ModeScreenRouter.tsx
4732
+ import React7 from "react";
4733
+ import { Box as Box7, Text as Text7 } from "ink";
4734
+
4735
+ // src/tui/components/PlanModeScreen.tsx
4736
+ import React3, { useMemo as useMemo2 } from "react";
4737
+ import { Box as Box3, Text as Text3 } from "ink";
4738
+
4739
+ // src/collaboration/types.ts
4740
+ var STANCES = Object.freeze(["approve", "concern", "reject"]);
4741
+ var STANCE_ICONS = Object.freeze({
4742
+ approve: "\u2705",
4743
+ concern: "\u26A0\uFE0F",
4744
+ reject: "\u274C"
4745
+ });
4746
+ function calculateConsensus(opinions) {
4747
+ const approveCount = opinions.filter((o) => o.stance === "approve").length;
4748
+ const concernCount = opinions.filter((o) => o.stance === "concern").length;
4749
+ const rejectCount = opinions.filter((o) => o.stance === "reject").length;
4750
+ return {
4751
+ totalAgents: opinions.length,
4752
+ approveCount,
4753
+ concernCount,
4754
+ rejectCount,
4755
+ reached: opinions.length > 0 && rejectCount === 0,
4756
+ criticalCount: rejectCount
4757
+ };
4758
+ }
4759
+
4760
+ // src/tui/components/plan-screen.pure.ts
4761
+ function renderAgentSummonList(agents, width) {
4762
+ const lines = [];
4763
+ lines.push({ type: "header", text: "\u{1F4CB} Summoned Agents" });
4764
+ if (agents.size === 0) {
4765
+ lines.push({ type: "empty", text: " (no agents summoned)" });
4766
+ return lines;
4767
+ }
4768
+ for (const agent of agents.values()) {
4769
+ const statusIcon = getAgentStatusIcon(agent.status);
4770
+ const primaryTag = agent.isPrimary ? " \u2605" : "";
4771
+ const text = ` ${statusIcon} ${agent.name}${primaryTag} [${agent.status}]`;
4772
+ lines.push({ type: "agent", text: text.slice(0, width) });
4773
+ }
4774
+ return lines;
4775
+ }
4776
+ function getAgentStatusIcon(status) {
4777
+ switch (status) {
4778
+ case "running":
4779
+ return "\u{1F504}";
4780
+ case "done":
4781
+ return "\u2705";
4782
+ case "error":
4783
+ return "\u274C";
4784
+ case "idle":
4785
+ return "\u23F3";
4786
+ case "blocked":
4787
+ return "\u{1F6AB}";
4788
+ }
4789
+ }
4790
+ function renderConsensusSummary(rounds) {
4791
+ if (rounds.length === 0) {
4792
+ return [{ type: "empty", text: " (no discussion yet)" }];
4793
+ }
4794
+ const lastRound = rounds[rounds.length - 1];
4795
+ const consensus = calculateConsensus(lastRound.opinions);
4796
+ const lines = [];
4797
+ lines.push({ type: "header", text: `\u{1F5F3}\uFE0F Consensus (Round ${lastRound.roundNumber})` });
4798
+ lines.push({
4799
+ type: "consensus",
4800
+ text: ` ${STANCE_ICONS.approve} ${consensus.approveCount} ${STANCE_ICONS.concern} ${consensus.concernCount} ${STANCE_ICONS.reject} ${consensus.rejectCount}`
4801
+ });
4802
+ lines.push({
4803
+ type: "consensus",
4804
+ text: consensus.reached ? " \u2705 Consensus reached" : " \u23F3 Consensus not reached"
4805
+ });
4806
+ return lines;
4807
+ }
4808
+ function renderPlanScreen(agents, rounds, width) {
4809
+ const lines = [];
4810
+ lines.push(...renderAgentSummonList(agents, width));
4811
+ lines.push({ type: "empty", text: "" });
4812
+ lines.push(...renderConsensusSummary(rounds));
4813
+ return lines;
4814
+ }
4815
+
4816
+ // src/tui/components/AgentDiscussionPanel.tsx
4494
4817
  import React2, { useMemo } from "react";
4495
4818
  import { Box as Box2, Text as Text2 } from "ink";
4496
4819
 
4820
+ // src/tui/components/agent-discussion-panel.pure.ts
4821
+ var AGENT_PALETTE = [
4822
+ "cyan",
4823
+ "magenta",
4824
+ "yellow",
4825
+ "green",
4826
+ "blue",
4827
+ "red",
4828
+ "white"
4829
+ ];
4830
+ var CROSS_REVIEW_VERBS = {
4831
+ approve: "agrees",
4832
+ concern: "notes",
4833
+ reject: "disagrees"
4834
+ };
4835
+ function renderStanceHistory(stances) {
4836
+ if (stances.length <= 1) return "";
4837
+ return stances.map((s) => STANCE_ICONS[s]).join(" \u2192 ");
4838
+ }
4839
+ function assignAgentColors(rounds) {
4840
+ const colors = {};
4841
+ let idx = 0;
4842
+ for (const round of rounds) {
4843
+ for (const opinion of round.opinions) {
4844
+ if (!colors[opinion.agentId]) {
4845
+ colors[opinion.agentId] = AGENT_PALETTE[idx % AGENT_PALETTE.length];
4846
+ idx++;
4847
+ }
4848
+ }
4849
+ }
4850
+ return colors;
4851
+ }
4852
+ function detectConflicts(opinions) {
4853
+ const hasApprove = opinions.some((o) => o.stance === "approve");
4854
+ const hasReject = opinions.some((o) => o.stance === "reject");
4855
+ if (!hasApprove || !hasReject) return /* @__PURE__ */ new Set();
4856
+ const conflicting = /* @__PURE__ */ new Set();
4857
+ for (const opinion of opinions) {
4858
+ if (opinion.stance === "reject") {
4859
+ conflicting.add(opinion.agentId);
4860
+ }
4861
+ }
4862
+ return conflicting;
4863
+ }
4864
+ function estimateBlockHeight(block) {
4865
+ switch (block.type) {
4866
+ case "agent-bubble":
4867
+ return 4;
4868
+ // label + border-top + content + border-bottom
4869
+ case "consensus-bar":
4870
+ return 2;
4871
+ default:
4872
+ return 1;
4873
+ }
4874
+ }
4875
+ function buildAgentNameMap(rounds) {
4876
+ const map = {};
4877
+ for (const round of rounds) {
4878
+ for (const opinion of round.opinions) {
4879
+ map[opinion.agentId] = opinion.agentName;
4880
+ }
4881
+ }
4882
+ return map;
4883
+ }
4884
+ function buildStanceHistories(rounds) {
4885
+ const histories = {};
4886
+ for (const round of rounds) {
4887
+ for (const opinion of round.opinions) {
4888
+ if (!histories[opinion.agentId]) {
4889
+ histories[opinion.agentId] = [];
4890
+ }
4891
+ histories[opinion.agentId].push(opinion.stance);
4892
+ }
4893
+ }
4894
+ return histories;
4895
+ }
4896
+ function renderCollaborationBlocks(rounds, _width) {
4897
+ if (rounds.length === 0) {
4898
+ return [{ type: "empty", text: "No agent discussion yet" }];
4899
+ }
4900
+ const blocks = [];
4901
+ const agentNames = buildAgentNameMap(rounds);
4902
+ const agentColors = assignAgentColors(rounds);
4903
+ const stanceHistories = buildStanceHistories(rounds);
4904
+ const latestRound = rounds[rounds.length - 1];
4905
+ const conflicts = detectConflicts(latestRound.opinions);
4906
+ blocks.push({
4907
+ type: "header",
4908
+ text: `\u2500\u2500 Agent Discussion (Round ${latestRound.roundNumber}) \u2500\u2500`
4909
+ });
4910
+ for (const opinion of latestRound.opinions) {
4911
+ const history = stanceHistories[opinion.agentId] ?? [];
4912
+ blocks.push({
4913
+ type: "agent-bubble",
4914
+ agentName: opinion.agentName,
4915
+ agentAvatar: getAgentAvatar(opinion.agentName),
4916
+ color: agentColors[opinion.agentId] ?? "white",
4917
+ stanceIcon: STANCE_ICONS[opinion.stance],
4918
+ reasoning: opinion.reasoning,
4919
+ isConflict: conflicts.has(opinion.agentId),
4920
+ stanceHistoryText: renderStanceHistory(history)
4921
+ });
4922
+ }
4923
+ for (const review of latestRound.crossReviews) {
4924
+ const fromName = agentNames[review.fromAgentId] ?? review.fromAgentId;
4925
+ const toName = agentNames[review.toAgentId] ?? review.toAgentId;
4926
+ blocks.push({
4927
+ type: "cross-review-block",
4928
+ fromName,
4929
+ fromAvatar: getAgentAvatar(fromName),
4930
+ fromColor: agentColors[review.fromAgentId] ?? "white",
4931
+ toName,
4932
+ toAvatar: getAgentAvatar(toName),
4933
+ toColor: agentColors[review.toAgentId] ?? "white",
4934
+ verb: CROSS_REVIEW_VERBS[review.stance],
4935
+ comment: review.comment
4936
+ });
4937
+ }
4938
+ const consensus = calculateConsensus(latestRound.opinions);
4939
+ const percentage = consensus.totalAgents > 0 ? Math.round(consensus.approveCount / consensus.totalAgents * 100) : 0;
4940
+ blocks.push({
4941
+ type: "consensus-bar",
4942
+ approveCount: consensus.approveCount,
4943
+ concernCount: consensus.concernCount,
4944
+ rejectCount: consensus.rejectCount,
4945
+ totalAgents: consensus.totalAgents,
4946
+ reached: consensus.reached,
4947
+ percentage
4948
+ });
4949
+ return blocks;
4950
+ }
4951
+
4952
+ // src/tui/components/AgentDiscussionPanel.tsx
4953
+ function SpeechBubble({
4954
+ block,
4955
+ maxWidth
4956
+ }) {
4957
+ const borderColor = block.isConflict ? "red" : block.color;
4958
+ const bubbleWidth = Math.min(maxWidth, 60);
4959
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: block.color, bold: true }, block.agentAvatar, " ", block.agentName), block.isConflict && /* @__PURE__ */ React2.createElement(Text2, { color: "red", bold: true }, " ", "\u26A1"), block.stanceHistoryText !== "" && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " (", block.stanceHistoryText, ")")), /* @__PURE__ */ React2.createElement(
4960
+ Box2,
4961
+ {
4962
+ borderStyle: "round",
4963
+ borderColor,
4964
+ paddingLeft: 1,
4965
+ paddingRight: 1,
4966
+ width: bubbleWidth
4967
+ },
4968
+ /* @__PURE__ */ React2.createElement(Text2, { wrap: "truncate" }, block.stanceIcon, " ", block.reasoning)
4969
+ ));
4970
+ }
4971
+ function ConsensusProgressBar({
4972
+ block,
4973
+ maxWidth
4974
+ }) {
4975
+ const barWidth = Math.min(20, Math.max(8, maxWidth - 30));
4976
+ const total = block.totalAgents || 1;
4977
+ const approveLen = Math.round(block.approveCount / total * barWidth);
4978
+ const concernLen = Math.round(block.concernCount / total * barWidth);
4979
+ const remaining = Math.max(0, barWidth - approveLen - concernLen);
4980
+ const filled = "\u2588";
4981
+ const empty = "\u2591";
4982
+ const statusIcon = block.reached ? "\u2705" : "\u23F3";
4983
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { bold: true }, statusIcon, " Consensus "), /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, filled.repeat(approveLen)), /* @__PURE__ */ React2.createElement(Text2, { color: "yellow" }, filled.repeat(concernLen)), block.rejectCount > 0 ? /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, filled.repeat(remaining)) : /* @__PURE__ */ React2.createElement(Text2, { color: "gray" }, empty.repeat(remaining)), /* @__PURE__ */ React2.createElement(Text2, { bold: true }, " ", block.percentage, "%")), /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2705", " ", block.approveCount), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, { color: "yellow" }, "\u26A0\uFE0F", " ", block.concernCount), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u274C", " ", block.rejectCount)));
4984
+ }
4985
+ function CrossReviewItem({ block }) {
4986
+ return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: block.fromColor }, block.fromAvatar, " ", block.fromName), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React2.createElement(Text2, { color: block.toColor }, block.toAvatar, " ", block.toName), /* @__PURE__ */ React2.createElement(Text2, null, " ", block.verb, ": "), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, '"', block.comment, '"'));
4987
+ }
4988
+ function AgentDiscussionPanel({
4989
+ rounds,
4990
+ width,
4991
+ height
4992
+ }) {
4993
+ const blocks = useMemo(() => renderCollaborationBlocks(rounds, width), [rounds, width]);
4994
+ const maxHeight = Math.max(0, height - 2);
4995
+ const visibleBlocks = [];
4996
+ let usedHeight = 0;
4997
+ for (const block of blocks) {
4998
+ const h = estimateBlockHeight(block);
4999
+ if (usedHeight + h > maxHeight) break;
5000
+ visibleBlocks.push(block);
5001
+ usedHeight += h;
5002
+ }
5003
+ const innerWidth = Math.max(10, width - 4);
5004
+ return /* @__PURE__ */ React2.createElement(
5005
+ Box2,
5006
+ {
5007
+ flexDirection: "column",
5008
+ width,
5009
+ height,
5010
+ borderStyle: "round",
5011
+ borderColor: BORDER_COLORS.panel
5012
+ },
5013
+ visibleBlocks.map((block, i) => {
5014
+ switch (block.type) {
5015
+ case "header":
5016
+ return /* @__PURE__ */ React2.createElement(Text2, { key: i, color: "magenta", bold: true }, block.text);
5017
+ case "agent-bubble":
5018
+ return /* @__PURE__ */ React2.createElement(SpeechBubble, { key: i, block, maxWidth: innerWidth });
5019
+ case "cross-review-block":
5020
+ return /* @__PURE__ */ React2.createElement(CrossReviewItem, { key: i, block });
5021
+ case "consensus-bar":
5022
+ return /* @__PURE__ */ React2.createElement(ConsensusProgressBar, { key: i, block, maxWidth: innerWidth });
5023
+ case "empty":
5024
+ return /* @__PURE__ */ React2.createElement(Text2, { key: i, color: "gray", dimColor: true }, block.text);
5025
+ }
5026
+ })
5027
+ );
5028
+ }
5029
+
5030
+ // src/tui/components/PlanModeScreen.tsx
5031
+ var LINE_COLORS = {
5032
+ header: "magenta",
5033
+ agent: "white",
5034
+ discussion: "cyan",
5035
+ consensus: "green",
5036
+ empty: "gray"
5037
+ };
5038
+ function PlanModeScreen({
5039
+ agents,
5040
+ rounds,
5041
+ width,
5042
+ height
5043
+ }) {
5044
+ const summaryLines = useMemo2(
5045
+ () => renderPlanScreen(agents, rounds, width - 2),
5046
+ [agents, rounds, width]
5047
+ );
5048
+ const summaryHeight = Math.max(5, Math.floor(height * 0.4));
5049
+ const discussionHeight = height - summaryHeight;
5050
+ return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", width, height }, /* @__PURE__ */ React3.createElement(
5051
+ Box3,
5052
+ {
5053
+ flexDirection: "column",
5054
+ width,
5055
+ height: summaryHeight,
5056
+ borderStyle: "round",
5057
+ borderColor: BORDER_COLORS.panel
5058
+ },
5059
+ summaryLines.slice(0, summaryHeight - 2).map((line, i) => /* @__PURE__ */ React3.createElement(
5060
+ Text3,
5061
+ {
5062
+ key: i,
5063
+ color: LINE_COLORS[line.type],
5064
+ bold: line.type === "header" || line.type === "consensus",
5065
+ dimColor: line.type === "empty",
5066
+ wrap: "truncate"
5067
+ },
5068
+ line.text
5069
+ ))
5070
+ ), rounds.length > 0 && discussionHeight > 3 && /* @__PURE__ */ React3.createElement(AgentDiscussionPanel, { rounds, width, height: discussionHeight }));
5071
+ }
5072
+
5073
+ // src/tui/components/ActModeScreen.tsx
5074
+ import React4, { useMemo as useMemo3 } from "react";
5075
+ import { Box as Box4, Text as Text4 } from "ink";
5076
+
5077
+ // src/tui/components/act-screen.pure.ts
5078
+ var PHASE_ICONS = {
5079
+ RED: "\u{1F534}",
5080
+ GREEN: "\u{1F7E2}",
5081
+ REFACTOR: "\u{1F527}"
5082
+ };
5083
+ var PHASE_COLORS = {
5084
+ RED: "red",
5085
+ GREEN: "green",
5086
+ REFACTOR: "yellow"
5087
+ };
5088
+ function renderTddPhaseBar(currentPhase, _width) {
5089
+ const lines = [];
5090
+ lines.push({ type: "header", text: "\u{1F9EA} TDD Cycle" });
5091
+ const phases = ["RED", "GREEN", "REFACTOR"];
5092
+ const parts = phases.map((phase) => {
5093
+ const icon = PHASE_ICONS[phase];
5094
+ const active = phase === currentPhase;
5095
+ const marker = active ? "\u25BA" : " ";
5096
+ return `${marker}${icon} ${phase}`;
5097
+ });
5098
+ lines.push({ type: "phase-bar", text: ` ${parts.join(" \u2192 ")}` });
5099
+ return lines;
5100
+ }
5101
+ function renderProgressBar(percent, barWidth) {
5102
+ const filled = Math.round(percent / 100 * barWidth);
5103
+ const empty = barWidth - filled;
5104
+ return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}] ${percent}%`;
5105
+ }
5106
+ function renderTddSteps(steps, width) {
5107
+ const lines = [];
5108
+ lines.push({ type: "header", text: "\u{1F4DD} Steps" });
5109
+ if (steps.length === 0) {
5110
+ lines.push({ type: "empty", text: " (no steps yet)" });
5111
+ return lines;
5112
+ }
5113
+ for (const step of steps) {
5114
+ const statusIcon = getStepStatusIcon(step.status);
5115
+ const phaseIcon = PHASE_ICONS[step.phase];
5116
+ const agent = step.agentId ? ` [${step.agentId}]` : "";
5117
+ const text = ` ${statusIcon} ${phaseIcon} ${step.label}${agent}`;
5118
+ lines.push({ type: "step", text: text.slice(0, width) });
5119
+ }
5120
+ return lines;
5121
+ }
5122
+ function getStepStatusIcon(status) {
5123
+ switch (status) {
5124
+ case "pending":
5125
+ return "\u25CB";
5126
+ case "active":
5127
+ return "\u25CF";
5128
+ case "done":
5129
+ return "\u2713";
5130
+ case "failed":
5131
+ return "\u2717";
5132
+ }
5133
+ }
5134
+ function renderOverallProgress(agents, steps, width) {
5135
+ const lines = [];
5136
+ const totalSteps = steps.length;
5137
+ const doneSteps = steps.filter((s) => s.status === "done").length;
5138
+ const stepPercent = totalSteps > 0 ? Math.round(doneSteps / totalSteps * 100) : 0;
5139
+ const agentArr = Array.from(agents.values()).filter((a) => a.status !== "idle" || a.isParallel);
5140
+ const agentPercent = agentArr.length > 0 ? Math.round(agentArr.reduce((sum, a) => sum + a.progress, 0) / agentArr.length) : 0;
5141
+ const overallPercent = totalSteps > 0 ? stepPercent : agentPercent;
5142
+ const barWidth = Math.max(10, Math.min(40, width - 20));
5143
+ lines.push({ type: "header", text: "\u{1F4CA} Overall Progress" });
5144
+ lines.push({ type: "progress", text: ` ${renderProgressBar(overallPercent, barWidth)}` });
5145
+ if (totalSteps > 0) {
5146
+ lines.push({ type: "progress", text: ` Steps: ${doneSteps}/${totalSteps}` });
5147
+ }
5148
+ return lines;
5149
+ }
5150
+ function renderActScreen(currentPhase, steps, agents, width) {
5151
+ const lines = [];
5152
+ lines.push(...renderTddPhaseBar(currentPhase, width));
5153
+ lines.push({ type: "empty", text: "" });
5154
+ lines.push(...renderTddSteps(steps, width));
5155
+ lines.push({ type: "empty", text: "" });
5156
+ lines.push(...renderOverallProgress(agents, steps, width));
5157
+ return lines;
5158
+ }
5159
+
5160
+ // src/tui/components/ActModeScreen.tsx
5161
+ function getLineColor(line, currentPhase) {
5162
+ if (line.type === "header") return "magenta";
5163
+ if (line.type === "phase-bar" && currentPhase) return PHASE_COLORS[currentPhase];
5164
+ if (line.type === "progress") return "cyan";
5165
+ if (line.type === "empty") return "gray";
5166
+ return "white";
5167
+ }
5168
+ function ActModeScreen({
5169
+ currentPhase,
5170
+ steps,
5171
+ agents,
5172
+ width,
5173
+ height
5174
+ }) {
5175
+ const lines = useMemo3(
5176
+ () => renderActScreen(currentPhase, steps, agents, width - 2),
5177
+ [currentPhase, steps, agents, width]
5178
+ );
5179
+ const maxLines = Math.max(0, height - 2);
5180
+ const visibleLines = lines.slice(0, maxLines);
5181
+ return /* @__PURE__ */ React4.createElement(
5182
+ Box4,
5183
+ {
5184
+ flexDirection: "column",
5185
+ width,
5186
+ height,
5187
+ borderStyle: "round",
5188
+ borderColor: BORDER_COLORS.panel
5189
+ },
5190
+ visibleLines.map((line, i) => /* @__PURE__ */ React4.createElement(
5191
+ Text4,
5192
+ {
5193
+ key: i,
5194
+ color: getLineColor(line, currentPhase),
5195
+ bold: line.type === "header" || line.type === "phase-bar",
5196
+ dimColor: line.type === "empty",
5197
+ wrap: "truncate"
5198
+ },
5199
+ line.text
5200
+ ))
5201
+ );
5202
+ }
5203
+
5204
+ // src/tui/components/EvalModeScreen.tsx
5205
+ import React5, { useMemo as useMemo4 } from "react";
5206
+ import { Box as Box5, Text as Text5 } from "ink";
5207
+
5208
+ // src/tui/components/eval-screen.pure.ts
5209
+ function renderScoreBar(score, maxScore, barWidth) {
5210
+ const percent = maxScore > 0 ? Math.round(score / maxScore * 100) : 0;
5211
+ const filled = Math.round(percent / 100 * barWidth);
5212
+ const empty = barWidth - filled;
5213
+ return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}] ${score}/${maxScore}`;
5214
+ }
5215
+ function renderAgentResult(result, width) {
5216
+ const lines = [];
5217
+ const statusIcon = getResultStatusIcon(result.status);
5218
+ const barWidth = Math.max(8, Math.min(20, width - 30));
5219
+ lines.push({
5220
+ type: "agent-result",
5221
+ text: ` ${statusIcon} ${result.agentName}`
5222
+ });
5223
+ for (const cat of result.categories) {
5224
+ const bar = renderScoreBar(cat.score, cat.maxScore, barWidth);
5225
+ lines.push({
5226
+ type: "category",
5227
+ text: ` ${padRight(cat.name, 16)} ${bar}`
5228
+ });
5229
+ }
5230
+ const totalBar = renderScoreBar(result.totalScore, result.maxTotalScore, barWidth);
5231
+ lines.push({
5232
+ type: "total-score",
5233
+ text: ` ${"Total".padEnd(16)} ${totalBar}`
5234
+ });
5235
+ return lines;
5236
+ }
5237
+ function getResultStatusIcon(status) {
5238
+ switch (status) {
5239
+ case "pending":
5240
+ return "\u23F3";
5241
+ case "in-progress":
5242
+ return "\u{1F504}";
5243
+ case "done":
5244
+ return "\u2705";
5245
+ }
5246
+ }
5247
+ function padRight(str, len) {
5248
+ return str.length >= len ? str.slice(0, len) : str + " ".repeat(len - str.length);
5249
+ }
5250
+ function calculateAggregateScore(results) {
5251
+ if (results.length === 0) return { total: 0, max: 0, percent: 0 };
5252
+ const total = results.reduce((sum, r) => sum + r.totalScore, 0);
5253
+ const max = results.reduce((sum, r) => sum + r.maxTotalScore, 0);
5254
+ const percent = max > 0 ? Math.round(total / max * 100) : 0;
5255
+ return { total, max, percent };
5256
+ }
5257
+ function renderEvalScreen(results, width) {
5258
+ const lines = [];
5259
+ lines.push({ type: "header", text: "\u{1F4CA} Review Results" });
5260
+ if (results.length === 0) {
5261
+ lines.push({ type: "empty", text: " (no review results yet)" });
5262
+ return lines;
5263
+ }
5264
+ for (const result of results) {
5265
+ lines.push(...renderAgentResult(result, width));
5266
+ lines.push({ type: "empty", text: "" });
5267
+ }
5268
+ const agg = calculateAggregateScore(results);
5269
+ const barWidth = Math.max(8, Math.min(20, width - 30));
5270
+ lines.push({ type: "header", text: "\u{1F3C6} Aggregate Score" });
5271
+ lines.push({
5272
+ type: "total-score",
5273
+ text: ` ${renderScoreBar(agg.total, agg.max, barWidth)} (${agg.percent}%)`
5274
+ });
5275
+ return lines;
5276
+ }
5277
+
5278
+ // src/tui/components/EvalModeScreen.tsx
5279
+ var LINE_COLORS2 = {
5280
+ header: "magenta",
5281
+ "agent-result": "white",
5282
+ category: "cyan",
5283
+ "total-score": "green",
5284
+ empty: "gray"
5285
+ };
5286
+ function EvalModeScreen({
5287
+ results,
5288
+ width,
5289
+ height
5290
+ }) {
5291
+ const lines = useMemo4(() => renderEvalScreen(results, width - 2), [results, width]);
5292
+ const maxLines = Math.max(0, height - 2);
5293
+ const visibleLines = lines.slice(0, maxLines);
5294
+ return /* @__PURE__ */ React5.createElement(
5295
+ Box5,
5296
+ {
5297
+ flexDirection: "column",
5298
+ width,
5299
+ height,
5300
+ borderStyle: "round",
5301
+ borderColor: BORDER_COLORS.panel
5302
+ },
5303
+ visibleLines.map((line, i) => /* @__PURE__ */ React5.createElement(
5304
+ Text5,
5305
+ {
5306
+ key: i,
5307
+ color: LINE_COLORS2[line.type],
5308
+ bold: line.type === "header" || line.type === "total-score",
5309
+ dimColor: line.type === "empty",
5310
+ wrap: "truncate"
5311
+ },
5312
+ line.text
5313
+ ))
5314
+ );
5315
+ }
5316
+
5317
+ // src/tui/components/FlowMap.tsx
5318
+ import React6, { useMemo as useMemo5 } from "react";
5319
+ import { Box as Box6, Text as Text6 } from "ink";
5320
+
4497
5321
  // src/tui/utils/color-buffer.ts
4498
5322
  var DEFAULT_CELL = Object.freeze({ char: " ", style: Object.freeze({}) });
4499
5323
  function stylesEqual(a, b) {
@@ -4989,13 +5813,13 @@ function trimTrailingSpaces(cells) {
4989
5813
  }
4990
5814
  return end === cells.length ? cells : cells.slice(0, end);
4991
5815
  }
4992
- var ColorRow = React2.memo(function ColorRow2({
5816
+ var ColorRow = React6.memo(function ColorRow2({
4993
5817
  cells
4994
5818
  }) {
4995
5819
  const trimmed = trimTrailingSpaces(cells);
4996
- if (trimmed.length === 0) return /* @__PURE__ */ React2.createElement(Box2, null);
5820
+ if (trimmed.length === 0) return /* @__PURE__ */ React6.createElement(Box6, null);
4997
5821
  const segments = groupByStyle(trimmed);
4998
- return /* @__PURE__ */ React2.createElement(Box2, null, segments.map((seg, i) => /* @__PURE__ */ React2.createElement(Text2, { key: i, color: seg.style.fg, bold: seg.style.bold, dimColor: seg.style.dim }, seg.text)));
5822
+ return /* @__PURE__ */ React6.createElement(Box6, null, segments.map((seg, i) => /* @__PURE__ */ React6.createElement(Text6, { key: i, color: seg.style.fg, bold: seg.style.bold, dimColor: seg.style.dim }, seg.text)));
4999
5823
  });
5000
5824
  function FlowMap({
5001
5825
  agents,
@@ -5009,22 +5833,22 @@ function FlowMap({
5009
5833
  }) {
5010
5834
  const contentWidth = Math.max(1, width - 2);
5011
5835
  const contentHeight = Math.max(1, height - 3);
5012
- const hasRunningAgents = useMemo(
5836
+ const hasRunningAgents = useMemo5(
5013
5837
  () => [...agents.values()].some((a) => a.status === "running"),
5014
5838
  [agents]
5015
5839
  );
5016
5840
  const liveTick = hasRunningAgents ? tick ?? 0 : 0;
5017
5841
  const liveNow = hasRunningAgents ? now : void 0;
5018
- const compactContent = useMemo(() => {
5842
+ const compactContent = useMemo5(() => {
5019
5843
  if (layoutMode !== "narrow") return null;
5020
5844
  return renderFlowMapCompact(agents);
5021
5845
  }, [agents, layoutMode]);
5022
- const lines = useMemo(() => {
5846
+ const lines = useMemo5(() => {
5023
5847
  if (layoutMode === "narrow") return null;
5024
5848
  const buf = layoutMode === "wide" ? renderFlowMap(agents, edges, contentWidth, contentHeight, activeStage) : renderFlowMapSimplified(agents, contentWidth, contentHeight);
5025
5849
  return buf.toLinesDirect();
5026
5850
  }, [agents, edges, contentWidth, contentHeight, layoutMode, activeStage]);
5027
- const liveOverlays = useMemo(() => {
5851
+ const liveOverlays = useMemo5(() => {
5028
5852
  if (liveNow === void 0) return null;
5029
5853
  const overlays = [];
5030
5854
  for (const [id, agent] of agents) {
@@ -5039,8 +5863,8 @@ function FlowMap({
5039
5863
  return overlays.length > 0 ? overlays : null;
5040
5864
  }, [agents, liveTick, liveNow]);
5041
5865
  if (layoutMode === "narrow") {
5042
- return /* @__PURE__ */ React2.createElement(
5043
- Box2,
5866
+ return /* @__PURE__ */ React6.createElement(
5867
+ Box6,
5044
5868
  {
5045
5869
  borderStyle: "single",
5046
5870
  borderColor: BORDER_COLORS.panel,
@@ -5048,14 +5872,14 @@ function FlowMap({
5048
5872
  width,
5049
5873
  height
5050
5874
  },
5051
- /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "FLOW MAP"),
5052
- /* @__PURE__ */ React2.createElement(Text2, null, compactContent),
5053
- liveOverlays?.map((o) => /* @__PURE__ */ React2.createElement(Text2, { key: o.id, color: "yellow" }, o.icon, " ", o.elapsed))
5875
+ /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "FLOW MAP"),
5876
+ /* @__PURE__ */ React6.createElement(Text6, null, compactContent),
5877
+ liveOverlays?.map((o) => /* @__PURE__ */ React6.createElement(Text6, { key: o.id, color: "yellow" }, o.icon, " ", o.elapsed))
5054
5878
  );
5055
5879
  }
5056
5880
  if (!lines) {
5057
- return /* @__PURE__ */ React2.createElement(
5058
- Box2,
5881
+ return /* @__PURE__ */ React6.createElement(
5882
+ Box6,
5059
5883
  {
5060
5884
  borderStyle: "single",
5061
5885
  borderColor: BORDER_COLORS.panel,
@@ -5063,11 +5887,11 @@ function FlowMap({
5063
5887
  width,
5064
5888
  height
5065
5889
  },
5066
- /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "FLOW MAP")
5890
+ /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "FLOW MAP")
5067
5891
  );
5068
5892
  }
5069
- return /* @__PURE__ */ React2.createElement(
5070
- Box2,
5893
+ return /* @__PURE__ */ React6.createElement(
5894
+ Box6,
5071
5895
  {
5072
5896
  borderStyle: "single",
5073
5897
  borderColor: BORDER_COLORS.panel,
@@ -5075,15 +5899,90 @@ function FlowMap({
5075
5899
  width,
5076
5900
  height
5077
5901
  },
5078
- /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "FLOW MAP"),
5079
- lines.map((row, y) => /* @__PURE__ */ React2.createElement(ColorRow, { key: y, cells: row })),
5080
- liveOverlays?.map((o) => /* @__PURE__ */ React2.createElement(Text2, { key: o.id, color: "yellow" }, o.icon, " ", o.elapsed))
5902
+ /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "FLOW MAP"),
5903
+ lines.map((row, y) => /* @__PURE__ */ React6.createElement(ColorRow, { key: y, cells: row })),
5904
+ liveOverlays?.map((o) => /* @__PURE__ */ React6.createElement(Text6, { key: o.id, color: "yellow" }, o.icon, " ", o.elapsed))
5081
5905
  );
5082
5906
  }
5083
5907
 
5908
+ // src/tui/components/ModeScreenRouter.tsx
5909
+ function resolveModeScreen(mode, hasDiscussionRounds, hasTddSteps, hasReviewResults) {
5910
+ if (mode === null) return "flow";
5911
+ if (mode === "AUTO") {
5912
+ if (hasReviewResults) return "eval";
5913
+ if (hasTddSteps) return "act";
5914
+ if (hasDiscussionRounds) return "plan";
5915
+ return "plan";
5916
+ }
5917
+ switch (mode) {
5918
+ case "PLAN":
5919
+ return "plan";
5920
+ case "ACT":
5921
+ return "act";
5922
+ case "EVAL":
5923
+ return "eval";
5924
+ }
5925
+ }
5926
+ function DisconnectBanner({
5927
+ status,
5928
+ width
5929
+ }) {
5930
+ if (status === "connected") return null;
5931
+ const icon = status === "reconnecting" ? "\u{1F504}" : "\u26A0\uFE0F";
5932
+ const msg = status === "reconnecting" ? "Reconnecting..." : "Disconnected";
5933
+ return /* @__PURE__ */ React7.createElement(Box7, { width, height: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow", bold: true }, `${icon} Event Bridge: ${msg}`));
5934
+ }
5935
+ function ModeScreenRouter({
5936
+ state,
5937
+ layoutMode,
5938
+ width,
5939
+ height,
5940
+ tick,
5941
+ now
5942
+ }) {
5943
+ const bannerHeight = state.connectionStatus !== "connected" ? 1 : 0;
5944
+ const contentHeight = height - bannerHeight;
5945
+ const screen = resolveModeScreen(
5946
+ state.currentMode,
5947
+ state.discussionRounds.length > 0,
5948
+ state.tddSteps.length > 0,
5949
+ state.reviewResults.length > 0
5950
+ );
5951
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", width, height }, /* @__PURE__ */ React7.createElement(DisconnectBanner, { status: state.connectionStatus, width }), screen === "plan" && /* @__PURE__ */ React7.createElement(
5952
+ PlanModeScreen,
5953
+ {
5954
+ agents: state.agents,
5955
+ rounds: state.discussionRounds,
5956
+ width,
5957
+ height: contentHeight
5958
+ }
5959
+ ), screen === "act" && /* @__PURE__ */ React7.createElement(
5960
+ ActModeScreen,
5961
+ {
5962
+ currentPhase: state.tddCurrentPhase,
5963
+ steps: state.tddSteps,
5964
+ agents: state.agents,
5965
+ width,
5966
+ height: contentHeight
5967
+ }
5968
+ ), screen === "eval" && /* @__PURE__ */ React7.createElement(EvalModeScreen, { results: state.reviewResults, width, height: contentHeight }), screen === "flow" && /* @__PURE__ */ React7.createElement(
5969
+ FlowMap,
5970
+ {
5971
+ agents: state.agents,
5972
+ edges: state.edges,
5973
+ layoutMode,
5974
+ width,
5975
+ height: contentHeight,
5976
+ activeStage: state.currentMode,
5977
+ tick,
5978
+ now
5979
+ }
5980
+ ));
5981
+ }
5982
+
5084
5983
  // src/tui/components/FocusedAgentPanel.tsx
5085
- import React4 from "react";
5086
- import { Box as Box4, Text as Text4 } from "ink";
5984
+ import React9 from "react";
5985
+ import { Box as Box9, Text as Text9 } from "ink";
5087
5986
 
5088
5987
  // src/tui/components/focused-agent.pure.ts
5089
5988
  function formatObjective(objectives, maxLines = 3) {
@@ -5129,8 +6028,8 @@ function formatEnhancedChecklist(tasks, maxItems = 6) {
5129
6028
  }
5130
6029
 
5131
6030
  // src/tui/components/ContextSection.tsx
5132
- import React3 from "react";
5133
- import { Box as Box3, Text as Text3 } from "ink";
6031
+ import React8 from "react";
6032
+ import { Box as Box8, Text as Text8 } from "ink";
5134
6033
 
5135
6034
  // src/tui/components/context-section.pure.ts
5136
6035
  function formatContextDecisions(decisions, maxItems = 5) {
@@ -5160,27 +6059,27 @@ function ContextSection({
5160
6059
  const hasDecisions = decisions.length > 0;
5161
6060
  const hasNotes = notes.length > 0;
5162
6061
  if (!hasDecisions && !hasNotes) return null;
5163
- return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, hasDecisions && /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true, bold: true }, "Decisions"), formatContextDecisions(decisions)?.split("\n").map((line, i) => /* @__PURE__ */ React3.createElement(Text3, { key: i, color: "cyan" }, line))), hasNotes && /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true, bold: true }, "Notes"), formatContextNotes(notes)?.split("\n").map((line, i) => /* @__PURE__ */ React3.createElement(Text3, { key: i, dimColor: true }, line))));
6062
+ return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, hasDecisions && /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text8, { dimColor: true, bold: true }, "Decisions"), formatContextDecisions(decisions)?.split("\n").map((line, i) => /* @__PURE__ */ React8.createElement(Text8, { key: i, color: "cyan" }, line))), hasNotes && /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text8, { dimColor: true, bold: true }, "Notes"), formatContextNotes(notes)?.split("\n").map((line, i) => /* @__PURE__ */ React8.createElement(Text8, { key: i, dimColor: true }, line))));
5164
6063
  }
5165
6064
 
5166
6065
  // src/tui/components/FocusedAgentPanel.tsx
5167
6066
  function SectionDivider({ title }) {
5168
- return /* @__PURE__ */ React4.createElement(Text4, { color: "magenta" }, formatSectionDivider(title));
6067
+ return /* @__PURE__ */ React9.createElement(Text9, { color: "magenta" }, formatSectionDivider(title));
5169
6068
  }
5170
6069
  function LogSection({
5171
6070
  logs,
5172
6071
  eventLog
5173
6072
  }) {
5174
- if (!logs) return /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No events");
6073
+ if (!logs) return /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "No events");
5175
6074
  const logLines = logs.split("\n");
5176
6075
  const offset = eventLog.length - logLines.length;
5177
- return /* @__PURE__ */ React4.createElement(React4.Fragment, null, logLines.map((line, i) => {
6076
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, logLines.map((line, i) => {
5178
6077
  const ev = eventLog[offset + i];
5179
6078
  const isError = ev?.level === "error";
5180
6079
  const spaceIdx = line.indexOf(" ");
5181
6080
  const timestamp = spaceIdx > 0 ? line.slice(0, spaceIdx) : line;
5182
6081
  const message = spaceIdx > 0 ? line.slice(spaceIdx + 1) : "";
5183
- return /* @__PURE__ */ React4.createElement(Box4, { key: i, gap: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, timestamp), /* @__PURE__ */ React4.createElement(Text4, { color: isError ? "red" : void 0 }, message));
6082
+ return /* @__PURE__ */ React9.createElement(Box9, { key: i, gap: 1 }, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, timestamp), /* @__PURE__ */ React9.createElement(Text9, { color: isError ? "red" : void 0 }, message));
5184
6083
  }));
5185
6084
  }
5186
6085
  function FocusedAgentPanel({
@@ -5196,8 +6095,8 @@ function FocusedAgentPanel({
5196
6095
  height
5197
6096
  }) {
5198
6097
  if (!agent) {
5199
- return /* @__PURE__ */ React4.createElement(
5200
- Box4,
6098
+ return /* @__PURE__ */ React9.createElement(
6099
+ Box9,
5201
6100
  {
5202
6101
  borderStyle: "single",
5203
6102
  borderColor: BORDER_COLORS.panel,
@@ -5205,7 +6104,7 @@ function FocusedAgentPanel({
5205
6104
  width,
5206
6105
  height
5207
6106
  },
5208
- /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No agent focused")
6107
+ /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "No agent focused")
5209
6108
  );
5210
6109
  }
5211
6110
  const avatar = getAgentAvatar(agent.name);
@@ -5218,8 +6117,8 @@ function FocusedAgentPanel({
5218
6117
  const isRunning = agent.status === "running";
5219
6118
  const showSpinner = isRunning && tick != null;
5220
6119
  const showElapsed = isRunning && now != null && agent.startedAt != null;
5221
- return /* @__PURE__ */ React4.createElement(
5222
- Box4,
6120
+ return /* @__PURE__ */ React9.createElement(
6121
+ Box9,
5223
6122
  {
5224
6123
  borderStyle: "single",
5225
6124
  borderColor: BORDER_COLORS.panel,
@@ -5227,22 +6126,22 @@ function FocusedAgentPanel({
5227
6126
  width,
5228
6127
  height
5229
6128
  },
5230
- /* @__PURE__ */ React4.createElement(Box4, { gap: 2 }, /* @__PURE__ */ React4.createElement(Text4, null, avatar), /* @__PURE__ */ React4.createElement(Text4, { bold: true }, agent.name), showSpinner ? /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, spinnerFrame(tick)) : /* @__PURE__ */ React4.createElement(Text4, { color: statusColor, bold: true }, icon), /* @__PURE__ */ React4.createElement(Text4, { color: statusColor }, statusLabel), showElapsed && /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, formatElapsed(agent.startedAt, now)), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, agent.stage)),
5231
- /* @__PURE__ */ React4.createElement(Box4, { gap: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "magenta" }, progressBar.bar), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, progressBar.label)),
5232
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Objective" }),
5233
- objective ? /* @__PURE__ */ React4.createElement(Text4, null, objective) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No objectives"),
5234
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Skills" }),
5235
- activeSkills.length > 0 ? activeSkills.map((s, i) => /* @__PURE__ */ React4.createElement(Text4, { key: i }, " ", s)) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No skills"),
5236
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Context" }),
5237
- contextDecisions.length > 0 || contextNotes.length > 0 ? /* @__PURE__ */ React4.createElement(ContextSection, { decisions: contextDecisions, notes: contextNotes, width }) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "No context"),
5238
- /* @__PURE__ */ React4.createElement(SectionDivider, { title: "Event Log" }),
5239
- /* @__PURE__ */ React4.createElement(LogSection, { logs, eventLog })
6129
+ /* @__PURE__ */ React9.createElement(Box9, { gap: 2 }, /* @__PURE__ */ React9.createElement(Text9, null, avatar), /* @__PURE__ */ React9.createElement(Text9, { bold: true }, agent.name), showSpinner ? /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, spinnerFrame(tick)) : /* @__PURE__ */ React9.createElement(Text9, { color: statusColor, bold: true }, icon), /* @__PURE__ */ React9.createElement(Text9, { color: statusColor }, statusLabel), showElapsed && /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, formatElapsed(agent.startedAt, now)), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, agent.stage)),
6130
+ /* @__PURE__ */ React9.createElement(Box9, { gap: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "magenta" }, progressBar.bar), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, progressBar.label)),
6131
+ /* @__PURE__ */ React9.createElement(SectionDivider, { title: "Objective" }),
6132
+ objective ? /* @__PURE__ */ React9.createElement(Text9, null, objective) : /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "No objectives"),
6133
+ /* @__PURE__ */ React9.createElement(SectionDivider, { title: "Skills" }),
6134
+ activeSkills.length > 0 ? activeSkills.map((s, i) => /* @__PURE__ */ React9.createElement(Text9, { key: i }, " ", s)) : /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "No skills"),
6135
+ /* @__PURE__ */ React9.createElement(SectionDivider, { title: "Context" }),
6136
+ contextDecisions.length > 0 || contextNotes.length > 0 ? /* @__PURE__ */ React9.createElement(ContextSection, { decisions: contextDecisions, notes: contextNotes, width }) : /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "No context"),
6137
+ /* @__PURE__ */ React9.createElement(SectionDivider, { title: "Event Log" }),
6138
+ /* @__PURE__ */ React9.createElement(LogSection, { logs, eventLog })
5240
6139
  );
5241
6140
  }
5242
6141
 
5243
6142
  // src/tui/components/ChecklistPanel.tsx
5244
- import React5 from "react";
5245
- import { Box as Box5, Text as Text5 } from "ink";
6143
+ import React10 from "react";
6144
+ import { Box as Box10, Text as Text10 } from "ink";
5246
6145
 
5247
6146
  // src/tui/components/checklist-panel.pure.ts
5248
6147
  function resolveChecklistTasks(tasks, contextDecisions, contextNotes) {
@@ -5270,8 +6169,8 @@ function ChecklistPanel({
5270
6169
  }) {
5271
6170
  const resolvedTasks = resolveChecklistTasks(tasks, contextDecisions, contextNotes);
5272
6171
  const checklist = formatEnhancedChecklist(resolvedTasks);
5273
- return /* @__PURE__ */ React5.createElement(
5274
- Box5,
6172
+ return /* @__PURE__ */ React10.createElement(
6173
+ Box10,
5275
6174
  {
5276
6175
  borderStyle: "single",
5277
6176
  borderColor: BORDER_COLORS.panel,
@@ -5279,17 +6178,17 @@ function ChecklistPanel({
5279
6178
  width,
5280
6179
  height
5281
6180
  },
5282
- /* @__PURE__ */ React5.createElement(Text5, { bold: true, dimColor: true }, "\u2500\u2500\u2500 Checklist"),
6181
+ /* @__PURE__ */ React10.createElement(Text10, { bold: true, dimColor: true }, "\u2500\u2500\u2500 Checklist"),
5283
6182
  checklist.split("\n").map((line, i) => {
5284
6183
  const isCompleted = line.includes("\u2714");
5285
- return /* @__PURE__ */ React5.createElement(Text5, { key: i, color: isCompleted ? "green" : void 0 }, line);
6184
+ return /* @__PURE__ */ React10.createElement(Text10, { key: i, color: isCompleted ? "green" : void 0 }, line);
5286
6185
  })
5287
6186
  );
5288
6187
  }
5289
6188
 
5290
6189
  // src/tui/components/StageHealthBar.tsx
5291
- import React6 from "react";
5292
- import { Box as Box6, Text as Text6 } from "ink";
6190
+ import React11 from "react";
6191
+ import { Box as Box11, Text as Text11 } from "ink";
5293
6192
 
5294
6193
  // src/tui/components/stage-health.pure.ts
5295
6194
  function formatCount(n) {
@@ -5357,29 +6256,29 @@ function StageStatDisplay({
5357
6256
  const parts = [];
5358
6257
  if (stats.running > 0)
5359
6258
  parts.push(
5360
- /* @__PURE__ */ React6.createElement(Text6, { key: "r", color: "green" }, " ", "\u25CF ", stats.running, " running")
6259
+ /* @__PURE__ */ React11.createElement(Text11, { key: "r", color: "green" }, " ", "\u25CF ", stats.running, " running")
5361
6260
  );
5362
6261
  if (stats.blocked > 0)
5363
6262
  parts.push(
5364
- /* @__PURE__ */ React6.createElement(Text6, { key: "b", color: "yellow" }, " ", "\u23F8 ", stats.blocked, " blocked")
6263
+ /* @__PURE__ */ React11.createElement(Text11, { key: "b", color: "yellow" }, " ", "\u23F8 ", stats.blocked, " blocked")
5365
6264
  );
5366
6265
  if (stats.waiting > 0)
5367
6266
  parts.push(
5368
- /* @__PURE__ */ React6.createElement(Text6, { key: "w", dimColor: true }, " ", "\u25CB ", stats.waiting, " waiting")
6267
+ /* @__PURE__ */ React11.createElement(Text11, { key: "w", dimColor: true }, " ", "\u25CB ", stats.waiting, " waiting")
5369
6268
  );
5370
6269
  if (stats.done > 0)
5371
6270
  parts.push(
5372
- /* @__PURE__ */ React6.createElement(Text6, { key: "d", color: "green" }, " ", "\u2713 ", stats.done)
6271
+ /* @__PURE__ */ React11.createElement(Text11, { key: "d", color: "green" }, " ", "\u2713 ", stats.done)
5373
6272
  );
5374
6273
  if (stats.error > 0)
5375
6274
  parts.push(
5376
- /* @__PURE__ */ React6.createElement(Text6, { key: "e", color: "red", bold: true }, " ", "! ", stats.error, " err")
6275
+ /* @__PURE__ */ React11.createElement(Text11, { key: "e", color: "red", bold: true }, " ", "! ", stats.error, " err")
5377
6276
  );
5378
6277
  if (parts.length === 0)
5379
6278
  parts.push(
5380
- /* @__PURE__ */ React6.createElement(Text6, { key: "idle", dimColor: true }, " ", "idle")
6279
+ /* @__PURE__ */ React11.createElement(Text11, { key: "idle", dimColor: true }, " ", "idle")
5381
6280
  );
5382
- return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color, bold: true }, mode, ":"), parts);
6281
+ return /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text11, { color, bold: true }, mode, ":"), parts);
5383
6282
  }
5384
6283
  function StageHealthBar({
5385
6284
  stageHealth,
@@ -5393,24 +6292,24 @@ function StageHealthBar({
5393
6292
  now: _now
5394
6293
  }) {
5395
6294
  const hasActivity = activityHistory && activityHistory.length > 0;
5396
- return /* @__PURE__ */ React6.createElement(
5397
- Box6,
6295
+ return /* @__PURE__ */ React11.createElement(
6296
+ Box11,
5398
6297
  {
5399
6298
  borderStyle: "double",
5400
6299
  borderColor: BORDER_COLORS.panel,
5401
6300
  width,
5402
6301
  flexDirection: "column"
5403
6302
  },
5404
- /* @__PURE__ */ React6.createElement(Box6, null, hasActivity && /* @__PURE__ */ React6.createElement(Box6, { gap: 1, marginRight: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, renderSparkline(
6303
+ /* @__PURE__ */ React11.createElement(Box11, null, hasActivity && /* @__PURE__ */ React11.createElement(Box11, { gap: 1, marginRight: 1 }, /* @__PURE__ */ React11.createElement(Text11, { color: "cyan" }, renderSparkline(
5405
6304
  activityHistory.map((s) => s.toolCalls),
5406
6305
  10
5407
- )), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, computeThroughput(activityHistory))), /* @__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))))
6306
+ )), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, computeThroughput(activityHistory))), /* @__PURE__ */ React11.createElement(Box11, { gap: 2 }, ["PLAN", "ACT", "EVAL"].map((mode) => /* @__PURE__ */ React11.createElement(StageStatDisplay, { key: mode, mode, stats: stageHealth[mode] }))), /* @__PURE__ */ React11.createElement(Box11, { flexGrow: 1 }), /* @__PURE__ */ React11.createElement(Box11, { gap: 2 }, bottlenecks.length > 0 && /* @__PURE__ */ React11.createElement(Text11, { color: "red", bold: true }, "\u26A1 Bottlenecks: ", bottlenecks.join(" / ")), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "\u{1F916} ", formatCount(agentCount)), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "\u2699 ", formatCount(skillCount)), /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "\u{1F527} ", formatCount(toolCount))))
5408
6307
  );
5409
6308
  }
5410
6309
 
5411
6310
  // src/tui/components/ActivityVisualizer.tsx
5412
- import React7, { useMemo as useMemo2 } from "react";
5413
- import { Box as Box7, Text as Text7 } from "ink";
6311
+ import React12, { useMemo as useMemo6 } from "react";
6312
+ import { Box as Box12, Text as Text12 } from "ink";
5414
6313
 
5415
6314
  // src/tui/utils/display-width.ts
5416
6315
  function isWide(code) {
@@ -5579,11 +6478,11 @@ function ActivityVisualizer({
5579
6478
  const treeWidth = Math.floor(width * 0.6);
5580
6479
  const cardWidth = width - treeWidth;
5581
6480
  const contentHeight = Math.max(1, height - 2);
5582
- const treeLines = useMemo2(
6481
+ const treeLines = useMemo6(
5583
6482
  () => renderAgentTree(agents, edges, activeSkills, Math.max(1, treeWidth - 2), contentHeight),
5584
6483
  [agents, edges, activeSkills, treeWidth, contentHeight]
5585
6484
  );
5586
- const cardLines = useMemo2(
6485
+ const cardLines = useMemo6(
5587
6486
  () => renderAgentStatusCard(
5588
6487
  focusedAgent,
5589
6488
  currentMode,
@@ -5595,10 +6494,10 @@ function ActivityVisualizer({
5595
6494
  [focusedAgent, currentMode, objectives, activeSkills, cardWidth, contentHeight]
5596
6495
  );
5597
6496
  if (width <= 0 || height <= 0) {
5598
- return /* @__PURE__ */ React7.createElement(Box7, null);
6497
+ return /* @__PURE__ */ React12.createElement(Box12, null);
5599
6498
  }
5600
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "row", width, height }, /* @__PURE__ */ React7.createElement(
5601
- Box7,
6499
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "row", width, height }, /* @__PURE__ */ React12.createElement(
6500
+ Box12,
5602
6501
  {
5603
6502
  borderStyle: "single",
5604
6503
  borderColor: BORDER_COLORS.panel,
@@ -5606,9 +6505,9 @@ function ActivityVisualizer({
5606
6505
  width: treeWidth,
5607
6506
  height
5608
6507
  },
5609
- treeLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: i }, line))
5610
- ), /* @__PURE__ */ React7.createElement(
5611
- Box7,
6508
+ treeLines.map((line, i) => /* @__PURE__ */ React12.createElement(Text12, { key: i }, line))
6509
+ ), /* @__PURE__ */ React12.createElement(
6510
+ Box12,
5612
6511
  {
5613
6512
  borderStyle: "single",
5614
6513
  borderColor: BORDER_COLORS.panel,
@@ -5616,7 +6515,7 @@ function ActivityVisualizer({
5616
6515
  width: cardWidth,
5617
6516
  height
5618
6517
  },
5619
- cardLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: i }, line))
6518
+ cardLines.map((line, i) => /* @__PURE__ */ React12.createElement(Text12, { key: i }, line))
5620
6519
  ));
5621
6520
  }
5622
6521
 
@@ -5703,17 +6602,17 @@ function DashboardApp({
5703
6602
  }) {
5704
6603
  const { columns, rows, layoutMode } = useTerminalSize();
5705
6604
  const tick = useTick(1e3);
5706
- const now = useMemo3(() => Date.now(), [tick]);
6605
+ const now = useMemo7(() => Date.now(), [tick]);
5707
6606
  const internalState = useDashboardState(externalState ? void 0 : eventBus);
5708
6607
  const state = externalState ?? internalState;
5709
6608
  const focusedAgent = state.focusedAgentId ? state.agents.get(state.focusedAgentId) ?? null : null;
5710
- const stageHealth = useMemo3(() => computeStageHealth(state.agents), [state.agents]);
5711
- const bottlenecks = useMemo3(() => detectBottlenecks(state.eventLog), [state.eventLog]);
5712
- const grid = useMemo3(
6609
+ const stageHealth = useMemo7(() => computeStageHealth(state.agents), [state.agents]);
6610
+ const bottlenecks = useMemo7(() => detectBottlenecks(state.eventLog), [state.eventLog]);
6611
+ const grid = useMemo7(
5713
6612
  () => computeGridLayout(columns, rows, layoutMode),
5714
6613
  [columns, rows, layoutMode]
5715
6614
  );
5716
- return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: grid.total.width, height: grid.total.height }, /* @__PURE__ */ React8.createElement(
6615
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", width: grid.total.width, height: grid.total.height }, /* @__PURE__ */ React13.createElement(
5717
6616
  HeaderBar,
5718
6617
  {
5719
6618
  workspace: workspaceProp ?? state.workspace,
@@ -5724,7 +6623,7 @@ function DashboardApp({
5724
6623
  tick,
5725
6624
  now
5726
6625
  }
5727
- ), layoutMode === "narrow" ? /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, grid.checklistPanel.height > 0 && /* @__PURE__ */ React8.createElement(
6626
+ ), layoutMode === "narrow" ? /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column" }, grid.checklistPanel.height > 0 && /* @__PURE__ */ React13.createElement(
5728
6627
  ChecklistPanel,
5729
6628
  {
5730
6629
  tasks: state.tasks,
@@ -5733,7 +6632,7 @@ function DashboardApp({
5733
6632
  width: grid.checklistPanel.width,
5734
6633
  height: grid.checklistPanel.height
5735
6634
  }
5736
- ), /* @__PURE__ */ React8.createElement(
6635
+ ), /* @__PURE__ */ React13.createElement(
5737
6636
  FocusedAgentPanel,
5738
6637
  {
5739
6638
  agent: focusedAgent,
@@ -5747,38 +6646,34 @@ function DashboardApp({
5747
6646
  tick,
5748
6647
  now
5749
6648
  }
5750
- ), /* @__PURE__ */ React8.createElement(
5751
- FlowMap,
6649
+ ), /* @__PURE__ */ React13.createElement(
6650
+ ModeScreenRouter,
5752
6651
  {
5753
- agents: state.agents,
5754
- edges: state.edges,
6652
+ state,
5755
6653
  layoutMode,
5756
6654
  width: grid.flowMap.width,
5757
6655
  height: grid.flowMap.height,
5758
- activeStage: state.currentMode,
5759
6656
  tick,
5760
6657
  now
5761
6658
  }
5762
- )) : /* @__PURE__ */ React8.createElement(
5763
- Box8,
6659
+ )) : /* @__PURE__ */ React13.createElement(
6660
+ Box13,
5764
6661
  {
5765
6662
  flexDirection: "row",
5766
6663
  width: grid.total.width,
5767
6664
  height: grid.checklistPanel.height + grid.focusedAgent.height
5768
6665
  },
5769
- /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: grid.flowMap.width }, /* @__PURE__ */ React8.createElement(
5770
- FlowMap,
6666
+ /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", width: grid.flowMap.width }, /* @__PURE__ */ React13.createElement(
6667
+ ModeScreenRouter,
5771
6668
  {
5772
- agents: state.agents,
5773
- edges: state.edges,
6669
+ state,
5774
6670
  layoutMode,
5775
6671
  width: grid.flowMap.width,
5776
6672
  height: grid.flowMap.height,
5777
- activeStage: state.currentMode,
5778
6673
  tick,
5779
6674
  now
5780
6675
  }
5781
- ), /* @__PURE__ */ React8.createElement(
6676
+ ), /* @__PURE__ */ React13.createElement(
5782
6677
  ActivityVisualizer,
5783
6678
  {
5784
6679
  currentMode: state.currentMode,
@@ -5791,7 +6686,7 @@ function DashboardApp({
5791
6686
  height: grid.monitorPanel.height
5792
6687
  }
5793
6688
  )),
5794
- /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", width: grid.checklistPanel.width }, /* @__PURE__ */ React8.createElement(
6689
+ /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", width: grid.checklistPanel.width }, /* @__PURE__ */ React13.createElement(
5795
6690
  ChecklistPanel,
5796
6691
  {
5797
6692
  tasks: state.tasks,
@@ -5800,7 +6695,7 @@ function DashboardApp({
5800
6695
  width: grid.checklistPanel.width,
5801
6696
  height: grid.checklistPanel.height
5802
6697
  }
5803
- ), /* @__PURE__ */ React8.createElement(
6698
+ ), /* @__PURE__ */ React13.createElement(
5804
6699
  FocusedAgentPanel,
5805
6700
  {
5806
6701
  agent: focusedAgent,
@@ -5815,7 +6710,7 @@ function DashboardApp({
5815
6710
  now
5816
6711
  }
5817
6712
  ))
5818
- ), /* @__PURE__ */ React8.createElement(
6713
+ ), /* @__PURE__ */ React13.createElement(
5819
6714
  StageHealthBar,
5820
6715
  {
5821
6716
  stageHealth,
@@ -5832,8 +6727,8 @@ function DashboardApp({
5832
6727
  }
5833
6728
 
5834
6729
  // src/tui/multi-session-app.tsx
5835
- import React10, { useMemo as useMemo4, useCallback as useCallback2 } from "react";
5836
- import { Box as Box10, useInput } from "ink";
6730
+ import React15, { useMemo as useMemo8, useCallback as useCallback2 } from "react";
6731
+ import { Box as Box15, useInput } from "ink";
5837
6732
  import * as path8 from "path";
5838
6733
 
5839
6734
  // src/tui/hooks/use-multi-session-state.ts
@@ -5989,8 +6884,8 @@ function useMultiSessionState(manager) {
5989
6884
  }
5990
6885
 
5991
6886
  // src/tui/components/SessionTabBar.tsx
5992
- import React9 from "react";
5993
- import { Box as Box9, Text as Text8 } from "ink";
6887
+ import React14 from "react";
6888
+ import { Box as Box14, Text as Text13 } from "ink";
5994
6889
 
5995
6890
  // src/tui/components/session-tab-bar.pure.ts
5996
6891
  var STATUS_ICONS2 = {
@@ -6044,14 +6939,14 @@ function SessionTabBar({
6044
6939
  if (formatted === "") {
6045
6940
  return null;
6046
6941
  }
6047
- return /* @__PURE__ */ React9.createElement(Box9, { width, height: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, formatted));
6942
+ return /* @__PURE__ */ React14.createElement(Box14, { width, height: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, formatted));
6048
6943
  }
6049
6944
 
6050
6945
  // src/tui/multi-session-app.tsx
6051
6946
  function MultiSessionApp({ manager }) {
6052
6947
  const { columns, layoutMode } = useTerminalSize();
6053
6948
  const { sessions, activeSessionPid, switchNext, switchPrev, switchByIndex } = useMultiSessionState(manager);
6054
- const tabs = useMemo4(() => {
6949
+ const tabs = useMemo8(() => {
6055
6950
  let index = 0;
6056
6951
  const result = [];
6057
6952
  for (const [pid, sessionState] of sessions) {
@@ -6066,13 +6961,13 @@ function MultiSessionApp({ manager }) {
6066
6961
  }
6067
6962
  return result;
6068
6963
  }, [sessions, activeSessionPid]);
6069
- const activeEventBus = useMemo4(() => {
6964
+ const activeEventBus = useMemo8(() => {
6070
6965
  if (activeSessionPid === null) return void 0;
6071
6966
  const managedSessions = manager.getSessions();
6072
6967
  const found = managedSessions.find((s) => s.instance.pid === activeSessionPid);
6073
6968
  return found?.eventBus;
6074
6969
  }, [manager, activeSessionPid]);
6075
- const activeProjectRoot = useMemo4(() => {
6970
+ const activeProjectRoot = useMemo8(() => {
6076
6971
  if (activeSessionPid === null) return void 0;
6077
6972
  return sessions.get(activeSessionPid)?.projectRoot;
6078
6973
  }, [sessions, activeSessionPid]);
@@ -6094,7 +6989,7 @@ function MultiSessionApp({ manager }) {
6094
6989
  [switchNext, switchPrev, switchByIndex]
6095
6990
  );
6096
6991
  useInput(handleInput);
6097
- return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(SessionTabBar, { sessions: tabs, width: columns, layoutMode }), /* @__PURE__ */ React10.createElement(
6992
+ return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column" }, /* @__PURE__ */ React15.createElement(SessionTabBar, { sessions: tabs, width: columns, layoutMode }), /* @__PURE__ */ React15.createElement(
6098
6993
  DashboardApp,
6099
6994
  {
6100
6995
  key: activeSessionPid ?? "none",
@@ -6142,19 +7037,22 @@ function createDefaultAgentState(params) {
6142
7037
  // src/tui/index.tsx
6143
7038
  function startTui(options) {
6144
7039
  const renderOptions = options.stdout ? { stdout: options.stdout } : {};
6145
- return render(/* @__PURE__ */ React11.createElement(DashboardApp, { eventBus: options.eventBus }), renderOptions);
7040
+ return render(/* @__PURE__ */ React16.createElement(DashboardApp, { eventBus: options.eventBus }), renderOptions);
6146
7041
  }
6147
7042
  function renderMultiSession(options) {
6148
- return render(/* @__PURE__ */ React11.createElement(MultiSessionApp, { manager: options.manager }));
7043
+ return render(/* @__PURE__ */ React16.createElement(MultiSessionApp, { manager: options.manager }));
6149
7044
  }
6150
7045
  export {
6151
7046
  AGENT_STATUSES,
6152
7047
  DASHBOARD_NODE_STATUSES,
6153
7048
  DashboardApp,
6154
7049
  MultiSessionApp,
7050
+ TDD_PHASES,
6155
7051
  TUI_EVENT_TYPES,
6156
7052
  createDefaultAgentState,
6157
7053
  createDefaultDashboardNode,
7054
+ createDefaultReviewResult,
7055
+ createDefaultTddStep,
6158
7056
  createEmptyStageStats,
6159
7057
  getLayoutMode,
6160
7058
  renderMultiSession,