aimux-cli 0.1.4 → 0.1.6

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 (166) hide show
  1. package/dist/builtin-metadata-watchers.js +1 -1
  2. package/dist/context/context-bridge.d.ts +1 -1
  3. package/dist/context/context-bridge.js +1 -1
  4. package/dist/dashboard/command-spec.d.ts +7 -0
  5. package/dist/dashboard/command-spec.js +127 -0
  6. package/dist/dashboard/command-spec.js.map +1 -0
  7. package/dist/{dashboard-feedback.js → dashboard/feedback.js} +1 -1
  8. package/dist/dashboard/feedback.js.map +1 -0
  9. package/dist/{dashboard.d.ts → dashboard/index.d.ts} +16 -5
  10. package/dist/{dashboard.js → dashboard/index.js} +7 -4
  11. package/dist/dashboard/index.js.map +1 -0
  12. package/dist/{dashboard-pending-actions.d.ts → dashboard/pending-actions.d.ts} +8 -3
  13. package/dist/dashboard/pending-actions.js +90 -0
  14. package/dist/dashboard/pending-actions.js.map +1 -0
  15. package/dist/{dashboard-session-actions.d.ts → dashboard/session-actions.d.ts} +2 -2
  16. package/dist/{dashboard-session-actions.js → dashboard/session-actions.js} +1 -1
  17. package/dist/dashboard/session-actions.js.map +1 -0
  18. package/dist/{dashboard-session-registry.d.ts → dashboard/session-registry.d.ts} +7 -7
  19. package/dist/{dashboard-session-registry.js → dashboard/session-registry.js} +2 -2
  20. package/dist/dashboard/session-registry.js.map +1 -0
  21. package/dist/{dashboard-state.d.ts → dashboard/state.d.ts} +1 -1
  22. package/dist/{dashboard-state.js → dashboard/state.js} +1 -1
  23. package/dist/dashboard/state.js.map +1 -0
  24. package/dist/{dashboard-targets.d.ts → dashboard/targets.d.ts} +1 -6
  25. package/dist/{dashboard-targets.js → dashboard/targets.js} +5 -123
  26. package/dist/dashboard/targets.js.map +1 -0
  27. package/dist/{dashboard-ui-state-store.d.ts → dashboard/ui-state-store.d.ts} +2 -2
  28. package/dist/{dashboard-ui-state-store.js → dashboard/ui-state-store.js} +2 -2
  29. package/dist/dashboard/ui-state-store.js.map +1 -0
  30. package/dist/fast-control.d.ts +1 -1
  31. package/dist/fast-control.js +1 -1
  32. package/dist/instance-directory.js +1 -1
  33. package/dist/main.js +27 -13
  34. package/dist/main.js.map +1 -1
  35. package/dist/metadata-server.js +4 -17
  36. package/dist/metadata-server.js.map +1 -1
  37. package/dist/multiplexer/agent-io-methods.d.ts +40 -0
  38. package/dist/multiplexer/agent-io-methods.js +177 -0
  39. package/dist/multiplexer/agent-io-methods.js.map +1 -0
  40. package/dist/multiplexer/archives.d.ts +15 -0
  41. package/dist/multiplexer/archives.js +270 -0
  42. package/dist/multiplexer/archives.js.map +1 -0
  43. package/dist/multiplexer/dashboard-actions-methods.d.ts +57 -0
  44. package/dist/multiplexer/dashboard-actions-methods.js +159 -0
  45. package/dist/multiplexer/dashboard-actions-methods.js.map +1 -0
  46. package/dist/multiplexer/dashboard-control.d.ts +36 -0
  47. package/dist/multiplexer/dashboard-control.js +571 -0
  48. package/dist/multiplexer/dashboard-control.js.map +1 -0
  49. package/dist/multiplexer/dashboard-interaction.d.ts +12 -0
  50. package/dist/multiplexer/dashboard-interaction.js +552 -0
  51. package/dist/multiplexer/dashboard-interaction.js.map +1 -0
  52. package/dist/multiplexer/dashboard-model.d.ts +40 -0
  53. package/dist/multiplexer/dashboard-model.js +420 -0
  54. package/dist/multiplexer/dashboard-model.js.map +1 -0
  55. package/dist/multiplexer/dashboard-ops.d.ts +35 -0
  56. package/dist/multiplexer/dashboard-ops.js +158 -0
  57. package/dist/multiplexer/dashboard-ops.js.map +1 -0
  58. package/dist/multiplexer/dashboard-state-methods.d.ts +56 -0
  59. package/dist/multiplexer/dashboard-state-methods.js +123 -0
  60. package/dist/multiplexer/dashboard-state-methods.js.map +1 -0
  61. package/dist/multiplexer/dashboard-tail-methods.d.ts +104 -0
  62. package/dist/multiplexer/dashboard-tail-methods.js +252 -0
  63. package/dist/multiplexer/dashboard-tail-methods.js.map +1 -0
  64. package/dist/multiplexer/dashboard-view-methods.d.ts +51 -0
  65. package/dist/multiplexer/dashboard-view-methods.js +159 -0
  66. package/dist/multiplexer/dashboard-view-methods.js.map +1 -0
  67. package/dist/multiplexer/index.d.ts +205 -0
  68. package/dist/multiplexer/index.js +444 -0
  69. package/dist/multiplexer/index.js.map +1 -0
  70. package/dist/multiplexer/navigation.d.ts +17 -0
  71. package/dist/multiplexer/navigation.js +203 -0
  72. package/dist/multiplexer/navigation.js.map +1 -0
  73. package/dist/multiplexer/notifications.d.ts +6 -0
  74. package/dist/multiplexer/notifications.js +75 -0
  75. package/dist/multiplexer/notifications.js.map +1 -0
  76. package/dist/multiplexer/persistence-methods.d.ts +77 -0
  77. package/dist/multiplexer/persistence-methods.js +347 -0
  78. package/dist/multiplexer/persistence-methods.js.map +1 -0
  79. package/dist/multiplexer/runtime-lifecycle-methods.d.ts +36 -0
  80. package/dist/multiplexer/runtime-lifecycle-methods.js +235 -0
  81. package/dist/multiplexer/runtime-lifecycle-methods.js.map +1 -0
  82. package/dist/multiplexer/runtime-state.d.ts +32 -0
  83. package/dist/multiplexer/runtime-state.js +439 -0
  84. package/dist/multiplexer/runtime-state.js.map +1 -0
  85. package/dist/{multiplexer-runtime-sync.d.ts → multiplexer/runtime-sync.d.ts} +2 -2
  86. package/dist/{multiplexer-runtime-sync.js → multiplexer/runtime-sync.js} +1 -1
  87. package/dist/multiplexer/runtime-sync.js.map +1 -0
  88. package/dist/multiplexer/services.d.ts +23 -0
  89. package/dist/multiplexer/services.js +187 -0
  90. package/dist/multiplexer/services.js.map +1 -0
  91. package/dist/multiplexer/session-actions.d.ts +36 -0
  92. package/dist/multiplexer/session-actions.js +108 -0
  93. package/dist/multiplexer/session-actions.js.map +1 -0
  94. package/dist/multiplexer/session-launch.d.ts +20 -0
  95. package/dist/multiplexer/session-launch.js +407 -0
  96. package/dist/multiplexer/session-launch.js.map +1 -0
  97. package/dist/multiplexer/session-runtime-core.d.ts +37 -0
  98. package/dist/multiplexer/session-runtime-core.js +400 -0
  99. package/dist/multiplexer/session-runtime-core.js.map +1 -0
  100. package/dist/multiplexer/subscreens.d.ts +30 -0
  101. package/dist/multiplexer/subscreens.js +685 -0
  102. package/dist/multiplexer/subscreens.js.map +1 -0
  103. package/dist/multiplexer/tool-picker.d.ts +8 -0
  104. package/dist/multiplexer/tool-picker.js +142 -0
  105. package/dist/multiplexer/tool-picker.js.map +1 -0
  106. package/dist/multiplexer/worktrees.d.ts +12 -0
  107. package/dist/multiplexer/worktrees.js +181 -0
  108. package/dist/multiplexer/worktrees.js.map +1 -0
  109. package/dist/notification-context.js +6 -1
  110. package/dist/notification-context.js.map +1 -1
  111. package/dist/notify.js +19 -1
  112. package/dist/notify.js.map +1 -1
  113. package/dist/project-events.d.ts +2 -0
  114. package/dist/project-events.js +1 -0
  115. package/dist/project-events.js.map +1 -1
  116. package/dist/project-scanner.d.ts +1 -1
  117. package/dist/project-scanner.js +1 -1
  118. package/dist/session-bootstrap.d.ts +1 -1
  119. package/dist/session-semantics.d.ts +1 -1
  120. package/dist/{tmux-doctor.d.ts → tmux/doctor.d.ts} +1 -1
  121. package/dist/{tmux-doctor.js → tmux/doctor.js} +4 -3
  122. package/dist/tmux/doctor.js.map +1 -0
  123. package/dist/{tmux-runtime-manager.d.ts → tmux/runtime-manager.d.ts} +3 -0
  124. package/dist/{tmux-runtime-manager.js → tmux/runtime-manager.js} +81 -17
  125. package/dist/tmux/runtime-manager.js.map +1 -0
  126. package/dist/{tmux-session-transport.d.ts → tmux/session-transport.d.ts} +2 -2
  127. package/dist/{tmux-session-transport.js → tmux/session-transport.js} +2 -2
  128. package/dist/tmux/session-transport.js.map +1 -0
  129. package/dist/{tmux-statusline.d.ts → tmux/statusline.d.ts} +1 -1
  130. package/dist/{tmux-statusline.js → tmux/statusline.js} +4 -4
  131. package/dist/tmux/statusline.js.map +1 -0
  132. package/dist/{tmux-switcher.js → tmux/switcher.js} +7 -7
  133. package/dist/tmux/switcher.js.map +1 -0
  134. package/dist/{tmux-window-open.d.ts → tmux/window-open.d.ts} +1 -1
  135. package/dist/{tmux-window-open.js → tmux/window-open.js} +1 -1
  136. package/dist/tmux/window-open.js.map +1 -0
  137. package/dist/tool-output-watchers.js +1 -1
  138. package/dist/tui/render/box.js +25 -6
  139. package/dist/tui/render/box.js.map +1 -1
  140. package/dist/tui/screens/dashboard-renderers.d.ts +3 -2
  141. package/dist/tui/screens/dashboard-renderers.js +17 -2
  142. package/dist/tui/screens/dashboard-renderers.js.map +1 -1
  143. package/dist/tui/screens/overlay-renderers.js +2 -2
  144. package/dist/tui/screens/overlay-renderers.js.map +1 -1
  145. package/package.json +1 -1
  146. package/dist/dashboard-feedback.js.map +0 -1
  147. package/dist/dashboard-pending-actions.js +0 -52
  148. package/dist/dashboard-pending-actions.js.map +0 -1
  149. package/dist/dashboard-session-actions.js.map +0 -1
  150. package/dist/dashboard-session-registry.js.map +0 -1
  151. package/dist/dashboard-state.js.map +0 -1
  152. package/dist/dashboard-targets.js.map +0 -1
  153. package/dist/dashboard-ui-state-store.js.map +0 -1
  154. package/dist/dashboard.js.map +0 -1
  155. package/dist/multiplexer-runtime-sync.js.map +0 -1
  156. package/dist/multiplexer.d.ts +0 -500
  157. package/dist/multiplexer.js +0 -6081
  158. package/dist/multiplexer.js.map +0 -1
  159. package/dist/tmux-doctor.js.map +0 -1
  160. package/dist/tmux-runtime-manager.js.map +0 -1
  161. package/dist/tmux-session-transport.js.map +0 -1
  162. package/dist/tmux-statusline.js.map +0 -1
  163. package/dist/tmux-switcher.js.map +0 -1
  164. package/dist/tmux-window-open.js.map +0 -1
  165. /package/dist/{dashboard-feedback.d.ts → dashboard/feedback.d.ts} +0 -0
  166. /package/dist/{tmux-switcher.d.ts → tmux/switcher.d.ts} +0 -0
