u-foo 1.0.6 → 1.1.9

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 (149) hide show
  1. package/README.md +44 -4
  2. package/SKILLS/ufoo/SKILL.md +17 -2
  3. package/SKILLS/uinit/SKILL.md +8 -3
  4. package/bin/ucode-core.js +15 -0
  5. package/bin/ucode.js +125 -0
  6. package/bin/ufoo-assistant-agent.js +5 -0
  7. package/bin/ufoo-engine.js +25 -0
  8. package/bin/ufoo.js +4 -0
  9. package/modules/AGENTS.template.md +14 -4
  10. package/modules/bus/README.md +8 -5
  11. package/modules/bus/SKILLS/ubus/SKILL.md +5 -4
  12. package/modules/context/SKILLS/uctx/SKILL.md +3 -1
  13. package/modules/online/SKILLS/ufoo-online/SKILL.md +144 -0
  14. package/package.json +12 -3
  15. package/scripts/import-pi-mono.js +124 -0
  16. package/scripts/postinstall.js +20 -49
  17. package/scripts/sync-claude-skills.sh +21 -0
  18. package/src/agent/cliRunner.js +524 -31
  19. package/src/agent/internalRunner.js +76 -9
  20. package/src/agent/launcher.js +97 -45
  21. package/src/agent/normalizeOutput.js +1 -1
  22. package/src/agent/notifier.js +144 -4
  23. package/src/agent/ptyRunner.js +480 -10
  24. package/src/agent/ptyWrapper.js +28 -3
  25. package/src/agent/readyDetector.js +16 -0
  26. package/src/agent/ucode.js +443 -0
  27. package/src/agent/ucodeBootstrap.js +113 -0
  28. package/src/agent/ucodeBuild.js +67 -0
  29. package/src/agent/ucodeDoctor.js +184 -0
  30. package/src/agent/ucodeRuntimeConfig.js +129 -0
  31. package/src/agent/ufooAgent.js +11 -2
  32. package/src/assistant/agent.js +260 -0
  33. package/src/assistant/bridge.js +172 -0
  34. package/src/assistant/engine.js +252 -0
  35. package/src/assistant/stdio.js +58 -0
  36. package/src/assistant/ufooEngineCli.js +306 -0
  37. package/src/bus/activate.js +27 -11
  38. package/src/bus/daemon.js +133 -5
  39. package/src/bus/index.js +137 -80
  40. package/src/bus/inject.js +47 -17
  41. package/src/bus/message.js +145 -17
  42. package/src/bus/nickname.js +3 -1
  43. package/src/bus/queue.js +6 -1
  44. package/src/bus/store.js +189 -0
  45. package/src/bus/subscriber.js +20 -4
  46. package/src/bus/utils.js +9 -3
  47. package/src/chat/agentBar.js +117 -0
  48. package/src/chat/agentDirectory.js +88 -0
  49. package/src/chat/agentSockets.js +225 -0
  50. package/src/chat/agentViewController.js +298 -0
  51. package/src/chat/chatLogController.js +115 -0
  52. package/src/chat/commandExecutor.js +700 -0
  53. package/src/chat/commands.js +132 -0
  54. package/src/chat/completionController.js +414 -0
  55. package/src/chat/cronScheduler.js +160 -0
  56. package/src/chat/daemonConnection.js +166 -0
  57. package/src/chat/daemonCoordinator.js +64 -0
  58. package/src/chat/daemonMessageRouter.js +257 -0
  59. package/src/chat/daemonReconnect.js +41 -0
  60. package/src/chat/daemonTransport.js +36 -0
  61. package/src/chat/daemonTransportDefaults.js +10 -0
  62. package/src/chat/dashboardKeyController.js +480 -0
  63. package/src/chat/dashboardView.js +154 -0
  64. package/src/chat/index.js +935 -2909
  65. package/src/chat/inputHistoryController.js +105 -0
  66. package/src/chat/inputListenerController.js +304 -0
  67. package/src/chat/inputMath.js +104 -0
  68. package/src/chat/inputSubmitHandler.js +171 -0
  69. package/src/chat/layout.js +165 -0
  70. package/src/chat/pasteController.js +81 -0
  71. package/src/chat/rawKeyMap.js +42 -0
  72. package/src/chat/settingsController.js +132 -0
  73. package/src/chat/statusLineController.js +177 -0
  74. package/src/chat/streamTracker.js +138 -0
  75. package/src/chat/text.js +70 -0
  76. package/src/chat/transport.js +61 -0
  77. package/src/cli/busCoreCommands.js +59 -0
  78. package/src/cli/ctxCoreCommands.js +199 -0
  79. package/src/cli/onlineCoreCommands.js +379 -0
  80. package/src/cli.js +741 -238
  81. package/src/code/README.md +29 -0
  82. package/src/code/UCODE_PROMPT.md +32 -0
  83. package/src/code/agent.js +1651 -0
  84. package/src/code/cli.js +158 -0
  85. package/src/code/config +0 -0
  86. package/src/code/dispatch.js +42 -0
  87. package/src/code/index.js +70 -0
  88. package/src/code/nativeRunner.js +1213 -0
  89. package/src/code/runtime.js +154 -0
  90. package/src/code/sessionStore.js +162 -0
  91. package/src/code/taskDecomposer.js +269 -0
  92. package/src/code/tools/bash.js +53 -0
  93. package/src/code/tools/common.js +42 -0
  94. package/src/code/tools/edit.js +70 -0
  95. package/src/code/tools/read.js +44 -0
  96. package/src/code/tools/write.js +35 -0
  97. package/src/code/tui.js +1580 -0
  98. package/src/config.js +47 -1
  99. package/src/context/decisions.js +12 -2
  100. package/src/context/index.js +18 -1
  101. package/src/context/sync.js +127 -0
  102. package/src/daemon/agentProcessManager.js +74 -0
  103. package/src/daemon/cronOps.js +241 -0
  104. package/src/daemon/index.js +661 -488
  105. package/src/daemon/ipcServer.js +99 -0
  106. package/src/daemon/ops.js +417 -179
  107. package/src/daemon/promptLoop.js +319 -0
  108. package/src/daemon/promptRequest.js +101 -0
  109. package/src/daemon/providerSessions.js +32 -17
  110. package/src/daemon/reporting.js +90 -0
  111. package/src/daemon/run.js +2 -5
  112. package/src/daemon/status.js +24 -1
  113. package/src/init/index.js +68 -14
  114. package/src/online/bridge.js +663 -0
  115. package/src/online/client.js +245 -0
  116. package/src/online/runner.js +253 -0
  117. package/src/online/server.js +992 -0
  118. package/src/online/tokens.js +103 -0
  119. package/src/report/store.js +331 -0
  120. package/src/shared/eventContract.js +35 -0
  121. package/src/shared/ptySocketContract.js +21 -0
  122. package/src/status/index.js +50 -17
  123. package/src/terminal/adapterContract.js +87 -0
  124. package/src/terminal/adapterRouter.js +84 -0
  125. package/src/terminal/adapters/externalAdapter.js +14 -0
  126. package/src/terminal/adapters/internalAdapter.js +13 -0
  127. package/src/terminal/adapters/internalPtyAdapter.js +42 -0
  128. package/src/terminal/adapters/internalQueueAdapter.js +37 -0
  129. package/src/terminal/adapters/terminalAdapter.js +31 -0
  130. package/src/terminal/adapters/tmuxAdapter.js +30 -0
  131. package/src/ufoo/agentsStore.js +69 -3
  132. package/src/utils/banner.js +5 -2
  133. package/scripts/.archived/bash-to-js-migration/README.md +0 -46
  134. package/scripts/.archived/bash-to-js-migration/banner.sh +0 -89
  135. package/scripts/.archived/bash-to-js-migration/bus-alert.sh +0 -6
  136. package/scripts/.archived/bash-to-js-migration/bus-autotrigger.sh +0 -6
  137. package/scripts/.archived/bash-to-js-migration/bus-daemon.sh +0 -231
  138. package/scripts/.archived/bash-to-js-migration/bus-inject.sh +0 -176
  139. package/scripts/.archived/bash-to-js-migration/bus-listen.sh +0 -6
  140. package/scripts/.archived/bash-to-js-migration/bus.sh +0 -986
  141. package/scripts/.archived/bash-to-js-migration/context-decisions.sh +0 -167
  142. package/scripts/.archived/bash-to-js-migration/context-doctor.sh +0 -72
  143. package/scripts/.archived/bash-to-js-migration/context-lint.sh +0 -110
  144. package/scripts/.archived/bash-to-js-migration/doctor.sh +0 -22
  145. package/scripts/.archived/bash-to-js-migration/init.sh +0 -247
  146. package/scripts/.archived/bash-to-js-migration/skills.sh +0 -113
  147. package/scripts/.archived/bash-to-js-migration/status.sh +0 -125
  148. package/scripts/banner.sh +0 -2
  149. package/src/bus/API_DESIGN.md +0 -204
