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.
- package/README.md +44 -4
- package/SKILLS/ufoo/SKILL.md +17 -2
- package/SKILLS/uinit/SKILL.md +8 -3
- package/bin/ucode-core.js +15 -0
- package/bin/ucode.js +125 -0
- package/bin/ufoo-assistant-agent.js +5 -0
- package/bin/ufoo-engine.js +25 -0
- package/bin/ufoo.js +4 -0
- package/modules/AGENTS.template.md +14 -4
- package/modules/bus/README.md +8 -5
- package/modules/bus/SKILLS/ubus/SKILL.md +5 -4
- package/modules/context/SKILLS/uctx/SKILL.md +3 -1
- package/modules/online/SKILLS/ufoo-online/SKILL.md +144 -0
- package/package.json +12 -3
- package/scripts/import-pi-mono.js +124 -0
- package/scripts/postinstall.js +20 -49
- package/scripts/sync-claude-skills.sh +21 -0
- package/src/agent/cliRunner.js +524 -31
- package/src/agent/internalRunner.js +76 -9
- package/src/agent/launcher.js +97 -45
- package/src/agent/normalizeOutput.js +1 -1
- package/src/agent/notifier.js +144 -4
- package/src/agent/ptyRunner.js +480 -10
- package/src/agent/ptyWrapper.js +28 -3
- package/src/agent/readyDetector.js +16 -0
- package/src/agent/ucode.js +443 -0
- package/src/agent/ucodeBootstrap.js +113 -0
- package/src/agent/ucodeBuild.js +67 -0
- package/src/agent/ucodeDoctor.js +184 -0
- package/src/agent/ucodeRuntimeConfig.js +129 -0
- package/src/agent/ufooAgent.js +11 -2
- package/src/assistant/agent.js +260 -0
- package/src/assistant/bridge.js +172 -0
- package/src/assistant/engine.js +252 -0
- package/src/assistant/stdio.js +58 -0
- package/src/assistant/ufooEngineCli.js +306 -0
- package/src/bus/activate.js +27 -11
- package/src/bus/daemon.js +133 -5
- package/src/bus/index.js +137 -80
- package/src/bus/inject.js +47 -17
- package/src/bus/message.js +145 -17
- package/src/bus/nickname.js +3 -1
- package/src/bus/queue.js +6 -1
- package/src/bus/store.js +189 -0
- package/src/bus/subscriber.js +20 -4
- package/src/bus/utils.js +9 -3
- package/src/chat/agentBar.js +117 -0
- package/src/chat/agentDirectory.js +88 -0
- package/src/chat/agentSockets.js +225 -0
- package/src/chat/agentViewController.js +298 -0
- package/src/chat/chatLogController.js +115 -0
- package/src/chat/commandExecutor.js +700 -0
- package/src/chat/commands.js +132 -0
- package/src/chat/completionController.js +414 -0
- package/src/chat/cronScheduler.js +160 -0
- package/src/chat/daemonConnection.js +166 -0
- package/src/chat/daemonCoordinator.js +64 -0
- package/src/chat/daemonMessageRouter.js +257 -0
- package/src/chat/daemonReconnect.js +41 -0
- package/src/chat/daemonTransport.js +36 -0
- package/src/chat/daemonTransportDefaults.js +10 -0
- package/src/chat/dashboardKeyController.js +480 -0
- package/src/chat/dashboardView.js +154 -0
- package/src/chat/index.js +935 -2909
- package/src/chat/inputHistoryController.js +105 -0
- package/src/chat/inputListenerController.js +304 -0
- package/src/chat/inputMath.js +104 -0
- package/src/chat/inputSubmitHandler.js +171 -0
- package/src/chat/layout.js +165 -0
- package/src/chat/pasteController.js +81 -0
- package/src/chat/rawKeyMap.js +42 -0
- package/src/chat/settingsController.js +132 -0
- package/src/chat/statusLineController.js +177 -0
- package/src/chat/streamTracker.js +138 -0
- package/src/chat/text.js +70 -0
- package/src/chat/transport.js +61 -0
- package/src/cli/busCoreCommands.js +59 -0
- package/src/cli/ctxCoreCommands.js +199 -0
- package/src/cli/onlineCoreCommands.js +379 -0
- package/src/cli.js +741 -238
- package/src/code/README.md +29 -0
- package/src/code/UCODE_PROMPT.md +32 -0
- package/src/code/agent.js +1651 -0
- package/src/code/cli.js +158 -0
- package/src/code/config +0 -0
- package/src/code/dispatch.js +42 -0
- package/src/code/index.js +70 -0
- package/src/code/nativeRunner.js +1213 -0
- package/src/code/runtime.js +154 -0
- package/src/code/sessionStore.js +162 -0
- package/src/code/taskDecomposer.js +269 -0
- package/src/code/tools/bash.js +53 -0
- package/src/code/tools/common.js +42 -0
- package/src/code/tools/edit.js +70 -0
- package/src/code/tools/read.js +44 -0
- package/src/code/tools/write.js +35 -0
- package/src/code/tui.js +1580 -0
- package/src/config.js +47 -1
- package/src/context/decisions.js +12 -2
- package/src/context/index.js +18 -1
- package/src/context/sync.js +127 -0
- package/src/daemon/agentProcessManager.js +74 -0
- package/src/daemon/cronOps.js +241 -0
- package/src/daemon/index.js +661 -488
- package/src/daemon/ipcServer.js +99 -0
- package/src/daemon/ops.js +417 -179
- package/src/daemon/promptLoop.js +319 -0
- package/src/daemon/promptRequest.js +101 -0
- package/src/daemon/providerSessions.js +32 -17
- package/src/daemon/reporting.js +90 -0
- package/src/daemon/run.js +2 -5
- package/src/daemon/status.js +24 -1
- package/src/init/index.js +68 -14
- package/src/online/bridge.js +663 -0
- package/src/online/client.js +245 -0
- package/src/online/runner.js +253 -0
- package/src/online/server.js +992 -0
- package/src/online/tokens.js +103 -0
- package/src/report/store.js +331 -0
- package/src/shared/eventContract.js +35 -0
- package/src/shared/ptySocketContract.js +21 -0
- package/src/status/index.js +50 -17
- package/src/terminal/adapterContract.js +87 -0
- package/src/terminal/adapterRouter.js +84 -0
- package/src/terminal/adapters/externalAdapter.js +14 -0
- package/src/terminal/adapters/internalAdapter.js +13 -0
- package/src/terminal/adapters/internalPtyAdapter.js +42 -0
- package/src/terminal/adapters/internalQueueAdapter.js +37 -0
- package/src/terminal/adapters/terminalAdapter.js +31 -0
- package/src/terminal/adapters/tmuxAdapter.js +30 -0
- package/src/ufoo/agentsStore.js +69 -3
- package/src/utils/banner.js +5 -2
- package/scripts/.archived/bash-to-js-migration/README.md +0 -46
- package/scripts/.archived/bash-to-js-migration/banner.sh +0 -89
- package/scripts/.archived/bash-to-js-migration/bus-alert.sh +0 -6
- package/scripts/.archived/bash-to-js-migration/bus-autotrigger.sh +0 -6
- package/scripts/.archived/bash-to-js-migration/bus-daemon.sh +0 -231
- package/scripts/.archived/bash-to-js-migration/bus-inject.sh +0 -176
- package/scripts/.archived/bash-to-js-migration/bus-listen.sh +0 -6
- package/scripts/.archived/bash-to-js-migration/bus.sh +0 -986
- package/scripts/.archived/bash-to-js-migration/context-decisions.sh +0 -167
- package/scripts/.archived/bash-to-js-migration/context-doctor.sh +0 -72
- package/scripts/.archived/bash-to-js-migration/context-lint.sh +0 -110
- package/scripts/.archived/bash-to-js-migration/doctor.sh +0 -22
- package/scripts/.archived/bash-to-js-migration/init.sh +0 -247
- package/scripts/.archived/bash-to-js-migration/skills.sh +0 -113
- package/scripts/.archived/bash-to-js-migration/status.sh +0 -125
- package/scripts/banner.sh +0 -2
- 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`)
|
|
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
|
-
│
|
|
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
|
-
│
|
|
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
|
package/SKILLS/ufoo/SKILL.md
CHANGED
|
@@ -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>` (
|
|
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`
|
package/SKILLS/uinit/SKILL.md
CHANGED
|
@@ -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
|
|
50
|
-
|
|
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,
|
|
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,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
|
|
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.
|
|
71
|
-
2
|
|
72
|
-
|
|
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
|
package/modules/bus/README.md
CHANGED
|
@@ -40,8 +40,9 @@ ufoo init --modules context,bus
|
|
|
40
40
|
### Join Bus
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
SUBSCRIBER
|
|
44
|
-
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
-
|
|
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
|
|
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.
|
|
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"
|