codingbuddy 5.0.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 (149) hide show
  1. package/dist/src/config/config.schema.d.ts +6 -0
  2. package/dist/src/config/config.schema.js +3 -0
  3. package/dist/src/config/config.schema.js.map +1 -1
  4. package/dist/src/context/context-archive.service.d.ts +14 -0
  5. package/dist/src/context/context-archive.service.js +257 -0
  6. package/dist/src/context/context-archive.service.js.map +1 -0
  7. package/dist/src/context/context-archive.types.d.ts +37 -0
  8. package/dist/src/context/context-archive.types.js +47 -0
  9. package/dist/src/context/context-archive.types.js.map +1 -0
  10. package/dist/src/context/context-document.service.d.ts +3 -1
  11. package/dist/src/context/context-document.service.js +18 -2
  12. package/dist/src/context/context-document.service.js.map +1 -1
  13. package/dist/src/context/context.module.js +3 -2
  14. package/dist/src/context/context.module.js.map +1 -1
  15. package/dist/src/context/index.d.ts +1 -0
  16. package/dist/src/context/index.js +1 -0
  17. package/dist/src/context/index.js.map +1 -1
  18. package/dist/src/impact/impact-event.service.d.ts +8 -0
  19. package/dist/src/impact/impact-event.service.js +53 -0
  20. package/dist/src/impact/impact-event.service.js.map +1 -0
  21. package/dist/src/impact/impact-report.service.d.ts +7 -0
  22. package/dist/src/impact/impact-report.service.js +92 -0
  23. package/dist/src/impact/impact-report.service.js.map +1 -0
  24. package/dist/src/impact/impact.module.d.ts +2 -0
  25. package/dist/src/impact/impact.module.js +22 -0
  26. package/dist/src/impact/impact.module.js.map +1 -0
  27. package/dist/src/impact/impact.types.d.ts +32 -0
  28. package/dist/src/impact/impact.types.js +5 -0
  29. package/dist/src/impact/impact.types.js.map +1 -0
  30. package/dist/src/impact/index.d.ts +4 -0
  31. package/dist/src/impact/index.js +25 -0
  32. package/dist/src/impact/index.js.map +1 -0
  33. package/dist/src/keyword/diff-analyzer.d.ts +19 -0
  34. package/dist/src/keyword/diff-analyzer.js +127 -0
  35. package/dist/src/keyword/diff-analyzer.js.map +1 -0
  36. package/dist/src/keyword/keyword.service.d.ts +1 -0
  37. package/dist/src/keyword/keyword.service.js +25 -3
  38. package/dist/src/keyword/keyword.service.js.map +1 -1
  39. package/dist/src/keyword/keyword.types.d.ts +25 -0
  40. package/dist/src/keyword/keyword.types.js +27 -0
  41. package/dist/src/keyword/keyword.types.js.map +1 -1
  42. package/dist/src/keyword/primary-agent-resolver.d.ts +2 -1
  43. package/dist/src/keyword/primary-agent-resolver.js +2 -1
  44. package/dist/src/keyword/primary-agent-resolver.js.map +1 -1
  45. package/dist/src/keyword/strategies/act-agent.strategy.js +8 -0
  46. package/dist/src/keyword/strategies/act-agent.strategy.js.map +1 -1
  47. package/dist/src/keyword/strategies/resolution-strategy.interface.d.ts +2 -0
  48. package/dist/src/keyword/visual-data.builder.d.ts +10 -0
  49. package/dist/src/keyword/visual-data.builder.js +62 -0
  50. package/dist/src/keyword/visual-data.builder.js.map +1 -0
  51. package/dist/src/mcp/handlers/agent.handler.d.ts +3 -1
  52. package/dist/src/mcp/handlers/agent.handler.js +13 -2
  53. package/dist/src/mcp/handlers/agent.handler.js.map +1 -1
  54. package/dist/src/mcp/handlers/checklist-context.handler.d.ts +3 -1
  55. package/dist/src/mcp/handlers/checklist-context.handler.js +13 -2
  56. package/dist/src/mcp/handlers/checklist-context.handler.js.map +1 -1
  57. package/dist/src/mcp/handlers/context-archive.handler.d.ts +14 -0
  58. package/dist/src/mcp/handlers/context-archive.handler.js +136 -0
  59. package/dist/src/mcp/handlers/context-archive.handler.js.map +1 -0
  60. package/dist/src/mcp/handlers/context-document.handler.d.ts +3 -1
  61. package/dist/src/mcp/handlers/context-document.handler.js +22 -2
  62. package/dist/src/mcp/handlers/context-document.handler.js.map +1 -1
  63. package/dist/src/mcp/handlers/impact.handler.d.ts +11 -0
  64. package/dist/src/mcp/handlers/impact.handler.js +53 -0
  65. package/dist/src/mcp/handlers/impact.handler.js.map +1 -0
  66. package/dist/src/mcp/handlers/index.d.ts +3 -0
  67. package/dist/src/mcp/handlers/index.js +7 -1
  68. package/dist/src/mcp/handlers/index.js.map +1 -1
  69. package/dist/src/mcp/handlers/mode.handler.d.ts +4 -1
  70. package/dist/src/mcp/handlers/mode.handler.js +51 -3
  71. package/dist/src/mcp/handlers/mode.handler.js.map +1 -1
  72. package/dist/src/mcp/handlers/rule-insights.handler.d.ts +14 -0
  73. package/dist/src/mcp/handlers/rule-insights.handler.js +78 -0
  74. package/dist/src/mcp/handlers/rule-insights.handler.js.map +1 -0
  75. package/dist/src/mcp/handlers/rules.handler.d.ts +3 -1
  76. package/dist/src/mcp/handlers/rules.handler.js +14 -2
  77. package/dist/src/mcp/handlers/rules.handler.js.map +1 -1
  78. package/dist/src/mcp/mcp.module.js +5 -0
  79. package/dist/src/mcp/mcp.module.js.map +1 -1
  80. package/dist/src/rules/rule-insights.service.d.ts +31 -0
  81. package/dist/src/rules/rule-insights.service.js +102 -0
  82. package/dist/src/rules/rule-insights.service.js.map +1 -0
  83. package/dist/src/rules/rules.module.js +3 -2
  84. package/dist/src/rules/rules.module.js.map +1 -1
  85. package/dist/src/shared/version.d.ts +1 -1
  86. package/dist/src/shared/version.js +1 -1
  87. package/dist/src/skill/i18n/keywords.types.d.ts +4 -2
  88. package/dist/src/skill/i18n/keywords.types.js +3 -0
  89. package/dist/src/skill/i18n/keywords.types.js.map +1 -1
  90. package/dist/src/tui/components/ActModeScreen.d.ts +10 -0
  91. package/dist/src/tui/components/ActModeScreen.js +25 -0
  92. package/dist/src/tui/components/ActModeScreen.js.map +1 -0
  93. package/dist/src/tui/components/AgentDiscussionPanel.js +105 -11
  94. package/dist/src/tui/components/AgentDiscussionPanel.js.map +1 -1
  95. package/dist/src/tui/components/AgentDiscussionPanel.spec.js +154 -10
  96. package/dist/src/tui/components/AgentDiscussionPanel.spec.js.map +1 -1
  97. package/dist/src/tui/components/EvalModeScreen.d.ts +8 -0
  98. package/dist/src/tui/components/EvalModeScreen.js +21 -0
  99. package/dist/src/tui/components/EvalModeScreen.js.map +1 -0
  100. package/dist/src/tui/components/FlowMap.spec.js.map +1 -1
  101. package/dist/src/tui/components/FocusedAgentPanel.js +1 -1
  102. package/dist/src/tui/components/FocusedAgentPanel.js.map +1 -1
  103. package/dist/src/tui/components/FocusedAgentPanel.spec.js +1 -3
  104. package/dist/src/tui/components/FocusedAgentPanel.spec.js.map +1 -1
  105. package/dist/src/tui/components/HeaderBar.js +3 -3
  106. package/dist/src/tui/components/HeaderBar.js.map +1 -1
  107. package/dist/src/tui/components/ModeScreenRouter.d.ts +13 -0
  108. package/dist/src/tui/components/ModeScreenRouter.js +51 -0
  109. package/dist/src/tui/components/ModeScreenRouter.js.map +1 -0
  110. package/dist/src/tui/components/PlanModeScreen.d.ts +10 -0
  111. package/dist/src/tui/components/PlanModeScreen.js +24 -0
  112. package/dist/src/tui/components/PlanModeScreen.js.map +1 -0
  113. package/dist/src/tui/components/SessionDashboard.d.ts +17 -0
  114. package/dist/src/tui/components/SessionDashboard.js +58 -0
  115. package/dist/src/tui/components/SessionDashboard.js.map +1 -0
  116. package/dist/src/tui/components/act-screen.pure.d.ts +12 -0
  117. package/dist/src/tui/components/act-screen.pure.js +93 -0
  118. package/dist/src/tui/components/act-screen.pure.js.map +1 -0
  119. package/dist/src/tui/components/agent-discussion-panel.pure.d.ts +45 -0
  120. package/dist/src/tui/components/agent-discussion-panel.pure.js +130 -23
  121. package/dist/src/tui/components/agent-discussion-panel.pure.js.map +1 -1
  122. package/dist/src/tui/components/eval-screen.pure.d.ts +13 -0
  123. package/dist/src/tui/components/eval-screen.pure.js +76 -0
  124. package/dist/src/tui/components/eval-screen.pure.js.map +1 -0
  125. package/dist/src/tui/components/index.d.ts +14 -0
  126. package/dist/src/tui/components/index.js +40 -1
  127. package/dist/src/tui/components/index.js.map +1 -1
  128. package/dist/src/tui/components/plan-screen.pure.d.ts +9 -0
  129. package/dist/src/tui/components/plan-screen.pure.js +61 -0
  130. package/dist/src/tui/components/plan-screen.pure.js.map +1 -0
  131. package/dist/src/tui/components/session-dashboard.pure.d.ts +43 -0
  132. package/dist/src/tui/components/session-dashboard.pure.js +182 -0
  133. package/dist/src/tui/components/session-dashboard.pure.js.map +1 -0
  134. package/dist/src/tui/dashboard-app.js +3 -4
  135. package/dist/src/tui/dashboard-app.js.map +1 -1
  136. package/dist/src/tui/dashboard-types.d.ts +47 -0
  137. package/dist/src/tui/dashboard-types.js +20 -1
  138. package/dist/src/tui/dashboard-types.js.map +1 -1
  139. package/dist/src/tui/events/index.d.ts +1 -1
  140. package/dist/src/tui/events/index.js.map +1 -1
  141. package/dist/src/tui/events/types.d.ts +23 -1
  142. package/dist/src/tui/events/types.js +4 -0
  143. package/dist/src/tui/events/types.js.map +1 -1
  144. package/dist/src/tui/hooks/use-dashboard-state.d.ts +13 -1
  145. package/dist/src/tui/hooks/use-dashboard-state.js +60 -1
  146. package/dist/src/tui/hooks/use-dashboard-state.js.map +1 -1
  147. package/dist/src/tui-bundle.mjs +965 -278
  148. package/dist/tsconfig.build.tsbuildinfo +1 -1
  149. package/package.json +2 -2
