u-foo 2.3.31 → 2.4.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 +157 -213
- package/README.zh-CN.md +151 -197
- package/SKILLS/ufoo/SKILL.md +8 -8
- package/bin/uagy.js +69 -0
- package/bin/uclaude.js +2 -2
- package/bin/ucode.js +4 -4
- package/bin/ucodex.js +2 -2
- package/bin/ufoo.js +5 -23
- package/modules/AGENTS.template.md +1 -1
- package/modules/bus/SKILLS/ubus/SKILL.md +35 -10
- package/package.json +9 -5
- package/scripts/chat-app-smoke.js +30 -0
- package/scripts/global-chat-switch-benchmark.js +5 -5
- package/scripts/ink-demo.js +23 -0
- package/scripts/ink-smoke.js +30 -0
- package/scripts/ucode-app-smoke.js +36 -0
- package/src/{agent → agents/activity}/activityDetector.js +39 -2
- package/src/{agent → agents/activity}/activityStatePublisher.js +1 -1
- package/src/{agent → agents/activity}/activityStateWriter.js +2 -2
- package/src/{agent → agents/activity}/activityTracker.js +1 -1
- package/src/agents/activity/index.js +8 -0
- package/src/{agent → agents/controller}/controllerToolExecutor.js +4 -4
- package/src/agents/controller/index.js +8 -0
- package/src/{agent → agents/controller}/loopObservability.js +2 -2
- package/src/{agent → agents/controller}/loopRuntime.js +1 -1
- package/src/{agent → agents/controller}/ufooAgent.js +9 -9
- package/src/agents/index.js +10 -0
- package/src/agents/internal/index.js +3 -0
- package/src/{agent → agents/internal}/internalRunner.js +45 -22
- package/src/agents/launch/agyConversation.js +159 -0
- package/src/agents/launch/index.js +12 -0
- package/src/{agent → agents/launch}/launchEnvironment.js +2 -3
- package/src/{agent → agents/launch}/launcher.js +64 -21
- package/src/{agent → agents/launch}/notifier.js +23 -12
- package/src/{agent → agents/launch}/ptyRunner.js +44 -12
- package/src/{agent → agents/launch}/ptyWrapper.js +2 -2
- package/src/{agent → agents/launch}/publisherRouting.js +1 -1
- package/src/{agent → agents/launch}/readyDetector.js +23 -0
- package/src/{agent → agents/prompts}/defaultBootstrap.js +63 -4
- package/src/{group/bootstrap.js → agents/prompts/groupBootstrap.js} +41 -6
- package/src/agents/prompts/index.js +8 -0
- package/src/{code/prompts → agents/prompts/native}/index.js +1 -1
- package/src/{agent → agents/providers}/claudeThreadProvider.js +1 -1
- package/src/{agent → agents/providers}/codexThreadProvider.js +1 -1
- package/src/{agent → agents/providers}/directAuthStatus.js +184 -1
- package/src/agents/providers/index.js +13 -0
- package/src/{agent → agents/providers}/upstreamTransport.js +2 -2
- package/src/{chat → app/chat}/agentSockets.js +1 -1
- package/src/{chat → app/chat}/commandExecutor.js +56 -28
- package/src/{chat → app/chat}/commands.js +119 -5
- package/src/{chat → app/chat}/daemonConnection.js +1 -1
- package/src/{chat → app/chat}/daemonMessageRouter.js +54 -4
- package/src/{chat → app/chat}/daemonTransport.js +2 -1
- package/src/{chat → app/chat}/dashboardView.js +2 -21
- package/src/app/chat/index.js +6 -0
- package/src/{chat → app/chat}/inputSubmitHandler.js +38 -13
- package/src/{chat → app/chat}/internalAgentLogHistory.js +1 -1
- package/src/app/chat/multiWindow/index.js +268 -0
- package/src/app/chat/multiWindow/paneLayout.js +84 -0
- package/src/app/chat/multiWindow/paneManager.js +299 -0
- package/src/app/chat/multiWindow/renderer.js +384 -0
- package/src/app/chat/multiWindow/virtualTerminal.js +327 -0
- package/src/{chat → app/chat}/projectCloseController.js +1 -1
- package/src/app/chat/shellCommand.js +42 -0
- package/src/{chat → app/chat}/transport.js +16 -3
- package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
- package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
- package/src/{init/index.js → app/cli/features/init.js} +14 -32
- package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
- package/src/app/cli/index.js +9 -0
- package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
- package/src/{cli.js → app/cli/run.js} +62 -59
- package/src/app/index.js +6 -0
- package/src/code/agent.js +10 -9
- package/src/code/index.js +2 -0
- package/src/code/launcher/index.js +9 -0
- package/src/{agent → code/launcher}/ucode.js +7 -8
- package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
- package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
- package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
- package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
- package/src/code/nativeRunner.js +4 -4
- package/src/code/taskDecomposer.js +5 -4
- package/src/code/tui.js +39 -1997
- package/src/config.js +15 -2
- package/src/{bus → coordination/bus}/activate.js +2 -2
- package/src/{bus → coordination/bus}/daemon.js +15 -5
- package/src/coordination/bus/envelope.js +173 -0
- package/src/{bus → coordination/bus}/index.js +7 -3
- package/src/{bus → coordination/bus}/inject.js +11 -3
- package/src/{bus → coordination/bus}/message.js +1 -1
- package/src/coordination/bus/messageMeta.js +130 -0
- package/src/coordination/bus/promptEnvelope.js +65 -0
- package/src/{bus → coordination/bus}/shake.js +1 -1
- package/src/{bus → coordination/bus}/store.js +3 -3
- package/src/{bus → coordination/bus}/subscriber.js +2 -2
- package/src/{bus → coordination/bus}/utils.js +2 -2
- package/src/{history → coordination/history}/inputTimeline.js +5 -5
- package/src/coordination/index.js +10 -0
- package/src/{memory → coordination/memory}/historySearch.js +1 -1
- package/src/{memory → coordination/memory}/index.js +3 -3
- package/src/{report → coordination/report}/store.js +2 -2
- package/src/{ufoo → coordination/state}/agentRegistryDiagnostics.js +43 -0
- package/src/{status → coordination/status}/index.js +3 -3
- package/src/online/bridge.js +2 -2
- package/src/{controller → orchestration/controller}/flags.js +1 -1
- package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
- package/src/orchestration/controller/index.js +10 -0
- package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
- package/src/orchestration/groups/bootstrap.js +3 -0
- package/src/orchestration/groups/index.js +10 -0
- package/src/orchestration/groups/promptProfiles.js +3 -0
- package/src/{group → orchestration/groups}/templates.js +1 -1
- package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
- package/src/orchestration/index.js +7 -0
- package/src/orchestration/solo/index.js +3 -0
- package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
- package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
- package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
- package/src/{daemon → runtime/daemon}/index.js +273 -79
- package/src/{daemon → runtime/daemon}/ipcServer.js +24 -2
- package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
- package/src/{daemon → runtime/daemon}/ops.js +48 -61
- package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
- package/src/{daemon → runtime/daemon}/promptRequest.js +13 -8
- package/src/runtime/daemon/providerSessions.js +230 -0
- package/src/{daemon → runtime/daemon}/reporting.js +4 -4
- package/src/{daemon → runtime/daemon}/run.js +12 -5
- package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
- package/src/{daemon → runtime/daemon}/status.js +5 -5
- package/src/runtime/index.js +10 -0
- package/src/runtime/process/nodeExecutable.js +26 -0
- package/src/{projects → runtime/projects}/registry.js +1 -1
- package/src/{projects → runtime/projects}/runtimes.js +1 -1
- package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
- package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
- package/src/tools/handlers/common.js +1 -1
- package/src/tools/handlers/listAgents.js +1 -1
- package/src/tools/handlers/memory.js +3 -3
- package/src/tools/handlers/readBusSummary.js +1 -1
- package/src/tools/handlers/readOpenDecisions.js +1 -1
- package/src/tools/handlers/readProjectRegistry.js +1 -1
- package/src/tools/handlers/readPromptHistory.js +2 -2
- package/src/tools/schemaFixtures.js +1 -1
- package/src/ui/MIGRATION.md +336 -0
- package/src/ui/format/index.js +974 -0
- package/src/ui/index.js +9 -0
- package/src/ui/ink/ChatApp.js +3674 -0
- package/src/ui/ink/DashboardBar.js +685 -0
- package/src/ui/ink/InkDemo.js +96 -0
- package/src/ui/ink/MultilineInput.js +612 -0
- package/src/ui/ink/UcodeApp.js +822 -0
- package/src/ui/ink/agentMirror.js +730 -0
- package/src/ui/ink/chatReducer.js +359 -0
- package/src/ui/runInk.js +57 -0
- package/src/bus/messageMeta.js +0 -52
- package/src/chat/agentViewController.js +0 -1072
- package/src/chat/chatLogController.js +0 -138
- package/src/chat/completionController.js +0 -533
- package/src/chat/dashboardKeyController.js +0 -573
- package/src/chat/index.js +0 -2214
- package/src/chat/inputHistoryController.js +0 -135
- package/src/chat/inputListenerController.js +0 -470
- package/src/chat/layout.js +0 -186
- package/src/chat/pasteController.js +0 -81
- package/src/chat/statusLineController.js +0 -223
- package/src/chat/streamTracker.js +0 -156
- package/src/code/config +0 -0
- package/src/daemon/providerSessions.js +0 -488
- package/src/terminal/adapters/internalPtyAdapter.js +0 -42
- /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
- /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
- /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
- /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
- /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
- /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
- /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
- /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
- /package/src/{agent → agents/providers}/credentials/index.js +0 -0
- /package/src/{chat → app/chat}/agentBar.js +0 -0
- /package/src/{chat → app/chat}/agentDirectory.js +0 -0
- /package/src/{chat → app/chat}/cronScheduler.js +0 -0
- /package/src/{chat → app/chat}/daemonCoordinator.js +0 -0
- /package/src/{chat → app/chat}/daemonReconnect.js +0 -0
- /package/src/{chat → app/chat}/daemonTransportDefaults.js +0 -0
- /package/src/{chat → app/chat}/inputMath.js +0 -0
- /package/src/{chat → app/chat}/rawKeyMap.js +0 -0
- /package/src/{chat → app/chat}/settingsController.js +0 -0
- /package/src/{chat → app/chat}/text.js +0 -0
- /package/src/{chat → app/chat}/transientAgentState.js +0 -0
- /package/src/{cli → app/cli}/busCoreCommands.js +0 -0
- /package/src/{skills/index.js → app/cli/features/skills.js} +0 -0
- /package/src/{bus → coordination/bus}/nickname.js +0 -0
- /package/src/{bus → coordination/bus}/queue.js +0 -0
- /package/src/{context → coordination/context}/decisions.js +0 -0
- /package/src/{context → coordination/context}/doctor.js +0 -0
- /package/src/{context → coordination/context}/index.js +0 -0
- /package/src/{context → coordination/context}/sync.js +0 -0
- /package/src/{ufoo → coordination/state}/agentsStore.js +0 -0
- /package/src/{ufoo → coordination/state}/paths.js +0 -0
- /package/src/{controller → orchestration/controller}/launchRouting.js +0 -0
- /package/src/{controller → orchestration/controller}/routerFastPath.js +0 -0
- /package/src/{controller → orchestration/controller}/routerFinalize.js +0 -0
- /package/src/{group → orchestration/groups}/diagram.js +0 -0
- /package/src/{group → orchestration/groups}/templateValidation.js +0 -0
- /package/src/{solo → orchestration/solo}/commands.js +0 -0
- /package/src/{shared → runtime/contracts}/eventContract.js +0 -0
- /package/src/{shared → runtime/contracts}/ptySocketContract.js +0 -0
- /package/src/{providerapi → runtime/privacy}/redactor.js +0 -0
- /package/src/{providerapi → runtime/privacy}/shadowDiff.js +0 -0
- /package/src/{projects → runtime/projects}/identity.js +0 -0
- /package/src/{projects → runtime/projects}/index.js +0 -0
- /package/src/{projects → runtime/projects}/projectId.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapterContract.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/externalAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/hostAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/internalQueueAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/terminalAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/tmuxAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/detect.js +0 -0
- /package/src/{terminal → runtime/terminal}/index.js +0 -0
- /package/src/{terminal → runtime/terminal}/iterm2.js +0 -0
- /package/src/{utils → ui/format}/banner.js +0 -0
- /package/src/{shared → ui/format}/markdownRenderer.js +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const { startDaemon, stopDaemon, isRunning } = require("./index");
|
|
3
|
-
const { loadConfig, defaultAgentModelForProvider } = require("
|
|
3
|
+
const { loadConfig, defaultAgentModelForProvider } = require("../../config");
|
|
4
|
+
const { resolveNodeExecutable } = require("../process/nodeExecutable");
|
|
4
5
|
|
|
5
6
|
function runDaemonCli(argv) {
|
|
6
7
|
const cmd = argv[1] || "start";
|
|
@@ -19,7 +20,7 @@ function runDaemonCli(argv) {
|
|
|
19
20
|
if (isRunning(projectRoot)) return;
|
|
20
21
|
if (!process.env.UFOO_DAEMON_CHILD) {
|
|
21
22
|
const { spawn } = require("child_process");
|
|
22
|
-
const child = spawn(
|
|
23
|
+
const child = spawn(resolveNodeExecutable(), [path.join(__dirname, "..", "..", "..", "bin", "ufoo.js"), "daemon", "start"], {
|
|
23
24
|
detached: true,
|
|
24
25
|
stdio: "ignore",
|
|
25
26
|
env: { ...process.env, UFOO_DAEMON_CHILD: "1" },
|
|
@@ -32,25 +33,31 @@ function runDaemonCli(argv) {
|
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
34
35
|
if (cmd === "stop" || cmd === "--stop") {
|
|
35
|
-
stopDaemon(projectRoot)
|
|
36
|
+
if (!stopDaemon(projectRoot, { source: process.env.UFOO_DAEMON_STOP_SOURCE || `daemon-cli:${cmd} pid=${process.pid}` })) {
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
}
|
|
36
39
|
return;
|
|
37
40
|
}
|
|
38
41
|
if (cmd === "restart" || cmd === "--restart") {
|
|
39
42
|
// Stop if running
|
|
40
43
|
if (isRunning(projectRoot)) {
|
|
41
|
-
stopDaemon(projectRoot);
|
|
44
|
+
const stopped = stopDaemon(projectRoot, { source: process.env.UFOO_DAEMON_STOP_SOURCE || `daemon-cli:${cmd} pid=${process.pid}` });
|
|
42
45
|
// Wait for clean shutdown
|
|
43
46
|
let attempts = 0;
|
|
44
47
|
while (isRunning(projectRoot) && attempts < 50) {
|
|
45
48
|
attempts++;
|
|
46
49
|
require("child_process").spawnSync("sleep", ["0.1"]);
|
|
47
50
|
}
|
|
51
|
+
if (!stopped && isRunning(projectRoot)) {
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
// Start fresh daemon
|
|
50
57
|
if (!process.env.UFOO_DAEMON_CHILD) {
|
|
51
58
|
const { spawn } = require("child_process");
|
|
52
59
|
const childEnv = { ...process.env, UFOO_DAEMON_CHILD: "1" };
|
|
53
|
-
const child = spawn(
|
|
60
|
+
const child = spawn(resolveNodeExecutable(), [path.join(__dirname, "..", "..", "..", "bin", "ufoo.js"), "daemon", "start"], {
|
|
54
61
|
detached: true,
|
|
55
62
|
stdio: "ignore",
|
|
56
63
|
env: childEnv,
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const path = require("path");
|
|
4
|
-
const EventBus = require("
|
|
5
|
-
const { prepareUcodeBootstrap } = require("
|
|
6
|
-
const { isMetaActive } = require("
|
|
7
|
-
const { getUfooPaths } = require("
|
|
4
|
+
const EventBus = require("../../coordination/bus");
|
|
5
|
+
const { prepareUcodeBootstrap } = require("../../code/launcher/ucodeBootstrap");
|
|
6
|
+
const { isMetaActive } = require("../../coordination/bus/utils");
|
|
7
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
8
8
|
const {
|
|
9
9
|
applyProjectNicknamePrefix,
|
|
10
10
|
resolveDisplayNickname,
|
|
11
11
|
resolveScopedNickname,
|
|
12
12
|
} = require("./nicknameScope");
|
|
13
|
-
const { loadAgentsData, saveAgentsData } = require("
|
|
13
|
+
const { loadAgentsData, saveAgentsData } = require("../../coordination/state/agentsStore");
|
|
14
14
|
const {
|
|
15
15
|
loadPromptProfileRegistry,
|
|
16
16
|
resolvePromptProfileReference,
|
|
17
|
-
} = require("
|
|
17
|
+
} = require("../../orchestration/groups/promptProfiles");
|
|
18
18
|
const {
|
|
19
19
|
buildSoloPromptMetadata,
|
|
20
20
|
composeSoloBootstrapPrompt,
|
|
21
|
-
} = require("
|
|
21
|
+
} = require("../../orchestration/groups/bootstrap");
|
|
22
22
|
|
|
23
23
|
function sleep(ms) {
|
|
24
24
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const { getUfooPaths } = require("
|
|
4
|
-
const { isMetaActive } = require("
|
|
3
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
4
|
+
const { isMetaActive } = require("../../coordination/bus/utils");
|
|
5
5
|
const { resolveDisplayNickname, resolveScopedNickname } = require("./nicknameScope");
|
|
6
|
-
const { readReportSummary, countControllerInboxEntries } = require("
|
|
7
|
-
const { readRecentLoopSummary } = require("
|
|
6
|
+
const { readReportSummary, countControllerInboxEntries } = require("../../coordination/report/store");
|
|
7
|
+
const { readRecentLoopSummary } = require("../../agents/controller/loopObservability");
|
|
8
8
|
|
|
9
9
|
function readBus(projectRoot) {
|
|
10
10
|
const busPath = getUfooPaths(projectRoot).agentsFile;
|
|
@@ -16,7 +16,7 @@ function readBus(projectRoot) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function readDecisions(projectRoot) {
|
|
19
|
-
const DecisionsManager = require("
|
|
19
|
+
const DecisionsManager = require("../../coordination/context/decisions");
|
|
20
20
|
const manager = new DecisionsManager(projectRoot);
|
|
21
21
|
const dir = manager.decisionsDir;
|
|
22
22
|
let open = 0;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
|
|
5
|
+
function resolveNodeExecutable(options = {}) {
|
|
6
|
+
const {
|
|
7
|
+
execPath = process.execPath,
|
|
8
|
+
env = process.env,
|
|
9
|
+
fsModule = fs,
|
|
10
|
+
} = options;
|
|
11
|
+
const override = String((env && env.UFOO_NODE_EXECUTABLE) || "").trim();
|
|
12
|
+
if (override) return override;
|
|
13
|
+
const current = String(execPath || "").trim();
|
|
14
|
+
if (current) {
|
|
15
|
+
try {
|
|
16
|
+
if (fsModule.existsSync(current)) return current;
|
|
17
|
+
} catch {
|
|
18
|
+
// Fall through to PATH lookup.
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return "node";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
resolveNodeExecutable,
|
|
26
|
+
};
|
|
@@ -2,7 +2,7 @@ const fs = require("fs");
|
|
|
2
2
|
const os = require("os");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const { canonicalProjectRoot, buildProjectId, trimTrailingSlashes } = require("./projectId");
|
|
5
|
-
const { getUfooPaths } = require("
|
|
5
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
6
6
|
|
|
7
7
|
const DEFAULT_STALE_TTL_MS = 30 * 1000;
|
|
8
8
|
const DEFAULT_TMP_CLEANUP_AGE_MS = 5 * 60 * 1000;
|
|
@@ -17,7 +17,7 @@ function filterVisibleProjectRuntimes(rows = []) {
|
|
|
17
17
|
const sourceRows = Array.isArray(rows) ? rows : [];
|
|
18
18
|
return sourceRows.filter((row) => {
|
|
19
19
|
const status = String((row && row.status) || "").trim().toLowerCase();
|
|
20
|
-
return status
|
|
20
|
+
return status === "running";
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -5,7 +5,6 @@ const {
|
|
|
5
5
|
const { createTerminalAdapter } = require("./adapters/terminalAdapter");
|
|
6
6
|
const { createTmuxAdapter } = require("./adapters/tmuxAdapter");
|
|
7
7
|
const { createInternalQueueAdapter } = require("./adapters/internalQueueAdapter");
|
|
8
|
-
const { createInternalPtyAdapter } = require("./adapters/internalPtyAdapter");
|
|
9
8
|
const { createHostAdapter } = require("./adapters/hostAdapter");
|
|
10
9
|
|
|
11
10
|
function createTerminalAdapterRouter(options = {}) {
|
|
@@ -65,15 +64,6 @@ function createTerminalAdapterRouter(options = {}) {
|
|
|
65
64
|
});
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
if (launchMode === "internal-pty") {
|
|
69
|
-
return createInternalPtyAdapter({
|
|
70
|
-
sendRaw,
|
|
71
|
-
sendResize,
|
|
72
|
-
requestSnapshot,
|
|
73
|
-
createAdapter,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
67
|
if (launchMode === "internal") {
|
|
78
68
|
return createInternalQueueAdapter({
|
|
79
69
|
sendRaw,
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
const { createInternalPtyAdapter } = require("./internalPtyAdapter");
|
|
2
1
|
const { createInternalQueueAdapter } = require("./internalQueueAdapter");
|
|
3
2
|
|
|
4
3
|
function createInternalAdapter(options = {}) {
|
|
5
|
-
if (options.usePty) {
|
|
6
|
-
return createInternalPtyAdapter(options);
|
|
7
|
-
}
|
|
8
4
|
return createInternalQueueAdapter(options);
|
|
9
5
|
}
|
|
10
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const MemoryManager = require("../../memory");
|
|
2
|
-
const { estimateTokens } = require("../../memory");
|
|
3
|
-
const { searchHistory } = require("../../memory/historySearch");
|
|
1
|
+
const MemoryManager = require("../../coordination/memory");
|
|
2
|
+
const { estimateTokens } = require("../../coordination/memory");
|
|
3
|
+
const { searchHistory } = require("../../coordination/memory/historySearch");
|
|
4
4
|
const { buildToolError, extractAuditFields, requireSubscriber } = require("./common");
|
|
5
5
|
|
|
6
6
|
const BANNED_TIME_PATTERNS = [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const DecisionsManager = require("../../context/decisions");
|
|
1
|
+
const DecisionsManager = require("../../coordination/context/decisions");
|
|
2
2
|
|
|
3
3
|
function readOpenDecisionsHandler(ctx = {}, args = {}) {
|
|
4
4
|
const limit = Number.isFinite(Number(args.limit)) && Number(args.limit) > 0
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
|
|
4
|
-
const { buildStatus } = require("../../daemon/status");
|
|
5
|
-
const { getUfooPaths } = require("../../
|
|
4
|
+
const { buildStatus } = require("../../runtime/daemon/status");
|
|
5
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
6
6
|
|
|
7
7
|
function clipPromptText(value = "", maxChars = 240) {
|
|
8
8
|
const text = String(value || "").replace(/\s+/g, " ").trim();
|
|
@@ -456,7 +456,7 @@ const LAUNCH_AGENT_SCHEMA = Object.freeze({
|
|
|
456
456
|
properties: Object.freeze({
|
|
457
457
|
agent: Object.freeze({
|
|
458
458
|
type: "string",
|
|
459
|
-
enum: Object.freeze(["codex", "claude", "ucode"]),
|
|
459
|
+
enum: Object.freeze(["codex", "claude", "ucode", "agy"]),
|
|
460
460
|
}),
|
|
461
461
|
count: Object.freeze({ type: "integer", minimum: 1 }),
|
|
462
462
|
nickname: Object.freeze({ type: "string" }),
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# Ink TUI Migration Plan
|
|
2
|
+
|
|
3
|
+
Status: Ink is the only TUI for chat and ucode. The legacy blessed renderer
|
|
4
|
+
has been removed after the parity close-out.
|
|
5
|
+
|
|
6
|
+
## Why
|
|
7
|
+
|
|
8
|
+
The removed legacy chat, ucode and internal-agent TUIs used blessed. Blessed
|
|
9
|
+
is an unmaintained imperative widget tree with no modern equivalent of
|
|
10
|
+
React's component model, and it was awkward to extend (manual layout math,
|
|
11
|
+
manual redraws, no useful test harness).
|
|
12
|
+
|
|
13
|
+
ink (the React-for-terminals library, what Claude Code, Codex CLI fronts
|
|
14
|
+
and the Gemini CLI all use) gives us declarative components, flexbox
|
|
15
|
+
layout, hooks, and proper isolation of pure logic from rendering.
|
|
16
|
+
|
|
17
|
+
## Approach
|
|
18
|
+
|
|
19
|
+
- Ink is the renderer for both chat and ucode.
|
|
20
|
+
- Pure helpers live in `src/ui/format/`, so behaviour parity is enforced by
|
|
21
|
+
test rather than copy/paste.
|
|
22
|
+
- Components live in `src/ui/ink/`, written in plain JS via
|
|
23
|
+
`React.createElement` (no JSX, no build step) so jest stays vanilla.
|
|
24
|
+
- ink is loaded through `src/ui/runInk.js`, a thin CJS→ESM bridge so the
|
|
25
|
+
rest of the codebase stays CommonJS.
|
|
26
|
+
|
|
27
|
+
## Progress
|
|
28
|
+
|
|
29
|
+
- **P0** ✅ ink + react deps, runtime bridge, `<InkDemo>` smoke harness,
|
|
30
|
+
pure helpers extracted to `src/ui/format/`.
|
|
31
|
+
- **P1** ✅ ucode TUI ported to ink.
|
|
32
|
+
- **P2** ✅ folded into P3.6 (internal agent view).
|
|
33
|
+
- **P3** ✅ chat TUI ported to ink. Daemon
|
|
34
|
+
connection, dashboard (5 views), tool-merge, status spinner, history,
|
|
35
|
+
agent selection, raw-PTY mirror and internal bus agent view are wired.
|
|
36
|
+
- **P4** ✅ parity close-out complete: full `commandExecutor` dispatch,
|
|
37
|
+
`daemonMessageRouter` callback coverage, persisted history, BUS streams,
|
|
38
|
+
transient agent state, loop summary, project rail switching, cron/settings
|
|
39
|
+
dashboard actions, completion popup, and default Ink entrypoints.
|
|
40
|
+
|
|
41
|
+
## P1 ucode TUI — what's wired
|
|
42
|
+
|
|
43
|
+
| Feature | Status |
|
|
44
|
+
|---|---|
|
|
45
|
+
| Banner + version + session id header | ✅ |
|
|
46
|
+
| Scrolling `<Static>` log (1000 line cap) | ✅ |
|
|
47
|
+
| Multiline input (cursor math, Ctrl+A/E/B/F/D/H/K/U/W, Meta+B/F/D, `\\\n` continuation, Alt+Enter newline, CJK wrap) | ✅ |
|
|
48
|
+
| Up/Down history walk + agent-selection mode | ✅ |
|
|
49
|
+
| Ctrl+C exit, Ctrl+O expand last tool group | ✅ |
|
|
50
|
+
| Tool merge/freeze/expand state machine | ✅ |
|
|
51
|
+
| Spinner + phase status line (request/thinking/text/tool labels) | ✅ |
|
|
52
|
+
| Esc abort with `AbortController` and "Cancelling..." status | ✅ |
|
|
53
|
+
| Agents footer with single-line truncation + `+N more` hint | ✅ |
|
|
54
|
+
| `runSingleCommand` empty/exit/probe/help/error/tool/nl/ubus/resume/nl_bg kinds | ✅ |
|
|
55
|
+
| Background tasks ("BG x/y/z" suffix) | ✅ |
|
|
56
|
+
| ubus / resume / nl_bg branches | ✅ |
|
|
57
|
+
| autoBus polling | ✅ |
|
|
58
|
+
|
|
59
|
+
## Real-TTY checklist
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
./bin/ucode.js
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Editor
|
|
66
|
+
|
|
67
|
+
- [ ] Type, see characters appear with the cursor on the next cell.
|
|
68
|
+
- [ ] Backspace deletes one cell back; cursor stays correct on CJK.
|
|
69
|
+
- [ ] Left/Right arrows move the cursor; resetting preferred col.
|
|
70
|
+
- [ ] Up/Down on a single line walks input history.
|
|
71
|
+
- [ ] Up/Down on a multiline value moves between visual rows.
|
|
72
|
+
- [ ] `\` followed by Enter inserts a newline; Enter alone submits.
|
|
73
|
+
- [ ] Alt+Enter inserts a newline.
|
|
74
|
+
- [ ] Ctrl+A / Ctrl+E jump to row start / end.
|
|
75
|
+
- [ ] Ctrl+W deletes the previous word (also Meta+Backspace).
|
|
76
|
+
- [ ] Long pasted text doesn't lock up the renderer.
|
|
77
|
+
- [ ] Resize the terminal — input frame and footer span the new width.
|
|
78
|
+
|
|
79
|
+
### Status line
|
|
80
|
+
|
|
81
|
+
- [ ] Shows `UCODE · Ready` while idle.
|
|
82
|
+
- [ ] Shows a spinning indicator + phase ("Waiting for model...",
|
|
83
|
+
"Thinking...", "Generating response...", "Calling X...") during
|
|
84
|
+
`runNaturalLanguageTask`.
|
|
85
|
+
- [ ] Appends `(<elapsed> s, esc cancel)` when a task is in flight.
|
|
86
|
+
- [ ] Esc on a running task flips to "Cancelling..." then back to Ready.
|
|
87
|
+
|
|
88
|
+
### Tool calls
|
|
89
|
+
|
|
90
|
+
- [ ] Single tool call renders one line (`· tool · detail`).
|
|
91
|
+
- [ ] Two+ consecutive tool calls collapse to one row + `(Ctrl+O expand)`.
|
|
92
|
+
- [ ] Ctrl+O expands the most recent group with `│`/`└` branch markers
|
|
93
|
+
and only fires once per group.
|
|
94
|
+
- [ ] When text arrives between tool calls, the previous group freezes
|
|
95
|
+
into the log and a fresh group starts on the next tool call.
|
|
96
|
+
|
|
97
|
+
### Agents footer
|
|
98
|
+
|
|
99
|
+
- [ ] Shows `Agents: none │ No target agents` when nothing's online.
|
|
100
|
+
- [ ] Shows `Agents: @x @y ... │ ↓ select target · ←/→ switch` otherwise.
|
|
101
|
+
- [ ] At narrow widths, the row stays single-line, drops trailing chips
|
|
102
|
+
and emits ` +N more`.
|
|
103
|
+
- [ ] Down enters selection mode (first chip inverse). Left/Right cycle.
|
|
104
|
+
Up exits. Prompt prefix changes to `›@<name> ` when locked.
|
|
105
|
+
|
|
106
|
+
### Smoke
|
|
107
|
+
|
|
108
|
+
- [ ] `node scripts/ucode-app-smoke.js` exits 0.
|
|
109
|
+
- [ ] `npx jest --silent` shows the pre-existing 5 OAuth failures only;
|
|
110
|
+
every ink suite passes.
|
|
111
|
+
|
|
112
|
+
## Decision log
|
|
113
|
+
|
|
114
|
+
- **Don't fork ink.** Claude-code-fixed inlines a customised ink under
|
|
115
|
+
`src/ink/`; we don't need React 19 / ConcurrentRoot / IDE bridging,
|
|
116
|
+
so depending on the public `ink@5` keeps maintenance cost low.
|
|
117
|
+
- **Don't add JSX.** `React.createElement` keeps jest CJS happy and
|
|
118
|
+
avoids a build step. We can revisit if any single component grows
|
|
119
|
+
past ~600 lines and readability suffers.
|
|
120
|
+
- **Don't enable `--experimental-vm-modules` for jest.** The risk of
|
|
121
|
+
surprise ESM behaviour across the existing 1800-test suite is too
|
|
122
|
+
high. Render coverage stays in `scripts/*-smoke.js`; component logic
|
|
123
|
+
is exercised by pure-function tests.
|
|
124
|
+
- **Codex isn't a useful reference.** Its TUI is a Rust ratatui app
|
|
125
|
+
(`codex-rs/tui`), not React-based. The architectural principle worth
|
|
126
|
+
borrowing is its hard split between TUI and core protocol.
|
|
127
|
+
|
|
128
|
+
## P2 dropped, folded into P3
|
|
129
|
+
|
|
130
|
+
The internal-agent view in chat is not an independent program. It is now
|
|
131
|
+
an Ink chat sub-mode, so the earlier plan to mount a separate subtree was
|
|
132
|
+
folded into P3.6.
|
|
133
|
+
|
|
134
|
+
## P3 audit (chat TUI surface)
|
|
135
|
+
|
|
136
|
+
Source: the current Ink host in `src/ui/ink/ChatApp.js`, with
|
|
137
|
+
shared daemon and command helpers under `src/app/chat/`. Highlights:
|
|
138
|
+
|
|
139
|
+
### Lifecycle
|
|
140
|
+
- Public entrypoint `runChat(projectRoot, { globalMode })` from
|
|
141
|
+
`src/app/chat/index.js`; it delegates directly to the Ink ChatApp.
|
|
142
|
+
- Runners injected via closures: `daemonCoordinator.send`,
|
|
143
|
+
`executeCommand`, `inputSubmitHandler.handleSubmit`,
|
|
144
|
+
`daemonMessageRouter.handleMessage`.
|
|
145
|
+
|
|
146
|
+
### View state machine
|
|
147
|
+
- `dashboardView` ∈ `projects | agents | mode | provider | cron`
|
|
148
|
+
- `focusMode` ∈ `input | dashboard` — toggled by Tab and arrow keys
|
|
149
|
+
- `globalMode` (boolean) + `globalScope` ∈ `controller | project` —
|
|
150
|
+
`globalMode=true` enables a multi-project rail; Esc/Enter walk the
|
|
151
|
+
scope ladder.
|
|
152
|
+
- `enterDashboardMode()` / `exitDashboardMode()` are the two transition
|
|
153
|
+
points; `setGlobalScope()` runs an async, debounced project switch.
|
|
154
|
+
|
|
155
|
+
### Input
|
|
156
|
+
- Submit accepts `@mention`, `@target`, `/command`, plain text and
|
|
157
|
+
numeric disambiguation (when `pending.disambiguate` is set).
|
|
158
|
+
- Editor keys cover Ctrl+A/E/B/F/D/H/K/U/W, Meta+B/F/D, arrows
|
|
159
|
+
(cursor + history when empty), Esc (3-layer: clear @target →
|
|
160
|
+
exit project scope → cancel input), bracketed paste, Tab (toggle
|
|
161
|
+
dashboard), PgUp/PgDn (scroll log).
|
|
162
|
+
- Completion fires on `/` and `@` with sources from command registry,
|
|
163
|
+
group templates, solo profiles and agent mentions; Up arrow jumps to
|
|
164
|
+
the latest suggestion.
|
|
165
|
+
- History is per-project, persisted to `input-history.jsonl` with
|
|
166
|
+
draft restoration on project switch.
|
|
167
|
+
|
|
168
|
+
### Daemon stack
|
|
169
|
+
- `daemonTransport` owns the socket path and retry policy.
|
|
170
|
+
- `daemonConnection` owns the queue + lifecycle (`connect`, `send`,
|
|
171
|
+
`requestStatus`, `close`, `markExit`, `switchConnection`,
|
|
172
|
+
`getState`).
|
|
173
|
+
- `daemonCoordinator` orchestrates project switches with a serialised
|
|
174
|
+
Promise chain.
|
|
175
|
+
- `daemonMessageRouter.handleMessage(msg)` is a stateless dispatcher
|
|
176
|
+
that turns daemon responses into log appends, dashboard updates, PTY
|
|
177
|
+
writes and transient agent state changes.
|
|
178
|
+
- `daemonReconnect.restartDaemonFlow()` provides a per-project lock for
|
|
179
|
+
daemon restarts.
|
|
180
|
+
|
|
181
|
+
### Shared helpers
|
|
182
|
+
- `cronScheduler` — `/cron start|stop|list` + the cron dashboard view.
|
|
183
|
+
- `settingsController` — launch mode / agent provider,
|
|
184
|
+
with daemon restart on mode/provider change. `autoResume` stays config/command-driven.
|
|
185
|
+
- `transientAgentState` — TTL-bounded `working / waiting_input /
|
|
186
|
+
blocked` markers per agent.
|
|
187
|
+
- `projectCloseController` — `requestCloseProject(index)` runs daemon
|
|
188
|
+
stop + project switch.
|
|
189
|
+
- `agentDirectory` — agent label resolution + window clamping (pure).
|
|
190
|
+
- `internalAgentLogHistory` — bus log replay for internal agents.
|
|
191
|
+
|
|
192
|
+
### Internal-agent sub-view
|
|
193
|
+
- Ink owns the agent sub-view inside ChatApp.
|
|
194
|
+
- PTY mirror mode uses `agentSockets.connectOutput/Input` and
|
|
195
|
+
`requestSnapshot`.
|
|
196
|
+
- Embedded bus mode keeps its own input value, cursor, log and status.
|
|
197
|
+
- `agentSockets.createAgentSockets` owns the PTY/bus socket
|
|
198
|
+
lifecycle.
|
|
199
|
+
|
|
200
|
+
### Layout (Ink)
|
|
201
|
+
- `ChatApp` owns the chat surface as a React tree under
|
|
202
|
+
`src/ui/ink/`.
|
|
203
|
+
- Dashboard, log, status, completion, internal-agent panes and input are
|
|
204
|
+
rendered from React state with ink flex layout. No blessed widget geometry
|
|
205
|
+
or controller layer remains.
|
|
206
|
+
|
|
207
|
+
### Commands
|
|
208
|
+
`/bus`, `/ctx`, `/daemon`, `/doctor`, `/cron`, `/group`, `/init`,
|
|
209
|
+
`/open`, `/launch`, `/project`, `/role`, `/solo`, `/settings`,
|
|
210
|
+
`/help`, plus nested subcommands (`/bus activate|list|rename|send|status`,
|
|
211
|
+
`/cron start|list|stop`, etc.). `commandExecutor.executeCommand(text)`
|
|
212
|
+
is the single dispatch point. `parseCommand(text)`, `parseAtTarget(text)`
|
|
213
|
+
and `shouldEchoCommandInChat(text)` are pure.
|
|
214
|
+
|
|
215
|
+
### Cross-cutting
|
|
216
|
+
- `text.js`: `escapeBlessed`, `stripBlessedTags`, `stripAnsi`,
|
|
217
|
+
`truncateAnsi`, `decodeEscapedNewlines`. The blessed-tag helpers are now
|
|
218
|
+
compatibility shims for older daemon/router log strings; ink call sites
|
|
219
|
+
strip or normalize those tags before rendering. The ANSI helpers stay
|
|
220
|
+
relevant.
|
|
221
|
+
- `rawKeyMap.keyToRaw(ch, key)`: converts ink-style key events to
|
|
222
|
+
PTY bytes for the agent view. Stays as-is.
|
|
223
|
+
- `transport.js`: `startDaemon`, `stopDaemon`, `connectWithRetry`.
|
|
224
|
+
Framework-agnostic, no migration needed.
|
|
225
|
+
|
|
226
|
+
### Removal notes
|
|
227
|
+
1. **Entry**: `src/app/chat/index.js` delegates directly to `runChatInk()`.
|
|
228
|
+
2. **ucode**: `src/code/tui.js` is a compatibility export wrapper around
|
|
229
|
+
`src/ui/format/` and `runUcodeInkTui()`.
|
|
230
|
+
3. **Controllers**: the blessed widget controllers and their tests were
|
|
231
|
+
removed with the fallback path.
|
|
232
|
+
4. **Markup**: old brace-tag helpers remain only where shared daemon/chat
|
|
233
|
+
helpers still emit or sanitize legacy log markup.
|
|
234
|
+
|
|
235
|
+
### P3 phase plan
|
|
236
|
+
|
|
237
|
+
| Step | Goal | Status |
|
|
238
|
+
|---|---|---|
|
|
239
|
+
| P3.1 | This audit | ✅ |
|
|
240
|
+
| P3.2 | Ink-only `runChat()` entrypoint | ✅ |
|
|
241
|
+
| P3.3 | ChatApp shell (banner + log + input + status) | ✅ |
|
|
242
|
+
| P3.4 | Five dashboard views as React components | ✅ |
|
|
243
|
+
| P3.5 | Daemon connection + PROMPT/BUS_SEND wiring | ✅ |
|
|
244
|
+
| P3.6 | Raw-PTY internal agent view as a ChatApp mode | ✅ |
|
|
245
|
+
| P3.7 | Real-TTY checklist | ✅ |
|
|
246
|
+
|
|
247
|
+
## P3 chat TUI — what's wired
|
|
248
|
+
|
|
249
|
+
| Feature | Status |
|
|
250
|
+
|---|---|
|
|
251
|
+
| Banner header (project + global mode + scope) | ✅ |
|
|
252
|
+
| Scrolling `<Static>` log (1000 line cap) | ✅ |
|
|
253
|
+
| Multiline input (P1 MultilineInput component) | ✅ |
|
|
254
|
+
| 5 dashboard views (projects/agents/mode/provider/cron) | ✅ |
|
|
255
|
+
| Tab toggles input/dashboard focus | ✅ |
|
|
256
|
+
| Up/Down history walk + agent selection mode | ✅ |
|
|
257
|
+
| Left/Right cycle agents while selected | ✅ |
|
|
258
|
+
| Spinner + phase status line | ✅ |
|
|
259
|
+
| Tool-merge state machine + Ctrl+O expand | ✅ |
|
|
260
|
+
| Daemon connect / send / status poll | ✅ |
|
|
261
|
+
| `PROMPT` for free text, `BUS_SEND` for `@target` | ✅ |
|
|
262
|
+
| `BUS_SEND_OK` / `RESPONSE` / `ERROR` / `STATUS` / `BUS` envelopes | ✅ |
|
|
263
|
+
| Raw PTY agent mirror (Enter on selected agent, Esc to leave) | ✅ |
|
|
264
|
+
| `daemonMessageRouter` (markdown streams, transient state, bus subview) | ✅ |
|
|
265
|
+
| `commandExecutor` full slash-command dispatch (`/cron`, `/group`, `/role`, `/settings` …) | ✅ |
|
|
266
|
+
| Slash + `@` autocomplete | ✅ |
|
|
267
|
+
| Input history persisted file load/save | ✅ |
|
|
268
|
+
| Cron dashboard actions | ✅ |
|
|
269
|
+
| Settings dashboard actions (launch mode, provider) | ✅ |
|
|
270
|
+
|
|
271
|
+
## Real-TTY checklist for chat
|
|
272
|
+
|
|
273
|
+
```sh
|
|
274
|
+
./bin/ufoo.js chat # project mode
|
|
275
|
+
./bin/ufoo.js chat --global # global controller mode
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Layout
|
|
279
|
+
- [ ] Banner shows the active project + global/project tag.
|
|
280
|
+
- [ ] `Agents:` footer, status line above input, log fills the rest.
|
|
281
|
+
- [ ] Resize the terminal — input frame and footer stay single-line.
|
|
282
|
+
|
|
283
|
+
### Input + history
|
|
284
|
+
- [ ] Type, Enter sends a `PROMPT`. Backspace, arrows, Ctrl+A/E etc.
|
|
285
|
+
behave the same as the ucode editor.
|
|
286
|
+
- [ ] Up/Down on an empty draft walks the in-memory history.
|
|
287
|
+
- [ ] `\` + Enter inserts a newline.
|
|
288
|
+
- [ ] Esc clears any active agent selection.
|
|
289
|
+
|
|
290
|
+
### Daemon
|
|
291
|
+
- [ ] On launch the daemon spawns automatically (look for the socket
|
|
292
|
+
under `~/.ufoo` or your project's `.ufoo`).
|
|
293
|
+
- [ ] Send a free-text message — daemon answers, status flips to
|
|
294
|
+
`Working on task...` and back to Ready.
|
|
295
|
+
- [ ] Type `@<agent> hi` (or select with arrow keys) — message is
|
|
296
|
+
sent via `BUS_SEND`, ack arrives as `✓ Message delivered`.
|
|
297
|
+
|
|
298
|
+
### Agents footer
|
|
299
|
+
- [ ] Tab into the dashboard, ↓ enters agent selection (first item
|
|
300
|
+
inverse), ←/→ cycles, ↑ exits.
|
|
301
|
+
- [ ] Enter on a selected agent attaches to its PTY (cleared screen +
|
|
302
|
+
scroll region + bottom hint bar). Esc returns to chat without
|
|
303
|
+
losing the previous draft or log.
|
|
304
|
+
|
|
305
|
+
### Tool-merge
|
|
306
|
+
- [ ] Daemon-driven tool calls collapse and `(Ctrl+O expand)` works
|
|
307
|
+
the same as ucode.
|
|
308
|
+
|
|
309
|
+
### Smoke
|
|
310
|
+
- [ ] `node scripts/chat-app-smoke.js` exits 0.
|
|
311
|
+
- [ ] `node scripts/ucode-app-smoke.js` exits 0.
|
|
312
|
+
- [ ] `npx jest --silent` shows the pre-existing 5 OAuth failures
|
|
313
|
+
only; every ink suite passes.
|
|
314
|
+
|
|
315
|
+
## P4 close-out
|
|
316
|
+
|
|
317
|
+
- **STATUS handler fix** — chat now reads `msg.data.active` /
|
|
318
|
+
`msg.data.active_meta` / `msg.data.cron.tasks` so the agents and cron
|
|
319
|
+
counts in the footer actually update.
|
|
320
|
+
- **Slash command dispatch** — `createCommandExecutor` is wired in Ink
|
|
321
|
+
with daemon stop/start/restart, cron IPC, project switching and agent
|
|
322
|
+
activation callbacks.
|
|
323
|
+
- **Input history persistence** — `<projectRoot>/.ufoo/chat/input-history.jsonl`
|
|
324
|
+
is loaded on mount and appended on every submit.
|
|
325
|
+
- **Daemon message routing** — Ink routes daemon envelopes through
|
|
326
|
+
`daemonMessageRouter`, including BUS phase status, transient states,
|
|
327
|
+
pending delivery markers, streams, close/launch refreshes and loop
|
|
328
|
+
summary dashboard display.
|
|
329
|
+
- **Inline completion popup** — `/<prefix>` matches commands from
|
|
330
|
+
`COMMAND_REGISTRY`; `@<prefix>` matches the live agents list. Tab
|
|
331
|
+
accepts the top suggestion. Pure helper `buildCompletions` lives in
|
|
332
|
+
`src/ui/format` with full jest coverage.
|
|
333
|
+
- **Exit hygiene** — Ctrl+C now flushes `\x1b[2J\x1b[H` so the shell
|
|
334
|
+
prompt comes back to a clean screen instead of sitting under the
|
|
335
|
+
final ink frame; `runUcodeInkTui` returns `{ code: 0 }` so
|
|
336
|
+
`agent.js`'s `process.exit(res.code)` no longer crashes.
|