u-foo 1.4.1 → 1.6.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.
- package/README.md +21 -0
- package/README.zh-CN.md +21 -0
- package/bin/ufoo.js +15 -7
- package/modules/AGENTS.template.md +4 -102
- package/package.json +3 -2
- package/scripts/global-chat-switch-benchmark.js +406 -0
- package/src/agent/activityDetector.js +328 -0
- package/src/agent/activityStatePublisher.js +67 -0
- package/src/agent/activityStateWriter.js +40 -0
- package/src/agent/internalRunner.js +13 -0
- package/src/agent/launcher.js +47 -7
- package/src/agent/notifier.js +73 -4
- package/src/agent/ptyRunner.js +81 -34
- package/src/agent/ufooAgent.js +192 -6
- package/src/bus/message.js +1 -9
- package/src/bus/subscriber.js +2 -0
- package/src/bus/utils.js +10 -0
- package/src/chat/agentBar.js +21 -3
- package/src/chat/agentViewController.js +2 -0
- package/src/chat/chatLogController.js +28 -5
- package/src/chat/commandExecutor.js +127 -3
- package/src/chat/commands.js +8 -0
- package/src/chat/daemonConnection.js +77 -4
- package/src/chat/daemonCoordinator.js +36 -0
- package/src/chat/daemonMessageRouter.js +22 -0
- package/src/chat/daemonTransport.js +47 -5
- package/src/chat/daemonTransportDefaults.js +1 -0
- package/src/chat/dashboardKeyController.js +89 -1
- package/src/chat/dashboardView.js +312 -93
- package/src/chat/index.js +683 -41
- package/src/chat/inputHistoryController.js +33 -3
- package/src/chat/inputListenerController.js +22 -12
- package/src/chat/layout.js +12 -7
- package/src/chat/projectCloseController.js +119 -0
- package/src/chat/projectRuntimes.js +55 -0
- package/src/chat/statusLineController.js +52 -6
- package/src/chat/streamTracker.js +6 -0
- package/src/chat/transport.js +41 -5
- package/src/cli.js +167 -4
- package/src/daemon/index.js +54 -5
- package/src/daemon/ipcServer.js +6 -1
- package/src/daemon/ops.js +245 -35
- package/src/daemon/status.js +3 -1
- package/src/init/index.js +32 -3
- package/src/projects/projectId.js +29 -0
- package/src/projects/registry.js +279 -0
- package/src/ufoo/agentsStore.js +44 -0
|
@@ -3,6 +3,7 @@ const DEFAULT_MODE_OPTIONS = ["terminal", "tmux", "internal"];
|
|
|
3
3
|
function createDashboardKeyController(options = {}) {
|
|
4
4
|
const {
|
|
5
5
|
state,
|
|
6
|
+
globalMode = false,
|
|
6
7
|
existsSync = () => false,
|
|
7
8
|
getInjectSockPath = () => "",
|
|
8
9
|
getAgentAdapter = () => null,
|
|
@@ -21,6 +22,8 @@ function createDashboardKeyController(options = {}) {
|
|
|
21
22
|
setAutoResume = () => {},
|
|
22
23
|
clampAgentWindow = () => {},
|
|
23
24
|
clampAgentWindowWithSelection = () => {},
|
|
25
|
+
requestProjectSwitch = () => {},
|
|
26
|
+
requestCloseProject = () => {},
|
|
24
27
|
renderDashboard = () => {},
|
|
25
28
|
renderAgentDashboard = () => {},
|
|
26
29
|
renderScreen = () => {},
|
|
@@ -374,6 +377,73 @@ function createDashboardKeyController(options = {}) {
|
|
|
374
377
|
return true;
|
|
375
378
|
}
|
|
376
379
|
|
|
380
|
+
function handleProjectsKey(key) {
|
|
381
|
+
const projects = Array.isArray(state.projects) ? state.projects : [];
|
|
382
|
+
if (projects.length === 0) {
|
|
383
|
+
if (key.name === "up" || key.name === "enter" || key.name === "return" || key.name === "escape") {
|
|
384
|
+
exitDashboardMode(false);
|
|
385
|
+
}
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (key.name === "x" && key.ctrl) {
|
|
390
|
+
const current = Number.isFinite(state.selectedProjectIndex) ? state.selectedProjectIndex : 0;
|
|
391
|
+
if (current >= 0 && current < projects.length) {
|
|
392
|
+
requestCloseProject(current);
|
|
393
|
+
}
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (key.name === "down") {
|
|
398
|
+
state.dashboardView = "agents";
|
|
399
|
+
if (!Array.isArray(state.activeAgents) || state.activeAgents.length === 0) {
|
|
400
|
+
state.selectedAgentIndex = -1;
|
|
401
|
+
} else if (!Number.isFinite(state.selectedAgentIndex) || state.selectedAgentIndex < 0) {
|
|
402
|
+
state.selectedAgentIndex = 0;
|
|
403
|
+
} else if (state.selectedAgentIndex >= state.activeAgents.length) {
|
|
404
|
+
state.selectedAgentIndex = state.activeAgents.length - 1;
|
|
405
|
+
}
|
|
406
|
+
clampAgentWindow();
|
|
407
|
+
syncTargetFromSelection();
|
|
408
|
+
renderDashboardAndScreen();
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (key.name === "left") {
|
|
413
|
+
const current = Number.isFinite(state.selectedProjectIndex) ? state.selectedProjectIndex : 0;
|
|
414
|
+
if (current > 0) {
|
|
415
|
+
const next = current - 1;
|
|
416
|
+
state.selectedProjectIndex = next;
|
|
417
|
+
renderDashboardAndScreen();
|
|
418
|
+
requestProjectSwitch(next);
|
|
419
|
+
}
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (key.name === "right") {
|
|
424
|
+
const current = Number.isFinite(state.selectedProjectIndex) ? state.selectedProjectIndex : 0;
|
|
425
|
+
if (current < projects.length - 1) {
|
|
426
|
+
const next = current + 1;
|
|
427
|
+
state.selectedProjectIndex = next;
|
|
428
|
+
renderDashboardAndScreen();
|
|
429
|
+
requestProjectSwitch(next);
|
|
430
|
+
}
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (key.name === "up" || key.name === "escape") {
|
|
435
|
+
exitDashboardMode(false);
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (key.name === "enter" || key.name === "return") {
|
|
440
|
+
exitDashboardMode(false);
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
|
|
377
447
|
function handleAgentsKey(key) {
|
|
378
448
|
if (key.name === "left") {
|
|
379
449
|
if (state.activeAgents.length > 0 && state.selectedAgentIndex > 0) {
|
|
@@ -403,7 +473,18 @@ function createDashboardKeyController(options = {}) {
|
|
|
403
473
|
return true;
|
|
404
474
|
}
|
|
405
475
|
|
|
406
|
-
if (key.name === "up"
|
|
476
|
+
if (key.name === "up") {
|
|
477
|
+
clearTargetAgent();
|
|
478
|
+
if (globalMode) {
|
|
479
|
+
state.dashboardView = "projects";
|
|
480
|
+
renderDashboardAndScreen();
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
exitDashboardMode(false);
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (key.name === "escape") {
|
|
407
488
|
clearTargetAgent();
|
|
408
489
|
exitDashboardMode(false);
|
|
409
490
|
return true;
|
|
@@ -461,6 +542,13 @@ function createDashboardKeyController(options = {}) {
|
|
|
461
542
|
return handleAgentDashboardKey(key);
|
|
462
543
|
}
|
|
463
544
|
|
|
545
|
+
if (globalMode && state.dashboardView === "projects") {
|
|
546
|
+
return handleProjectsKey(key);
|
|
547
|
+
}
|
|
548
|
+
if (!globalMode && state.dashboardView === "projects") {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
|
|
464
552
|
if (state.dashboardView === "mode") return handleModeKey(key);
|
|
465
553
|
if (state.dashboardView === "provider") return handleProviderKey(key);
|
|
466
554
|
if (state.dashboardView === "assistant") return handleAssistantKey(key);
|
|
@@ -22,18 +22,116 @@ function ensureAtPrefix(value) {
|
|
|
22
22
|
return text.startsWith("@") ? text : `@${text}`;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function
|
|
25
|
+
function activityMarker(state = "") {
|
|
26
|
+
const normalized = String(state || "").trim().toLowerCase();
|
|
27
|
+
if (normalized === "working") return "*";
|
|
28
|
+
if (normalized === "waiting_input") return "?";
|
|
29
|
+
if (normalized === "blocked") return "!";
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function withActivityMarker(label = "", state = "") {
|
|
34
|
+
const marker = activityMarker(state);
|
|
35
|
+
if (!marker) return label;
|
|
36
|
+
return `${marker}${label}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function buildSummaryLine(options = {}) {
|
|
26
40
|
const {
|
|
27
|
-
|
|
41
|
+
activeAgents = [],
|
|
42
|
+
getAgentLabel = (id) => id,
|
|
43
|
+
getAgentState = () => "",
|
|
44
|
+
launchMode = "terminal",
|
|
45
|
+
agentProvider = "codex-cli",
|
|
46
|
+
assistantEngine = "auto",
|
|
47
|
+
cronTasks = [],
|
|
48
|
+
} = options;
|
|
49
|
+
const agents = activeAgents.length > 0
|
|
50
|
+
? activeAgents.slice(0, 3)
|
|
51
|
+
.map((id) => withActivityMarker(ensureAtPrefix(getAgentLabel(id)), getAgentState(id)))
|
|
52
|
+
.join(", ")
|
|
53
|
+
+ (activeAgents.length > 3 ? ` +${activeAgents.length - 3}` : "")
|
|
54
|
+
: "none";
|
|
55
|
+
return `{gray-fg}Agents:{/gray-fg} {cyan-fg}${agents}{/cyan-fg}`
|
|
56
|
+
+ ` {gray-fg}Mode:{/gray-fg} {cyan-fg}${launchMode}{/cyan-fg}`
|
|
57
|
+
+ ` {gray-fg}Agent:{/gray-fg} {cyan-fg}${providerLabel(agentProvider)}{/cyan-fg}`
|
|
58
|
+
+ ` {gray-fg}Assistant:{/gray-fg} {cyan-fg}${assistantLabel(assistantEngine)}{/cyan-fg}`
|
|
59
|
+
+ ` {gray-fg}Cron:{/gray-fg} {cyan-fg}${Array.isArray(cronTasks) ? cronTasks.length : 0}{/cyan-fg}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildProjectRailLine(options = {}) {
|
|
63
|
+
const {
|
|
64
|
+
projects = [],
|
|
65
|
+
selectedProjectIndex = -1,
|
|
66
|
+
projectListWindowStart = 0,
|
|
67
|
+
maxProjectWindow = 5,
|
|
68
|
+
activeProjectRoot = "",
|
|
69
|
+
projectsFocused = false,
|
|
70
|
+
} = options;
|
|
71
|
+
const rows = Array.isArray(projects) ? projects : [];
|
|
72
|
+
let windowStart = projectListWindowStart;
|
|
73
|
+
if (rows.length === 0) {
|
|
74
|
+
return {
|
|
75
|
+
hasProjects: false,
|
|
76
|
+
line: " {gray-fg}Projects:{/gray-fg} {cyan-fg}none{/cyan-fg}",
|
|
77
|
+
windowStart,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const activeRoot = String(activeProjectRoot || "");
|
|
82
|
+
const fallbackIndex = rows.findIndex((row) => String((row || {}).project_root || "") === activeRoot);
|
|
83
|
+
const normalizedSelectedIndex = Number.isFinite(selectedProjectIndex)
|
|
84
|
+
? Math.trunc(selectedProjectIndex)
|
|
85
|
+
: -1;
|
|
86
|
+
const safeSelectedIndex = normalizedSelectedIndex >= 0 && normalizedSelectedIndex < rows.length
|
|
87
|
+
? normalizedSelectedIndex
|
|
88
|
+
: (fallbackIndex >= 0 ? fallbackIndex : 0);
|
|
89
|
+
|
|
90
|
+
windowStart = clampAgentWindowWithSelection({
|
|
91
|
+
activeCount: rows.length,
|
|
92
|
+
maxWindow: Math.max(1, maxProjectWindow),
|
|
93
|
+
windowStart,
|
|
94
|
+
selectionIndex: safeSelectedIndex,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const maxItems = Math.max(1, Math.min(Math.max(1, maxProjectWindow), rows.length));
|
|
98
|
+
const start = windowStart;
|
|
99
|
+
const end = start + maxItems;
|
|
100
|
+
const visibleRows = rows.slice(start, end);
|
|
101
|
+
const projectParts = visibleRows.map((row, i) => {
|
|
102
|
+
const absoluteIndex = start + i;
|
|
103
|
+
const name = String((row && row.project_name) || (row && row.project_root) || "-");
|
|
104
|
+
const rowRoot = String((row && row.project_root) || "");
|
|
105
|
+
const isActiveProject = Boolean(activeRoot && rowRoot === activeRoot);
|
|
106
|
+
const isSelected = absoluteIndex === safeSelectedIndex;
|
|
107
|
+
if (projectsFocused && isSelected) {
|
|
108
|
+
return `{inverse}${name}{/inverse}`;
|
|
109
|
+
}
|
|
110
|
+
if (isActiveProject) {
|
|
111
|
+
return `{bold}{cyan-fg}${name}{/cyan-fg}{/bold}`;
|
|
112
|
+
}
|
|
113
|
+
return `{cyan-fg}${name}{/cyan-fg}`;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const leftMore = start > 0 ? "{gray-fg}<{/gray-fg} " : "";
|
|
117
|
+
const rightMore = end < rows.length ? " {gray-fg}>{/gray-fg}" : "";
|
|
118
|
+
return {
|
|
119
|
+
hasProjects: true,
|
|
120
|
+
line: ` {gray-fg}Projects:{/gray-fg} ${leftMore}${projectParts.join(" ")}${rightMore}`,
|
|
121
|
+
windowStart,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function buildDashboardDetailLine(options = {}) {
|
|
126
|
+
const {
|
|
127
|
+
globalMode = false,
|
|
28
128
|
dashboardView = "agents",
|
|
29
129
|
activeAgents = [],
|
|
30
130
|
selectedAgentIndex = -1,
|
|
31
131
|
agentListWindowStart = 0,
|
|
32
132
|
maxAgentWindow = 4,
|
|
33
133
|
getAgentLabel = (id) => id,
|
|
34
|
-
|
|
35
|
-
agentProvider = "codex-cli",
|
|
36
|
-
assistantEngine = "auto",
|
|
134
|
+
getAgentState = () => "",
|
|
37
135
|
selectedModeIndex = 0,
|
|
38
136
|
selectedProviderIndex = 0,
|
|
39
137
|
selectedAssistantIndex = 0,
|
|
@@ -49,105 +147,226 @@ function computeDashboardContent(options = {}) {
|
|
|
49
147
|
let content = " ";
|
|
50
148
|
let windowStart = agentListWindowStart;
|
|
51
149
|
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
150
|
+
if (dashboardView === "mode") {
|
|
151
|
+
const modeParts = modeOptions.map((mode, i) => {
|
|
152
|
+
if (i === selectedModeIndex) {
|
|
153
|
+
return `{inverse}${mode}{/inverse}`;
|
|
154
|
+
}
|
|
155
|
+
return `{cyan-fg}${mode}{/cyan-fg}`;
|
|
156
|
+
});
|
|
157
|
+
content += `{gray-fg}Mode:{/gray-fg} ${modeParts.join(" ")}`;
|
|
158
|
+
content += ` {gray-fg}│ ${dashHints.mode || ""}{/gray-fg}`;
|
|
159
|
+
return { content, windowStart };
|
|
160
|
+
}
|
|
64
161
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
162
|
+
if (dashboardView === "provider") {
|
|
163
|
+
const providerParts = providerOptions.map((opt, i) => {
|
|
164
|
+
if (i === selectedProviderIndex) {
|
|
165
|
+
return `{inverse}${opt.label}{/inverse}`;
|
|
166
|
+
}
|
|
167
|
+
return `{cyan-fg}${opt.label}{/cyan-fg}`;
|
|
168
|
+
});
|
|
169
|
+
content += `{gray-fg}Agent:{/gray-fg} ${providerParts.join(" ")}`;
|
|
170
|
+
content += ` {gray-fg}│ ${dashHints.provider || ""}{/gray-fg}`;
|
|
171
|
+
return { content, windowStart };
|
|
172
|
+
}
|
|
76
173
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
174
|
+
if (dashboardView === "assistant") {
|
|
175
|
+
const assistantParts = assistantOptions.map((opt, i) => {
|
|
176
|
+
if (i === selectedAssistantIndex) {
|
|
177
|
+
return `{inverse}${opt.label}{/inverse}`;
|
|
178
|
+
}
|
|
179
|
+
return `{cyan-fg}${opt.label}{/cyan-fg}`;
|
|
180
|
+
});
|
|
181
|
+
content += `{gray-fg}Assistant:{/gray-fg} ${assistantParts.join(" ")}`;
|
|
182
|
+
content += ` {gray-fg}│ ${dashHints.assistant || ""}{/gray-fg}`;
|
|
183
|
+
return { content, windowStart };
|
|
184
|
+
}
|
|
88
185
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
186
|
+
if (dashboardView === "resume") {
|
|
187
|
+
const resumeParts = resumeOptions.map((opt, i) => {
|
|
188
|
+
if (i === selectedResumeIndex) {
|
|
189
|
+
return `{inverse}${opt.label}{/inverse}`;
|
|
190
|
+
}
|
|
191
|
+
return `{cyan-fg}${opt.label}{/cyan-fg}`;
|
|
192
|
+
});
|
|
193
|
+
content += `{gray-fg}Resume:{/gray-fg} ${resumeParts.join(" ")}`;
|
|
194
|
+
content += ` {gray-fg}│ ${dashHints.resume || ""}{/gray-fg}`;
|
|
195
|
+
return { content, windowStart };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (dashboardView === "cron") {
|
|
199
|
+
const items = Array.isArray(cronTasks) ? cronTasks : [];
|
|
200
|
+
const summary = items.length > 0
|
|
201
|
+
? items.map((item) => item.summary || item.id || "").filter(Boolean).join(", ")
|
|
202
|
+
: "none";
|
|
203
|
+
content += `{gray-fg}Cron:{/gray-fg} {cyan-fg}${summary}{/cyan-fg}`;
|
|
204
|
+
content += ` {gray-fg}│ ${dashHints.cron || ""}{/gray-fg}`;
|
|
205
|
+
return { content, windowStart };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (activeAgents.length > 0) {
|
|
209
|
+
windowStart = clampAgentWindowWithSelection({
|
|
210
|
+
activeCount: activeAgents.length,
|
|
211
|
+
maxWindow: maxAgentWindow,
|
|
212
|
+
windowStart,
|
|
213
|
+
selectionIndex: selectedAgentIndex,
|
|
214
|
+
});
|
|
215
|
+
const maxItems = Math.max(1, Math.min(maxAgentWindow, activeAgents.length));
|
|
216
|
+
const start = windowStart;
|
|
217
|
+
const end = start + maxItems;
|
|
218
|
+
const visibleAgents = activeAgents.slice(start, end);
|
|
219
|
+
const agentParts = visibleAgents.map((agent, i) => {
|
|
220
|
+
const absoluteIndex = start + i;
|
|
221
|
+
const label = withActivityMarker(
|
|
222
|
+
ensureAtPrefix(getAgentLabel(agent)),
|
|
223
|
+
getAgentState(agent)
|
|
224
|
+
);
|
|
225
|
+
if (absoluteIndex === selectedAgentIndex) {
|
|
226
|
+
return `{inverse}${label}{/inverse}`;
|
|
227
|
+
}
|
|
228
|
+
return `{cyan-fg}${label}{/cyan-fg}`;
|
|
229
|
+
});
|
|
230
|
+
const leftMore = start > 0 ? "{gray-fg}<{/gray-fg} " : "";
|
|
231
|
+
const rightMore = end < activeAgents.length ? " {gray-fg}>{/gray-fg}" : "";
|
|
232
|
+
content += `{gray-fg}Agents:{/gray-fg} ${leftMore}${agentParts.join(" ")}${rightMore}`;
|
|
233
|
+
const agentsHint = globalMode
|
|
234
|
+
? (dashHints.agentsGlobal || dashHints.agents || "")
|
|
235
|
+
: (dashHints.agents || "");
|
|
236
|
+
content += ` {gray-fg}│ ${agentsHint}{/gray-fg}`;
|
|
237
|
+
} else {
|
|
238
|
+
content += "{gray-fg}Agents:{/gray-fg} {cyan-fg}none{/cyan-fg}";
|
|
239
|
+
content += ` {gray-fg}│ ${dashHints.agentsEmpty || ""}{/gray-fg}`;
|
|
240
|
+
}
|
|
241
|
+
return { content, windowStart };
|
|
242
|
+
}
|
|
100
243
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
244
|
+
function computeDashboardContent(options = {}) {
|
|
245
|
+
const {
|
|
246
|
+
globalMode = false,
|
|
247
|
+
focusMode = "input",
|
|
248
|
+
dashboardView = "agents",
|
|
249
|
+
activeAgents = [],
|
|
250
|
+
projects = [],
|
|
251
|
+
selectedProjectIndex = -1,
|
|
252
|
+
projectListWindowStart = 0,
|
|
253
|
+
maxProjectWindow = 5,
|
|
254
|
+
activeProjectRoot = "",
|
|
255
|
+
selectedAgentIndex = -1,
|
|
256
|
+
agentListWindowStart = 0,
|
|
257
|
+
maxAgentWindow = 4,
|
|
258
|
+
getAgentLabel = (id) => id,
|
|
259
|
+
getAgentState = () => "",
|
|
260
|
+
launchMode = "terminal",
|
|
261
|
+
agentProvider = "codex-cli",
|
|
262
|
+
assistantEngine = "auto",
|
|
263
|
+
selectedModeIndex = 0,
|
|
264
|
+
selectedProviderIndex = 0,
|
|
265
|
+
selectedAssistantIndex = 0,
|
|
266
|
+
selectedResumeIndex = 0,
|
|
267
|
+
cronTasks = [],
|
|
268
|
+
providerOptions = [],
|
|
269
|
+
assistantOptions = [],
|
|
270
|
+
resumeOptions = [],
|
|
271
|
+
dashHints = {},
|
|
272
|
+
modeOptions = DEFAULT_MODE_OPTIONS,
|
|
273
|
+
} = options;
|
|
274
|
+
|
|
275
|
+
if (globalMode) {
|
|
276
|
+
const projectsFocused = focusMode === "dashboard" && dashboardView === "projects";
|
|
277
|
+
const rail = buildProjectRailLine({
|
|
278
|
+
projects,
|
|
279
|
+
selectedProjectIndex,
|
|
280
|
+
projectListWindowStart,
|
|
281
|
+
maxProjectWindow,
|
|
282
|
+
activeProjectRoot,
|
|
283
|
+
projectsFocused,
|
|
284
|
+
});
|
|
285
|
+
if (!rail.hasProjects) {
|
|
286
|
+
const line2 = ` {gray-fg}${dashHints.projectsEmpty || "Run ufoo chat/daemon in projects to populate registry"}{/gray-fg}`;
|
|
287
|
+
return {
|
|
288
|
+
content: `${rail.line}\n${line2}`,
|
|
289
|
+
windowStart: rail.windowStart,
|
|
290
|
+
};
|
|
109
291
|
}
|
|
110
292
|
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const end = start + maxItems;
|
|
121
|
-
const visibleAgents = activeAgents.slice(start, end);
|
|
122
|
-
const agentParts = visibleAgents.map((agent, i) => {
|
|
123
|
-
const absoluteIndex = start + i;
|
|
124
|
-
const label = ensureAtPrefix(getAgentLabel(agent));
|
|
125
|
-
if (absoluteIndex === selectedAgentIndex) {
|
|
126
|
-
return `{inverse}${label}{/inverse}`;
|
|
127
|
-
}
|
|
128
|
-
return `{cyan-fg}${label}{/cyan-fg}`;
|
|
293
|
+
if (focusMode !== "dashboard" || projectsFocused) {
|
|
294
|
+
const line2 = buildSummaryLine({
|
|
295
|
+
activeAgents,
|
|
296
|
+
getAgentLabel,
|
|
297
|
+
getAgentState,
|
|
298
|
+
launchMode,
|
|
299
|
+
agentProvider,
|
|
300
|
+
assistantEngine,
|
|
301
|
+
cronTasks,
|
|
129
302
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
} else {
|
|
135
|
-
content += "{gray-fg}Agents:{/gray-fg} {cyan-fg}none{/cyan-fg}";
|
|
136
|
-
content += ` {gray-fg}│ ${dashHints.agentsEmpty || ""}{/gray-fg}`;
|
|
303
|
+
return {
|
|
304
|
+
content: `${rail.line}\n ${line2}`,
|
|
305
|
+
windowStart: rail.windowStart,
|
|
306
|
+
};
|
|
137
307
|
}
|
|
138
|
-
|
|
308
|
+
|
|
309
|
+
const detail = buildDashboardDetailLine({
|
|
310
|
+
globalMode,
|
|
311
|
+
dashboardView,
|
|
312
|
+
activeAgents,
|
|
313
|
+
selectedAgentIndex,
|
|
314
|
+
agentListWindowStart,
|
|
315
|
+
maxAgentWindow,
|
|
316
|
+
getAgentLabel,
|
|
317
|
+
getAgentState,
|
|
318
|
+
selectedModeIndex,
|
|
319
|
+
selectedProviderIndex,
|
|
320
|
+
selectedAssistantIndex,
|
|
321
|
+
selectedResumeIndex,
|
|
322
|
+
cronTasks,
|
|
323
|
+
providerOptions,
|
|
324
|
+
assistantOptions,
|
|
325
|
+
resumeOptions,
|
|
326
|
+
dashHints,
|
|
327
|
+
modeOptions,
|
|
328
|
+
});
|
|
329
|
+
return {
|
|
330
|
+
content: `${rail.line}\n${detail.content}`,
|
|
331
|
+
windowStart: detail.windowStart,
|
|
332
|
+
};
|
|
139
333
|
}
|
|
140
334
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
335
|
+
if (focusMode === "dashboard") {
|
|
336
|
+
return buildDashboardDetailLine({
|
|
337
|
+
globalMode,
|
|
338
|
+
dashboardView,
|
|
339
|
+
activeAgents,
|
|
340
|
+
selectedAgentIndex,
|
|
341
|
+
agentListWindowStart,
|
|
342
|
+
maxAgentWindow,
|
|
343
|
+
getAgentLabel,
|
|
344
|
+
getAgentState,
|
|
345
|
+
selectedModeIndex,
|
|
346
|
+
selectedProviderIndex,
|
|
347
|
+
selectedAssistantIndex,
|
|
348
|
+
selectedResumeIndex,
|
|
349
|
+
cronTasks,
|
|
350
|
+
providerOptions,
|
|
351
|
+
assistantOptions,
|
|
352
|
+
resumeOptions,
|
|
353
|
+
dashHints,
|
|
354
|
+
modeOptions,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
149
357
|
|
|
150
|
-
|
|
358
|
+
let content = " ";
|
|
359
|
+
content += buildSummaryLine({
|
|
360
|
+
activeAgents,
|
|
361
|
+
getAgentLabel,
|
|
362
|
+
getAgentState,
|
|
363
|
+
launchMode,
|
|
364
|
+
agentProvider,
|
|
365
|
+
assistantEngine,
|
|
366
|
+
cronTasks,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
return { content, windowStart: agentListWindowStart };
|
|
151
370
|
}
|
|
152
371
|
|
|
153
372
|
module.exports = {
|