u-foo 2.3.32 → 2.4.1

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 (235) hide show
  1. package/README.md +157 -213
  2. package/README.zh-CN.md +151 -197
  3. package/SKILLS/ufoo/SKILL.md +8 -8
  4. package/bin/uagy.js +69 -0
  5. package/bin/uclaude.js +2 -2
  6. package/bin/ucode.js +4 -4
  7. package/bin/ucodex.js +2 -2
  8. package/bin/ufoo.js +5 -23
  9. package/modules/AGENTS.template.md +1 -1
  10. package/modules/bus/SKILLS/ubus/SKILL.md +35 -10
  11. package/package.json +5 -5
  12. package/scripts/chat-app-smoke.js +1 -1
  13. package/scripts/global-chat-switch-benchmark.js +5 -5
  14. package/scripts/ink-demo.js +1 -1
  15. package/scripts/ink-smoke.js +1 -1
  16. package/scripts/ucode-app-smoke.js +1 -1
  17. package/src/{agent → agents/activity}/activityDetector.js +39 -2
  18. package/src/{agent → agents/activity}/activityStatePublisher.js +1 -1
  19. package/src/{agent → agents/activity}/activityStateWriter.js +2 -2
  20. package/src/{agent → agents/activity}/activityTracker.js +1 -1
  21. package/src/agents/activity/index.js +8 -0
  22. package/src/{agent → agents/controller}/controllerToolExecutor.js +4 -4
  23. package/src/agents/controller/index.js +8 -0
  24. package/src/{agent → agents/controller}/loopObservability.js +2 -2
  25. package/src/{agent → agents/controller}/loopRuntime.js +1 -1
  26. package/src/{agent → agents/controller}/ufooAgent.js +9 -9
  27. package/src/agents/index.js +10 -0
  28. package/src/agents/internal/index.js +3 -0
  29. package/src/{agent → agents/internal}/internalRunner.js +45 -22
  30. package/src/agents/launch/agyConversation.js +159 -0
  31. package/src/agents/launch/index.js +12 -0
  32. package/src/{agent → agents/launch}/launchEnvironment.js +2 -3
  33. package/src/{agent → agents/launch}/launcher.js +64 -21
  34. package/src/{agent → agents/launch}/notifier.js +23 -12
  35. package/src/{agent → agents/launch}/ptyRunner.js +44 -12
  36. package/src/{agent → agents/launch}/ptyWrapper.js +2 -2
  37. package/src/{agent → agents/launch}/publisherRouting.js +1 -1
  38. package/src/{agent → agents/launch}/readyDetector.js +23 -0
  39. package/src/{agent → agents/prompts}/defaultBootstrap.js +63 -4
  40. package/src/{group/bootstrap.js → agents/prompts/groupBootstrap.js} +41 -6
  41. package/src/agents/prompts/index.js +8 -0
  42. package/src/{code/prompts → agents/prompts/native}/index.js +1 -1
  43. package/src/{agent → agents/providers}/claudeThreadProvider.js +1 -1
  44. package/src/{agent → agents/providers}/codexThreadProvider.js +1 -1
  45. package/src/{agent → agents/providers}/directAuthStatus.js +184 -1
  46. package/src/agents/providers/index.js +13 -0
  47. package/src/{agent → agents/providers}/upstreamTransport.js +2 -2
  48. package/src/{chat → app/chat}/agentSockets.js +1 -1
  49. package/src/{chat → app/chat}/commandExecutor.js +63 -26
  50. package/src/{chat → app/chat}/commands.js +119 -5
  51. package/src/{chat → app/chat}/daemonConnection.js +1 -1
  52. package/src/{chat → app/chat}/daemonMessageRouter.js +45 -3
  53. package/src/{chat → app/chat}/daemonReconnect.js +3 -0
  54. package/src/{chat → app/chat}/dashboardView.js +2 -1
  55. package/src/app/chat/index.js +6 -0
  56. package/src/{chat → app/chat}/inputSubmitHandler.js +4 -13
  57. package/src/{chat → app/chat}/internalAgentLogHistory.js +1 -1
  58. package/src/app/chat/multiWindow/index.js +268 -0
  59. package/src/app/chat/multiWindow/paneLayout.js +84 -0
  60. package/src/app/chat/multiWindow/paneManager.js +299 -0
  61. package/src/app/chat/multiWindow/renderer.js +384 -0
  62. package/src/app/chat/multiWindow/virtualTerminal.js +327 -0
  63. package/src/{chat → app/chat}/transport.js +1 -1
  64. package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
  65. package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
  66. package/src/{init/index.js → app/cli/features/init.js} +14 -32
  67. package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
  68. package/src/app/cli/index.js +9 -0
  69. package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
  70. package/src/{cli.js → app/cli/run.js} +59 -57
  71. package/src/app/index.js +6 -0
  72. package/src/code/agent.js +10 -9
  73. package/src/code/index.js +2 -0
  74. package/src/code/launcher/index.js +9 -0
  75. package/src/{agent → code/launcher}/ucode.js +7 -8
  76. package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
  77. package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
  78. package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
  79. package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
  80. package/src/code/nativeRunner.js +4 -4
  81. package/src/code/tui.js +3 -1454
  82. package/src/config.js +15 -2
  83. package/src/{bus → coordination/bus}/activate.js +2 -2
  84. package/src/{bus → coordination/bus}/daemon.js +15 -5
  85. package/src/coordination/bus/envelope.js +173 -0
  86. package/src/{bus → coordination/bus}/index.js +7 -3
  87. package/src/{bus → coordination/bus}/inject.js +11 -3
  88. package/src/{bus → coordination/bus}/message.js +1 -1
  89. package/src/coordination/bus/messageMeta.js +130 -0
  90. package/src/coordination/bus/promptEnvelope.js +65 -0
  91. package/src/{bus → coordination/bus}/shake.js +1 -1
  92. package/src/{bus → coordination/bus}/store.js +3 -3
  93. package/src/{bus → coordination/bus}/subscriber.js +2 -2
  94. package/src/{bus → coordination/bus}/utils.js +2 -2
  95. package/src/{history → coordination/history}/inputTimeline.js +5 -5
  96. package/src/coordination/index.js +10 -0
  97. package/src/{memory → coordination/memory}/historySearch.js +1 -1
  98. package/src/{memory → coordination/memory}/index.js +3 -3
  99. package/src/{report → coordination/report}/store.js +2 -2
  100. package/src/{status → coordination/status}/index.js +3 -3
  101. package/src/online/bridge.js +2 -2
  102. package/src/{controller → orchestration/controller}/flags.js +1 -1
  103. package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
  104. package/src/orchestration/controller/index.js +10 -0
  105. package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
  106. package/src/orchestration/groups/bootstrap.js +3 -0
  107. package/src/orchestration/groups/index.js +10 -0
  108. package/src/orchestration/groups/promptProfiles.js +3 -0
  109. package/src/{group → orchestration/groups}/templates.js +1 -1
  110. package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
  111. package/src/orchestration/index.js +7 -0
  112. package/src/orchestration/solo/index.js +3 -0
  113. package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
  114. package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
  115. package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
  116. package/src/{daemon → runtime/daemon}/index.js +105 -53
  117. package/src/{daemon → runtime/daemon}/ipcServer.js +1 -1
  118. package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
  119. package/src/{daemon → runtime/daemon}/ops.js +48 -61
  120. package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
  121. package/src/{daemon → runtime/daemon}/promptRequest.js +7 -7
  122. package/src/runtime/daemon/providerSessions.js +230 -0
  123. package/src/{daemon → runtime/daemon}/reporting.js +4 -4
  124. package/src/{daemon → runtime/daemon}/run.js +4 -4
  125. package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
  126. package/src/{daemon → runtime/daemon}/status.js +5 -5
  127. package/src/runtime/index.js +10 -0
  128. package/src/{projects → runtime/projects}/registry.js +1 -1
  129. package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
  130. package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
  131. package/src/tools/handlers/common.js +1 -1
  132. package/src/tools/handlers/listAgents.js +1 -1
  133. package/src/tools/handlers/memory.js +3 -3
  134. package/src/tools/handlers/readBusSummary.js +1 -1
  135. package/src/tools/handlers/readOpenDecisions.js +1 -1
  136. package/src/tools/handlers/readProjectRegistry.js +1 -1
  137. package/src/tools/handlers/readPromptHistory.js +2 -2
  138. package/src/tools/schemaFixtures.js +1 -1
  139. package/src/ui/MIGRATION.md +42 -88
  140. package/src/ui/format/index.js +5 -28
  141. package/src/ui/index.js +1 -1
  142. package/src/ui/{components → ink}/ChatApp.js +812 -88
  143. package/src/ui/ink/DashboardBar.js +685 -0
  144. package/src/ui/{components → ink}/MultilineInput.js +230 -5
  145. package/src/ui/{components → ink}/UcodeApp.js +16 -7
  146. package/src/ui/{components → ink}/agentMirror.js +24 -19
  147. package/src/ui/{components → ink}/chatReducer.js +29 -7
  148. package/src/bus/messageMeta.js +0 -52
  149. package/src/chat/agentViewController.js +0 -1072
  150. package/src/chat/chatLogController.js +0 -138
  151. package/src/chat/completionController.js +0 -533
  152. package/src/chat/dashboardKeyController.js +0 -533
  153. package/src/chat/index.js +0 -2222
  154. package/src/chat/inputHistoryController.js +0 -135
  155. package/src/chat/inputListenerController.js +0 -470
  156. package/src/chat/layout.js +0 -186
  157. package/src/chat/pasteController.js +0 -81
  158. package/src/chat/statusLineController.js +0 -223
  159. package/src/chat/streamTracker.js +0 -156
  160. package/src/code/config +0 -0
  161. package/src/daemon/providerSessions.js +0 -488
  162. package/src/terminal/adapters/internalPtyAdapter.js +0 -42
  163. package/src/ui/components/DashboardBar.js +0 -417
  164. /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
  165. /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
  166. /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
  167. /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
  168. /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
  169. /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
  170. /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
  171. /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
  172. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
  173. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
  174. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
  175. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
  176. /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
  177. /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
  178. /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
  179. /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
  180. /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
  181. /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
  182. /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
  183. /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
  184. /package/src/{agent → agents/providers}/credentials/index.js +0 -0
  185. /package/src/{chat → app/chat}/agentBar.js +0 -0
  186. /package/src/{chat → app/chat}/agentDirectory.js +0 -0
  187. /package/src/{chat → app/chat}/cronScheduler.js +0 -0
  188. /package/src/{chat → app/chat}/daemonCoordinator.js +0 -0
  189. /package/src/{chat → app/chat}/daemonTransport.js +0 -0
  190. /package/src/{chat → app/chat}/daemonTransportDefaults.js +0 -0
  191. /package/src/{chat → app/chat}/inputMath.js +0 -0
  192. /package/src/{chat → app/chat}/projectCloseController.js +0 -0
  193. /package/src/{chat → app/chat}/rawKeyMap.js +0 -0
  194. /package/src/{chat → app/chat}/settingsController.js +0 -0
  195. /package/src/{chat → app/chat}/shellCommand.js +0 -0
  196. /package/src/{chat → app/chat}/text.js +0 -0
  197. /package/src/{chat → app/chat}/transientAgentState.js +0 -0
  198. /package/src/{cli → app/cli}/busCoreCommands.js +0 -0
  199. /package/src/{skills/index.js → app/cli/features/skills.js} +0 -0
  200. /package/src/{bus → coordination/bus}/nickname.js +0 -0
  201. /package/src/{bus → coordination/bus}/queue.js +0 -0
  202. /package/src/{context → coordination/context}/decisions.js +0 -0
  203. /package/src/{context → coordination/context}/doctor.js +0 -0
  204. /package/src/{context → coordination/context}/index.js +0 -0
  205. /package/src/{context → coordination/context}/sync.js +0 -0
  206. /package/src/{ufoo → coordination/state}/agentRegistryDiagnostics.js +0 -0
  207. /package/src/{ufoo → coordination/state}/agentsStore.js +0 -0
  208. /package/src/{ufoo → coordination/state}/paths.js +0 -0
  209. /package/src/{controller → orchestration/controller}/launchRouting.js +0 -0
  210. /package/src/{controller → orchestration/controller}/routerFastPath.js +0 -0
  211. /package/src/{controller → orchestration/controller}/routerFinalize.js +0 -0
  212. /package/src/{group → orchestration/groups}/diagram.js +0 -0
  213. /package/src/{group → orchestration/groups}/templateValidation.js +0 -0
  214. /package/src/{solo → orchestration/solo}/commands.js +0 -0
  215. /package/src/{shared → runtime/contracts}/eventContract.js +0 -0
  216. /package/src/{shared → runtime/contracts}/ptySocketContract.js +0 -0
  217. /package/src/{providerapi → runtime/privacy}/redactor.js +0 -0
  218. /package/src/{providerapi → runtime/privacy}/shadowDiff.js +0 -0
  219. /package/src/{utils → runtime/process}/nodeExecutable.js +0 -0
  220. /package/src/{projects → runtime/projects}/identity.js +0 -0
  221. /package/src/{projects → runtime/projects}/index.js +0 -0
  222. /package/src/{projects → runtime/projects}/projectId.js +0 -0
  223. /package/src/{projects → runtime/projects}/runtimes.js +0 -0
  224. /package/src/{terminal → runtime/terminal}/adapterContract.js +0 -0
  225. /package/src/{terminal → runtime/terminal}/adapters/externalAdapter.js +0 -0
  226. /package/src/{terminal → runtime/terminal}/adapters/hostAdapter.js +0 -0
  227. /package/src/{terminal → runtime/terminal}/adapters/internalQueueAdapter.js +0 -0
  228. /package/src/{terminal → runtime/terminal}/adapters/terminalAdapter.js +0 -0
  229. /package/src/{terminal → runtime/terminal}/adapters/tmuxAdapter.js +0 -0
  230. /package/src/{terminal → runtime/terminal}/detect.js +0 -0
  231. /package/src/{terminal → runtime/terminal}/index.js +0 -0
  232. /package/src/{terminal → runtime/terminal}/iterm2.js +0 -0
  233. /package/src/{utils → ui/format}/banner.js +0 -0
  234. /package/src/{shared → ui/format}/markdownRenderer.js +0 -0
  235. /package/src/ui/{components → ink}/InkDemo.js +0 -0