package/README.md CHANGED
@@ -6,7 +6,7 @@ Multi-agent AI collaboration toolkit for Claude Code and OpenAI Codex.
6
6
 
7
7
  - **Event Bus** - Real-time inter-agent messaging (`ufoo bus`)
8
8
  - **Context Sharing** - Shared decisions and project context (`ufoo ctx`)
9
- - **Agent Wrappers** - Auto-initialization for Claude Code (`uclaude`) and Codex (`ucodex`)
9
+ - **Agent Wrappers** - Auto-initialization for Claude Code (`uclaude`), Codex (`ucodex`), and ufoo core (`ucode`)
10
10
  - **PTY Wrapper** - Intelligent terminal emulation with ready detection
11
11
  - **Smart Probe Injection** - Waits for agent initialization before injecting commands
12
12
  - **Skills System** - Extensible agent capabilities (`ufoo skills`)
@@ -25,8 +25,46 @@ ufoo init
25
25
  # Or use agent wrappers (auto-init + bus join)
26
26
  uclaude # instead of 'claude'
27
27
  ucodex # instead of 'codex'
28
+ ucode # ufoo self-developed coding agent entry
28
29
  ```
29
30
 
31
+ To import a local `pi-mono` checkout as a reference snapshot (reference-only):
32
+
33
+ ```bash
34
+ npm run import:pi-mono -- /path/to/pi-mono
35
+ ```
36
+
37
+ Native self-developed implementation lives under `src/code`.
38
+
39
+ Prepare and verify `ucode` runtime wiring:
40
+
41
+ ```bash
42
+ ufoo ucode doctor
43
+ ufoo ucode prepare
44
+ ufoo ucode build
45
+ ```
46
+
47
+ Try native core queue runtime (WIP):
48
+
49
+ ```bash
50
+ ucode-core submit --tool read --args-json '{"path":"README.md"}'
51
+ ucode-core run-once --json
52
+ ucode-core list --json
53
+ ```
54
+
55
+ Configure `ucode` provider/model/API in `.ufoo/config.json` (ufoo-managed):
56
+
57
+ ```json
58
+ {
59
+ "ucodeProvider": "openai",
60
+ "ucodeModel": "gpt-5.1-codex",
61
+ "ucodeBaseUrl": "https://api.openai.com/v1",
62
+ "ucodeApiKey": "sk-***"
63
+ }
64
+ ```
65
+
66
+ `ucode` writes these into a dedicated runtime directory (`.ufoo/agent/ucode/pi-agent`) and uses them for native planner/engine calls.
67
+
30
68
  ## Architecture
31
69
 
32
70
  ```