@@ -1371,12 +1371,12 @@ var require_eventemitter2 = __commonJS({
1371
1371
  });
1372
1372
 
1373
1373
  // src/tui/index.tsx
1374
- import React12 from "react";
1374
+ import React16 from "react";
1375
1375
  import { render } from "ink";
1376
1376
 
1377
1377
  // src/tui/dashboard-app.tsx
1378
- import React9, { useMemo as useMemo4 } from "react";
1379
- import { Box as Box9 } 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 };
@@ -1497,7 +1514,11 @@ var TUI_EVENTS = Object.freeze({
1497
1514
  OBJECTIVE_SET: "objective:set",
1498
1515
  SESSION_RESET: "session:reset",
1499
1516
  CONTEXT_UPDATED: "context:updated",
1500
- DISCUSSION_ROUND_ADDED: "discussion:round-added"
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"
1501
1522
  });
1502
1523
 
1503
1524
  // src/tui/events/parse-agent.ts
@@ -2151,6 +2172,21 @@ var SUPPORTED_LANGUAGES = {
2151
2172
  name: "Spanish",
2152
2173
  nativeName: "Espa\xF1ol",
2153
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"
2154
2190
  }
2155
2191
  };
2156
2192
  var SUPPORTED_LANGUAGE_CODES = Object.keys(
@@ -2630,11 +2666,106 @@ RulesService = __decorateClass([
2630
2666
  Injectable4()
2631
2667
  ], RulesService);
2632
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
+
2633
2764
  // src/custom/custom.module.ts
2634
2765
  import { Module } from "@nestjs/common";
2635
2766
 
2636
2767
  // src/custom/custom.service.ts
2637
- import { Injectable as Injectable5, Logger as Logger5 } from "@nestjs/common";
2768
+ import { Injectable as Injectable6, Logger as Logger5 } from "@nestjs/common";
2638
2769
  import * as fs from "fs/promises";
2639
2770
  import * as path3 from "path";
2640
2771
 
@@ -2754,7 +2885,7 @@ var CustomService = class {
2754
2885
  }
2755
2886
  };
2756
2887
  CustomService = __decorateClass([
2757
- Injectable5()
2888
+ Injectable6()
2758
2889
  ], CustomService);
2759
2890
 
2760
2891
  // src/custom/custom.module.ts