@@ -1,135 +0,0 @@
1
- const fs = require("fs");
2
-
3
- function createInputHistoryController(options = {}) {
4
- const {
5
- inputHistoryFile: inputHistoryFileOption,
6
- historyDir: historyDirOption,
7
- setInputValue = () => {},
8
- getInputValue = () => "",
9
- fsMod = fs,
10
- } = options;
11
-
12
- if (!inputHistoryFileOption || !historyDirOption) {
13
- throw new Error("createInputHistoryController requires inputHistoryFile and historyDir");
14
- }
15
- let inputHistoryFile = inputHistoryFileOption;
16
- let historyDir = historyDirOption;
17
-
18
- const inputHistory = [];
19
- let historyIndex = 0;
20
- let historyDraft = "";
21
-
22
- function appendInputHistory(text) {
23
- if (!text) return;
24
- fsMod.mkdirSync(historyDir, { recursive: true });
25
- fsMod.appendFileSync(inputHistoryFile, `${JSON.stringify({ text })}\n`);
26
- }
27
-
28
- function loadInputHistory(limit = 2000) {
29
- inputHistory.length = 0;
30
- historyIndex = 0;
31
- historyDraft = "";
32
- try {
33
- const raw = fsMod.readFileSync(inputHistoryFile, "utf8");
34
- const lines = String(raw || "").trim().split(/\r?\n/).filter(Boolean);
35
- const items = lines.slice(-limit).map((line) => JSON.parse(line));
36
- for (const item of items) {
37
- if (item && typeof item.text === "string" && item.text.trim() !== "") {
38
- inputHistory.push(item.text);
39
- }
40
- }
41
- } catch {
42
- // ignore missing/invalid history
43
- }
44
- historyIndex = inputHistory.length;
45
- }
46
-
47
- function updateDraftFromInput() {
48
- if (historyIndex === inputHistory.length) {
49
- historyDraft = getInputValue();
50
- }
51
- }
52
-
53
- function setIndexToEnd() {
54
- historyIndex = inputHistory.length;
55
- historyDraft = "";
56
- }
57
-
58
- function historyUp() {
59
- if (inputHistory.length === 0) return false;
60
- if (historyIndex === inputHistory.length) {
61
- historyDraft = getInputValue();
62
- }
63
- if (historyIndex > 0) {
64
- historyIndex -= 1;
65
- setInputValue(inputHistory[historyIndex]);
66
- return true;
67
- }
68
- return true;
69
- }
70
-
71
- function historyDown() {
72
- if (inputHistory.length === 0) return false;
73
- if (historyIndex < inputHistory.length - 1) {
74
- historyIndex += 1;
75
- setInputValue(inputHistory[historyIndex]);
76
- return true;
77
- }
78
- if (historyIndex === inputHistory.length - 1) {
79
- historyIndex = inputHistory.length;
80
- setInputValue(historyDraft || "");
81
- return true;
82
- }
83
- return false;
84
- }
85
-
86
- function commitSubmittedText(text) {
87
- if (!text) return;
88
- inputHistory.push(text);
89
- appendInputHistory(text);
90
- setIndexToEnd();
91
- }
92
-
93
- function setHistoryTarget(next = {}) {
94
- if (!next.inputHistoryFile || !next.historyDir) {
95
- throw new Error("setHistoryTarget requires inputHistoryFile and historyDir");
96
- }
97
- inputHistoryFile = next.inputHistoryFile;
98
- historyDir = next.historyDir;
99
- }
100
-
101
- function restoreDraft(draft = "") {
102
- const nextDraft = String(draft || "");
103
- setInputValue(nextDraft);
104
- historyIndex = inputHistory.length;
105
- historyDraft = nextDraft;
106
- }
107
-
108
- function getDraftForPersistence() {
109
- if (historyIndex === inputHistory.length) {
110
- return String(getInputValue() || "");
111
- }
112
- return String(historyDraft || "");
113
- }
114
-
115
- return {
116
- loadInputHistory,
117
- updateDraftFromInput,
118
- historyUp,
119
- historyDown,
120
- commitSubmittedText,
121
- setIndexToEnd,
122
- setHistoryTarget,
123
- restoreDraft,
124
- getDraftForPersistence,
125
- getState: () => ({
126
- history: [...inputHistory],
127
- historyIndex,
128
- historyDraft,
129
- }),
130
- };
131
- }
132
-
133
- module.exports = {
134
- createInputHistoryController,
135
- };
@@ -1,470 +0,0 @@
1
- function createInputListenerController(options = {}) {
2
- const {
3
- getCurrentView = () => "main",
4
- exitHandler = () => {},
5
- getFocusMode = () => "input",
6
- getDashboardView = () => "agents",
7
- getSelectedAgentIndex = () => -1,
8
- getActiveAgents = () => [],
9
- getTargetAgent = () => null,
10
- getGlobalScope = () => "",
11
- clearTargetAgent = () => {},
12
- exitProjectScope = () => {},
13
- requestCloseAgent = () => {},
14
- logMessage = () => {},
15
- isSuppressKeypress = () => false,
16
- normalizeCommandPrefix = () => {},
17
- handleDashboardKey = () => false,
18
- exitDashboardMode = () => {},
19
- completionController,
20
- getLogHeight = () => 10,
21
- scrollLog = () => {},
22
- insertTextAtCursor = () => {},
23
- normalizePaste = (text) => text,
24
- resetPreferredCol = () => {},
25
- getCursorPos = () => 0,
26
- setCursorPos = () => {},
27
- ensureInputCursorVisible = () => {},
28
- getWrapWidth = () => 0,
29
- getCursorRowCol = () => ({ row: 0, col: 0 }),
30
- countLines = () => 1,
31
- getCursorPosForRowCol = () => 0,
32
- getPreferredCol = () => null,
33
- setPreferredCol = () => {},
34
- historyUp = () => false,
35
- historyDown = () => false,
36
- enterDashboardMode = () => {},
37
- resizeInput = () => {},
38
- updateDraftFromInput = () => {},
39
- } = options;
40
-
41
- if (!completionController) {
42
- throw new Error("createInputListenerController requires completionController");
43
- }
44
-
45
- function shouldShowCompletion(value = "") {
46
- const text = String(value || "");
47
- return text.startsWith("/") || text.startsWith("@");
48
- }
49
-
50
- function render(textarea) {
51
- if (textarea && textarea.screen && typeof textarea.screen.render === "function") {
52
- textarea.screen.render();
53
- }
54
- }
55
-
56
- function updateCursor(textarea) {
57
- if (textarea && typeof textarea._updateCursor === "function") {
58
- textarea._updateCursor();
59
- }
60
- }
61
-
62
- function clampCursorPos(pos = 0, value = "") {
63
- const text = String(value || "");
64
- const normalized = Number.isFinite(pos) ? Math.floor(pos) : 0;
65
- return Math.max(0, Math.min(text.length, normalized));
66
- }
67
-
68
- function refreshAfterEdit(textarea) {
69
- resizeInput();
70
- ensureInputCursorVisible();
71
- updateCursor(textarea);
72
- updateDraftFromInput();
73
-
74
- if (textarea && shouldShowCompletion(textarea.value)) {
75
- completionController.show(textarea.value);
76
- } else {
77
- completionController.hide();
78
- }
79
-
80
- render(textarea);
81
- }
82
-
83
- function replaceInputRange(textarea, start, end, replacement = "") {
84
- if (!textarea) return;
85
- const value = String(textarea.value || "");
86
- const safeStart = clampCursorPos(start, value);
87
- const safeEnd = clampCursorPos(end, value);
88
- const from = Math.min(safeStart, safeEnd);
89
- const to = Math.max(safeStart, safeEnd);
90
- const insert = String(replacement || "");
91
- textarea.value = value.slice(0, from) + insert + value.slice(to);
92
- setCursorPos(from + insert.length);
93
- resetPreferredCol();
94
- refreshAfterEdit(textarea);
95
- }
96
-
97
- function deleteWordBefore(textarea) {
98
- const value = String((textarea && textarea.value) || "");
99
- const cursorPos = clampCursorPos(getCursorPos(), value);
100
- if (cursorPos <= 0) return;
101
- const before = value.slice(0, cursorPos);
102
- const match = before.match(/\s*\S+\s*$/);
103
- const start = match ? cursorPos - match[0].length : Math.max(0, cursorPos - 1);
104
- replaceInputRange(textarea, start, cursorPos, "");
105
- }
106
-
107
- function moveCursorByWord(textarea, direction = "forward") {
108
- const value = String((textarea && textarea.value) || "");
109
- const cursorPos = clampCursorPos(getCursorPos(), value);
110
- if (direction === "backward") {
111
- const before = value.slice(0, cursorPos);
112
- const trimmedEnd = before.search(/\S\s*$/) >= 0 ? before.replace(/\s+$/, "") : before;
113
- const match = trimmedEnd.match(/\S+$/);
114
- return match ? trimmedEnd.length - match[0].length : 0;
115
- }
116
- const after = value.slice(cursorPos);
117
- const match = after.match(/^\s*\S+/);
118
- return match ? Math.min(value.length, cursorPos + match[0].length) : value.length;
119
- }
120
-
121
- function moveCursorToVisualBoundary(textarea, boundary = "start") {
122
- const width = getWrapWidth();
123
- const value = String((textarea && textarea.value) || "");
124
- if (width <= 0) return boundary === "end" ? value.length : 0;
125
- const cursorPos = clampCursorPos(getCursorPos(), value);
126
- const { row } = getCursorRowCol(value, cursorPos, width);
127
- const targetCol = boundary === "end" ? width : 0;
128
- return getCursorPosForRowCol(value, row, targetCol, width);
129
- }
130
-
131
- function setCursorAndRender(textarea, nextPos) {
132
- setCursorPos(clampCursorPos(nextPos, (textarea && textarea.value) || ""));
133
- ensureInputCursorVisible();
134
- updateCursor(textarea);
135
- render(textarea);
136
- }
137
-
138
- function handleKey(ch, key = {}, textarea) {
139
- const keyName = key && key.name;
140
-
141
- if (getCurrentView() === "agent") return;
142
-
143
- if (key && key.ctrl && keyName === "c") {
144
- exitHandler();
145
- return;
146
- }
147
-
148
- if (key && key.ctrl && keyName === "x") {
149
- const focusMode = getFocusMode();
150
- const dashboardView = getDashboardView();
151
- if (focusMode === "dashboard" && dashboardView !== "agents") {
152
- handleDashboardKey(key);
153
- return;
154
- }
155
- const selectedAgentIndex = getSelectedAgentIndex();
156
- const activeAgents = getActiveAgents();
157
- const targetAgent = getTargetAgent();
158
- if (
159
- focusMode === "dashboard" &&
160
- dashboardView === "agents" &&
161
- selectedAgentIndex >= 0 &&
162
- selectedAgentIndex < activeAgents.length
163
- ) {
164
- requestCloseAgent(activeAgents[selectedAgentIndex]);
165
- } else if (targetAgent) {
166
- requestCloseAgent(targetAgent);
167
- } else {
168
- logMessage("error", "{white-fg}✗{/white-fg} No agent selected");
169
- }
170
- return;
171
- }
172
-
173
- if (isSuppressKeypress()) {
174
- return;
175
- }
176
-
177
- normalizeCommandPrefix();
178
-
179
- if (getFocusMode() === "dashboard") {
180
- if (handleDashboardKey(key)) return;
181
- const dashboardView = getDashboardView();
182
- if (
183
- dashboardView === "agents" &&
184
- ch &&
185
- ch.length === 1 &&
186
- !(key && key.ctrl) &&
187
- !(key && key.meta) &&
188
- !/^[\x00-\x1f\x7f]$/.test(ch)
189
- ) {
190
- exitDashboardMode(true);
191
- } else {
192
- return;
193
- }
194
- }
195
-
196
- if (completionController.isActive() && completionController.handleKey(ch, key)) return;
197
-
198
- if (keyName === "pageup" || keyName === "pagedown") {
199
- const delta = Math.max(1, Math.floor(getLogHeight() / 2));
200
- scrollLog(keyName === "pageup" ? -delta : delta);
201
- return;
202
- }
203
-
204
- if (ch && ch.length > 1 && (!keyName || keyName.length !== 1)) {
205
- insertTextAtCursor(normalizePaste(ch));
206
- return;
207
- }
208
-
209
- if (ch && (ch.includes("\n") || ch.includes("\r")) && (keyName !== "return" && keyName !== "enter")) {
210
- insertTextAtCursor(normalizePaste(ch));
211
- return;
212
- }
213
-
214
- if (keyName === "return" || keyName === "enter") {
215
- const value = String((textarea && textarea.value) || "");
216
- const cursorPos = clampCursorPos(getCursorPos(), value);
217
- if (key && (key.shift || key.meta)) {
218
- insertTextAtCursor("\n");
219
- } else if (cursorPos > 0 && value[cursorPos - 1] === "\\") {
220
- replaceInputRange(textarea, cursorPos - 1, cursorPos, "\n");
221
- } else {
222
- resetPreferredCol();
223
- if (textarea && typeof textarea._done === "function") {
224
- textarea._done(null, textarea.value);
225
- }
226
- }
227
- return;
228
- }
229
-
230
- if (key && key.ctrl) {
231
- if (keyName === "a") {
232
- setCursorAndRender(textarea, moveCursorToVisualBoundary(textarea, "start"));
233
- resetPreferredCol();
234
- return;
235
- }
236
- if (keyName === "e") {
237
- setCursorAndRender(textarea, moveCursorToVisualBoundary(textarea, "end"));
238
- resetPreferredCol();
239
- return;
240
- }
241
- if (keyName === "b") {
242
- const cursorPos = getCursorPos();
243
- if (cursorPos > 0) setCursorAndRender(textarea, cursorPos - 1);
244
- resetPreferredCol();
245
- return;
246
- }
247
- if (keyName === "f") {
248
- const cursorPos = getCursorPos();
249
- const value = String((textarea && textarea.value) || "");
250
- if (cursorPos < value.length) setCursorAndRender(textarea, cursorPos + 1);
251
- resetPreferredCol();
252
- return;
253
- }
254
- if (keyName === "d") {
255
- const cursorPos = getCursorPos();
256
- const value = String((textarea && textarea.value) || "");
257
- if (cursorPos < value.length) replaceInputRange(textarea, cursorPos, cursorPos + 1, "");
258
- return;
259
- }
260
- if (keyName === "h") {
261
- const cursorPos = getCursorPos();
262
- if (cursorPos > 0) replaceInputRange(textarea, cursorPos - 1, cursorPos, "");
263
- return;
264
- }
265
- if (keyName === "k") {
266
- const cursorPos = getCursorPos();
267
- const value = String((textarea && textarea.value) || "");
268
- const target = moveCursorToVisualBoundary(textarea, "end");
269
- if (target === cursorPos && value[cursorPos] === "\n") {
270
- replaceInputRange(textarea, cursorPos, cursorPos + 1, "");
271
- } else {
272
- replaceInputRange(textarea, cursorPos, target, "");
273
- }
274
- return;
275
- }
276
- if (keyName === "u") {
277
- const cursorPos = getCursorPos();
278
- const value = String((textarea && textarea.value) || "");
279
- const target = moveCursorToVisualBoundary(textarea, "start");
280
- if (target === cursorPos && value[cursorPos - 1] === "\n") {
281
- replaceInputRange(textarea, cursorPos - 1, cursorPos, "");
282
- } else {
283
- replaceInputRange(textarea, target, cursorPos, "");
284
- }
285
- return;
286
- }
287
- if (keyName === "w") {
288
- deleteWordBefore(textarea);
289
- return;
290
- }
291
- }
292
-
293
- if (key && key.meta) {
294
- if (keyName === "b") {
295
- setCursorAndRender(textarea, moveCursorByWord(textarea, "backward"));
296
- resetPreferredCol();
297
- return;
298
- }
299
- if (keyName === "f") {
300
- setCursorAndRender(textarea, moveCursorByWord(textarea, "forward"));
301
- resetPreferredCol();
302
- return;
303
- }
304
- if (keyName === "d") {
305
- const cursorPos = getCursorPos();
306
- replaceInputRange(textarea, cursorPos, moveCursorByWord(textarea, "forward"), "");
307
- return;
308
- }
309
- }
310
-
311
- if (keyName === "left") {
312
- const cursorPos = getCursorPos();
313
- if (cursorPos > 0) setCursorPos(cursorPos - 1);
314
- resetPreferredCol();
315
- ensureInputCursorVisible();
316
- updateCursor(textarea);
317
- render(textarea);
318
- return;
319
- }
320
-
321
- if (keyName === "right") {
322
- const cursorPos = getCursorPos();
323
- if (cursorPos < (textarea && textarea.value ? textarea.value.length : 0)) {
324
- setCursorPos(cursorPos + 1);
325
- }
326
- resetPreferredCol();
327
- ensureInputCursorVisible();
328
- updateCursor(textarea);
329
- render(textarea);
330
- return;
331
- }
332
-
333
- if (keyName === "home") {
334
- setCursorAndRender(textarea, moveCursorToVisualBoundary(textarea, "start"));
335
- resetPreferredCol();
336
- return;
337
- }
338
-
339
- if (keyName === "end") {
340
- setCursorAndRender(textarea, moveCursorToVisualBoundary(textarea, "end"));
341
- resetPreferredCol();
342
- return;
343
- }
344
-
345
- if (keyName === "up") {
346
- if (completionController.isActive() && textarea && textarea.value === "/" && getCursorPos() === 1) {
347
- completionController.jumpToLast();
348
- return;
349
- }
350
- }
351
-
352
- if (keyName === "up" || keyName === "down") {
353
- const innerWidth = getWrapWidth();
354
- if (innerWidth <= 0) {
355
- if (keyName === "down") {
356
- enterDashboardMode();
357
- return;
358
- }
359
- ensureInputCursorVisible();
360
- updateCursor(textarea);
361
- render(textarea);
362
- return;
363
- }
364
-
365
- const cursorPos = getCursorPos();
366
- const value = (textarea && textarea.value) || "";
367
- if (!value) {
368
- if (keyName === "up") {
369
- if (historyUp()) completionController.hide();
370
- return;
371
- }
372
- if (historyDown()) {
373
- completionController.hide();
374
- return;
375
- }
376
- enterDashboardMode();
377
- return;
378
- }
379
- const { row, col } = getCursorRowCol(value, cursorPos, innerWidth);
380
- if (getPreferredCol() === null) setPreferredCol(col);
381
- const totalRows = countLines(value, innerWidth);
382
-
383
- if (keyName === "up" && row <= 0) {
384
- if (historyUp()) {
385
- completionController.hide();
386
- }
387
- return;
388
- }
389
-
390
- if (keyName === "down" && row >= totalRows - 1) {
391
- if (historyDown()) {
392
- completionController.hide();
393
- return;
394
- }
395
- enterDashboardMode();
396
- return;
397
- }
398
-
399
- const targetRow = keyName === "up"
400
- ? Math.max(0, row - 1)
401
- : Math.min(totalRows - 1, row + 1);
402
- setCursorPos(getCursorPosForRowCol(value, targetRow, getPreferredCol(), innerWidth));
403
-
404
- ensureInputCursorVisible();
405
- updateCursor(textarea);
406
- render(textarea);
407
- return;
408
- }
409
-
410
- if (keyName === "escape") {
411
- // Layer 1: clear @target agent
412
- if (getTargetAgent && getTargetAgent()) {
413
- clearTargetAgent();
414
- return;
415
- }
416
- // Layer 2: exit project scope → global scope
417
- if (getGlobalScope && getGlobalScope() === "project") {
418
- exitProjectScope();
419
- return;
420
- }
421
- // Layer 3: existing behavior (cancel input)
422
- if (textarea && typeof textarea._done === "function") {
423
- textarea._done(null, null);
424
- }
425
- return;
426
- }
427
-
428
- if (keyName === "backspace") {
429
- const cursorPos = getCursorPos();
430
- if (cursorPos > 0 && textarea) {
431
- if (key && (key.ctrl || key.meta)) {
432
- deleteWordBefore(textarea);
433
- } else {
434
- replaceInputRange(textarea, cursorPos - 1, cursorPos, "");
435
- }
436
- }
437
- return;
438
- }
439
-
440
- if (keyName === "delete") {
441
- const cursorPos = getCursorPos();
442
- if (textarea && cursorPos < textarea.value.length) {
443
- if (key && key.meta) {
444
- replaceInputRange(textarea, cursorPos, moveCursorToVisualBoundary(textarea, "end"), "");
445
- } else {
446
- replaceInputRange(textarea, cursorPos, cursorPos + 1, "");
447
- }
448
- }
449
- return;
450
- }
451
-
452
- const insertChar = (ch && ch.length === 1)
453
- ? ch
454
- : (keyName && keyName.length === 1 ? keyName : null);
455
-
456
- if (insertChar && !/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(insertChar) && textarea) {
457
- const cursorPos = getCursorPos();
458
- normalizeCommandPrefix();
459
- replaceInputRange(textarea, cursorPos, cursorPos, insertChar);
460
- }
461
- }
462
-
463
- return {
464
- handleKey,
465
- };
466
- }
467
-
468
- module.exports = {
469
- createInputListenerController,
470
- };