@@ -0,0 +1,685 @@
1
+ import { parseKeys } from "../key-parser.js";
2
+ import { markThreadSeen, setThreadStatus } from "../threads.js";
3
+ import { acceptHandoff, acceptTask, approveReview, blockTask, completeHandoff, completeTask, reopenTask, requestTaskChanges, } from "../orchestration-actions.js";
4
+ import { buildThreadEntries, buildWorkflowEntries, filterWorkflowEntries, } from "../workflow.js";
5
+ import { renderActivityScreen, renderThreadDetails, renderThreadsScreen, renderWorkflowDetails, renderWorkflowScreen, } from "../tui/screens/subscreen-renderers.js";
6
+ import { navigationUrgencyScore } from "../fast-control.js";
7
+ export function attentionScore(host, entry) {
8
+ return navigationUrgencyScore(entry);
9
+ }
10
+ export function getActivityEntries(host) {
11
+ return host
12
+ .getDashboardSessionsInVisualOrder()
13
+ .filter((entry) => attentionScore(host, entry) > 0 ||
14
+ !!entry.activity ||
15
+ entry.status === "running" ||
16
+ entry.status === "waiting" ||
17
+ (entry.unseenCount ?? 0) > 0)
18
+ .sort((a, b) => {
19
+ const scoreDiff = attentionScore(host, b) - attentionScore(host, a);
20
+ if (scoreDiff !== 0)
21
+ return scoreDiff;
22
+ const activeDiff = Number(b.active) - Number(a.active);
23
+ if (activeDiff !== 0)
24
+ return activeDiff;
25
+ const aName = a.label ?? a.command;
26
+ const bName = b.label ?? b.command;
27
+ return aName.localeCompare(bName);
28
+ });
29
+ }
30
+ export function showActivityDashboard(host) {
31
+ host.clearDashboardSubscreens();
32
+ host.activityEntries = getActivityEntries(host);
33
+ if (host.activityIndex >= host.activityEntries.length) {
34
+ host.activityIndex = Math.max(0, host.activityEntries.length - 1);
35
+ }
36
+ host.setDashboardScreen("activity");
37
+ host.writeStatuslineFile();
38
+ renderActivityDashboard(host);
39
+ }
40
+ export function buildWorkflowEntriesForHost(host) {
41
+ return filterWorkflowEntries(buildWorkflowEntries("user"), host.workflowFilter, "user");
42
+ }
43
+ export function showWorkflow(host) {
44
+ host.clearDashboardSubscreens();
45
+ host.workflowEntries = buildWorkflowEntriesForHost(host);
46
+ if (host.workflowIndex >= host.workflowEntries.length) {
47
+ host.workflowIndex = Math.max(0, host.workflowEntries.length - 1);
48
+ }
49
+ host.setDashboardScreen("workflow");
50
+ host.writeStatuslineFile();
51
+ renderWorkflow(host);
52
+ }
53
+ export function renderWorkflow(host) {
54
+ renderWorkflowScreen(host);
55
+ }
56
+ export function renderWorkflowDetailsForHost(host, width, height) {
57
+ return renderWorkflowDetails(host, width, height);
58
+ }
59
+ export function handleWorkflowKey(host, data) {
60
+ const events = parseKeys(data);
61
+ if (events.length === 0)
62
+ return;
63
+ const event = events[0];
64
+ const key = event.name || event.char;
65
+ const isTabToggle = key === "tab" || event.raw === "\t" || (event.ctrl && key === "i");
66
+ if (isTabToggle) {
67
+ host.dashboardState.toggleDetailsSidebar();
68
+ renderWorkflow(host);
69
+ return;
70
+ }
71
+ if (key === "q") {
72
+ host.exitDashboardClientOrProcess();
73
+ return;
74
+ }
75
+ if (key === "escape" || key === "d") {
76
+ host.setDashboardScreen("dashboard");
77
+ host.renderDashboard();
78
+ return;
79
+ }
80
+ if (host.handleDashboardSubscreenNavigationKey(key, "workflow"))
81
+ return;
82
+ if (key === "?") {
83
+ host.showHelp();
84
+ return;
85
+ }
86
+ if (key === "f") {
87
+ cycleWorkflowFilter(host);
88
+ return;
89
+ }
90
+ if (key === "s") {
91
+ const entry = host.workflowEntries[host.workflowIndex];
92
+ if (entry) {
93
+ host.threadEntries = buildThreadEntries();
94
+ host.threadIndex = Math.max(0, host.threadEntries.findIndex((thread) => thread.thread.id === entry.thread.id));
95
+ host.threadReplyActive = true;
96
+ host.threadReplyBuffer = "";
97
+ host.setDashboardScreen("threads");
98
+ renderThreadReply(host);
99
+ }
100
+ return;
101
+ }
102
+ if (key === "a" || key === "c" || key === "b" || key === "o" || key === "x") {
103
+ const entry = host.workflowEntries[host.workflowIndex];
104
+ if (!entry)
105
+ return;
106
+ if (entry.task) {
107
+ if (key === "a") {
108
+ void runTaskLifecycleAction(host, "accept", entry.task.id);
109
+ return;
110
+ }
111
+ if (key === "b") {
112
+ void runTaskLifecycleAction(host, "block", entry.task.id);
113
+ return;
114
+ }
115
+ if (key === "c" || key === "x") {
116
+ void runTaskLifecycleAction(host, "complete", entry.task.id);
117
+ return;
118
+ }
119
+ }
120
+ if (key === "a" && entry.thread.kind === "handoff") {
121
+ void runThreadHandoffAction(host, "accept", entry.thread.id);
122
+ return;
123
+ }
124
+ if (key === "c" && entry.thread.kind === "handoff") {
125
+ void runThreadHandoffAction(host, "complete", entry.thread.id);
126
+ return;
127
+ }
128
+ const statusMap = { b: "blocked", o: "open", x: "done" };
129
+ const status = statusMap[key];
130
+ if (status) {
131
+ void runThreadStatusAction(host, entry.thread.id, status);
132
+ }
133
+ return;
134
+ }
135
+ if (key === "P" || key === "J" || key === "E") {
136
+ const entry = host.workflowEntries[host.workflowIndex];
137
+ if (!entry?.task)
138
+ return;
139
+ if (key === "P") {
140
+ void runReviewLifecycleAction(host, "approve", entry.task.id);
141
+ return;
142
+ }
143
+ if (key === "J") {
144
+ void runReviewLifecycleAction(host, "request_changes", entry.task.id);
145
+ return;
146
+ }
147
+ if (key === "E") {
148
+ void runTaskLifecycleAction(host, "reopen", entry.task.id);
149
+ }
150
+ return;
151
+ }
152
+ if (key === "down" || key === "j" || key === "n") {
153
+ if (host.workflowEntries.length > 1) {
154
+ host.workflowIndex = (host.workflowIndex + 1) % host.workflowEntries.length;
155
+ renderWorkflow(host);
156
+ }
157
+ return;
158
+ }
159
+ if (key === "up" || key === "k") {
160
+ if (host.workflowEntries.length > 1) {
161
+ host.workflowIndex = (host.workflowIndex - 1 + host.workflowEntries.length) % host.workflowEntries.length;
162
+ renderWorkflow(host);
163
+ }
164
+ return;
165
+ }
166
+ if (key >= "1" && key <= "9") {
167
+ const idx = parseInt(key, 10) - 1;
168
+ if (idx < host.workflowEntries.length) {
169
+ host.workflowIndex = idx;
170
+ renderWorkflow(host);
171
+ }
172
+ return;
173
+ }
174
+ if (key === "enter" || key === "return") {
175
+ const entry = host.workflowEntries[host.workflowIndex];
176
+ if (!entry)
177
+ return;
178
+ host.threadEntries = buildThreadEntries();
179
+ host.threadIndex = Math.max(0, host.threadEntries.findIndex((thread) => thread.thread.id === entry.thread.id));
180
+ host.setDashboardScreen("threads");
181
+ renderThreads(host);
182
+ }
183
+ }
184
+ export function renderActivityDashboard(host) {
185
+ renderActivityScreen(host);
186
+ }
187
+ export function handleActivityKey(host, data) {
188
+ const events = parseKeys(data);
189
+ if (events.length === 0)
190
+ return;
191
+ const event = events[0];
192
+ const key = event.name || event.char;
193
+ const isTabToggle = key === "tab" || event.raw === "\t" || (event.ctrl && key === "i");
194
+ if (isTabToggle) {
195
+ host.dashboardState.toggleDetailsSidebar();
196
+ renderActivityDashboard(host);
197
+ return;
198
+ }
199
+ if (key === "q") {
200
+ host.exitDashboardClientOrProcess();
201
+ return;
202
+ }
203
+ if (key === "escape" || key === "d") {
204
+ host.setDashboardScreen("dashboard");
205
+ host.renderDashboard();
206
+ return;
207
+ }
208
+ if (host.handleDashboardSubscreenNavigationKey(key, "activity"))
209
+ return;
210
+ if (key === "?") {
211
+ host.showHelp();
212
+ return;
213
+ }
214
+ if (key === "u") {
215
+ void activateNextAttentionEntry(host);
216
+ return;
217
+ }
218
+ if (key === "down" || key === "j" || key === "n") {
219
+ if (host.activityEntries.length > 1) {
220
+ host.activityIndex = (host.activityIndex + 1) % host.activityEntries.length;
221
+ renderActivityDashboard(host);
222
+ }
223
+ return;
224
+ }
225
+ if (key === "up" || key === "k") {
226
+ if (host.activityEntries.length > 1) {
227
+ host.activityIndex = (host.activityIndex - 1 + host.activityEntries.length) % host.activityEntries.length;
228
+ renderActivityDashboard(host);
229
+ }
230
+ return;
231
+ }
232
+ if (key >= "1" && key <= "9") {
233
+ const idx = parseInt(key, 10) - 1;
234
+ const entry = host.activityEntries[idx];
235
+ if (entry)
236
+ void host.activateDashboardEntry(entry);
237
+ return;
238
+ }
239
+ if (key === "enter" || key === "return") {
240
+ const entry = host.activityEntries[host.activityIndex];
241
+ if (entry)
242
+ void host.activateDashboardEntry(entry);
243
+ }
244
+ }
245
+ export function showThreads(host) {
246
+ host.clearDashboardSubscreens();
247
+ host.threadEntries = buildThreadEntries();
248
+ if (host.threadIndex >= host.threadEntries.length) {
249
+ host.threadIndex = Math.max(0, host.threadEntries.length - 1);
250
+ }
251
+ host.setDashboardScreen("threads");
252
+ host.writeStatuslineFile();
253
+ renderThreads(host);
254
+ }
255
+ export function getPreferredThreadIndexForParticipant(_host, participantId, entries) {
256
+ const participantEntries = entries.filter((entry) => entry.thread.participants.includes(participantId));
257
+ const targetEntries = participantEntries;
258
+ if (targetEntries.length === 0)
259
+ return -1;
260
+ const scored = targetEntries
261
+ .map((entry) => {
262
+ const waitingOnMe = (entry.thread.waitingOn ?? []).includes(participantId) ? 3 : 0;
263
+ const unread = (entry.thread.unreadBy ?? []).includes(participantId) ? 2 : 0;
264
+ const ownsWaiting = entry.thread.owner === participantId && (entry.thread.waitingOn?.length ?? 0) > 0 ? 1 : 0;
265
+ return { entry, score: waitingOnMe + unread + ownsWaiting };
266
+ })
267
+ .sort((a, b) => b.score - a.score ||
268
+ (a.entry.thread.updatedAt < b.entry.thread.updatedAt
269
+ ? 1
270
+ : a.entry.thread.updatedAt > b.entry.thread.updatedAt
271
+ ? -1
272
+ : 0));
273
+ const targetId = scored[0].entry.thread.id;
274
+ return entries.findIndex((entry) => entry.thread.id === targetId);
275
+ }
276
+ export function openRelevantThreadForSession(host, sessionId) {
277
+ const entries = buildThreadEntries();
278
+ const idx = getPreferredThreadIndexForParticipant(host, sessionId, entries);
279
+ if (idx < 0 || idx >= entries.length) {
280
+ host.footerFlash = `No thread for ${sessionId}`;
281
+ host.footerFlashTicks = 3;
282
+ host.renderDashboard();
283
+ return;
284
+ }
285
+ host.threadEntries = entries;
286
+ host.threadIndex = idx;
287
+ host.setDashboardScreen("threads");
288
+ host.writeStatuslineFile();
289
+ const entry = host.threadEntries[host.threadIndex];
290
+ if (entry && (entry.thread.waitingOn ?? []).includes(sessionId)) {
291
+ host.threadReplyActive = true;
292
+ host.threadReplyBuffer = "";
293
+ renderThreadReply(host);
294
+ return;
295
+ }
296
+ renderThreads(host);
297
+ }
298
+ export function renderThreads(host) {
299
+ renderThreadsScreen(host);
300
+ }
301
+ export function renderThreadDetailsForHost(host, width, height) {
302
+ return renderThreadDetails(host, width, height);
303
+ }
304
+ export function handleThreadsKey(host, data) {
305
+ const events = parseKeys(data);
306
+ if (events.length === 0)
307
+ return;
308
+ const event = events[0];
309
+ const key = event.name || event.char;
310
+ const isTabToggle = key === "tab" || event.raw === "\t" || (event.ctrl && key === "i");
311
+ if (isTabToggle) {
312
+ host.dashboardState.toggleDetailsSidebar();
313
+ renderThreads(host);
314
+ return;
315
+ }
316
+ if (key === "q") {
317
+ host.exitDashboardClientOrProcess();
318
+ return;
319
+ }
320
+ if (key === "escape" || key === "d") {
321
+ host.setDashboardScreen("dashboard");
322
+ host.renderDashboard();
323
+ return;
324
+ }
325
+ if (host.handleDashboardSubscreenNavigationKey(key, "threads"))
326
+ return;
327
+ if (key === "?") {
328
+ host.showHelp();
329
+ return;
330
+ }
331
+ if (key === "r") {
332
+ host.threadEntries = buildThreadEntries();
333
+ if (host.threadIndex >= host.threadEntries.length) {
334
+ host.threadIndex = Math.max(0, host.threadEntries.length - 1);
335
+ }
336
+ renderThreads(host);
337
+ return;
338
+ }
339
+ if (key === "s") {
340
+ if (host.threadEntries[host.threadIndex]) {
341
+ host.threadReplyActive = true;
342
+ host.threadReplyBuffer = "";
343
+ renderThreadReply(host);
344
+ }
345
+ return;
346
+ }
347
+ if (key === "a") {
348
+ const entry = host.threadEntries[host.threadIndex];
349
+ if (entry?.thread.kind === "handoff")
350
+ void runThreadHandoffAction(host, "accept", entry.thread.id);
351
+ return;
352
+ }
353
+ if (key === "c") {
354
+ const entry = host.threadEntries[host.threadIndex];
355
+ if (entry?.thread.kind === "handoff")
356
+ void runThreadHandoffAction(host, "complete", entry.thread.id);
357
+ return;
358
+ }
359
+ if (key === "b") {
360
+ const entry = host.threadEntries[host.threadIndex];
361
+ if (entry)
362
+ void runThreadStatusAction(host, entry.thread.id, "blocked");
363
+ return;
364
+ }
365
+ if (key === "o") {
366
+ const entry = host.threadEntries[host.threadIndex];
367
+ if (entry)
368
+ void runThreadStatusAction(host, entry.thread.id, "open");
369
+ return;
370
+ }
371
+ if (key === "x") {
372
+ const entry = host.threadEntries[host.threadIndex];
373
+ if (entry)
374
+ void runThreadStatusAction(host, entry.thread.id, "done");
375
+ return;
376
+ }
377
+ if (key === "down" || key === "j" || key === "n") {
378
+ if (host.threadEntries.length > 1) {
379
+ host.threadIndex = (host.threadIndex + 1) % host.threadEntries.length;
380
+ renderThreads(host);
381
+ }
382
+ return;
383
+ }
384
+ if (key === "up" || key === "k") {
385
+ if (host.threadEntries.length > 1) {
386
+ host.threadIndex = (host.threadIndex - 1 + host.threadEntries.length) % host.threadEntries.length;
387
+ renderThreads(host);
388
+ }
389
+ return;
390
+ }
391
+ if (key >= "1" && key <= "9") {
392
+ const idx = parseInt(key, 10) - 1;
393
+ if (idx < host.threadEntries.length) {
394
+ host.threadIndex = idx;
395
+ renderThreads(host);
396
+ }
397
+ return;
398
+ }
399
+ if (key === "enter" || key === "return") {
400
+ const entry = host.threadEntries[host.threadIndex];
401
+ if (!entry)
402
+ return;
403
+ const targetSessionId = entry.thread.owner ?? entry.thread.waitingOn?.[0] ?? entry.thread.participants[0];
404
+ if (targetSessionId) {
405
+ markThreadSeen(entry.thread.id, targetSessionId);
406
+ const dashEntry = host.getDashboardSessions().find((session) => session.id === targetSessionId);
407
+ if (dashEntry)
408
+ void host.activateDashboardEntry(dashEntry);
409
+ }
410
+ }
411
+ }
412
+ export function renderThreadReply(host) {
413
+ const entry = host.threadEntries[host.threadIndex];
414
+ if (!entry)
415
+ return;
416
+ const cols = process.stdout.columns ?? 80;
417
+ const rows = process.stdout.rows ?? 24;
418
+ const targets = entry.thread.waitingOn?.length && entry.thread.waitingOn.length > 0
419
+ ? entry.thread.waitingOn
420
+ : entry.thread.participants.filter((participant) => participant !== "user");
421
+ const title = host.truncatePlain(entry.displayTitle, Math.max(16, cols - 24));
422
+ const buffer = host.truncatePlain(host.threadReplyBuffer, Math.max(12, cols - 24));
423
+ const lines = [
424
+ "Reply in thread:",
425
+ "",
426
+ ` Thread: ${title}`,
427
+ ` To: ${targets.join(", ") || "participants"}`,
428
+ "",
429
+ ` Message: ${buffer}_`,
430
+ "",
431
+ " [Enter] send [Esc] cancel",
432
+ ];
433
+ const boxWidth = Math.max(...lines.map((line) => host.stripAnsi(line).length)) + 4;
434
+ const startRow = Math.floor((rows - lines.length - 2) / 2);
435
+ const startCol = Math.floor((cols - boxWidth) / 2);
436
+ let output = "\x1b7";
437
+ for (let i = 0; i < lines.length + 2; i++) {
438
+ const row = startRow + i;
439
+ output += `\x1b[${row};${startCol}H`;
440
+ if (i === 0 || i === lines.length + 1) {
441
+ output += `\x1b[44;97m${"─".repeat(boxWidth)}\x1b[0m`;
442
+ }
443
+ else {
444
+ const line = lines[i - 1];
445
+ output += `\x1b[44;97m ${line.padEnd(boxWidth - 2)}\x1b[0m`;
446
+ }
447
+ }
448
+ output += "\x1b8";
449
+ process.stdout.write(output);
450
+ }
451
+ export function describeHandoffState(_host, thread) {
452
+ if (thread.status === "done")
453
+ return `completed by ${thread.owner ?? "unknown"}`;
454
+ if ((thread.waitingOn?.length ?? 0) > 0) {
455
+ return `${thread.owner ?? thread.createdBy} waiting on ${thread.waitingOn.join(", ")}`;
456
+ }
457
+ if (thread.owner && thread.owner !== thread.createdBy)
458
+ return `accepted by ${thread.owner}`;
459
+ return `awaiting acceptance from ${thread.participants.filter((id) => id !== thread.createdBy).join(", ") || "recipient"}`;
460
+ }
461
+ export async function runThreadHandoffAction(host, mode, threadId) {
462
+ try {
463
+ if (mode === "accept") {
464
+ await host.postToProjectService("/handoff/accept", { threadId, from: "user" });
465
+ host.footerFlash = "⇢ Handoff accepted";
466
+ }
467
+ else {
468
+ await host.postToProjectService("/handoff/complete", { threadId, from: "user" });
469
+ host.footerFlash = "⇢ Handoff completed";
470
+ }
471
+ host.footerFlashTicks = 3;
472
+ }
473
+ catch {
474
+ try {
475
+ if (mode === "accept") {
476
+ acceptHandoff({ threadId, from: "user" });
477
+ host.footerFlash = "⇢ Handoff accepted";
478
+ }
479
+ else {
480
+ completeHandoff({ threadId, from: "user" });
481
+ host.footerFlash = "⇢ Handoff completed";
482
+ }
483
+ host.footerFlashTicks = 3;
484
+ }
485
+ catch (error) {
486
+ host.showDashboardError(`Failed to ${mode} handoff`, [error instanceof Error ? error.message : String(error)]);
487
+ return;
488
+ }
489
+ }
490
+ host.threadEntries = buildThreadEntries();
491
+ host.threadIndex = Math.min(host.threadIndex, Math.max(0, host.threadEntries.length - 1));
492
+ renderThreads(host);
493
+ }
494
+ export async function runThreadStatusAction(host, threadId, status) {
495
+ try {
496
+ await host.postToProjectService("/threads/status", { threadId, status });
497
+ host.footerFlash = `Thread marked ${status}`;
498
+ host.footerFlashTicks = 3;
499
+ }
500
+ catch {
501
+ try {
502
+ setThreadStatus(threadId, status);
503
+ host.footerFlash = `Thread marked ${status}`;
504
+ host.footerFlashTicks = 3;
505
+ }
506
+ catch (error) {
507
+ host.showDashboardError("Failed to update thread status", [
508
+ error instanceof Error ? error.message : String(error),
509
+ ]);
510
+ return;
511
+ }
512
+ }
513
+ host.threadEntries = buildThreadEntries();
514
+ host.threadIndex = Math.min(host.threadIndex, Math.max(0, host.threadEntries.length - 1));
515
+ renderThreads(host);
516
+ }
517
+ export async function runTaskLifecycleAction(host, mode, taskId) {
518
+ try {
519
+ if (mode === "accept") {
520
+ await host.postToProjectService("/tasks/accept", { taskId, from: "user" });
521
+ host.footerFlash = "⧫ Task accepted";
522
+ }
523
+ else if (mode === "block") {
524
+ await host.postToProjectService("/tasks/block", { taskId, from: "user" });
525
+ host.footerFlash = "⧫ Task blocked";
526
+ }
527
+ else if (mode === "reopen") {
528
+ await host.postToProjectService("/tasks/reopen", { taskId, from: "user" });
529
+ host.footerFlash = "↺ Task reopened";
530
+ }
531
+ else {
532
+ await host.postToProjectService("/tasks/complete", { taskId, from: "user" });
533
+ host.footerFlash = "✓ Task completed";
534
+ }
535
+ host.footerFlashTicks = 3;
536
+ }
537
+ catch {
538
+ try {
539
+ if (mode === "accept") {
540
+ await acceptTask({ taskId, from: "user" });
541
+ host.footerFlash = "⧫ Task accepted";
542
+ }
543
+ else if (mode === "block") {
544
+ await blockTask({ taskId, from: "user" });
545
+ host.footerFlash = "⧫ Task blocked";
546
+ }
547
+ else if (mode === "reopen") {
548
+ await reopenTask({ taskId, from: "user" });
549
+ host.footerFlash = "↺ Task reopened";
550
+ }
551
+ else {
552
+ await completeTask({ taskId, from: "user" });
553
+ host.footerFlash = "✓ Task completed";
554
+ }
555
+ host.footerFlashTicks = 3;
556
+ }
557
+ catch (error) {
558
+ host.showDashboardError(`Failed to ${mode} task`, [error instanceof Error ? error.message : String(error)]);
559
+ return;
560
+ }
561
+ }
562
+ host.workflowEntries = buildWorkflowEntriesForHost(host);
563
+ host.workflowIndex = Math.min(host.workflowIndex, Math.max(0, host.workflowEntries.length - 1));
564
+ renderWorkflow(host);
565
+ }
566
+ export async function runReviewLifecycleAction(host, mode, taskId) {
567
+ try {
568
+ if (mode === "approve") {
569
+ await host.postToProjectService("/reviews/approve", { taskId, from: "user" });
570
+ host.footerFlash = "✓ Review approved";
571
+ }
572
+ else {
573
+ await host.postToProjectService("/reviews/request-changes", { taskId, from: "user" });
574
+ host.footerFlash = "↺ Changes requested";
575
+ }
576
+ host.footerFlashTicks = 3;
577
+ }
578
+ catch {
579
+ try {
580
+ if (mode === "approve") {
581
+ await approveReview({ taskId, from: "user" });
582
+ host.footerFlash = "✓ Review approved";
583
+ }
584
+ else {
585
+ await requestTaskChanges({ taskId, from: "user" });
586
+ host.footerFlash = "↺ Changes requested";
587
+ }
588
+ host.footerFlashTicks = 3;
589
+ }
590
+ catch (error) {
591
+ host.showDashboardError(`Failed to ${mode === "approve" ? "approve review" : "request changes"}`, [
592
+ error instanceof Error ? error.message : String(error),
593
+ ]);
594
+ return;
595
+ }
596
+ }
597
+ host.workflowEntries = buildWorkflowEntriesForHost(host);
598
+ host.workflowIndex = Math.min(host.workflowIndex, Math.max(0, host.workflowEntries.length - 1));
599
+ renderWorkflow(host);
600
+ }
601
+ export function describeWorkflowFilter(host) {
602
+ if (host.workflowFilter === "on_me")
603
+ return "waiting on me";
604
+ if (host.workflowFilter === "blocked")
605
+ return "blocked";
606
+ if (host.workflowFilter === "families")
607
+ return "families";
608
+ return "all";
609
+ }
610
+ export function cycleWorkflowFilter(host) {
611
+ const order = ["all", "on_me", "blocked", "families"];
612
+ const current = order.indexOf(host.workflowFilter);
613
+ host.workflowFilter = order[(current + 1) % order.length] ?? "all";
614
+ host.workflowEntries = buildWorkflowEntriesForHost(host);
615
+ host.workflowIndex = Math.min(host.workflowIndex, Math.max(0, host.workflowEntries.length - 1));
616
+ host.footerFlash = `Workflow filter: ${describeWorkflowFilter(host)}`;
617
+ host.footerFlashTicks = 3;
618
+ renderWorkflow(host);
619
+ }
620
+ export function handleThreadReplyKey(host, data) {
621
+ const events = parseKeys(data);
622
+ if (events.length === 0)
623
+ return;
624
+ const event = events[0];
625
+ const key = event.name || event.char;
626
+ if (key === "escape") {
627
+ host.threadReplyActive = false;
628
+ host.threadReplyBuffer = "";
629
+ renderThreads(host);
630
+ return;
631
+ }
632
+ if (key === "enter" || key === "return") {
633
+ const body = host.threadReplyBuffer.trim();
634
+ const entry = host.threadEntries[host.threadIndex];
635
+ host.threadReplyActive = false;
636
+ host.threadReplyBuffer = "";
637
+ if (!entry || !body) {
638
+ renderThreads(host);
639
+ return;
640
+ }
641
+ try {
642
+ host.sendOrchestrationMessage({
643
+ threadId: entry.thread.id,
644
+ from: "user",
645
+ kind: "reply",
646
+ body,
647
+ });
648
+ }
649
+ catch (error) {
650
+ host.showDashboardError("Failed to reply in thread", [error instanceof Error ? error.message : String(error)]);
651
+ return;
652
+ }
653
+ host.threadEntries = buildThreadEntries();
654
+ host.threadIndex = Math.min(host.threadIndex, Math.max(0, host.threadEntries.length - 1));
655
+ renderThreads(host);
656
+ return;
657
+ }
658
+ if (key === "backspace" || key === "delete") {
659
+ host.threadReplyBuffer = host.threadReplyBuffer.slice(0, -1);
660
+ renderThreadReply(host);
661
+ return;
662
+ }
663
+ if (event.char && event.char.length === 1 && !event.ctrl && !event.alt) {
664
+ host.threadReplyBuffer += event.char;
665
+ renderThreadReply(host);
666
+ }
667
+ }
668
+ export async function activateNextAttentionEntry(host) {
669
+ const ordered = host
670
+ .getDashboardSessionsInVisualOrder()
671
+ .map((entry, index) => ({ entry, index, score: attentionScore(host, entry) }))
672
+ .filter((entry) => entry.score > 0)
673
+ .sort((a, b) => b.score - a.score || a.index - b.index);
674
+ if (ordered.length === 0)
675
+ return;
676
+ const currentSessionId = host.dashboardState.level === "sessions" && host.dashboardState.worktreeEntries.length > 0
677
+ ? host.dashboardState.worktreeEntries[host.dashboardState.sessionIndex]?.kind === "session"
678
+ ? host.dashboardState.worktreeEntries[host.dashboardState.sessionIndex]?.id
679
+ : undefined
680
+ : host.getDashboardSessions()[host.activeIndex]?.id;
681
+ const currentIdx = currentSessionId ? ordered.findIndex((entry) => entry.entry.id === currentSessionId) : -1;
682
+ const next = ordered[currentIdx >= 0 ? (currentIdx + 1) % ordered.length : 0];
683
+ await host.activateDashboardEntryByNumber(next.index);
684
+ }
685
+ //# sourceMappingURL=subscreens.js.map