@@ -2771,7 +2902,7 @@ CustomModule = __decorateClass([
2771
2902
  import { Module as Module2 } from "@nestjs/common";
2772
2903
 
2773
2904
  // src/config/config.service.ts
2774
- import { Injectable as Injectable6, Logger as Logger6 } from "@nestjs/common";
2905
+ import { Injectable as Injectable7, Logger as Logger6 } from "@nestjs/common";
2775
2906
  import { existsSync as existsSync5, statSync } from "fs";
2776
2907
  import { stat, realpath } from "fs/promises";
2777
2908
  import * as path7 from "path";
@@ -2916,6 +3047,10 @@ var CodingBuddyConfigSchema = z3.object({
2916
3047
  projectName: z3.string().optional(),
2917
3048
  description: z3.string().optional(),
2918
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(),
2919
3054
  // Technical Configuration
2920
3055
  techStack: TechStackConfigSchema.optional(),
2921
3056
  architecture: ArchitectureConfigSchema.optional(),
@@ -3777,11 +3912,11 @@ var ConfigService = class {
3777
3912
  }
3778
3913
  };
3779
3914
  ConfigService = __decorateClass([
3780
- Injectable6()
3915
+ Injectable7()
3781
3916
  ], ConfigService);
3782
3917
 
3783
3918
  // src/config/config-diff.service.ts
3784
- import { Injectable as Injectable7 } from "@nestjs/common";
3919
+ import { Injectable as Injectable8 } from "@nestjs/common";
3785
3920
  var ConfigDiffService = class {
3786
3921
  /**
3787
3922
  * Compare project analysis with current config
@@ -3953,7 +4088,7 @@ var ConfigDiffService = class {
3953
4088
  }
3954
4089
  };
3955
4090
  ConfigDiffService = __decorateClass([
3956
- Injectable7()
4091
+ Injectable8()
3957
4092
  ], ConfigDiffService);
3958
4093
 
3959
4094
  // src/config/config.module.ts
@@ -3972,8 +4107,8 @@ var RulesModule = class {
3972
4107
  RulesModule = __decorateClass([
3973
4108
  Module3({
3974
4109
  imports: [CustomModule, CodingBuddyConfigModule],
3975
- providers: [RulesService],
3976
- exports: [RulesService]
4110
+ providers: [RulesService, RuleInsightsService],
4111
+ exports: [RulesService, RuleInsightsService]
3977
4112
  })
3978
4113
  ], RulesModule);
3979
4114
 
@@ -4037,7 +4172,14 @@ function createInitialDashboardState() {
4037
4172
  contextNotes: [],
4038
4173
  contextMode: null,
4039
4174
  contextStatus: null,
4040
- discussionRounds: []
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 }
4041
4183
  };
4042
4184
  }
4043
4185
  function cloneAgents(agents) {
@@ -4105,7 +4247,16 @@ function dashboardReducer(state, action) {
4105
4247
  }
4106
4248
  }
4107
4249
  const focusedAgentId = selectFocusedAgent(agents, state.focusedAgentId);
4108
- 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
+ };
4109
4260
  }
4110
4261
  case "AGENT_RELATIONSHIP": {
4111
4262
  const edge = {
@@ -4170,13 +4321,21 @@ function dashboardReducer(state, action) {
4170
4321
  } else {
4171
4322
  activityHistory = [...history.slice(-59), { timestamp: sec, toolCalls: 1 }];
4172
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
+ }
4173
4331
  return {
4174
4332
  ...state,
4175
4333
  agents,
4176
4334
  eventLog: [...base, entry],
4177
4335
  toolCalls: [...toolCallsBase, toolCall],
4178
4336
  activityHistory,
4179
- toolInvokeCount: state.toolInvokeCount + 1
4337
+ toolInvokeCount: state.toolInvokeCount + 1,
4338
+ fileChanges
4180
4339
  };
4181
4340
  }
4182
4341
  case "OBJECTIVE_SET":
@@ -4243,6 +4402,24 @@ function dashboardReducer(state, action) {
4243
4402
  const focusedAgentId = selectFocusedAgent(agents, state.focusedAgentId);
4244
4403
  return { ...state, agents, focusedAgentId };
4245
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
+ }
4246
4423
  // Reserved: no emitter currently produces PARALLEL_COMPLETED. Subscription kept for forward compatibility.
4247
4424
  case "PARALLEL_COMPLETED":
4248
4425
  case "AGENTS_LOADED":
@@ -4271,6 +4448,10 @@ function useDashboardState(eventBus) {
4271
4448
  const onSessionReset = (p) => dispatch({ type: "SESSION_RESET", payload: p });
4272
4449
  const onContextUpdated = (p) => dispatch({ type: "CONTEXT_UPDATED", payload: p });
4273
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 });
4274
4455
  eventBus.on(TUI_EVENTS.AGENT_ACTIVATED, onActivated);
4275
4456
  eventBus.on(TUI_EVENTS.AGENT_DEACTIVATED, onDeactivated);
4276
4457
  eventBus.on(TUI_EVENTS.MODE_CHANGED, onModeChanged);
@@ -4285,6 +4466,10 @@ function useDashboardState(eventBus) {
4285
4466
  eventBus.on(TUI_EVENTS.SESSION_RESET, onSessionReset);
4286
4467
  eventBus.on(TUI_EVENTS.CONTEXT_UPDATED, onContextUpdated);
4287
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);
4288
4473
  return () => {
4289
4474
  eventBus.off(TUI_EVENTS.AGENT_ACTIVATED, onActivated);
4290
4475
  eventBus.off(TUI_EVENTS.AGENT_DEACTIVATED, onDeactivated);
@@ -4300,6 +4485,10 @@ function useDashboardState(eventBus) {
4300
4485
  eventBus.off(TUI_EVENTS.SESSION_RESET, onSessionReset);
4301
4486
  eventBus.off(TUI_EVENTS.CONTEXT_UPDATED, onContextUpdated);
4302
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);
4303
4492
  };
4304
4493
  }, [eventBus]);
4305
4494
  useEffect3(() => {
@@ -4539,10 +4728,596 @@ function HeaderBar({
4539
4728
  );
4540
4729
  }
4541
4730
 
4542
- // 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
4543
4817
  import React2, { useMemo } from "react";
4544
4818
  import { Box as Box2, Text as Text2 } from "ink";
4545
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
+
4546
5321
  // src/tui/utils/color-buffer.ts
4547
5322
  var DEFAULT_CELL = Object.freeze({ char: " ", style: Object.freeze({}) });
4548
5323
  function stylesEqual(a, b) {
@@ -5038,13 +5813,13 @@ function trimTrailingSpaces(cells) {
5038
5813
  }
5039
5814
  return end === cells.length ? cells : cells.slice(0, end);
5040
5815
  }
5041
- var ColorRow = React2.memo(function ColorRow2({
5816
+ var ColorRow = React6.memo(function ColorRow2({
5042
5817
  cells
5043
5818
  }) {
5044
5819
  const trimmed = trimTrailingSpaces(cells);
5045
- if (trimmed.length === 0) return /* @__PURE__ */ React2.createElement(Box2, null);
5820
+ if (trimmed.length === 0) return /* @__PURE__ */ React6.createElement(Box6, null);
5046
5821
  const segments = groupByStyle(trimmed);
5047
- 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)));
5048
5823
  });
