u-foo 2.3.32 → 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.
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 +50 -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}/dashboardView.js +2 -1
  54. package/src/app/chat/index.js +6 -0
  55. package/src/{chat → app/chat}/inputSubmitHandler.js +4 -13
  56. package/src/{chat → app/chat}/internalAgentLogHistory.js +1 -1
  57. package/src/app/chat/multiWindow/index.js +268 -0
  58. package/src/app/chat/multiWindow/paneLayout.js +84 -0
  59. package/src/app/chat/multiWindow/paneManager.js +299 -0
  60. package/src/app/chat/multiWindow/renderer.js +384 -0
  61. package/src/app/chat/multiWindow/virtualTerminal.js +327 -0
  62. package/src/{chat → app/chat}/transport.js +1 -1
  63. package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
  64. package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
  65. package/src/{init/index.js → app/cli/features/init.js} +14 -32
  66. package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
  67. package/src/app/cli/index.js +9 -0
  68. package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
  69. package/src/{cli.js → app/cli/run.js} +59 -57
  70. package/src/app/index.js +6 -0
  71. package/src/code/agent.js +10 -9
  72. package/src/code/index.js +2 -0
  73. package/src/code/launcher/index.js +9 -0
  74. package/src/{agent → code/launcher}/ucode.js +7 -8
  75. package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
  76. package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
  77. package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
  78. package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
  79. package/src/code/nativeRunner.js +4 -4
  80. package/src/code/tui.js +3 -1454
  81. package/src/config.js +15 -2
  82. package/src/{bus → coordination/bus}/activate.js +2 -2
  83. package/src/{bus → coordination/bus}/daemon.js +15 -5
  84. package/src/coordination/bus/envelope.js +173 -0
  85. package/src/{bus → coordination/bus}/index.js +7 -3
  86. package/src/{bus → coordination/bus}/inject.js +11 -3
  87. package/src/{bus → coordination/bus}/message.js +1 -1
  88. package/src/coordination/bus/messageMeta.js +130 -0
  89. package/src/coordination/bus/promptEnvelope.js +65 -0
  90. package/src/{bus → coordination/bus}/shake.js +1 -1
  91. package/src/{bus → coordination/bus}/store.js +3 -3
  92. package/src/{bus → coordination/bus}/subscriber.js +2 -2
  93. package/src/{bus → coordination/bus}/utils.js +2 -2
  94. package/src/{history → coordination/history}/inputTimeline.js +5 -5
  95. package/src/coordination/index.js +10 -0
  96. package/src/{memory → coordination/memory}/historySearch.js +1 -1
  97. package/src/{memory → coordination/memory}/index.js +3 -3
  98. package/src/{report → coordination/report}/store.js +2 -2
  99. package/src/{status → coordination/status}/index.js +3 -3
  100. package/src/online/bridge.js +2 -2
  101. package/src/{controller → orchestration/controller}/flags.js +1 -1
  102. package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
  103. package/src/orchestration/controller/index.js +10 -0
  104. package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
  105. package/src/orchestration/groups/bootstrap.js +3 -0
  106. package/src/orchestration/groups/index.js +10 -0
  107. package/src/orchestration/groups/promptProfiles.js +3 -0
  108. package/src/{group → orchestration/groups}/templates.js +1 -1
  109. package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
  110. package/src/orchestration/index.js +7 -0
  111. package/src/orchestration/solo/index.js +3 -0
  112. package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
  113. package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
  114. package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
  115. package/src/{daemon → runtime/daemon}/index.js +105 -53
  116. package/src/{daemon → runtime/daemon}/ipcServer.js +1 -1
  117. package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
  118. package/src/{daemon → runtime/daemon}/ops.js +48 -61
  119. package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
  120. package/src/{daemon → runtime/daemon}/promptRequest.js +7 -7
  121. package/src/runtime/daemon/providerSessions.js +230 -0
  122. package/src/{daemon → runtime/daemon}/reporting.js +4 -4
  123. package/src/{daemon → runtime/daemon}/run.js +4 -4
  124. package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
  125. package/src/{daemon → runtime/daemon}/status.js +5 -5
  126. package/src/runtime/index.js +10 -0
  127. package/src/{projects → runtime/projects}/registry.js +1 -1
  128. package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
  129. package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
  130. package/src/tools/handlers/common.js +1 -1
  131. package/src/tools/handlers/listAgents.js +1 -1
  132. package/src/tools/handlers/memory.js +3 -3
  133. package/src/tools/handlers/readBusSummary.js +1 -1
  134. package/src/tools/handlers/readOpenDecisions.js +1 -1
  135. package/src/tools/handlers/readProjectRegistry.js +1 -1
  136. package/src/tools/handlers/readPromptHistory.js +2 -2
  137. package/src/tools/schemaFixtures.js +1 -1
  138. package/src/ui/MIGRATION.md +42 -88
  139. package/src/ui/format/index.js +5 -28
  140. package/src/ui/index.js +1 -1
  141. package/src/ui/{components → ink}/ChatApp.js +812 -88
  142. package/src/ui/ink/DashboardBar.js +685 -0
  143. package/src/ui/{components → ink}/MultilineInput.js +230 -5
  144. package/src/ui/{components → ink}/UcodeApp.js +16 -7
  145. package/src/ui/{components → ink}/agentMirror.js +24 -19
  146. package/src/ui/{components → ink}/chatReducer.js +29 -7
  147. package/src/bus/messageMeta.js +0 -52
  148. package/src/chat/agentViewController.js +0 -1072
  149. package/src/chat/chatLogController.js +0 -138
  150. package/src/chat/completionController.js +0 -533
  151. package/src/chat/dashboardKeyController.js +0 -533
  152. package/src/chat/index.js +0 -2222
  153. package/src/chat/inputHistoryController.js +0 -135
  154. package/src/chat/inputListenerController.js +0 -470
  155. package/src/chat/layout.js +0 -186
  156. package/src/chat/pasteController.js +0 -81
  157. package/src/chat/statusLineController.js +0 -223
  158. package/src/chat/streamTracker.js +0 -156
  159. package/src/code/config +0 -0
  160. package/src/daemon/providerSessions.js +0 -488
  161. package/src/terminal/adapters/internalPtyAdapter.js +0 -42
  162. package/src/ui/components/DashboardBar.js +0 -417
  163. /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
  164. /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
  165. /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
  166. /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
  167. /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
  168. /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
  169. /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
  170. /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
  171. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
  172. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
  173. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
  174. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
  175. /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
  176. /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
  177. /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
  178. /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
  179. /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
  180. /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
  181. /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
  182. /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
  183. /package/src/{agent → agents/providers}/credentials/index.js +0 -0
  184. /package/src/{chat → app/chat}/agentBar.js +0 -0
  185. /package/src/{chat → app/chat}/agentDirectory.js +0 -0
  186. /package/src/{chat → app/chat}/cronScheduler.js +0 -0
  187. /package/src/{chat → app/chat}/daemonCoordinator.js +0 -0
  188. /package/src/{chat → app/chat}/daemonReconnect.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