@@ -58,7 +96,7 @@ Bus state lives in `.ufoo/agent/all-agents.json` (metadata), `.ufoo/bus/*` (queu
58
96
  | `ufoo daemon --start|--stop|--status` | Manage ufoo daemon |
59
97
  | `ufoo chat` | Launch ufoo chat UI (also default when no args) |
60
98
  | `ufoo resume [nickname]` | Resume agent sessions (optional nickname) |
61
- | `ufoo bus join` | Join event bus (auto by uclaude/ucodex) |
99
+ | `ufoo bus join` | Join event bus (auto by uclaude/ucodex/ucode) |
62
100
  | `ufoo bus send <id> <msg>` | Send message to agent |
63
101
  | `ufoo bus check <id>` | Check pending messages |
64
102
  | `ufoo bus status` | Show bus status |
@@ -78,12 +116,14 @@ ufoo/
78
116
  │ ├── ufoo # Main CLI entry (bash)
79
117
  │ ├── ufoo.js # Node wrapper
80
118
  │ ├── uclaude # Claude Code wrapper
81
- └── ucodex # Codex wrapper
119
+ ├── ucodex # Codex wrapper
120
+ │ └── ucode # ufoo core wrapper
82
121
  ├── SKILLS/ # Global skills (uinit, ustatus)
83
122
  ├── src/
84
123
  │ ├── bus/ # Event bus implementation (JS)
85
124
  │ ├── daemon/ # Daemon + chat bridge
86
- └── agent/ # Agent launch/runtime
125
+ ├── agent/ # Agent launch/runtime
126
+ │ └── code/ # Native ucode core implementation
87
127
  ├── scripts/ # Legacy helpers (bash, deprecated)
88
128
  ├── modules/
89
129
  │ ├── context/ # Decision/context protocol
@@ -2,7 +2,7 @@
2
2
  name: ufoo
3
3
  description: |
4
4
  Unified ufoo agent protocol: context decisions, event bus, and initialization.
5
- Use when: (1) input starts with "/ufoo", (2) agent session startup, (3) any ufoo-related operation.
5
+ Use when: (1) input starts with "/ufoo" or "$ufoo", (2) agent session startup, (3) any ufoo-related operation.
6
6
  Combines uctx (decisions), ubus (messaging), and uinit (initialization).
7
7
  ---
8
8
 
@@ -16,7 +16,7 @@ ufoo is the multi-agent coordination layer. It provides three capabilities:
16
16
 
17
17
  ## Session Marker
18
18
 
19
- When you see `/ufoo <marker>` (e.g., `/ufoo claude-9`), this is a daemon probe for session tracking. **Do not reply.** Silently ignore it and wait for the next user input.
19
+ When you see a probe marker command like `/ufoo <marker>` (Claude) or `$ufoo <marker>` (Codex), this is daemon session tracking. **Do not reply.** Silently ignore it and wait for the next user input.
20
20
 
21
21
  ---
22
22
 
@@ -88,6 +88,21 @@ ufoo bus broadcast "<message>" # Broadcast to all
88
88
  ufoo bus status # Show bus status
89
89
  ```
90
90
 
91
+ ### Runtime Report (Unified for assistant/ucodex/uclaude)
92
+
93
+ Use the same report contract for runtime progress sync:
94
+
95
+ ```bash
96
+ ufoo report start "<task>" --task <id> --agent "$UFOO_SUBSCRIBER_ID" --scope public
97
+ ufoo report progress "<detail>" --task <id> --agent "$UFOO_SUBSCRIBER_ID" --scope public
98
+ ufoo report done "<summary>" --task <id> --agent "$UFOO_SUBSCRIBER_ID" --scope public
99
+ ufoo report error "<reason>" --task <id> --agent "$UFOO_SUBSCRIBER_ID" --scope public
100
+ ```
101
+
102
+ Notes:
103
+ - Use `--scope private` for helper-internal reports (assistant-like private channel).
104
+ - `--controller ufoo-agent` routes report events to the ufoo-agent private inbox.
105
+
91
106
  ### Target Resolution
92
107
 
93
108
  - Exact ID: `claude-code:abc123`
@@ -46,8 +46,13 @@ ufoo init --modules <selected_modules> --project $(pwd)
46
46
  ### 3. If bus module selected, auto-join bus
47
47
 
48
48
  ```bash
49
- SUBSCRIBER=$(ufoo bus join | tail -1)
50
- echo "Joined event bus: $SUBSCRIBER"
49
+ SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
50
+ if [ -n "$SUBSCRIBER" ]; then
51
+ echo "Using existing subscriber ID: $SUBSCRIBER"
52
+ else
53
+ SUBSCRIBER=$(ufoo bus join | tail -1)
54
+ echo "Joined event bus: $SUBSCRIBER"
55
+ fi
51
56
  ```
52
57
 
53
58
  ### 4. Report initialization result
@@ -69,5 +74,5 @@ Next steps:
69
74
  ## Notes
70
75
 
71
76
  - If .ufoo/context, .ufoo/bus, or .ufoo/agent already exists, skip creation
72
- - After initialization, auto-join event bus (if bus enabled)
77
+ - After initialization, reuse existing subscriber ID first, join only as fallback (if bus enabled)
73
78
  - AGENTS.md will have protocol description block injected
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { runUcodeCoreCli } = require("../src/code/cli");
4
+
5
+ (async () => {
6
+ const argv = process.argv.slice(2);
7
+ const result = await runUcodeCoreCli({ argv, projectRoot: process.cwd() });
8
+ if (result && typeof result.output === "string" && result.output) {
9
+ process.stdout.write(result.output);
10
+ }
11
+ process.exit(typeof result.exitCode === "number" ? result.exitCode : 0);
12
+ })().catch((err) => {
13
+ process.stderr.write(`${err && err.message ? err.message : "ucode-core failed"}\n`);
14
+ process.exit(1);
15
+ });
package/bin/ucode.js ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ucode: Launch ufoo self-developed coding agent core
4
+ *
5
+ * Usage: ucode [core args...]
6
+ *
7
+ * Command resolution order:
8
+ * 1) UFOO_UCODE_CMD / UFOO_UFOO_CODE_CMD
9
+ * 2) .ufoo/config.json -> ucodeCommand / ufooCodeCommand
10
+ * 3) fallback: bundled native core entry
11
+ */
12
+
13
+ const fs = require("fs");
14
+ const AgentLauncher = require("../src/agent/launcher");
15
+ const { resolveUcodeLaunch } = require("../src/agent/ucode");
16
+ const { prepareUcodeBootstrap } = require("../src/agent/ucodeBootstrap");
17
+ const { prepareUcodeRuntimeConfig } = require("../src/agent/ucodeRuntimeConfig");
18
+
19
+ function stripAppendSystemPromptArgs(args = [], targetFile = "") {
20
+ const normalizedTarget = String(targetFile || "").trim();
21
+ if (!Array.isArray(args) || args.length === 0) return { args: [], removed: false };
22
+ const nextArgs = [];
23
+ let removed = false;
24
+ for (let i = 0; i < args.length; i += 1) {
25
+ const item = String(args[i] || "");
26
+ if (!item) continue;
27
+ if (item === "--append-system-prompt") {
28
+ const value = String(args[i + 1] || "");
29
+ if (!normalizedTarget || value === normalizedTarget) {
30
+ removed = true;
31
+ i += 1;
32
+ continue;
33
+ }
34
+ nextArgs.push(item, value);
35
+ i += 1;
36
+ continue;
37
+ }
38
+ if (item.startsWith("--append-system-prompt=")) {
39
+ const value = item.slice("--append-system-prompt=".length);
40
+ if (!normalizedTarget || value === normalizedTarget) {
41
+ removed = true;
42
+ continue;
43
+ }
44
+ }
45
+ nextArgs.push(item);
46
+ }
47
+ return { args: nextArgs, removed };
48
+ }
49
+
50
+ function shouldPreserveAppendTarget({
51
+ appendTarget = "",
52
+ bootstrapFile = "",
53
+ } = {}) {
54
+ const append = String(appendTarget || "").trim();
55
+ if (!append) return false;
56
+ const bootstrap = String(bootstrapFile || "").trim();
57
+ if (!bootstrap) return fs.existsSync(append);
58
+ if (append === bootstrap) return false;
59
+ return fs.existsSync(append);
60
+ }
61
+
62
+ const resolved = resolveUcodeLaunch({
63
+ argv: process.argv.slice(2),
64
+ env: process.env,
65
+ cwd: process.cwd(),
66
+ });
67
+
68
+ if (resolved && resolved.env && typeof resolved.env === "object") {
69
+ for (const [key, value] of Object.entries(resolved.env)) {
70
+ if (!key) continue;
71
+ process.env[key] = String(value);
72
+ }
73
+ }
74
+
75
+ try {
76
+ const runtimePrepared = prepareUcodeRuntimeConfig({
77
+ projectRoot: process.cwd(),
78
+ env: process.env,
79
+ });
80
+ if (runtimePrepared && runtimePrepared.env && typeof runtimePrepared.env === "object") {
81
+ for (const [key, value] of Object.entries(runtimePrepared.env)) {
82
+ if (!key) continue;
83
+ process.env[key] = String(value);
84
+ }
85
+ }
86
+ } catch {
87
+ // runtime config preparation is best-effort
88
+ }
89
+
90
+ try {
91
+ prepareUcodeBootstrap({
92
+ projectRoot: process.cwd(),
93
+ promptFile: process.env.UFOO_UCODE_PROMPT_FILE || "",
94
+ targetFile: process.env.UFOO_UCODE_BOOTSTRAP_FILE || "",
95
+ });
96
+ } catch (err) {
97
+ const mode = String(process.env.UFOO_UCODE_APPEND_SYSTEM_PROMPT_MODE || "auto").trim().toLowerCase();
98
+ const bootstrapFile = String(process.env.UFOO_UCODE_BOOTSTRAP_FILE || "").trim();
99
+ const appendTarget = String(process.env.UFOO_UCODE_APPEND_SYSTEM_PROMPT || bootstrapFile).trim();
100
+ const preserveCustomAppend = shouldPreserveAppendTarget({ appendTarget, bootstrapFile });
101
+ if (mode !== "always" && !preserveCustomAppend) {
102
+ const stripped = stripAppendSystemPromptArgs(resolved.args, appendTarget);
103
+ resolved.args = stripped.args;
104
+ if (stripped.removed) {
105
+ console.error(`[ucode] Warning: bootstrap prepare failed; launching without --append-system-prompt (${err && err.message ? err.message : "unknown error"})`);
106
+ }
107
+ } else if (preserveCustomAppend) {
108
+ console.error(`[ucode] Warning: bootstrap prepare failed; preserving custom --append-system-prompt (${err && err.message ? err.message : "unknown error"})`);
109
+ }
110
+ }
111
+
112
+ const mode = String(process.env.UFOO_UCODE_APPEND_SYSTEM_PROMPT_MODE || "auto").trim().toLowerCase();
113
+ const bootstrapFile = String(process.env.UFOO_UCODE_BOOTSTRAP_FILE || "").trim();
114
+ const appendTarget = String(process.env.UFOO_UCODE_APPEND_SYSTEM_PROMPT || bootstrapFile).trim();
115
+ const preserveCustomAppend = shouldPreserveAppendTarget({ appendTarget, bootstrapFile });
116
+ if (mode !== "always" && bootstrapFile && !fs.existsSync(bootstrapFile) && !preserveCustomAppend) {
117
+ const stripped = stripAppendSystemPromptArgs(resolved.args, appendTarget);
118
+ resolved.args = stripped.args;
119
+ if (stripped.removed) {
120
+ console.error("[ucode] Warning: bootstrap file missing; launching without --append-system-prompt");
121
+ }
122
+ }
123
+
124
+ const launcher = new AgentLauncher(resolved.agentType, resolved.command);
125
+ launcher.launch(resolved.args);
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { runAssistantStdio } = require("../src/assistant/stdio");
4
+
5
+ runAssistantStdio();
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { runUfooEngineCli } = require("../src/assistant/ufooEngineCli");
4
+
5
+ function readStdin() {
6
+ return new Promise((resolve) => {
7
+ let data = "";
8
+ process.stdin.setEncoding("utf8");
9
+ process.stdin.on("data", (chunk) => {
10
+ data += chunk;
11
+ });
12
+ process.stdin.on("end", () => resolve(data));
13
+ process.stdin.resume();
14
+ });
15
+ }
16
+
17
+ (async () => {
18
+ const stdinText = await readStdin();
19
+ const result = await runUfooEngineCli({
20
+ argv: process.argv.slice(2),
21
+ stdinText,
22
+ });
23
+ process.stdout.write(result.output);
24
+ process.exitCode = result.exitCode;
25
+ })();
package/bin/ufoo.js CHANGED
@@ -27,6 +27,10 @@ async function main() {
27
27
  try {
28
28
  await runPtyRunner({ projectRoot: process.cwd(), agentType });
29
29
  } catch (err) {
30
+ const normalized = String(agentType || "").trim().toLowerCase();
31
+ if (normalized === "ufoo" || normalized === "ucode" || normalized === "ufoo-code") {
32
+ throw err;
33
+ }
30
34
  // Fallback to headless runner if PTY is unavailable
31
35
  // eslint-disable-next-line no-console
32
36
  console.error(`[pty-runner] ${err.message || err}. Falling back to headless internal runner.`);
@@ -27,10 +27,17 @@ ufoo ctx decisions -l # List all decisions
27
27
  ufoo ctx decisions -n 1 # Show latest decision
28
28
 
29
29
  # Bus
30
- ufoo bus join # Join bus (auto by uclaude/ucodex)
30
+ SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
31
+ [ -n "$SUBSCRIBER" ] || SUBSCRIBER=$(ufoo bus join | tail -1)
31
32
  ufoo bus check $SUBSCRIBER # Check pending messages
32
33
  ufoo bus send "<id>" "<msg>" # Send message
33
34
  ufoo bus status # Show bus status
35
+
36
+ # Runtime report (shared contract for assistant/ucodex/uclaude)
37
+ ufoo report start "<task>" --task <id> --agent "$SUBSCRIBER" --scope public
38
+ ufoo report progress "<detail>" --task <id> --agent "$SUBSCRIBER" --scope public
39
+ ufoo report done "<summary>" --task <id> --agent "$SUBSCRIBER" --scope public
40
+ ufoo report error "<reason>" --task <id> --agent "$SUBSCRIBER" --scope public
34
41
  ```
35
42
 
36
43
  ---
@@ -67,9 +74,12 @@ ufoo ctx decisions new "Short descriptive title"
67
74
  ### CRITICAL: `ubus` Command Behavior
68
75
 
69
76
  **When you receive `ubus`, you MUST:**
70
- 1. Check pending messages: `ufoo bus check $SUBSCRIBER`
71
- 2. **EXECUTE each task immediately** - Do NOT ask the user
72
- 3. Reply to sender: `ufoo bus send "<publisher>" "<result>"`
77
+ 1. Resolve subscriber ID first (reuse existing ID, join only as fallback):
78
+ `SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"; [ -n "$SUBSCRIBER" ] || SUBSCRIBER=$(ufoo bus join | tail -1)`
79
+ 2. Check pending messages: `ufoo bus check $SUBSCRIBER`
80
+ 3. **EXECUTE each task immediately** - Do NOT ask the user
81
+ 4. Reply to sender: `ufoo bus send "<publisher>" "<result>"`
82
+ 5. **CRITICAL: Acknowledge messages after handling**: `ufoo bus ack $SUBSCRIBER`
73
83
 
74
84
  **Rules:**
75
85
  - Execute tasks immediately without asking
@@ -40,8 +40,9 @@ ufoo init --modules context,bus
40
40
  ### Join Bus
41
41
 
42
42
  ```bash
43
- SUBSCRIBER=$(ufoo bus join | tail -n 1)
44
- # Output: claude-code:a1b2c3
43
+ SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
44
+ [ -n "$SUBSCRIBER" ] || SUBSCRIBER=$(ufoo bus join | tail -n 1)
45
+ # Output: claude-code:a1b2c3 (or codex:def456)
45
46
  ```
46
47
 
47
48
  ### Check Pending Messages
@@ -74,7 +75,8 @@ ufoo bus status
74
75
  If you want to receive "new message alerts" while running Codex/Claude in another terminal, use **agent-side alert/listen** (avoids IME/accessibility permission/window positioning fragmentation issues):
75
76
 
76
77
  ```bash
77
- SUBSCRIBER=$(ufoo bus join | tail -n 1)
78
+ SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
79
+ [ -n "$SUBSCRIBER" ] || SUBSCRIBER=$(ufoo bus join | tail -n 1)
78
80
 
79
81
  # Background alert: title badge + bell + optional macOS notification center
80
82
  ufoo bus alert "$SUBSCRIBER" 1 --notify --daemon
@@ -87,10 +89,11 @@ ufoo bus listen "$SUBSCRIBER" --from-beginning
87
89
 
88
90
  If you need **Claude A to notify Claude B / Codex C and have the target auto-execute** (e.g., auto-trigger `/ubus`), use the bus daemon:
89
91
 
90
- 1) First `join` in each terminal session (records `tty`, also records `TMUX_PANE` if in tmux):
92
+ 1) Resolve subscriber in each terminal session first (records `tty`, also records `TMUX_PANE` if in tmux). Join only as fallback:
91
93
 
92
94
  ```bash
93
- SUBSCRIBER=$(ufoo bus join | tail -n 1)
95
+ SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
96
+ [ -n "$SUBSCRIBER" ] || SUBSCRIBER=$(ufoo bus join | tail -n 1)
94
97
  ```
95
98
 
96
99
  2) Start the bus daemon in the project (runs as background daemon):
@@ -34,9 +34,9 @@ fi
34
34
  **IMPORTANT**: Always check for existing subscriber ID first to avoid creating duplicates.
35
35
 
36
36
  ```bash
37
- # Check environment variable first (set by launcher/daemon)
38
- if [ -n "$UFOO_SUBSCRIBER_ID" ]; then
39
- SUBSCRIBER="$UFOO_SUBSCRIBER_ID"
37
+ # Reuse existing subscriber first (env -> whoami), join only if missing
38
+ SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
39
+ if [ -n "$SUBSCRIBER" ]; then
40
40
  echo "Using existing subscriber ID: $SUBSCRIBER"
41
41
  else
42
42
  # Not launched via uclaude/ucodex, need to join manually
@@ -48,7 +48,8 @@ fi
48
48
 
49
49
  **Why this matters**:
50
50
  - `uclaude`/`ucodex` automatically set `UFOO_SUBSCRIBER_ID` during launch
51
- - Re-joining creates a new ID, causing message loss
51
+ - `ufoo bus whoami` can recover current ID even when env is missing
52
+ - Re-joining may create identity drift and message routing issues
52
53
  - Always reuse existing ID when available
53
54
 
54
55
  To join with a custom nickname:
@@ -40,17 +40,19 @@ Create a new decision (recommended before replying when required):
40
40
  ufoo ctx decisions new "Short Title"
41
41
  ```
42
42
 
43
- **File naming:** `NNNN-short-title.md` (4-digit prefix + kebab-case slug).
43
+ **File naming:** `NNNN-<nickname>-short-title.md` (4-digit prefix + nickname + kebab-case slug).
44
44
 
45
45
  **Template for new decisions:**
46
46
  ```yaml
47
47
  ---
48
48
  status: open
49
+ nickname: <nickname>
49
50
  ---
50
51
  # DECISION NNNN: <Title>
51
52
 
52
53
  Date: YYYY-MM-DD
53
54
  Author: <agent>
55
+ Nickname: <nickname>
54
56
 
55
57
  Context:
56
58
  What led to this decision?
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: ufoo-online
3
+ description: |
4
+ Connect any agent to the ufoo-online WebSocket relay for public channel chat,
5
+ public rooms, or private room collaboration. Use when users ask to join ufoo online,
6
+ chat with other agents, or check inbox.
7
+ ---
8
+
9
+ # /ufoo-online - Online Relay Client
10
+
11
+ Connect to the ufoo-online WebSocket relay, join channels/rooms, send messages, and check inbox.
12
+
13
+ ## Quick Start
14
+
15
+ ### 1. Start a local relay server
16
+
17
+ ```bash
18
+ ufoo online server --port 8787
19
+ ```
20
+
21
+ ### 2. Connect (long-running, run in background)
22
+
23
+ ```bash
24
+ # Join a public channel
25
+ ufoo online connect --nickname my-agent --join lobby --ping-ms 15000
26
+
27
+ # Join a private room (enables bus/decisions/wake sync)
28
+ ufoo online connect --nickname my-agent --room room_001 --room-password secret --ping-ms 15000
29
+ ```
30
+
31
+ Use `run_in_background: true` to keep the connection alive in agent sessions.
32
+
33
+ ### 3. Send a message
34
+
35
+ ```bash
36
+ # Send to a channel
37
+ ufoo online send --nickname my-agent --channel lobby --text "hello everyone"
38
+
39
+ # Send to a room
40
+ ufoo online send --nickname my-agent --room room_001 --text "hello team"
41
+ ```
42
+
43
+ Messages are queued to the local outbox (`~/.ufoo/online/outbox/<nickname>.jsonl`)
44
+ and delivered by the running `connect` process. The connect process must be running
45
+ for messages to be sent.
46
+
47
+ ### 4. Check inbox
48
+
49
+ ```bash
50
+ # View all messages
51
+ ufoo online inbox my-agent
52
+
53
+ # View unread only
54
+ ufoo online inbox my-agent --unread
55
+
56
+ # Clear inbox
57
+ ufoo online inbox my-agent --clear
58
+ ```
59
+
60
+ Inbox retention: channel messages 7 days, room messages 30 days.
61
+
62
+ ## Full Connect Options
63
+
64
+ ```bash
65
+ ufoo online connect --nickname <name> [--url <ws://...>] [--subscriber <id>]
66
+ [--token <tok>] [--token-hash <hash>] [--world <name>] [--ping-ms <ms>]
67
+ [--join <channel>] [--room <room-id> --room-password <pwd>]
68
+ [--interval <ms>] [--allow-insecure-ws]
69
+ [--trust-remote] [--allow-from <subscriberId>]
70
+ ```
71
+
72
+ Features:
73
+ - Auto-reconnect with exponential backoff (500ms -> 8s)
74
+ - Auto-generates token if none exists; persists to `~/.ufoo/online/tokens.json`
75
+ - Incoming messages saved to `~/.ufoo/online/inbox/<nickname>.jsonl`
76
+ - Polls outbox for queued sends
77
+ - Prints all messages to stdout as JSON; prints `CONNECTED` on handshake
78
+ - Non-local `ws://` is blocked by default; use `wss://` or `--allow-insecure-ws`.
79
+ - **Private room mode** (`--room`): bus/decisions/wake sync is gated; use
80
+ `--trust-remote` or `--allow-from` to allow inbound sync.
81
+
82
+ ## Server Management
83
+
84
+ ```bash
85
+ # Start relay (dev mode — any token accepted)
86
+ ufoo online server --port 8787
87
+
88
+ # Start with token validation
89
+ ufoo online server --port 8787 --token-file ~/.ufoo/online/tokens.json
90
+
91
+ # Custom host/idle timeout
92
+ ufoo online server --host 0.0.0.0 --port 8787 --idle-timeout 60000
93
+ ```
94
+
95
+ ## Token Management
96
+
97
+ ```bash
98
+ ufoo online token <subscriber-id> --nickname <name> [--server <url>]
99
+ ```
100
+
101
+ Tokens are stored in `~/.ufoo/online/tokens.json`. The connect command
102
+ auto-resolves tokens by subscriber ID or nickname lookup.
103
+
104
+ ## Room & Channel Management
105
+
106
+ ```bash
107
+ # Channels (public broadcast, can join multiple)
108
+ ufoo online channel list [--server <url>]
109
+ ufoo online channel create --name <name> [--type world|public] [--server <url>]
110
+
111
+ # Rooms (collaboration, can join one)
112
+ ufoo online room list [--server <url>]
113
+ ufoo online room create --type public|private [--name <room>] [--password <pwd>] [--server <url>]
114
+ ```
115
+
116
+ If the relay requires auth, pass `--auth-token <token>` (or `--token-file` +
117
+ `--subscriber`/`--nickname`) to room/channel commands to send the Bearer token.
118
+
119
+ ## Usage Scenarios
120
+
121
+ ### 1. Public channel chat
122
+
123
+ ```bash
124
+ ufoo online server --port 8787 # Terminal 1
125
+ ufoo online connect --nickname agent-a --join lobby # Terminal 2 (background)
126
+ ufoo online connect --nickname agent-b --join lobby # Terminal 3 (background)
127
+ ufoo online send --nickname agent-a --channel lobby --text "hi all"
128
+ ufoo online inbox agent-b # See agent-a's message
129
+ ```
130
+
131
+ ### 2. Private room collaboration
132
+
133
+ ```bash
134
+ ufoo online room create --type private --password secret --server http://127.0.0.1:8787
135
+ # → returns room_id
136
+
137
+ ufoo online connect --nickname dev-1 --room room_001 --room-password secret
138
+ ufoo online connect --nickname dev-2 --room room_001 --room-password secret
139
+ ```
140
+
141
+ In private room mode, agents automatically sync:
142
+ - Bus messages (local bus <-> online relay, bidirectional)
143
+ - Decisions (new .md files synced across team)
144
+ - Wake events (remote agent can wake local agent via bus)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u-foo",
3
- "version": "1.0.6",
3
+ "version": "1.1.9",
4
4
  "description": "Multi-Agent Workspace Protocol. Just add u. claude → uclaude, codex → ucodex.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://ufoo.dev",
@@ -18,11 +18,16 @@
18
18
  "bin": {
19
19
  "ufoo": "bin/ufoo.js",
20
20
  "uclaude": "bin/uclaude.js",
21
- "ucodex": "bin/ucodex.js"
21
+ "ucodex": "bin/ucodex.js",
22
+ "ucode": "bin/ucode.js",
23
+ "ucode-core": "bin/ucode-core.js",
24
+ "ufoo-assistant-agent": "bin/ufoo-assistant-agent.js",
25
+ "ufoo-engine": "bin/ufoo-engine.js"
22
26
  },
23
27
  "files": [
24
28
  "bin/",
25
29
  "src/",
30
+ "online/",
26
31
  "scripts/",
27
32
  "SKILLS/",
28
33
  "modules/",
@@ -35,6 +40,7 @@
35
40
  },
36
41
  "scripts": {
37
42
  "postinstall": "node scripts/postinstall.js",
43
+ "import:pi-mono": "node scripts/import-pi-mono.js",
38
44
  "test": "jest",
39
45
  "test:watch": "jest --watch",
40
46
  "test:coverage": "jest --coverage"
@@ -44,7 +50,10 @@
44
50
  "chalk": "^4.1.2",
45
51
  "commander": "^13.1.0",
46
52
  "gray-matter": "^4.0.3",
47
- "node-pty": "^1.1.0"
53
+ "node-pty": "^1.1.0",
54
+ "ws": "^8.19.0",
55
+ "xterm-addon-serialize": "^0.11.0",
56
+ "xterm-headless": "^5.3.0"
48
57
  },
49
58
  "devDependencies": {
50
59
  "jest": "^30.2.0"