5049
5824
  function FlowMap({
5050
5825
  agents,
@@ -5058,22 +5833,22 @@ function FlowMap({
5058
5833
  }) {
5059
5834
  const contentWidth = Math.max(1, width - 2);
5060
5835
  const contentHeight = Math.max(1, height - 3);
5061
- const hasRunningAgents = useMemo(
5836
+ const hasRunningAgents = useMemo5(
5062
5837
  () => [...agents.values()].some((a) => a.status === "running"),
5063
5838
  [agents]
5064
5839
  );
5065
5840
  const liveTick = hasRunningAgents ? tick ?? 0 : 0;
5066
5841
  const liveNow = hasRunningAgents ? now : void 0;
5067
- const compactContent = useMemo(() => {
5842
+ const compactContent = useMemo5(() => {
5068
5843
  if (layoutMode !== "narrow") return null;
5069
5844
  return renderFlowMapCompact(agents);
5070
5845
  }, [agents, layoutMode]);
5071
- const lines = useMemo(() => {
5846
+ const lines = useMemo5(() => {
5072
5847
  if (layoutMode === "narrow") return null;
5073
5848
  const buf = layoutMode === "wide" ? renderFlowMap(agents, edges, contentWidth, contentHeight, activeStage) : renderFlowMapSimplified(agents, contentWidth, contentHeight);
5074
5849
  return buf.toLinesDirect();
5075
5850
  }, [agents, edges, contentWidth, contentHeight, layoutMode, activeStage]);
5076
- const liveOverlays = useMemo(() => {
5851
+ const liveOverlays = useMemo5(() => {
5077
5852
  if (liveNow === void 0) return null;
5078
5853
  const overlays = [];
5079
5854
  for (const [id, agent] of agents) {
@@ -5088,8 +5863,8 @@ function FlowMap({
5088
5863
  return overlays.length > 0 ? overlays : null;
5089
5864
  }, [agents, liveTick, liveNow]);
5090
5865
  if (layoutMode === "narrow") {
5091
- return /* @__PURE__ */ React2.createElement(
5092
- Box2,
5866
+ return /* @__PURE__ */ React6.createElement(
5867
+ Box6,
5093
5868
  {
5094
5869
  borderStyle: "single",
5095
5870
  borderColor: BORDER_COLORS.panel,
@@ -5097,14 +5872,14 @@ function FlowMap({
5097
5872
  width,
5098
5873
  height
5099
5874
  },
5100
- /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "FLOW MAP"),
5101
- /* @__PURE__ */ React2.createElement(Text2, null, compactContent),
5102
- 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))
5103
5878
  );
5104
5879
  }
5105
5880
  if (!lines) {
5106
- return /* @__PURE__ */ React2.createElement(
5107
- Box2,
5881
+ return /* @__PURE__ */ React6.createElement(
5882
+ Box6,
5108
5883
  {
5109
5884
  borderStyle: "single",
5110
5885
  borderColor: BORDER_COLORS.panel,
@@ -5112,11 +5887,11 @@ function FlowMap({
5112
5887
  width,
5113
5888
  height
5114
5889
  },
5115
- /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "FLOW MAP")
5890
+ /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "FLOW MAP")
5116
5891
  );
5117
5892
  }
5118
- return /* @__PURE__ */ React2.createElement(
5119
- Box2,
5893
+ return /* @__PURE__ */ React6.createElement(
5894
+ Box6,
5120
5895
  {
5121
5896
  borderStyle: "single",
5122
5897
  borderColor: BORDER_COLORS.panel,
@@ -5124,163 +5899,90 @@ function FlowMap({
5124
5899
  width,
5125
5900
  height
5126
5901
  },
5127
- /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "FLOW MAP"),
5128
- lines.map((row, y) => /* @__PURE__ */ React2.createElement(ColorRow, { key: y, cells: row })),
5129
- 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))
5130
5905
  );
5131
5906
  }
5132
5907
 
5133
- // src/tui/components/AgentDiscussionPanel.tsx
5134
- import React3, { useMemo as useMemo2 } from "react";
5135
- import { Box as Box3, Text as Text3 } from "ink";
5136
-
5137
- // src/collaboration/types.ts
5138
- var STANCES = Object.freeze(["approve", "concern", "reject"]);
5139
- var STANCE_ICONS = Object.freeze({
5140
- approve: "\u2705",
5141
- concern: "\u26A0\uFE0F",
5142
- reject: "\u274C"
5143
- });
5144
- function calculateConsensus(opinions) {
5145
- const approveCount = opinions.filter((o) => o.stance === "approve").length;
5146
- const concernCount = opinions.filter((o) => o.stance === "concern").length;
5147
- const rejectCount = opinions.filter((o) => o.stance === "reject").length;
5148
- return {
5149
- totalAgents: opinions.length,
5150
- approveCount,
5151
- concernCount,
5152
- rejectCount,
5153
- reached: opinions.length > 0 && rejectCount === 0,
5154
- criticalCount: rejectCount
5155
- };
5156
- }
5157
-
5158
- // src/tui/components/agent-discussion-panel.pure.ts
5159
- var CROSS_REVIEW_VERBS = {
5160
- approve: "agrees",
5161
- concern: "notes",
5162
- reject: "disagrees"
5163
- };
5164
- function renderOpinionLine(opinion, stanceHistory) {
5165
- const avatar = getAgentAvatar(opinion.agentName);
5166
- const icon = STANCE_ICONS[opinion.stance];
5167
- let suffix = "";
5168
- if (stanceHistory && stanceHistory.length > 1) {
5169
- const lastStance = stanceHistory[stanceHistory.length - 1];
5170
- const prevStance = stanceHistory[stanceHistory.length - 2];
5171
- if (lastStance !== prevStance) {
5172
- suffix = ` (${prevStance === "reject" || prevStance === "concern" ? "revised" : "alt"})`;
5173
- }
5174
- }
5175
- return {
5176
- text: `${avatar} ${opinion.agentName} ${icon} ${opinion.stance}${suffix}: "${opinion.reasoning}"`,
5177
- type: "opinion"
5178
- };
5179
- }
5180
- function renderCrossReviewLine(review, agentNames) {
5181
- const fromAvatar = getAgentAvatar(agentNames[review.fromAgentId] ?? "unknown");
5182
- const fromName = agentNames[review.fromAgentId] ?? review.fromAgentId;
5183
- const toAvatar = getAgentAvatar(agentNames[review.toAgentId] ?? "unknown");
5184
- const toName = agentNames[review.toAgentId] ?? review.toAgentId;
5185
- const verb = CROSS_REVIEW_VERBS[review.stance];
5186
- return {
5187
- text: `${fromAvatar} ${fromName} \u2192 ${toAvatar} ${toName} ${verb}: "${review.comment}"`,
5188
- type: "cross-review"
5189
- };
5190
- }
5191
- function renderConsensusLine(consensus) {
5192
- const icon = consensus.reached ? "\u2705" : "\u274C";
5193
- return {
5194
- text: `${icon} Consensus: ${consensus.approveCount}/${consensus.totalAgents} | Critical: ${consensus.criticalCount}`,
5195
- type: "consensus"
5196
- };
5197
- }
5198
- function buildAgentNameMap(rounds) {
5199
- const map = {};
5200
- for (const round of rounds) {
5201
- for (const opinion of round.opinions) {
5202
- map[opinion.agentId] = opinion.agentName;
5203
- }
5204
- }
5205
- return map;
5206
- }
5207
- function buildStanceHistories(rounds) {
5208
- const histories = {};
5209
- for (const round of rounds) {
5210
- for (const opinion of round.opinions) {
5211
- if (!histories[opinion.agentId]) {
5212
- histories[opinion.agentId] = [];
5213
- }
5214
- histories[opinion.agentId].push(opinion.stance);
5215
- }
5216
- }
5217
- return histories;
5218
- }
5219
- function renderDiscussionPanel(rounds, _width) {
5220
- if (rounds.length === 0) {
5221
- return [{ text: "No agent discussion yet", type: "empty" }];
5222
- }
5223
- const lines = [];
5224
- const agentNames = buildAgentNameMap(rounds);
5225
- const stanceHistories = buildStanceHistories(rounds);
5226
- const latestRound = rounds[rounds.length - 1];
5227
- lines.push({ text: `\u2500\u2500 Agent Discussion \u2500\u2500`, type: "header" });
5228
- for (const opinion of latestRound.opinions) {
5229
- lines.push(renderOpinionLine(opinion, stanceHistories[opinion.agentId]));
5230
- }
5231
- for (const review of latestRound.crossReviews) {
5232
- lines.push(renderCrossReviewLine(review, agentNames));
5233
- }
5234
- const consensus = calculateConsensus(latestRound.opinions);
5235
- lines.push(renderConsensusLine(consensus));
5236
- return lines;
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}`));
5237
5934
  }
5238
-
5239
- // src/tui/components/AgentDiscussionPanel.tsx
5240
- var LINE_COLORS = {
5241
- opinion: "white",
5242
- "cross-review": "cyan",
5243
- consensus: "green",
5244
- header: "magenta",
5245
- empty: "gray"
5246
- };
5247
- function AgentDiscussionPanel({
5248
- rounds,
5935
+ function ModeScreenRouter({
5936
+ state,
5937
+ layoutMode,
5249
5938
  width,
5250
- height
5939
+ height,
5940
+ tick,
5941
+ now
5251
5942
  }) {
5252
- const lines = useMemo2(
5253
- () => renderDiscussionPanel(rounds, width),
5254
- [rounds, width]
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
5255
5950
  );
5256
- const maxLines = Math.max(0, height - 2);
5257
- const visibleLines = lines.slice(0, maxLines);
5258
- return /* @__PURE__ */ React3.createElement(
5259
- Box3,
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,
5260
5953
  {
5261
- flexDirection: "column",
5954
+ agents: state.agents,
5955
+ rounds: state.discussionRounds,
5262
5956
  width,
5263
- height,
5264
- borderStyle: "round",
5265
- borderColor: BORDER_COLORS.panel
5266
- },
5267
- visibleLines.map((line, i) => /* @__PURE__ */ React3.createElement(
5268
- Text3,
5269
- {
5270
- key: i,
5271
- color: LINE_COLORS[line.type],
5272
- bold: line.type === "header" || line.type === "consensus",
5273
- dimColor: line.type === "empty",
5274
- wrap: "truncate"
5275
- },
5276
- line.text
5277
- ))
5278
- );
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
+ ));
5279
5981
  }
5280
5982
 
5281
5983
  // src/tui/components/FocusedAgentPanel.tsx
5282
- import React5 from "react";
5283
- import { Box as Box5, Text as Text5 } from "ink";
5984
+ import React9 from "react";
5985
+ import { Box as Box9, Text as Text9 } from "ink";
5284
5986
 
5285
5987
  // src/tui/components/focused-agent.pure.ts
5286
5988
  function formatObjective(objectives, maxLines = 3) {
@@ -5326,8 +6028,8 @@ function formatEnhancedChecklist(tasks, maxItems = 6) {
5326
6028
  }
5327
6029
 
5328
6030
  // src/tui/components/ContextSection.tsx
5329
- import React4 from "react";
5330
- import { Box as Box4, Text as Text4 } from "ink";
6031
+ import React8 from "react";
6032
+ import { Box as Box8, Text as Text8 } from "ink";
5331
6033
 
5332
6034
  // src/tui/components/context-section.pure.ts
5333
6035
  function formatContextDecisions(decisions, maxItems = 5) {
@@ -5357,27 +6059,27 @@ function ContextSection({
5357
6059
  const hasDecisions = decisions.length > 0;
5358
6060
  const hasNotes = notes.length > 0;
5359
6061
  if (!hasDecisions && !hasNotes) return null;
5360
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, hasDecisions && /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true, bold: true }, "Decisions"), formatContextDecisions(decisions)?.split("\n").map((line, i) => /* @__PURE__ */ React4.createElement(Text4, { key: i, color: "cyan" }, line))), hasNotes && /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true, bold: true }, "Notes"), formatContextNotes(notes)?.split("\n").map((line, i) => /* @__PURE__ */ React4.createElement(Text4, { 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))));
5361
6063
  }
5362
6064
 
5363
6065
  // src/tui/components/FocusedAgentPanel.tsx
5364
6066
  function SectionDivider({ title }) {
5365
- return /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, formatSectionDivider(title));
6067
+ return /* @__PURE__ */ React9.createElement(Text9, { color: "magenta" }, formatSectionDivider(title));
5366
6068
  }
5367
6069
  function LogSection({
5368
6070
  logs,
5369
6071
  eventLog
5370
6072
  }) {
5371
- if (!logs) return /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No events");
6073
+ if (!logs) return /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "No events");
5372
6074
  const logLines = logs.split("\n");
5373
6075
  const offset = eventLog.length - logLines.length;
5374
- return /* @__PURE__ */ React5.createElement(React5.Fragment, null, logLines.map((line, i) => {
6076
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, logLines.map((line, i) => {
5375
6077
  const ev = eventLog[offset + i];
5376
6078
  const isError = ev?.level === "error";
5377
6079
  const spaceIdx = line.indexOf(" ");
5378
6080
  const timestamp = spaceIdx > 0 ? line.slice(0, spaceIdx) : line;
5379
6081
  const message = spaceIdx > 0 ? line.slice(spaceIdx + 1) : "";
5380
- return /* @__PURE__ */ React5.createElement(Box5, { key: i, gap: 1 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, timestamp), /* @__PURE__ */ React5.createElement(Text5, { 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));
5381
6083
  }));
5382
6084
  }
5383
6085
  function FocusedAgentPanel({
@@ -5393,8 +6095,8 @@ function FocusedAgentPanel({
5393
6095
  height
5394
6096
  }) {
5395
6097
  if (!agent) {
5396
- return /* @__PURE__ */ React5.createElement(
5397
- Box5,
6098
+ return /* @__PURE__ */ React9.createElement(
6099
+ Box9,
5398
6100
  {
5399
6101
  borderStyle: "single",
5400
6102
  borderColor: BORDER_COLORS.panel,
@@ -5402,7 +6104,7 @@ function FocusedAgentPanel({
5402
6104
  width,
5403
6105
  height
5404
6106
  },
5405
- /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No agent focused")
6107
+ /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "No agent focused")
5406
6108
  );
5407
6109
  }
5408
6110
  const avatar = getAgentAvatar(agent.name);
@@ -5415,8 +6117,8 @@ function FocusedAgentPanel({
5415
6117
  const isRunning = agent.status === "running";
5416
6118
  const showSpinner = isRunning && tick != null;
5417
6119
  const showElapsed = isRunning && now != null && agent.startedAt != null;
5418
- return /* @__PURE__ */ React5.createElement(
5419
- Box5,
6120
+ return /* @__PURE__ */ React9.createElement(
6121
+ Box9,
5420
6122
  {
5421
6123
  borderStyle: "single",
5422
6124
  borderColor: BORDER_COLORS.panel,
@@ -5424,22 +6126,22 @@ function FocusedAgentPanel({
5424
6126
  width,
5425
6127
  height
5426
6128
  },
5427
- /* @__PURE__ */ React5.createElement(Box5, { gap: 2 }, /* @__PURE__ */ React5.createElement(Text5, null, avatar), /* @__PURE__ */ React5.createElement(Text5, { bold: true }, agent.name), showSpinner ? /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, spinnerFrame(tick)) : /* @__PURE__ */ React5.createElement(Text5, { color: statusColor, bold: true }, icon), /* @__PURE__ */ React5.createElement(Text5, { color: statusColor }, statusLabel), showElapsed && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, formatElapsed(agent.startedAt, now)), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, agent.stage)),
5428
- /* @__PURE__ */ React5.createElement(Box5, { gap: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, progressBar.bar), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, progressBar.label)),
5429
- /* @__PURE__ */ React5.createElement(SectionDivider, { title: "Objective" }),
5430
- objective ? /* @__PURE__ */ React5.createElement(Text5, null, objective) : /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No objectives"),
5431
- /* @__PURE__ */ React5.createElement(SectionDivider, { title: "Skills" }),
5432
- activeSkills.length > 0 ? activeSkills.map((s, i) => /* @__PURE__ */ React5.createElement(Text5, { key: i }, " ", s)) : /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No skills"),
5433
- /* @__PURE__ */ React5.createElement(SectionDivider, { title: "Context" }),
5434
- contextDecisions.length > 0 || contextNotes.length > 0 ? /* @__PURE__ */ React5.createElement(ContextSection, { decisions: contextDecisions, notes: contextNotes, width }) : /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "No context"),
5435
- /* @__PURE__ */ React5.createElement(SectionDivider, { title: "Event Log" }),
5436
- /* @__PURE__ */ React5.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 })
5437
6139
  );
5438
6140
  }
5439
6141
 
5440
6142
  // src/tui/components/ChecklistPanel.tsx
5441
- import React6 from "react";
5442
- import { Box as Box6, Text as Text6 } from "ink";
6143
+ import React10 from "react";
6144
+ import { Box as Box10, Text as Text10 } from "ink";
5443
6145
 
5444
6146
  // src/tui/components/checklist-panel.pure.ts
5445
6147
  function resolveChecklistTasks(tasks, contextDecisions, contextNotes) {
@@ -5467,8 +6169,8 @@ function ChecklistPanel({
5467
6169
  }) {
5468
6170
  const resolvedTasks = resolveChecklistTasks(tasks, contextDecisions, contextNotes);
5469
6171
  const checklist = formatEnhancedChecklist(resolvedTasks);
5470
- return /* @__PURE__ */ React6.createElement(
5471
- Box6,
6172
+ return /* @__PURE__ */ React10.createElement(
6173
+ Box10,
5472
6174
  {
5473
6175
  borderStyle: "single",
5474
6176
  borderColor: BORDER_COLORS.panel,
@@ -5476,17 +6178,17 @@ function ChecklistPanel({
5476
6178
  width,
5477
6179
  height
5478
6180
  },
5479
- /* @__PURE__ */ React6.createElement(Text6, { bold: true, dimColor: true }, "\u2500\u2500\u2500 Checklist"),
6181
+ /* @__PURE__ */ React10.createElement(Text10, { bold: true, dimColor: true }, "\u2500\u2500\u2500 Checklist"),
5480
6182
  checklist.split("\n").map((line, i) => {
5481
6183
  const isCompleted = line.includes("\u2714");
5482
- return /* @__PURE__ */ React6.createElement(Text6, { key: i, color: isCompleted ? "green" : void 0 }, line);
6184
+ return /* @__PURE__ */ React10.createElement(Text10, { key: i, color: isCompleted ? "green" : void 0 }, line);
5483
6185
  })
5484
6186
  );
5485
6187
  }
5486
6188
 
5487
6189
  // src/tui/components/StageHealthBar.tsx
5488
- import React7 from "react";
5489
- import { Box as Box7, Text as Text7 } from "ink";
6190
+ import React11 from "react";
6191
+ import { Box as Box11, Text as Text11 } from "ink";
5490
6192
 
5491
6193
  // src/tui/components/stage-health.pure.ts
5492
6194
  function formatCount(n) {
@@ -5554,29 +6256,29 @@ function StageStatDisplay({
5554
6256
  const parts = [];
5555
6257
  if (stats.running > 0)
5556
6258
  parts.push(
5557
- /* @__PURE__ */ React7.createElement(Text7, { key: "r", color: "green" }, " ", "\u25CF ", stats.running, " running")
6259
+ /* @__PURE__ */ React11.createElement(Text11, { key: "r", color: "green" }, " ", "\u25CF ", stats.running, " running")
5558
6260
  );
5559
6261
  if (stats.blocked > 0)
5560
6262
  parts.push(
5561
- /* @__PURE__ */ React7.createElement(Text7, { key: "b", color: "yellow" }, " ", "\u23F8 ", stats.blocked, " blocked")
6263
+ /* @__PURE__ */ React11.createElement(Text11, { key: "b", color: "yellow" }, " ", "\u23F8 ", stats.blocked, " blocked")
5562
6264
  );
5563
6265
  if (stats.waiting > 0)
5564
6266
  parts.push(
5565
- /* @__PURE__ */ React7.createElement(Text7, { key: "w", dimColor: true }, " ", "\u25CB ", stats.waiting, " waiting")
6267
+ /* @__PURE__ */ React11.createElement(Text11, { key: "w", dimColor: true }, " ", "\u25CB ", stats.waiting, " waiting")
5566
6268
  );
5567
6269
  if (stats.done > 0)
5568
6270
  parts.push(
5569
- /* @__PURE__ */ React7.createElement(Text7, { key: "d", color: "green" }, " ", "\u2713 ", stats.done)
6271
+ /* @__PURE__ */ React11.createElement(Text11, { key: "d", color: "green" }, " ", "\u2713 ", stats.done)
5570
6272
  );
5571
6273
  if (stats.error > 0)
5572
6274
  parts.push(
5573
- /* @__PURE__ */ React7.createElement(Text7, { key: "e", color: "red", bold: true }, " ", "! ", stats.error, " err")
6275
+ /* @__PURE__ */ React11.createElement(Text11, { key: "e", color: "red", bold: true }, " ", "! ", stats.error, " err")
5574
6276
  );
5575
6277
  if (parts.length === 0)
5576
6278
  parts.push(
5577
- /* @__PURE__ */ React7.createElement(Text7, { key: "idle", dimColor: true }, " ", "idle")
6279
+ /* @__PURE__ */ React11.createElement(Text11, { key: "idle", dimColor: true }, " ", "idle")
5578
6280
  );
5579
- return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color, bold: true }, mode, ":"), parts);
6281
+ return /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text11, { color, bold: true }, mode, ":"), parts);
5580
6282
  }
5581
6283
  function StageHealthBar({
5582
6284
  stageHealth,
@@ -5590,24 +6292,24 @@ function StageHealthBar({
5590
6292
  now: _now
5591
6293
  }) {
5592
6294
  const hasActivity = activityHistory && activityHistory.length > 0;
5593
- return /* @__PURE__ */ React7.createElement(
5594
- Box7,
6295
+ return /* @__PURE__ */ React11.createElement(
6296
+ Box11,
5595
6297
  {
5596
6298
  borderStyle: "double",
5597
6299
  borderColor: BORDER_COLORS.panel,
5598
6300
  width,
5599
6301
  flexDirection: "column"
5600
6302
  },
5601
- /* @__PURE__ */ React7.createElement(Box7, null, hasActivity && /* @__PURE__ */ React7.createElement(Box7, { gap: 1, marginRight: 1 }, /* @__PURE__ */ React7.createElement(Text7, { 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(
5602
6304
  activityHistory.map((s) => s.toolCalls),
5603
6305
  10
5604
- )), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, computeThroughput(activityHistory))), /* @__PURE__ */ React7.createElement(Box7, { gap: 2 }, ["PLAN", "ACT", "EVAL"].map((mode) => /* @__PURE__ */ React7.createElement(StageStatDisplay, { key: mode, mode, stats: stageHealth[mode] }))), /* @__PURE__ */ React7.createElement(Box7, { flexGrow: 1 }), /* @__PURE__ */ React7.createElement(Box7, { gap: 2 }, bottlenecks.length > 0 && /* @__PURE__ */ React7.createElement(Text7, { color: "red", bold: true }, "\u26A1 Bottlenecks: ", bottlenecks.join(" / ")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "\u{1F916} ", formatCount(agentCount)), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "\u2699 ", formatCount(skillCount)), /* @__PURE__ */ React7.createElement(Text7, { 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))))
5605
6307
  );
5606
6308
  }
5607
6309
 
5608
6310
  // src/tui/components/ActivityVisualizer.tsx
5609
- import React8, { useMemo as useMemo3 } from "react";
5610
- import { Box as Box8, Text as Text8 } from "ink";
6311
+ import React12, { useMemo as useMemo6 } from "react";
6312
+ import { Box as Box12, Text as Text12 } from "ink";
5611
6313
 
5612
6314
  // src/tui/utils/display-width.ts
5613
6315
  function isWide(code) {
@@ -5776,11 +6478,11 @@ function ActivityVisualizer({
5776
6478
  const treeWidth = Math.floor(width * 0.6);
5777
6479
  const cardWidth = width - treeWidth;
5778
6480
  const contentHeight = Math.max(1, height - 2);
5779
- const treeLines = useMemo3(
6481
+ const treeLines = useMemo6(
5780
6482
  () => renderAgentTree(agents, edges, activeSkills, Math.max(1, treeWidth - 2), contentHeight),
5781
6483
  [agents, edges, activeSkills, treeWidth, contentHeight]
5782
6484
  );
5783
- const cardLines = useMemo3(
6485
+ const cardLines = useMemo6(
5784
6486
  () => renderAgentStatusCard(
5785
6487
  focusedAgent,
5786
6488
  currentMode,
@@ -5792,10 +6494,10 @@ function ActivityVisualizer({
5792
6494
  [focusedAgent, currentMode, objectives, activeSkills, cardWidth, contentHeight]
5793
6495
  );
5794
6496
  if (width <= 0 || height <= 0) {
5795
- return /* @__PURE__ */ React8.createElement(Box8, null);
6497
+ return /* @__PURE__ */ React12.createElement(Box12, null);
5796
6498
  }
5797
- return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "row", width, height }, /* @__PURE__ */ React8.createElement(
5798
- Box8,
6499
+ return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "row", width, height }, /* @__PURE__ */ React12.createElement(
6500
+ Box12,
5799
6501
  {
5800
6502
  borderStyle: "single",
5801
6503
  borderColor: BORDER_COLORS.panel,
@@ -5803,9 +6505,9 @@ function ActivityVisualizer({
5803
6505
  width: treeWidth,
5804
6506
  height
5805
6507
  },
5806
- treeLines.map((line, i) => /* @__PURE__ */ React8.createElement(Text8, { key: i }, line))
5807
- ), /* @__PURE__ */ React8.createElement(
5808
- Box8,
6508
+ treeLines.map((line, i) => /* @__PURE__ */ React12.createElement(Text12, { key: i }, line))
6509
+ ), /* @__PURE__ */ React12.createElement(
6510
+ Box12,
5809
6511
  {
5810
6512
  borderStyle: "single",
5811
6513
  borderColor: BORDER_COLORS.panel,
@@ -5813,7 +6515,7 @@ function ActivityVisualizer({
5813
6515
  width: cardWidth,
5814
6516
  height
5815
6517
  },
5816
- cardLines.map((line, i) => /* @__PURE__ */ React8.createElement(Text8, { key: i }, line))
6518
+ cardLines.map((line, i) => /* @__PURE__ */ React12.createElement(Text12, { key: i }, line))
5817
6519
  ));
5818
6520
  }
5819
6521
 
@@ -5900,17 +6602,17 @@ function DashboardApp({
5900
6602
  }) {
5901
6603
  const { columns, rows, layoutMode } = useTerminalSize();
5902
6604
  const tick = useTick(1e3);
5903
- const now = useMemo4(() => Date.now(), [tick]);
6605
+ const now = useMemo7(() => Date.now(), [tick]);
5904
6606
  const internalState = useDashboardState(externalState ? void 0 : eventBus);
5905
6607
  const state = externalState ?? internalState;
5906
6608
  const focusedAgent = state.focusedAgentId ? state.agents.get(state.focusedAgentId) ?? null : null;
5907
- const stageHealth = useMemo4(() => computeStageHealth(state.agents), [state.agents]);
5908
- const bottlenecks = useMemo4(() => detectBottlenecks(state.eventLog), [state.eventLog]);
5909
- const grid = useMemo4(
6609
+ const stageHealth = useMemo7(() => computeStageHealth(state.agents), [state.agents]);
6610
+ const bottlenecks = useMemo7(() => detectBottlenecks(state.eventLog), [state.eventLog]);
6611
+ const grid = useMemo7(
5910
6612
  () => computeGridLayout(columns, rows, layoutMode),
5911
6613
  [columns, rows, layoutMode]
5912
6614
  );
5913
- return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", width: grid.total.width, height: grid.total.height }, /* @__PURE__ */ React9.createElement(
6615
+ return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", width: grid.total.width, height: grid.total.height }, /* @__PURE__ */ React13.createElement(
5914
6616
  HeaderBar,
5915
6617
  {
5916
6618
  workspace: workspaceProp ?? state.workspace,
@@ -5921,7 +6623,7 @@ function DashboardApp({
5921
6623
  tick,
5922
6624
  now
5923
6625
  }
5924
- ), layoutMode === "narrow" ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, grid.checklistPanel.height > 0 && /* @__PURE__ */ React9.createElement(
6626
+ ), layoutMode === "narrow" ? /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column" }, grid.checklistPanel.height > 0 && /* @__PURE__ */ React13.createElement(
5925
6627
  ChecklistPanel,
5926
6628
  {
5927
6629
  tasks: state.tasks,
@@ -5930,7 +6632,7 @@ function DashboardApp({
5930
6632
  width: grid.checklistPanel.width,
5931
6633
  height: grid.checklistPanel.height
5932
6634
  }
5933
- ), /* @__PURE__ */ React9.createElement(
6635
+ ), /* @__PURE__ */ React13.createElement(
5934
6636
  FocusedAgentPanel,
5935
6637
  {
5936
6638
  agent: focusedAgent,
@@ -5944,52 +6646,34 @@ function DashboardApp({
5944
6646
  tick,
5945
6647
  now
5946
6648
  }
5947
- ), state.discussionRounds.length > 0 ? /* @__PURE__ */ React9.createElement(
5948
- AgentDiscussionPanel,
6649
+ ), /* @__PURE__ */ React13.createElement(
6650
+ ModeScreenRouter,
5949
6651
  {
5950
- rounds: state.discussionRounds,
5951
- width: grid.flowMap.width,
5952
- height: grid.flowMap.height
5953
- }
5954
- ) : /* @__PURE__ */ React9.createElement(
5955
- FlowMap,
5956
- {
5957
- agents: state.agents,
5958
- edges: state.edges,
6652
+ state,
5959
6653
  layoutMode,
5960
6654
  width: grid.flowMap.width,
5961
6655
  height: grid.flowMap.height,
5962
- activeStage: state.currentMode,
5963
6656
  tick,
5964
6657
  now
5965
6658
  }
5966
- )) : /* @__PURE__ */ React9.createElement(
5967
- Box9,
6659
+ )) : /* @__PURE__ */ React13.createElement(
6660
+ Box13,
5968
6661
  {
5969
6662
  flexDirection: "row",
5970
6663
  width: grid.total.width,
5971
6664
  height: grid.checklistPanel.height + grid.focusedAgent.height
5972
6665
  },
5973
- /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", width: grid.flowMap.width }, state.discussionRounds.length > 0 ? /* @__PURE__ */ React9.createElement(
5974
- AgentDiscussionPanel,
5975
- {
5976
- rounds: state.discussionRounds,
5977
- width: grid.flowMap.width,
5978
- height: grid.flowMap.height
5979
- }
5980
- ) : /* @__PURE__ */ React9.createElement(
5981
- FlowMap,
6666
+ /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", width: grid.flowMap.width }, /* @__PURE__ */ React13.createElement(
6667
+ ModeScreenRouter,
5982
6668
  {
5983
- agents: state.agents,
5984
- edges: state.edges,
6669
+ state,
5985
6670
  layoutMode,
5986
6671
  width: grid.flowMap.width,
5987
6672
  height: grid.flowMap.height,
5988
- activeStage: state.currentMode,
5989
6673
  tick,
5990
6674
  now
5991
6675
  }
5992
- ), /* @__PURE__ */ React9.createElement(
6676
+ ), /* @__PURE__ */ React13.createElement(
5993
6677
  ActivityVisualizer,
5994
6678
  {
5995
6679
  currentMode: state.currentMode,
@@ -6002,7 +6686,7 @@ function DashboardApp({
6002
6686
  height: grid.monitorPanel.height
6003
6687
  }
6004
6688
  )),
6005
- /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", width: grid.checklistPanel.width }, /* @__PURE__ */ React9.createElement(
6689
+ /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", width: grid.checklistPanel.width }, /* @__PURE__ */ React13.createElement(
6006
6690
  ChecklistPanel,
6007
6691
  {
6008
6692
  tasks: state.tasks,
@@ -6011,7 +6695,7 @@ function DashboardApp({
6011
6695
  width: grid.checklistPanel.width,
6012
6696
  height: grid.checklistPanel.height
6013
6697
  }
6014
- ), /* @__PURE__ */ React9.createElement(
6698
+ ), /* @__PURE__ */ React13.createElement(
6015
6699
  FocusedAgentPanel,
6016
6700
  {
6017
6701
  agent: focusedAgent,
@@ -6026,7 +6710,7 @@ function DashboardApp({
6026
6710
  now
6027
6711
  }
6028
6712
  ))
6029
- ), /* @__PURE__ */ React9.createElement(
6713
+ ), /* @__PURE__ */ React13.createElement(
6030
6714
  StageHealthBar,
6031
6715
  {
6032
6716
  stageHealth,
@@ -6043,8 +6727,8 @@ function DashboardApp({
6043
6727
  }
6044
6728
 
6045
6729
  // src/tui/multi-session-app.tsx
6046
- import React11, { useMemo as useMemo5, useCallback as useCallback2 } from "react";
6047
- import { Box as Box11, useInput } from "ink";
6730
+ import React15, { useMemo as useMemo8, useCallback as useCallback2 } from "react";
6731
+ import { Box as Box15, useInput } from "ink";
6048
6732
  import * as path8 from "path";
6049
6733
 
6050
6734
  // src/tui/hooks/use-multi-session-state.ts
@@ -6200,8 +6884,8 @@ function useMultiSessionState(manager) {
6200
6884
  }
6201
6885
 
6202
6886
  // src/tui/components/SessionTabBar.tsx
6203
- import React10 from "react";
6204
- import { Box as Box10, Text as Text9 } from "ink";
6887
+ import React14 from "react";
6888
+ import { Box as Box14, Text as Text13 } from "ink";
6205
6889
 
6206
6890
  // src/tui/components/session-tab-bar.pure.ts
6207
6891
  var STATUS_ICONS2 = {
@@ -6255,14 +6939,14 @@ function SessionTabBar({
6255
6939
  if (formatted === "") {
6256
6940
  return null;
6257
6941
  }
6258
- return /* @__PURE__ */ React10.createElement(Box10, { width, height: 1 }, /* @__PURE__ */ React10.createElement(Text9, null, formatted));
6942
+ return /* @__PURE__ */ React14.createElement(Box14, { width, height: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, formatted));
6259
6943
  }
6260
6944
 
6261
6945
  // src/tui/multi-session-app.tsx
6262
6946
  function MultiSessionApp({ manager }) {
6263
6947
  const { columns, layoutMode } = useTerminalSize();
6264
6948
  const { sessions, activeSessionPid, switchNext, switchPrev, switchByIndex } = useMultiSessionState(manager);
6265
- const tabs = useMemo5(() => {
6949
+ const tabs = useMemo8(() => {
6266
6950
  let index = 0;
6267
6951
  const result = [];
6268
6952
  for (const [pid, sessionState] of sessions) {
@@ -6277,13 +6961,13 @@ function MultiSessionApp({ manager }) {
6277
6961
  }
6278
6962
  return result;
6279
6963
  }, [sessions, activeSessionPid]);
6280
- const activeEventBus = useMemo5(() => {
6964
+ const activeEventBus = useMemo8(() => {
6281
6965
  if (activeSessionPid === null) return void 0;
6282
6966
  const managedSessions = manager.getSessions();
6283
6967
  const found = managedSessions.find((s) => s.instance.pid === activeSessionPid);
6284
6968
  return found?.eventBus;
6285
6969
  }, [manager, activeSessionPid]);
6286
- const activeProjectRoot = useMemo5(() => {
6970
+ const activeProjectRoot = useMemo8(() => {
6287
6971
  if (activeSessionPid === null) return void 0;
6288
6972
  return sessions.get(activeSessionPid)?.projectRoot;
6289
6973
  }, [sessions, activeSessionPid]);
@@ -6305,7 +6989,7 @@ function MultiSessionApp({ manager }) {
6305
6989
  [switchNext, switchPrev, switchByIndex]
6306
6990
  );
6307
6991
  useInput(handleInput);
6308
- return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(SessionTabBar, { sessions: tabs, width: columns, layoutMode }), /* @__PURE__ */ React11.createElement(
6992
+ return /* @__PURE__ */ React15.createElement(Box15, { flexDirection: "column" }, /* @__PURE__ */ React15.createElement(SessionTabBar, { sessions: tabs, width: columns, layoutMode }), /* @__PURE__ */ React15.createElement(
6309
6993
  DashboardApp,
6310
6994
  {
6311
6995
  key: activeSessionPid ?? "none",
@@ -6353,19 +7037,22 @@ function createDefaultAgentState(params) {
6353
7037
  // src/tui/index.tsx
6354
7038
  function startTui(options) {
6355
7039
  const renderOptions = options.stdout ? { stdout: options.stdout } : {};
6356
- return render(/* @__PURE__ */ React12.createElement(DashboardApp, { eventBus: options.eventBus }), renderOptions);
7040
+ return render(/* @__PURE__ */ React16.createElement(DashboardApp, { eventBus: options.eventBus }), renderOptions);
6357
7041
  }
6358
7042
  function renderMultiSession(options) {
6359
- return render(/* @__PURE__ */ React12.createElement(MultiSessionApp, { manager: options.manager }));
7043
+ return render(/* @__PURE__ */ React16.createElement(MultiSessionApp, { manager: options.manager }));
6360
7044
  }
6361
7045
  export {
6362
7046
  AGENT_STATUSES,
6363
7047
  DASHBOARD_NODE_STATUSES,
6364
7048
  DashboardApp,
6365
7049
  MultiSessionApp,
7050
+ TDD_PHASES,
6366
7051
  TUI_EVENT_TYPES,
6367
7052
  createDefaultAgentState,
6368
7053
  createDefaultDashboardNode,
7054
+ createDefaultReviewResult,
7055
+ createDefaultTddStep,
6369
7056
  createEmptyStageStats,
6370
7057
  getLayoutMode,
6371
7058
  renderMultiSession,