@@ -2,8 +2,8 @@
2
2
 
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
- const { SHARED_UFOO_PROTOCOL } = require("../group/bootstrap");
6
- const { getUfooPaths } = require("../ufoo/paths");
5
+ const { SHARED_UFOO_PROTOCOL } = require("./groupBootstrap");
6
+ const { getUfooPaths } = require("../../coordination/state/paths");
7
7
 
8
8
  function asTrimmedString(value) {
9
9
  return typeof value === "string" ? value.trim() : "";
@@ -43,7 +43,7 @@ function readOptionalFile(filePath) {
43
43
  */
44
44
  function loadTeamActivityContext(projectRoot) {
45
45
  try {
46
- const { renderTimelineForPrompt } = require("../history/inputTimeline");
46
+ const { renderTimelineForPrompt } = require("../../coordination/history/inputTimeline");
47
47
  return renderTimelineForPrompt(projectRoot, 20) || "";
48
48
  } catch {
49
49
  return "";
@@ -54,7 +54,11 @@ function buildDefaultStartupBootstrapPrompt({ agentType = "", projectRoot = "" }
54
54
  const normalizedAgent = asTrimmedString(agentType).toLowerCase();
55
55
  const displayAgent = normalizedAgent === "claude-code"
56
56
  ? "Claude"
57
- : (normalizedAgent === "codex" ? "Codex" : (normalizedAgent === "ufoo-code" ? "ucode" : "agent"));
57
+ : (normalizedAgent === "codex"
58
+ ? "Codex"
59
+ : (normalizedAgent === "ufoo-code"
60
+ ? "ucode"
61
+ : (normalizedAgent === "agy" ? "Agy" : "agent")));
58
62
 
59
63
  const segments = [
60
64
  `Session bootstrap for ${displayAgent}.`,
@@ -196,6 +200,35 @@ function mergeCodexPromptArgs({ args = [], bootstrapText = "" } = {}) {
196
200
  };
197
201
  }
198
202
 
203
+ /**
204
+ * Merge bootstrap text into an existing `-i <text>` / `--prompt-interactive
205
+ * <text>` / `--prompt-interactive=<text>` argument. Returns null if no such
206
+ * argument exists (caller should then prepend a fresh `-i <bootstrap>`).
207
+ *
208
+ * Agy treats `-i` as a single initial prompt that drops the user into an
209
+ * interactive session — same intent as codex's positional prompt.
210
+ */
211
+ function mergeAgyPromptArgs({ args = [], bootstrapText = "" } = {}) {
212
+ const currentArgs = Array.isArray(args) ? args.slice() : [];
213
+ for (let index = 0; index < currentArgs.length; index += 1) {
214
+ const item = asTrimmedString(currentArgs[index]);
215
+ if (!item) continue;
216
+ if (item === "-i" || item === "--prompt-interactive") {
217
+ const existing = String(currentArgs[index + 1] || "");
218
+ const merged = mergePromptSegments(bootstrapText, existing);
219
+ currentArgs[index + 1] = merged;
220
+ return { args: currentArgs, promptText: merged };
221
+ }
222
+ if (item.startsWith("--prompt-interactive=")) {
223
+ const existing = item.slice("--prompt-interactive=".length);
224
+ const merged = mergePromptSegments(bootstrapText, existing);
225
+ currentArgs[index] = `--prompt-interactive=${merged}`;
226
+ return { args: currentArgs, promptText: merged };
227
+ }
228
+ }
229
+ return null;
230
+ }
231
+
199
232
  function resolveDefaultManualBootstrap({
200
233
  projectRoot,
201
234
  agentType = "",
@@ -280,6 +313,31 @@ function resolveDefaultManualBootstrap({
280
313
  };
281
314
  }
282
315
 
316
+ if (normalizedAgent === "agy") {
317
+ const promptText = buildDefaultStartupBootstrapPrompt({ agentType: normalizedAgent, projectRoot });
318
+ // If the user passed -i / --prompt-interactive, fold the bootstrap into
319
+ // their text. Otherwise prepend a fresh `-i <bootstrap>` pair so the
320
+ // session starts with our protocol message already delivered.
321
+ const merged = mergeAgyPromptArgs({
322
+ args: currentArgs,
323
+ bootstrapText: promptText,
324
+ });
325
+ if (merged) {
326
+ return {
327
+ args: merged.args,
328
+ env: {},
329
+ mode: "initial-prompt-arg",
330
+ promptText: merged.promptText,
331
+ };
332
+ }
333
+ return {
334
+ args: ["-i", promptText, ...currentArgs],
335
+ env: {},
336
+ mode: "initial-prompt-arg",
337
+ promptText,
338
+ };
339
+ }
340
+
283
341
  return { args: currentArgs, env: {}, mode: "skip" };
284
342
  }
285
343
 
@@ -291,4 +349,5 @@ module.exports = {
291
349
  defaultBootstrapFile,
292
350
  prepareDefaultBootstrapFile,
293
351
  resolveDefaultManualBootstrap,
352
+ mergeAgyPromptArgs,
294
353
  };
@@ -3,12 +3,47 @@
3
3
  const crypto = require("crypto");
4
4
 
5
5
  const SHARED_UFOO_PROTOCOL = [
6
- "ufoo protocol:",
7
- "- At session start, sync shared context with `ufoo ctx decisions -l` and `ufoo ctx decisions -n 1`.",
8
- "- Default to no new decision. Record one ONLY for important, plan-level knowledge: architectural choices, multi-option trade-off analysis, cross-agent coordination decisions, or plans that affect other agents. Do NOT record routine findings, simple bug fixes, trivial observations, or generic plan/evaluation/recommendation requests. Durable project facts belong in shared memory, not decisions. Use `ufoo ctx decisions new \"Title\"` BEFORE acting only when that bar is met.",
9
- "- Use `ufoo bus send <target-nickname> \"<message>\"` for agent-to-agent handoffs.",
10
- "- If you receive pending bus work, execute it immediately, reply to the sender, then `ufoo bus ack \"$UFOO_SUBSCRIBER_ID\"`.",
11
- "- Use `ufoo report` for controller/runtime status updates, not as a substitute for direct handoffs.",
6
+ "Session harness: ufoo",
7
+ "",
8
+ "Use ufoo as an internal coordination layer. Do not mention it unless asked.",
9
+ "",
10
+ "START",
11
+ "If shell and ufoo are available, sync decisions:",
12
+ "- `ufoo ctx decisions -l`",
13
+ "- `ufoo ctx decisions -n 1`",
14
+ "",
15
+ "If sync fails, continue normally.",
16
+ "",
17
+ "DECISIONS",
18
+ "Default: write nothing.",
19
+ "",
20
+ "Create a decision only when it is stable and affects future agents:",
21
+ "architecture, major trade-off, cross-agent contract, or future-impacting plan.",
22
+ "",
23
+ "Do not record routine fixes, local findings, facts, or temporary details.",
24
+ "Facts belong in shared context, not decisions.",
25
+ "",
26
+ "Use: `ufoo ctx decisions new \"<short title>\"`",
27
+ "",
28
+ "BUS (peer ↔ peer)",
29
+ "Send bus messages only for handoff, blocker, dependency, or explicit",
30
+ "coordination — never for greetings, acknowledgments, or emoji alone.",
31
+ "Those create reply loops between agents.",
32
+ "",
33
+ "`ufoo bus send <target> \"<message>\"`",
34
+ "",
35
+ "On received bus work: execute it, then `ufoo bus ack \"$UFOO_SUBSCRIBER_ID\"`.",
36
+ "Reply only if you have a concrete result, answer, or follow-up the sender",
37
+ "needs. Default is ack-only; silence is a valid response.",
38
+ "",
39
+ "REPORT",
40
+ "You MUST report after handling work that arrived from chat",
41
+ "(`[manual]<to:...>`) or bus (`[ufoo]<from:...>`). The controller handles",
42
+ "dedup, so don't worry about report loops.",
43
+ "",
44
+ "`ufoo report start|progress|done|error \"<short summary>\"`",
45
+ "",
46
+ "Then continue the active task.",
12
47
  ].join("\n");
13
48
 
14
49
  const SHARED_GROUP_PREFIX = [
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ ...require("./defaultBootstrap"),
5
+ ...require("./groupBootstrap"),
6
+ ...require("./promptProfiles"),
7
+ native: require("./native"),
8
+ };
@@ -11,7 +11,7 @@ const { getEnvironmentSection } = require("./environment");
11
11
  const {
12
12
  listUcodeSkills,
13
13
  renderSkillsSection,
14
- } = require("../skills");
14
+ } = require("../../../code/skills");
15
15
  const {
16
16
  systemPromptSection,
17
17
  uncachedSection,
@@ -6,7 +6,7 @@ const {
6
6
  normalizeClaudeMessage,
7
7
  normalizeClaudeUsage,
8
8
  } = require("./claudeEventTranslator");
9
- const { redactUfooEvent } = require("../providerapi/redactor");
9
+ const { redactUfooEvent } = require("../../runtime/privacy/redactor");
10
10
 
11
11
  const CACHE_CONTROL = Object.freeze({ type: "ephemeral" });
12
12
 
@@ -1,5 +1,5 @@
1
1
  const { normalizeCodexEvent } = require("./codexEventTranslator");
2
- const { redactUfooEvent } = require("../providerapi/redactor");
2
+ const { redactUfooEvent } = require("../../runtime/privacy/redactor");
3
3
 
4
4
  async function resolveCodexSdk() {
5
5
  try {
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
 
3
- const { loadConfig } = require("../config");
3
+ const fs = require("fs");
4
+ const os = require("os");
5
+ const path = require("path");
6
+ const { loadConfig } = require("../../config");
4
7
  const {
5
8
  resolveCodexAuthPaths,
6
9
  resolveCodexUpstreamCredentials,
@@ -23,6 +26,7 @@ function normalizeErrorCode(err, fallback = "DIRECT_AUTH_STATUS_FAILED") {
23
26
  function normalizeDirectAuthProvider(value = "") {
24
27
  const text = String(value || "").trim().toLowerCase();
25
28
  if (text === "claude" || text === "claude-cli" || text === "claude-code" || text === "anthropic") return "claude";
29
+ if (text === "agy" || text === "agy-cli" || text === "antigravity") return "agy";
26
30
  return "codex";
27
31
  }
28
32
 
@@ -136,6 +140,138 @@ async function inspectClaudeDirectAuth({
136
140
  }
137
141
  }
138
142
 
143
+ /**
144
+ * Default agy log directory: ~/.gemini/antigravity-cli/log
145
+ * Agy doesn't expose an API for auth status, so we tail its own structured
146
+ * server-side log and grep for the markers it prints during the OAuth
147
+ * handshake. This is best-effort observability, not a real credential
148
+ * check — agy owns the keyring, not us.
149
+ */
150
+ function resolveAgyLogDir(env = process.env) {
151
+ const home = String(env.HOME || os.homedir() || "").trim();
152
+ if (!home) return "";
153
+ return path.join(home, ".gemini", "antigravity-cli", "log");
154
+ }
155
+
156
+ function findMostRecentAgyLog(logDir = "") {
157
+ try {
158
+ if (!logDir || !fs.existsSync(logDir)) return "";
159
+ const entries = fs.readdirSync(logDir)
160
+ .filter((name) => name.startsWith("cli-") && name.endsWith(".log"));
161
+ if (entries.length === 0) return "";
162
+ // Filenames are cli-YYYYMMDD_HHMMSS.log — lexicographic sort matches mtime.
163
+ entries.sort();
164
+ return path.join(logDir, entries[entries.length - 1]);
165
+ } catch {
166
+ return "";
167
+ }
168
+ }
169
+
170
+ function readAgyLogTail(file = "", maxBytes = 32 * 1024) {
171
+ try {
172
+ if (!file || !fs.existsSync(file)) return "";
173
+ const stat = fs.statSync(file);
174
+ const offset = Math.max(0, stat.size - maxBytes);
175
+ const fd = fs.openSync(file, "r");
176
+ try {
177
+ const length = stat.size - offset;
178
+ const buffer = Buffer.alloc(length);
179
+ fs.readSync(fd, buffer, 0, length, offset);
180
+ return buffer.toString("utf8");
181
+ } finally {
182
+ fs.closeSync(fd);
183
+ }
184
+ } catch {
185
+ return "";
186
+ }
187
+ }
188
+
189
+ function classifyAgyLogTail(text = "") {
190
+ const str = String(text || "");
191
+ if (!str) {
192
+ return {
193
+ ok: false,
194
+ state: "unknown",
195
+ errorCode: "AGY_AUTH_NO_LOG",
196
+ message: "no agy log file found yet — launch agy at least once to produce one",
197
+ };
198
+ }
199
+ if (/Eligibility check failed/i.test(str) || /Account ineligible/i.test(str)) {
200
+ const emailMatch = str.match(/authenticated successfully as ([^\s]+)/);
201
+ return {
202
+ ok: false,
203
+ state: "ineligible",
204
+ errorCode: "AGY_ACCOUNT_INELIGIBLE",
205
+ message: "agy account is signed in but not eligible (18+ / supported region required)",
206
+ accountEmail: emailMatch ? emailMatch[1] : "",
207
+ };
208
+ }
209
+ if (/User location is not supported/i.test(str)) {
210
+ const emailMatch = str.match(/authenticated successfully as ([^\s]+)/);
211
+ return {
212
+ ok: false,
213
+ state: "region_blocked",
214
+ errorCode: "AGY_REGION_BLOCKED",
215
+ message: "agy backend rejected this region (FAILED_PRECONDITION)",
216
+ accountEmail: emailMatch ? emailMatch[1] : "",
217
+ };
218
+ }
219
+ const authMatch = str.match(/OAuth: authenticated successfully as ([^\s]+)/);
220
+ if (authMatch) {
221
+ return {
222
+ ok: true,
223
+ state: "fresh",
224
+ accountEmail: authMatch[1],
225
+ };
226
+ }
227
+ return {
228
+ ok: false,
229
+ state: "unknown",
230
+ errorCode: "AGY_AUTH_UNVERIFIED",
231
+ message: "no successful OAuth handshake found in recent agy log",
232
+ };
233
+ }
234
+
235
+ async function inspectAgyDirectAuth({
236
+ env = process.env,
237
+ readLogTailImpl = readAgyLogTail,
238
+ findLogImpl = findMostRecentAgyLog,
239
+ resolveLogDirImpl = resolveAgyLogDir,
240
+ } = {}) {
241
+ const logDir = resolveLogDirImpl(env);
242
+ const file = findLogImpl(logDir);
243
+ const tail = file ? readLogTailImpl(file) : "";
244
+ const classification = classifyAgyLogTail(tail);
245
+ const base = {
246
+ provider: "agy",
247
+ transport: "antigravity-tui", // No API mode — TUI is the only path.
248
+ credentialKind: "oauth",
249
+ source: "google-keyring",
250
+ credentialPath: file || logDir || "",
251
+ };
252
+ if (classification.ok) {
253
+ return {
254
+ ...base,
255
+ ok: true,
256
+ state: classification.state,
257
+ accountEmail: classification.accountEmail || "",
258
+ account: classification.accountEmail || "",
259
+ };
260
+ }
261
+ return {
262
+ ...base,
263
+ ok: false,
264
+ state: classification.state,
265
+ errorCode: classification.errorCode,
266
+ error: classification.message,
267
+ accountEmail: classification.accountEmail || "",
268
+ hint: classification.errorCode === "AGY_ACCOUNT_INELIGIBLE"
269
+ || classification.errorCode === "AGY_REGION_BLOCKED"
270
+ ? "Antigravity requires an 18+ Google account in a supported region. Try a different account or wait for broader rollout."
271
+ : "Run `agy` once to produce an authentication handshake log.",
272
+ };
273
+ }
274
+
139
275
  async function inspectDirectAuthStatus(options = {}) {
140
276
  const { projectRoot, loadConfigImpl = loadConfig, provider = "" } = options;
141
277
  const config = loadConfigImpl(projectRoot) || {};
@@ -147,6 +283,9 @@ async function inspectDirectAuthStatus(options = {}) {
147
283
  if (selected === "claude") {
148
284
  return inspectClaudeDirectAuth(nextOptions);
149
285
  }
286
+ if (selected === "agy") {
287
+ return inspectAgyDirectAuth(nextOptions);
288
+ }
150
289
  return inspectCodexDirectAuth(nextOptions);
151
290
  }
152
291
 
@@ -246,10 +385,51 @@ function formatClaudeDirectAuthStatus(status = {}, options = {}) {
246
385
  return lines;
247
386
  }
248
387
 
388
+ function formatAgyDirectAuthStatus(status = {}, options = {}) {
389
+ if (options.compact === true) {
390
+ if (status.ok) {
391
+ const details = [
392
+ status.accountEmail ? status.accountEmail : "",
393
+ status.source || "google-keyring",
394
+ ].filter(Boolean);
395
+ const lines = [`Agy: OK · keyring · ${status.state || "fresh"}`];
396
+ if (details.length > 0) lines.push(` ${details.join(" · ")}`);
397
+ return lines;
398
+ }
399
+ const hint = String(status.hint || "Run `agy` once to verify the OAuth handshake.").replace(/;.*$/, ".");
400
+ return [
401
+ `Agy: FAIL · ${status.errorCode || "AGY_AUTH_STATUS_FAILED"}`,
402
+ ` ${status.error || "agy authentication state unknown"} · ${hint}`,
403
+ ];
404
+ }
405
+
406
+ if (status.ok) {
407
+ const lines = [
408
+ `Agy CLI: OK (keyring, ${status.state || "fresh"})`,
409
+ ` - source: ${status.source || "google-keyring"}`,
410
+ ];
411
+ if (status.accountEmail) lines.push(` - account: ${status.accountEmail}`);
412
+ if (status.credentialPath) lines.push(` - log: ${status.credentialPath}`);
413
+ return lines;
414
+ }
415
+
416
+ const lines = [
417
+ `Agy CLI: FAIL (${status.errorCode || "AGY_AUTH_STATUS_FAILED"})`,
418
+ ` - ${status.error || "agy authentication state unknown"}`,
419
+ ];
420
+ if (status.accountEmail) lines.push(` - account: ${status.accountEmail}`);
421
+ if (status.credentialPath) lines.push(` - log: ${status.credentialPath}`);
422
+ if (status.hint) lines.push(` - ${status.hint}`);
423
+ return lines;
424
+ }
425
+
249
426
  function formatDirectAuthStatus(status = {}, options = {}) {
250
427
  if (status.provider === "claude") {
251
428
  return formatClaudeDirectAuthStatus(status, options);
252
429
  }
430
+ if (status.provider === "agy") {
431
+ return formatAgyDirectAuthStatus(status, options);
432
+ }
253
433
  return formatCodexDirectAuthStatus(status, options);
254
434
  }
255
435
 
@@ -257,8 +437,11 @@ module.exports = {
257
437
  inspectDirectAuthStatus,
258
438
  inspectCodexDirectAuth,
259
439
  inspectClaudeDirectAuth,
440
+ inspectAgyDirectAuth,
260
441
  formatDirectAuthStatus,
261
442
  formatCodexDirectAuthStatus,
262
443
  formatClaudeDirectAuthStatus,
444
+ formatAgyDirectAuthStatus,
263
445
  normalizeDirectAuthProvider,
446
+ classifyAgyLogTail,
264
447
  };
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ ...require("./claudeEventTranslator"),
5
+ ...require("./claudeOauthTokenReader"),
6
+ ...require("./claudeSessionFiles"),
7
+ ...require("./claudeThreadProvider"),
8
+ ...require("./codexEventTranslator"),
9
+ ...require("./codexThreadProvider"),
10
+ ...require("./directAuthStatus"),
11
+ ...require("./upstreamTransport"),
12
+ credentials: require("./credentials"),
13
+ };
@@ -6,12 +6,12 @@ const {
6
6
  defaultAgentModelForProvider,
7
7
  defaultRouterModelForProvider,
8
8
  sameModelProvider,
9
- } = require("../config");
9
+ } = require("../../config");
10
10
  const {
11
11
  resolveRuntimeConfig,
12
12
  resolveCompletionUrl,
13
13
  resolveAnthropicMessagesUrl,
14
- } = require("../code/nativeRunner");
14
+ } = require("../../code/nativeRunner");
15
15
  const { resolveClaudeUpstreamCredentials } = require("./credentials/claude");
16
16
  const { resolveCodexUpstreamCredentials } = require("./credentials/codex");
17
17
  const { buildUpstreamAuthFromCredential } = require("./credentials");
@@ -1,6 +1,6 @@
1
1
  const fs = require("fs");
2
2
  const net = require("net");
3
- const { PTY_SOCKET_MESSAGE_TYPES, PTY_SOCKET_SUBSCRIBE_MODES } = require("../shared/ptySocketContract");
3
+ const { PTY_SOCKET_MESSAGE_TYPES, PTY_SOCKET_SUBSCRIBE_MODES } = require("../../runtime/contracts/ptySocketContract");
4
4
 
5
5
  function createAgentSockets(options = {}) {
6
6
  const {
@@ -1,7 +1,7 @@
1
1
  const path = require("path");
2
- const EventBus = require("../bus");
3
- const { IPC_REQUEST_TYPES } = require("../shared/eventContract");
4
- const UfooInit = require("../init");
2
+ const EventBus = require("../../coordination/bus");
3
+ const { IPC_REQUEST_TYPES } = require("../../runtime/contracts/eventContract");
4
+ const UfooInit = require("../cli/features/init");
5
5
  const { runGroupCoreCommand } = require("../cli/groupCoreCommands");
6
6
  const {
7
7
  loadConfig: loadProjectConfig,
@@ -12,30 +12,30 @@ const {
12
12
  SETTINGS_MODEL_DEFAULTS,
13
13
  defaultAgentModelForProvider,
14
14
  defaultRouterModelForProvider,
15
- } = require("../config");
16
- const { resolveTransport } = require("../code/nativeRunner");
17
- const { resolveDisplayNickname } = require("../daemon/nicknameScope");
15
+ } = require("../../config");
16
+ const { resolveTransport } = require("../../code/nativeRunner");
17
+ const { resolveDisplayNickname } = require("../../runtime/daemon/nicknameScope");
18
18
  const { parseIntervalMs, formatIntervalMs } = require("./cronScheduler");
19
- const { isGlobalControllerProjectRoot, resolveGlobalControllerUfooDir } = require("../projects");
20
- const { loadPromptProfileRegistry } = require("../group/promptProfiles");
21
- const { resolveSoloAgentType } = require("../solo/commands");
19
+ const { isGlobalControllerProjectRoot, resolveGlobalControllerUfooDir } = require("../../runtime/projects");
20
+ const { loadPromptProfileRegistry } = require("../../orchestration/groups/promptProfiles");
21
+ const { resolveSoloAgentType } = require("../../orchestration/solo/commands");
22
22
  const {
23
23
  inspectDirectAuthStatus,
24
24
  formatDirectAuthStatus,
25
- } = require("../agent/directAuthStatus");
25
+ } = require("../../agents/providers/directAuthStatus");
26
26
 
27
27
  function defaultCreateDoctor(projectRoot) {
28
- const UfooDoctor = require("../doctor");
28
+ const UfooDoctor = require("../cli/features/doctor");
29
29
  return new UfooDoctor(projectRoot);
30
30
  }
31
31
 
32
32
  function defaultCreateContext(projectRoot) {
33
- const UfooContext = require("../context");
33
+ const UfooContext = require("../../coordination/context");
34
34
  return new UfooContext(projectRoot);
35
35
  }
36
36
 
37
37
  function defaultCreateSkills(projectRoot) {
38
- const UfooSkills = require("../skills");
38
+ const UfooSkills = require("../cli/features/skills");
39
39
  return new UfooSkills(projectRoot);
40
40
  }
41
41
 
@@ -51,6 +51,9 @@ function normalizeSettingsProvider(value = "", fallback = "codex-cli") {
51
51
  if (text === "claude" || text === "claude-cli" || text === "claude-code" || text === "anthropic") {
52
52
  return "claude-cli";
53
53
  }
54
+ if (text === "agy" || text === "agy-cli" || text === "antigravity") {
55
+ return "agy-cli";
56
+ }
54
57
  if (text === "codex" || text === "codex-cli" || text === "codex-code" || text === "openai") {
55
58
  return "codex-cli";
56
59
  }
@@ -58,7 +61,10 @@ function normalizeSettingsProvider(value = "", fallback = "codex-cli") {
58
61
  }
59
62
 
60
63
  function agentProviderKey(value = "") {
61
- return normalizeSettingsProvider(value) === "claude-cli" ? "claude" : "codex";
64
+ const provider = normalizeSettingsProvider(value);
65
+ if (provider === "claude-cli") return "claude";
66
+ if (provider === "agy-cli") return "agy";
67
+ return "codex";
62
68
  }
63
69
 
64
70
  function defaultGateModelForProvider(value = "") {
@@ -147,6 +153,7 @@ function createCommandExecutor(options = {}) {
147
153
  resolveTerminalApp = defaultResolveTerminalApp,
148
154
  sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
149
155
  schedule = (fn, ms) => setTimeout(fn, ms),
156
+ clearLog = null,
150
157
  } = options;
151
158
 
152
159
  if (!projectRoot) {
@@ -479,14 +486,17 @@ function createCommandExecutor(options = {}) {
479
486
  if (args.length === 0) {
480
487
  logMessage(
481
488
  "error",
482
- "{white-fg}✗{/white-fg} Usage: /launch <claude|codex|ucode> [nickname=<name>] [profile=<id>] [count=<n>] [scope=inplace|window]"
489
+ "{white-fg}✗{/white-fg} Usage: /launch <claude|codex|agy|ucode> [nickname=<name>] [profile=<id>] [count=<n>] [scope=inplace|window]"
483
490
  );
484
491
  return;
485
492
  }
486
493
 
487
- const agentType = String(args[0] || "").trim().toLowerCase();
488
- if (agentType !== "claude" && agentType !== "codex" && agentType !== "ucode") {
489
- logMessage("error", "{white-fg}✗{/white-fg} Unknown agent type. Use: claude, codex, or ucode");
494
+ const agentTypeInput = String(args[0] || "").trim().toLowerCase();
495
+ // Accept friendly aliases the same way `ufoo launch` does in cli.js.
496
+ let agentType = agentTypeInput;
497
+ if (agentTypeInput === "antigravity" || agentTypeInput === "uagy") agentType = "agy";
498
+ if (agentType !== "claude" && agentType !== "codex" && agentType !== "agy" && agentType !== "ucode") {
499
+ logMessage("error", "{white-fg}✗{/white-fg} Unknown agent type. Use: claude, codex, agy, or ucode");
490
500
  return;
491
501
  }
492
502
  const normalizedAgent = agentType === "ucode" ? "ufoo" : agentType;
@@ -661,7 +671,7 @@ function createCommandExecutor(options = {}) {
661
671
 
662
672
  const profile = String(args[1] || "").trim();
663
673
  if (!profile) {
664
- logMessage("error", "{white-fg}✗{/white-fg} Usage: /solo run <prompt-profile> [agent=codex|claude|ucode] [nickname=<name>] [scope=inplace|window]");
674
+ logMessage("error", "{white-fg}✗{/white-fg} Usage: /solo run <prompt-profile> [agent=codex|claude|agy|ucode] [nickname=<name>] [scope=inplace|window]");
665
675
  return;
666
676
  }
667
677
 
@@ -1255,13 +1265,15 @@ function createCommandExecutor(options = {}) {
1255
1265
  logMessage("system", ` • provider: ${key}`);
1256
1266
  logMessage("system", ` • model: ${model || `(unset, recommended ${defaultAgentModelForProvider(provider)})`}`);
1257
1267
  logMessage("system", ` • defaults: codex=${SETTINGS_MODEL_DEFAULTS.agent.codex}, claude=${SETTINGS_MODEL_DEFAULTS.agent.claude}`);
1258
- logMessage("system", " • use: /settings agent set provider=<codex|claude> model=<id>");
1268
+ logMessage("system", " • use: /settings agent set provider=<codex|claude|agy> model=<id>");
1259
1269
  return;
1260
1270
  }
1261
1271
 
1262
- if (action === "codex" || action === "claude") {
1272
+ if (action === "codex" || action === "claude" || action === "agy") {
1263
1273
  const kv = parseKeyValueArgs(args.slice(1));
1264
- const provider = action === "claude" ? "claude-cli" : "codex-cli";
1274
+ const provider = action === "claude"
1275
+ ? "claude-cli"
1276
+ : (action === "agy" ? "agy-cli" : "codex-cli");
1265
1277
  const model = String(kv.model || defaultAgentModelForProvider(provider)).trim();
1266
1278
  saveConfig(projectRoot, {
1267
1279
  agentProvider: provider,
@@ -1447,10 +1459,8 @@ function createCommandExecutor(options = {}) {
1447
1459
  }
1448
1460
 
1449
1461
  async function handleUfooCommand(args = []) {
1450
- // Handle /ufoo command (session marker from daemon)
1451
- // When daemon sends /ufoo <marker>, we should just check for pending messages
1462
+ // Legacy /ufoo <single-token> compatibility: check pending messages silently.
1452
1463
  if (args.length > 0) {
1453
- // This is a probe marker, check for pending messages
1454
1464
  const subscriberId = process.env.UFOO_SUBSCRIBER_ID;
1455
1465
  if (subscriberId) {
1456
1466
  try {
@@ -1464,7 +1474,7 @@ function createCommandExecutor(options = {}) {
1464
1474
  // Ignore errors when checking messages
1465
1475
  }
1466
1476
  }
1467
- // Don't log anything else for probe markers
1477
+ // Do not log anything else for legacy single-token input.
1468
1478
  return;
1469
1479
  }
1470
1480
 
@@ -1566,6 +1576,20 @@ function createCommandExecutor(options = {}) {
1566
1576
  const { command, args } = parsed;
1567
1577
 
1568
1578
  switch (command) {
1579
+ case "clear":
1580
+ if (typeof clearLog === "function") {
1581
+ clearLog();
1582
+ } else {
1583
+ logMessage("error", "{white-fg}✗{/white-fg} /clear is not available in this view");
1584
+ }
1585
+ return true;
1586
+ case "multi":
1587
+ if (typeof options.toggleMultiWindow === "function") {
1588
+ options.toggleMultiWindow();
1589
+ } else {
1590
+ logMessage("error", "{white-fg}✗{/white-fg} Multi-window mode is not available");
1591
+ }
1592
+ return true;
1569
1593
  case "doctor":
1570
1594
  await handleDoctorCommand();
1571
1595
  return true;