u-foo 1.7.4 → 1.8.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 +9 -1
- package/README.zh-CN.md +9 -1
- package/bin/ufoo.js +4 -2
- package/package.json +1 -1
- package/src/agent/cliRunner.js +3 -2
- package/src/agent/ucodeBootstrap.js +5 -3
- package/src/agent/ufooAgent.js +185 -6
- package/src/assistant/constants.js +1 -1
- package/src/assistant/engine.js +1 -6
- package/src/chat/commandExecutor.js +116 -19
- package/src/chat/commands.js +8 -1
- package/src/chat/completionController.js +40 -0
- package/src/chat/cronScheduler.js +37 -6
- package/src/chat/daemonMessageRouter.js +23 -3
- package/src/chat/dashboardKeyController.js +48 -59
- package/src/chat/dashboardView.js +31 -39
- package/src/chat/index.js +154 -77
- package/src/chat/inputListenerController.js +14 -0
- package/src/chat/inputSubmitHandler.js +9 -5
- package/src/chat/settingsController.js +0 -28
- package/src/chat/transientAgentState.js +64 -0
- package/src/cli/groupCoreCommands.js +21 -12
- package/src/cli.js +23 -1
- package/src/daemon/cronOps.js +48 -11
- package/src/daemon/groupOrchestrator.js +581 -97
- package/src/daemon/index.js +420 -5
- package/src/daemon/ops.js +25 -7
- package/src/daemon/promptLoop.js +16 -0
- package/src/daemon/promptRequest.js +126 -2
- package/src/daemon/reporting.js +18 -0
- package/src/daemon/soloBootstrap.js +435 -0
- package/src/daemon/status.js +7 -1
- package/src/globalMode.js +33 -0
- package/src/group/bootstrap.js +157 -0
- package/src/group/promptProfiles.js +646 -0
- package/src/group/templateValidation.js +99 -0
- package/src/group/validateTemplate.js +36 -5
- package/src/init/index.js +13 -7
- package/src/report/store.js +6 -0
- package/src/shared/eventContract.js +1 -0
- package/templates/groups/{dev-basic.json → build-lane.json} +38 -34
- package/templates/groups/product-discovery.json +79 -0
- package/templates/groups/ui-polish.json +87 -0
- package/templates/groups/verify-ship.json +79 -0
- package/templates/groups/research-quick.json +0 -49
|
@@ -18,12 +18,14 @@ function createDashboardKeyController(options = {}) {
|
|
|
18
18
|
exitDashboardMode = () => {},
|
|
19
19
|
setLaunchMode = () => {},
|
|
20
20
|
setAgentProvider = () => {},
|
|
21
|
-
setAssistantEngine = () => {},
|
|
22
21
|
setAutoResume = () => {},
|
|
23
22
|
clampAgentWindow = () => {},
|
|
24
23
|
clampAgentWindowWithSelection = () => {},
|
|
25
24
|
requestProjectSwitch = () => {},
|
|
26
25
|
requestCloseProject = () => {},
|
|
26
|
+
requestCron = () => {},
|
|
27
|
+
setGlobalScope = () => {},
|
|
28
|
+
getGlobalScope = () => "",
|
|
27
29
|
renderDashboard = () => {},
|
|
28
30
|
renderAgentDashboard = () => {},
|
|
29
31
|
renderScreen = () => {},
|
|
@@ -232,10 +234,9 @@ function createDashboardKeyController(options = {}) {
|
|
|
232
234
|
}
|
|
233
235
|
|
|
234
236
|
if (key.name === "down") {
|
|
235
|
-
state.dashboardView = "
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
state.selectedAssistantIndex = nextIndex >= 0 ? nextIndex : 0;
|
|
237
|
+
state.dashboardView = "cron";
|
|
238
|
+
const cronTasks = Array.isArray(state.cronTasks) ? state.cronTasks : [];
|
|
239
|
+
state.selectedCronIndex = cronTasks.length > 0 ? 0 : -1;
|
|
239
240
|
renderDashboardAndScreen();
|
|
240
241
|
return true;
|
|
241
242
|
}
|
|
@@ -261,34 +262,23 @@ function createDashboardKeyController(options = {}) {
|
|
|
261
262
|
return true;
|
|
262
263
|
}
|
|
263
264
|
|
|
264
|
-
function
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
if (key.name === "up") {
|
|
268
|
-
state.dashboardView = "provider";
|
|
269
|
-
renderDashboardAndScreen();
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
if (key.name === "escape" || key.name === "enter" || key.name === "return") {
|
|
273
|
-
exitDashboardMode(false);
|
|
274
|
-
return true;
|
|
275
|
-
}
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
265
|
+
function handleCronKey(key) {
|
|
266
|
+
const cronTasks = Array.isArray(state.cronTasks) ? state.cronTasks : [];
|
|
267
|
+
const maxIndex = cronTasks.length - 1;
|
|
278
268
|
|
|
279
269
|
if (key.name === "left") {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
270
|
+
if (maxIndex >= 0 && state.selectedCronIndex > 0) {
|
|
271
|
+
state.selectedCronIndex -= 1;
|
|
272
|
+
renderDashboardAndScreen();
|
|
273
|
+
}
|
|
284
274
|
return true;
|
|
285
275
|
}
|
|
286
276
|
|
|
287
277
|
if (key.name === "right") {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
278
|
+
if (maxIndex >= 0 && state.selectedCronIndex < maxIndex) {
|
|
279
|
+
state.selectedCronIndex += 1;
|
|
280
|
+
renderDashboardAndScreen();
|
|
281
|
+
}
|
|
292
282
|
return true;
|
|
293
283
|
}
|
|
294
284
|
|
|
@@ -298,35 +288,15 @@ function createDashboardKeyController(options = {}) {
|
|
|
298
288
|
return true;
|
|
299
289
|
}
|
|
300
290
|
|
|
301
|
-
if (key.name === "down") {
|
|
302
|
-
state.dashboardView = "cron";
|
|
303
|
-
renderDashboardAndScreen();
|
|
304
|
-
return true;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (key.name === "enter" || key.name === "return") {
|
|
308
|
-
const selected = options[state.selectedAssistantIndex];
|
|
309
|
-
if (selected) setAssistantEngine(selected.value);
|
|
310
|
-
exitDashboardMode(false);
|
|
311
|
-
return true;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (key.name === "escape") {
|
|
315
|
-
exitDashboardMode(false);
|
|
316
|
-
return true;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return true;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function handleCronKey(key) {
|
|
323
|
-
if (key.name === "up") {
|
|
324
|
-
state.dashboardView = "assistant";
|
|
325
|
-
renderDashboardAndScreen();
|
|
326
|
-
return true;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
291
|
if (key.name === "x" && key.ctrl) {
|
|
292
|
+
if (maxIndex >= 0 && state.selectedCronIndex >= 0 && state.selectedCronIndex <= maxIndex) {
|
|
293
|
+
const task = cronTasks[state.selectedCronIndex];
|
|
294
|
+
const id = task && task.id ? String(task.id).trim() : "";
|
|
295
|
+
if (id) {
|
|
296
|
+
requestCron({ operation: "stop", id });
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
330
300
|
exitDashboardMode(false);
|
|
331
301
|
return true;
|
|
332
302
|
}
|
|
@@ -415,7 +385,9 @@ function createDashboardKeyController(options = {}) {
|
|
|
415
385
|
const next = current - 1;
|
|
416
386
|
state.selectedProjectIndex = next;
|
|
417
387
|
renderDashboardAndScreen();
|
|
418
|
-
|
|
388
|
+
if (getGlobalScope() === "project") {
|
|
389
|
+
requestProjectSwitch(next);
|
|
390
|
+
}
|
|
419
391
|
}
|
|
420
392
|
return true;
|
|
421
393
|
}
|
|
@@ -426,17 +398,35 @@ function createDashboardKeyController(options = {}) {
|
|
|
426
398
|
const next = current + 1;
|
|
427
399
|
state.selectedProjectIndex = next;
|
|
428
400
|
renderDashboardAndScreen();
|
|
429
|
-
|
|
401
|
+
if (getGlobalScope() === "project") {
|
|
402
|
+
requestProjectSwitch(next);
|
|
403
|
+
}
|
|
430
404
|
}
|
|
431
405
|
return true;
|
|
432
406
|
}
|
|
433
407
|
|
|
434
|
-
if (key.name === "up"
|
|
408
|
+
if (key.name === "up") {
|
|
409
|
+
exitDashboardMode(false);
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (key.name === "escape") {
|
|
414
|
+
if (getGlobalScope() === "project") {
|
|
415
|
+
setGlobalScope("controller");
|
|
416
|
+
}
|
|
435
417
|
exitDashboardMode(false);
|
|
436
418
|
return true;
|
|
437
419
|
}
|
|
438
420
|
|
|
439
421
|
if (key.name === "enter" || key.name === "return") {
|
|
422
|
+
const current = Number.isFinite(state.selectedProjectIndex) ? state.selectedProjectIndex : 0;
|
|
423
|
+
if (current >= 0 && current < projects.length) {
|
|
424
|
+
const selectedRow = projects[current];
|
|
425
|
+
const selectedProjectRoot = String((selectedRow && selectedRow.project_root) || "");
|
|
426
|
+
if (selectedProjectRoot) {
|
|
427
|
+
setGlobalScope("project", selectedProjectRoot);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
440
430
|
exitDashboardMode(false);
|
|
441
431
|
return true;
|
|
442
432
|
}
|
|
@@ -552,7 +542,6 @@ function createDashboardKeyController(options = {}) {
|
|
|
552
542
|
|
|
553
543
|
if (state.dashboardView === "mode") return handleModeKey(key);
|
|
554
544
|
if (state.dashboardView === "provider") return handleProviderKey(key);
|
|
555
|
-
if (state.dashboardView === "assistant") return handleAssistantKey(key);
|
|
556
545
|
if (state.dashboardView === "resume") return handleResumeKey(key);
|
|
557
546
|
if (state.dashboardView === "cron") return handleCronKey(key);
|
|
558
547
|
|
|
@@ -8,14 +8,6 @@ function providerLabel(value) {
|
|
|
8
8
|
return "codex";
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
function assistantLabel(value) {
|
|
12
|
-
if (value === "codex") return "codex";
|
|
13
|
-
if (value === "claude") return "claude";
|
|
14
|
-
if (value === "ufoo") return "ucode";
|
|
15
|
-
if (value === "ucode") return "ucode";
|
|
16
|
-
return "auto";
|
|
17
|
-
}
|
|
18
|
-
|
|
19
11
|
function ensureAtPrefix(value) {
|
|
20
12
|
const text = String(value || "").trim();
|
|
21
13
|
if (!text) return text;
|
|
@@ -43,8 +35,8 @@ function buildSummaryLine(options = {}) {
|
|
|
43
35
|
getAgentState = () => "",
|
|
44
36
|
launchMode = "terminal",
|
|
45
37
|
agentProvider = "codex-cli",
|
|
46
|
-
assistantEngine = "auto",
|
|
47
38
|
cronTasks = [],
|
|
39
|
+
pendingReports = 0,
|
|
48
40
|
} = options;
|
|
49
41
|
const agents = activeAgents.length > 0
|
|
50
42
|
? activeAgents.slice(0, 3)
|
|
@@ -55,7 +47,7 @@ function buildSummaryLine(options = {}) {
|
|
|
55
47
|
return `{gray-fg}Agents:{/gray-fg} {cyan-fg}${agents}{/cyan-fg}`
|
|
56
48
|
+ ` {gray-fg}Mode:{/gray-fg} {cyan-fg}${launchMode}{/cyan-fg}`
|
|
57
49
|
+ ` {gray-fg}Agent:{/gray-fg} {cyan-fg}${providerLabel(agentProvider)}{/cyan-fg}`
|
|
58
|
-
+ ` {gray-fg}
|
|
50
|
+
+ ` {gray-fg}Reports:{/gray-fg} {cyan-fg}${Number.isFinite(pendingReports) ? pendingReports : 0}{/cyan-fg}`
|
|
59
51
|
+ ` {gray-fg}Cron:{/gray-fg} {cyan-fg}${Array.isArray(cronTasks) ? cronTasks.length : 0}{/cyan-fg}`;
|
|
60
52
|
}
|
|
61
53
|
|
|
@@ -67,6 +59,8 @@ function buildProjectRailLine(options = {}) {
|
|
|
67
59
|
maxProjectWindow = 5,
|
|
68
60
|
activeProjectRoot = "",
|
|
69
61
|
projectsFocused = false,
|
|
62
|
+
globalScope = "",
|
|
63
|
+
dashboardHint = "",
|
|
70
64
|
} = options;
|
|
71
65
|
const rows = Array.isArray(projects) ? projects : [];
|
|
72
66
|
let windowStart = projectListWindowStart;
|
|
@@ -102,7 +96,7 @@ function buildProjectRailLine(options = {}) {
|
|
|
102
96
|
const absoluteIndex = start + i;
|
|
103
97
|
const name = String((row && row.project_name) || (row && row.project_root) || "-");
|
|
104
98
|
const rowRoot = String((row && row.project_root) || "");
|
|
105
|
-
const isActiveProject = Boolean(activeRoot && rowRoot === activeRoot);
|
|
99
|
+
const isActiveProject = globalScope !== "controller" && Boolean(activeRoot && rowRoot === activeRoot);
|
|
106
100
|
const isSelected = absoluteIndex === safeSelectedIndex;
|
|
107
101
|
if (projectsFocused && isSelected) {
|
|
108
102
|
return `{inverse}${name}{/inverse}`;
|
|
@@ -115,9 +109,10 @@ function buildProjectRailLine(options = {}) {
|
|
|
115
109
|
|
|
116
110
|
const leftMore = start > 0 ? "{gray-fg}<{/gray-fg} " : "";
|
|
117
111
|
const rightMore = end < rows.length ? " {gray-fg}>{/gray-fg}" : "";
|
|
112
|
+
const hintPart = dashboardHint ? `{|}{gray-fg}${dashboardHint}{/gray-fg}` : "";
|
|
118
113
|
return {
|
|
119
114
|
hasProjects: true,
|
|
120
|
-
line: ` {gray-fg}Projects:{/gray-fg} ${leftMore}${projectParts.join(" ")}${rightMore}`,
|
|
115
|
+
line: ` {gray-fg}Projects:{/gray-fg} ${leftMore}${projectParts.join(" ")}${rightMore}${hintPart}`,
|
|
121
116
|
windowStart,
|
|
122
117
|
};
|
|
123
118
|
}
|
|
@@ -134,11 +129,11 @@ function buildDashboardDetailLine(options = {}) {
|
|
|
134
129
|
getAgentState = () => "",
|
|
135
130
|
selectedModeIndex = 0,
|
|
136
131
|
selectedProviderIndex = 0,
|
|
137
|
-
selectedAssistantIndex = 0,
|
|
138
132
|
selectedResumeIndex = 0,
|
|
133
|
+
selectedCronIndex = -1,
|
|
139
134
|
cronTasks = [],
|
|
135
|
+
pendingReports = 0,
|
|
140
136
|
providerOptions = [],
|
|
141
|
-
assistantOptions = [],
|
|
142
137
|
resumeOptions = [],
|
|
143
138
|
dashHints = {},
|
|
144
139
|
modeOptions = DEFAULT_MODE_OPTIONS,
|
|
@@ -171,18 +166,6 @@ function buildDashboardDetailLine(options = {}) {
|
|
|
171
166
|
return { content, windowStart };
|
|
172
167
|
}
|
|
173
168
|
|
|
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
|
-
}
|
|
185
|
-
|
|
186
169
|
if (dashboardView === "resume") {
|
|
187
170
|
const resumeParts = resumeOptions.map((opt, i) => {
|
|
188
171
|
if (i === selectedResumeIndex) {
|
|
@@ -198,9 +181,15 @@ function buildDashboardDetailLine(options = {}) {
|
|
|
198
181
|
if (dashboardView === "cron") {
|
|
199
182
|
const items = Array.isArray(cronTasks) ? cronTasks : [];
|
|
200
183
|
const summary = items.length > 0
|
|
201
|
-
? items.map((item) =>
|
|
202
|
-
|
|
203
|
-
|
|
184
|
+
? items.map((item, index) => {
|
|
185
|
+
const label = item.label || item.summary || item.id || "";
|
|
186
|
+
if (!label) return "";
|
|
187
|
+
return index === selectedCronIndex
|
|
188
|
+
? `{inverse}${label}{/inverse}`
|
|
189
|
+
: `{cyan-fg}${label}{/cyan-fg}`;
|
|
190
|
+
}).filter(Boolean).join(", ")
|
|
191
|
+
: "{cyan-fg}none{/cyan-fg}";
|
|
192
|
+
content += `{gray-fg}Cron:{/gray-fg} ${summary}`;
|
|
204
193
|
content += ` {gray-fg}│ ${dashHints.cron || ""}{/gray-fg}`;
|
|
205
194
|
return { content, windowStart };
|
|
206
195
|
}
|
|
@@ -244,6 +233,7 @@ function buildDashboardDetailLine(options = {}) {
|
|
|
244
233
|
function computeDashboardContent(options = {}) {
|
|
245
234
|
const {
|
|
246
235
|
globalMode = false,
|
|
236
|
+
globalScope = "controller",
|
|
247
237
|
focusMode = "input",
|
|
248
238
|
dashboardView = "agents",
|
|
249
239
|
activeAgents = [],
|
|
@@ -259,14 +249,13 @@ function computeDashboardContent(options = {}) {
|
|
|
259
249
|
getAgentState = () => "",
|
|
260
250
|
launchMode = "terminal",
|
|
261
251
|
agentProvider = "codex-cli",
|
|
262
|
-
assistantEngine = "auto",
|
|
263
252
|
selectedModeIndex = 0,
|
|
264
253
|
selectedProviderIndex = 0,
|
|
265
|
-
selectedAssistantIndex = 0,
|
|
266
254
|
selectedResumeIndex = 0,
|
|
255
|
+
selectedCronIndex = -1,
|
|
267
256
|
cronTasks = [],
|
|
257
|
+
pendingReports = 0,
|
|
268
258
|
providerOptions = [],
|
|
269
|
-
assistantOptions = [],
|
|
270
259
|
resumeOptions = [],
|
|
271
260
|
dashHints = {},
|
|
272
261
|
modeOptions = DEFAULT_MODE_OPTIONS,
|
|
@@ -274,6 +263,10 @@ function computeDashboardContent(options = {}) {
|
|
|
274
263
|
|
|
275
264
|
if (globalMode) {
|
|
276
265
|
const projectsFocused = focusMode === "dashboard" && dashboardView === "projects";
|
|
266
|
+
let dashboardHint = "";
|
|
267
|
+
if (projectsFocused) {
|
|
268
|
+
dashboardHint = globalScope === "controller" ? "Enter\u2192project" : "Esc\u2192global";
|
|
269
|
+
}
|
|
277
270
|
const rail = buildProjectRailLine({
|
|
278
271
|
projects,
|
|
279
272
|
selectedProjectIndex,
|
|
@@ -281,6 +274,8 @@ function computeDashboardContent(options = {}) {
|
|
|
281
274
|
maxProjectWindow,
|
|
282
275
|
activeProjectRoot,
|
|
283
276
|
projectsFocused,
|
|
277
|
+
globalScope,
|
|
278
|
+
dashboardHint,
|
|
284
279
|
});
|
|
285
280
|
if (!rail.hasProjects) {
|
|
286
281
|
const line2 = ` {gray-fg}${dashHints.projectsEmpty || "Run ufoo chat/daemon in projects to populate registry"}{/gray-fg}`;
|
|
@@ -297,8 +292,8 @@ function computeDashboardContent(options = {}) {
|
|
|
297
292
|
getAgentState,
|
|
298
293
|
launchMode,
|
|
299
294
|
agentProvider,
|
|
300
|
-
assistantEngine,
|
|
301
295
|
cronTasks,
|
|
296
|
+
pendingReports,
|
|
302
297
|
});
|
|
303
298
|
return {
|
|
304
299
|
content: `${rail.line}\n ${line2}`,
|
|
@@ -317,11 +312,10 @@ function computeDashboardContent(options = {}) {
|
|
|
317
312
|
getAgentState,
|
|
318
313
|
selectedModeIndex,
|
|
319
314
|
selectedProviderIndex,
|
|
320
|
-
selectedAssistantIndex,
|
|
321
315
|
selectedResumeIndex,
|
|
316
|
+
selectedCronIndex,
|
|
322
317
|
cronTasks,
|
|
323
318
|
providerOptions,
|
|
324
|
-
assistantOptions,
|
|
325
319
|
resumeOptions,
|
|
326
320
|
dashHints,
|
|
327
321
|
modeOptions,
|
|
@@ -344,11 +338,10 @@ function computeDashboardContent(options = {}) {
|
|
|
344
338
|
getAgentState,
|
|
345
339
|
selectedModeIndex,
|
|
346
340
|
selectedProviderIndex,
|
|
347
|
-
selectedAssistantIndex,
|
|
348
341
|
selectedResumeIndex,
|
|
342
|
+
selectedCronIndex,
|
|
349
343
|
cronTasks,
|
|
350
344
|
providerOptions,
|
|
351
|
-
assistantOptions,
|
|
352
345
|
resumeOptions,
|
|
353
346
|
dashHints,
|
|
354
347
|
modeOptions,
|
|
@@ -362,8 +355,8 @@ function computeDashboardContent(options = {}) {
|
|
|
362
355
|
getAgentState,
|
|
363
356
|
launchMode,
|
|
364
357
|
agentProvider,
|
|
365
|
-
assistantEngine,
|
|
366
358
|
cronTasks,
|
|
359
|
+
pendingReports,
|
|
367
360
|
});
|
|
368
361
|
|
|
369
362
|
return { content, windowStart: agentListWindowStart };
|
|
@@ -372,5 +365,4 @@ function computeDashboardContent(options = {}) {
|
|
|
372
365
|
module.exports = {
|
|
373
366
|
computeDashboardContent,
|
|
374
367
|
providerLabel,
|
|
375
|
-
assistantLabel,
|
|
376
368
|
};
|