u-foo 2.4.6 → 2.4.7
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 +5 -5
- package/README.zh-CN.md +5 -5
- package/SKILLS/ufoo/SKILL.md +2 -2
- package/SKILLS/uinit/SKILL.md +8 -11
- package/package.json +1 -2
- package/src/agents/launch/launcher.js +1 -13
- package/src/app/chat/commandExecutor.js +3 -3
- package/src/app/chat/commands.js +2 -2
- package/src/app/cli/features/doctor.js +11 -18
- package/src/app/cli/features/init.js +12 -191
- package/src/app/cli/run.js +11 -6
- package/src/coordination/context/doctor.js +4 -22
- package/src/runtime/daemon/index.js +1 -1
- package/src/runtime/daemon/mcpServer.js +1 -1
- package/src/runtime/terminal/index.js +1 -1
- package/src/ui/ink/ChatApp.js +1 -1
- package/modules/AGENTS.template.md +0 -8
- package/modules/bus/README.md +0 -140
- package/modules/context/README.md +0 -60
- package/modules/online/README.md +0 -92
- package/modules/resources/ICONS/README.md +0 -12
- package/modules/resources/ICONS/libraries/README.md +0 -17
- package/modules/resources/ICONS/libraries/heroicons/LICENSE +0 -22
- package/modules/resources/ICONS/libraries/heroicons/README.md +0 -15
- package/modules/resources/ICONS/libraries/heroicons/arrow-right.svg +0 -4
- package/modules/resources/ICONS/libraries/heroicons/check.svg +0 -4
- package/modules/resources/ICONS/libraries/heroicons/chevron-down.svg +0 -4
- package/modules/resources/ICONS/libraries/heroicons/cog-6-tooth.svg +0 -5
- package/modules/resources/ICONS/libraries/heroicons/magnifying-glass.svg +0 -4
- package/modules/resources/ICONS/libraries/heroicons/x-mark.svg +0 -4
- package/modules/resources/ICONS/libraries/lucide/LICENSE +0 -40
- package/modules/resources/ICONS/libraries/lucide/README.md +0 -15
- package/modules/resources/ICONS/libraries/lucide/arrow-right.svg +0 -15
- package/modules/resources/ICONS/libraries/lucide/check.svg +0 -14
- package/modules/resources/ICONS/libraries/lucide/chevron-down.svg +0 -14
- package/modules/resources/ICONS/libraries/lucide/search.svg +0 -15
- package/modules/resources/ICONS/libraries/lucide/settings.svg +0 -15
- package/modules/resources/ICONS/libraries/lucide/x.svg +0 -15
- package/modules/resources/ICONS/rules.md +0 -7
- package/modules/resources/README.md +0 -9
- package/modules/resources/UI/ANTI-PATTERNS.md +0 -6
- package/modules/resources/UI/TONE.md +0 -6
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ Initialize a project and open the chat dashboard:
|
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
71
|
cd your-project
|
|
72
|
-
ufoo init --
|
|
72
|
+
ufoo init --targets context,bus
|
|
73
73
|
ufoo
|
|
74
74
|
```
|
|
75
75
|
|
|
@@ -167,7 +167,7 @@ still available, but the normal ufoo workflow is to work from chat.
|
|
|
167
167
|
These are setup or troubleshooting commands. In chat, use slash commands:
|
|
168
168
|
|
|
169
169
|
```text
|
|
170
|
-
/init context bus
|
|
170
|
+
/init context bus
|
|
171
171
|
/doctor
|
|
172
172
|
/status
|
|
173
173
|
/daemon status
|
|
@@ -177,11 +177,11 @@ These are setup or troubleshooting commands. In chat, use slash commands:
|
|
|
177
177
|
```
|
|
178
178
|
|
|
179
179
|
`ufoo init` creates `.ufoo/`, ensures `AGENTS.md` and `CLAUDE.md`, initializes
|
|
180
|
-
selected
|
|
181
|
-
edit project instructions in `AGENTS.md`.
|
|
180
|
+
selected workspace state, and prepares shared storage. `CLAUDE.md` may be a
|
|
181
|
+
symlink; edit project instructions in `AGENTS.md`.
|
|
182
182
|
|
|
183
183
|
Before a project has been initialized, the equivalent CLI form is also useful:
|
|
184
|
-
`ufoo init --
|
|
184
|
+
`ufoo init --targets context,bus`.
|
|
185
185
|
|
|
186
186
|
### Event Bus
|
|
187
187
|
|
package/README.zh-CN.md
CHANGED
|
@@ -67,7 +67,7 @@ npm link
|
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
69
|
cd your-project
|
|
70
|
-
ufoo init --
|
|
70
|
+
ufoo init --targets context,bus
|
|
71
71
|
ufoo
|
|
72
72
|
```
|
|
73
73
|
|
|
@@ -163,7 +163,7 @@ ufoo -g
|
|
|
163
163
|
这些是初始化或排障命令。进入 chat 后优先使用 slash command:
|
|
164
164
|
|
|
165
165
|
```text
|
|
166
|
-
/init context bus
|
|
166
|
+
/init context bus
|
|
167
167
|
/doctor
|
|
168
168
|
/status
|
|
169
169
|
/daemon status
|
|
@@ -173,10 +173,10 @@ ufoo -g
|
|
|
173
173
|
```
|
|
174
174
|
|
|
175
175
|
`ufoo init` 会创建 `.ufoo/`,确保 `AGENTS.md` 和 `CLAUDE.md` 存在,
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
初始化选中的工作区状态,并准备共享存储。`CLAUDE.md` 可以是 symlink;
|
|
177
|
+
项目指令优先编辑 `AGENTS.md`。
|
|
178
178
|
|
|
179
|
-
项目尚未初始化时,也可以先在外部执行等价 CLI:`ufoo init --
|
|
179
|
+
项目尚未初始化时,也可以先在外部执行等价 CLI:`ufoo init --targets context,bus`。
|
|
180
180
|
|
|
181
181
|
### 事件总线
|
|
182
182
|
|
package/SKILLS/ufoo/SKILL.md
CHANGED
|
@@ -13,7 +13,7 @@ ufoo is the multi-agent coordination layer. It provides four capabilities:
|
|
|
13
13
|
1. **Context Decisions** — Sparse log of major plan-level choices shared across agents
|
|
14
14
|
2. **Shared Memory** — Durable, low-noise project facts shared across agents
|
|
15
15
|
3. **Event Bus** — Inter-agent messaging
|
|
16
|
-
4. **Initialization** — Project setup for ufoo
|
|
16
|
+
4. **Initialization** — Project setup for ufoo workspace state
|
|
17
17
|
|
|
18
18
|
## 1. Context Decisions (uctx)
|
|
19
19
|
|
|
@@ -199,7 +199,7 @@ ufoo history prompt [limit] # Render as injectable prompt block
|
|
|
199
199
|
Trigger: `/uinit` or `/ufoo init`
|
|
200
200
|
|
|
201
201
|
```bash
|
|
202
|
-
ufoo init --
|
|
202
|
+
ufoo init --targets context,bus --project $(pwd)
|
|
203
203
|
```
|
|
204
204
|
|
|
205
205
|
After init, auto-join bus if enabled.
|
package/SKILLS/uinit/SKILL.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: uinit
|
|
3
3
|
description: |
|
|
4
|
-
Initialize ufoo
|
|
4
|
+
Initialize ufoo workspace state in current project.
|
|
5
5
|
Use when: (1) new project needs context/bus enabled, (2) user inputs /uinit or /ufoo init.
|
|
6
|
-
Provides interactive
|
|
6
|
+
Provides interactive target selection, defaults to all selected.
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# uinit
|
|
10
10
|
|
|
11
|
-
Initialize ufoo
|
|
11
|
+
Initialize ufoo workspace state in current project.
|
|
12
12
|
|
|
13
13
|
## Trigger
|
|
14
14
|
|
|
@@ -16,22 +16,20 @@ User inputs `/uinit` or `/ufoo init`
|
|
|
16
16
|
|
|
17
17
|
## Execution Flow
|
|
18
18
|
|
|
19
|
-
### 1. Ask user to select
|
|
19
|
+
### 1. Ask user to select init targets
|
|
20
20
|
|
|
21
21
|
Use AskUserQuestion tool, provide multi-select, default all selected:
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
Please select
|
|
24
|
+
Please select ufoo state to enable:
|
|
25
25
|
|
|
26
26
|
☑ context - Shared context protocol (.ufoo/context/)
|
|
27
27
|
☑ bus - Agent event bus (.ufoo/bus/ + .ufoo/agent/)
|
|
28
|
-
☐ resources - UI/Icons resources (optional)
|
|
29
28
|
```
|
|
30
29
|
|
|
31
30
|
Options:
|
|
32
31
|
- `context` (recommended) - Shared context, sparse decision log for major plan-level choices
|
|
33
32
|
- `bus` (recommended) - Multi-agent communication, task delegation, message passing
|
|
34
|
-
- `resources` (optional) - UI tone guide, icon library
|
|
35
33
|
|
|
36
34
|
Default selected: context, bus
|
|
37
35
|
|
|
@@ -40,10 +38,10 @@ Default selected: context, bus
|
|
|
40
38
|
Based on user selection, execute:
|
|
41
39
|
|
|
42
40
|
```bash
|
|
43
|
-
ufoo init --
|
|
41
|
+
ufoo init --targets <selected_targets> --project $(pwd)
|
|
44
42
|
```
|
|
45
43
|
|
|
46
|
-
### 3. If bus
|
|
44
|
+
### 3. If bus target selected, auto-join bus
|
|
47
45
|
|
|
48
46
|
```bash
|
|
49
47
|
SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
|
|
@@ -60,7 +58,7 @@ fi
|
|
|
60
58
|
```
|
|
61
59
|
=== ufoo initialization complete ===
|
|
62
60
|
|
|
63
|
-
Enabled
|
|
61
|
+
Enabled ufoo state:
|
|
64
62
|
✓ core memory → .ufoo/memory/
|
|
65
63
|
✓ context → .ufoo/context/
|
|
66
64
|
✓ bus → .ufoo/bus/ + .ufoo/agent/
|
|
@@ -76,4 +74,3 @@ Next steps:
|
|
|
76
74
|
|
|
77
75
|
- If .ufoo/memory, .ufoo/context, .ufoo/bus, or .ufoo/agent already exists, skip creation
|
|
78
76
|
- After initialization, reuse existing subscriber ID first, join only as fallback (if bus enabled)
|
|
79
|
-
- AGENTS.md will have protocol description block injected
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u-foo",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.7",
|
|
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",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"online/",
|
|
30
30
|
"scripts/",
|
|
31
31
|
"SKILLS/",
|
|
32
|
-
"modules/",
|
|
33
32
|
"LICENSE",
|
|
34
33
|
"README.md"
|
|
35
34
|
],
|
|
@@ -323,23 +323,11 @@ class AgentLauncher {
|
|
|
323
323
|
|
|
324
324
|
if (!fs.existsSync(busDir)) {
|
|
325
325
|
// 调用 ufoo init
|
|
326
|
-
spawnSync("ufoo", ["init", "--
|
|
326
|
+
spawnSync("ufoo", ["init", "--targets", "context,bus"], {
|
|
327
327
|
cwd: this.cwd,
|
|
328
328
|
stdio: "ignore",
|
|
329
329
|
});
|
|
330
330
|
}
|
|
331
|
-
|
|
332
|
-
// 检查 AGENTS.md 是否有 ufoo template
|
|
333
|
-
const agentsFile = path.join(this.cwd, "AGENTS.md");
|
|
334
|
-
if (fs.existsSync(agentsFile)) {
|
|
335
|
-
const content = fs.readFileSync(agentsFile, "utf8");
|
|
336
|
-
if (!content.includes("<!-- ufoo -->")) {
|
|
337
|
-
spawnSync("ufoo", ["init", "--modules", "context,bus"], {
|
|
338
|
-
cwd: this.cwd,
|
|
339
|
-
stdio: "ignore",
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
331
|
}
|
|
344
332
|
|
|
345
333
|
/**
|
|
@@ -303,7 +303,7 @@ function createCommandExecutor(options = {}) {
|
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
async function handleInitCommand(args = []) {
|
|
306
|
-
logMessage("system", "{white-fg}⚙{/white-fg} Initializing ufoo
|
|
306
|
+
logMessage("system", "{white-fg}⚙{/white-fg} Initializing ufoo workspace...");
|
|
307
307
|
|
|
308
308
|
await withCapturedConsole(
|
|
309
309
|
{
|
|
@@ -319,8 +319,8 @@ function createCommandExecutor(options = {}) {
|
|
|
319
319
|
try {
|
|
320
320
|
const repoRoot = path.join(__dirname, "..", "..");
|
|
321
321
|
const init = createInit(repoRoot);
|
|
322
|
-
const
|
|
323
|
-
await init.init({
|
|
322
|
+
const targets = args.length > 0 ? args.join(",") : "context,bus";
|
|
323
|
+
await init.init({ targets, project: projectRoot });
|
|
324
324
|
|
|
325
325
|
logMessage("system", "{white-fg}✓{/white-fg} Initialization complete");
|
|
326
326
|
renderScreen();
|
package/src/app/chat/commands.js
CHANGED
|
@@ -47,7 +47,7 @@ const COMMAND_TREE = {
|
|
|
47
47
|
templates: { desc: "List available templates" },
|
|
48
48
|
},
|
|
49
49
|
},
|
|
50
|
-
"/init": { desc: "Initialize
|
|
50
|
+
"/init": { desc: "Initialize workspace" },
|
|
51
51
|
"/multi": { desc: "Toggle multi-window agent view" },
|
|
52
52
|
"/open": { desc: "Open project path in global mode" },
|
|
53
53
|
"/launch": {
|
|
@@ -305,7 +305,7 @@ function describeCommandForChat(text) {
|
|
|
305
305
|
if (command === "multi") return "Toggling multi-pane view";
|
|
306
306
|
if (command === "open") return `Opening project ${args[0] || ""}`.trim();
|
|
307
307
|
if (command === "resume") return args[0] === "list" ? "Listing recoverable agents" : `Resuming ${args[0] || "agents"}`;
|
|
308
|
-
if (command === "init") return "Initializing ufoo
|
|
308
|
+
if (command === "init") return "Initializing ufoo workspace";
|
|
309
309
|
|
|
310
310
|
return `Running /${command}`;
|
|
311
311
|
}
|
|
@@ -14,29 +14,22 @@ class RepoDoctor {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
run() {
|
|
17
|
-
const
|
|
17
|
+
const skillsDir = path.join(this.repoRoot, "SKILLS");
|
|
18
|
+
const contextSkill = path.join(skillsDir, "uctx", "SKILL.md");
|
|
19
|
+
const busSkill = path.join(skillsDir, "ubus", "SKILL.md");
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
if (!
|
|
21
|
-
this.fail(`missing ${contextMod}`);
|
|
22
|
-
}
|
|
21
|
+
if (!fs.existsSync(contextSkill)) this.fail(`missing ${contextSkill}`);
|
|
22
|
+
if (!fs.existsSync(busSkill)) this.fail(`missing ${busSkill}`);
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!ok) this.failed = true;
|
|
28
|
-
}
|
|
24
|
+
const contextDoctor = new ContextDoctor(this.repoRoot);
|
|
25
|
+
const ok = contextDoctor.lintProtocol();
|
|
26
|
+
if (!ok) this.failed = true;
|
|
29
27
|
|
|
30
28
|
console.log("=== ufoo doctor ===");
|
|
31
29
|
console.log(`Monorepo: ${this.repoRoot}`);
|
|
32
|
-
console.log("
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
const resources = path.join(this.repoRoot, "modules", "resources");
|
|
37
|
-
if (fs.existsSync(resources)) {
|
|
38
|
-
console.log(`- resources: ${resources}`);
|
|
39
|
-
}
|
|
30
|
+
console.log("Skills:");
|
|
31
|
+
if (fs.existsSync(contextSkill)) console.log(`- uctx: ${contextSkill}`);
|
|
32
|
+
if (fs.existsSync(busSkill)) console.log(`- ubus: ${busSkill}`);
|
|
40
33
|
|
|
41
34
|
if (this.failed) {
|
|
42
35
|
console.log("Status: FAILED");
|
|
@@ -7,23 +7,22 @@ const path = require("path");
|
|
|
7
7
|
class UfooInit {
|
|
8
8
|
constructor(repoRoot) {
|
|
9
9
|
this.repoRoot = repoRoot;
|
|
10
|
-
this.contextMod = path.join(repoRoot, "modules", "context");
|
|
11
|
-
this.busMod = path.join(repoRoot, "modules", "bus");
|
|
12
|
-
this.resourcesMod = path.join(repoRoot, "modules", "resources");
|
|
13
|
-
this.agentsTemplate = path.join(repoRoot, "modules", "AGENTS.template.md");
|
|
14
10
|
}
|
|
15
11
|
|
|
16
12
|
/**
|
|
17
13
|
* 初始化项目
|
|
18
14
|
*/
|
|
19
15
|
async init(options = {}) {
|
|
20
|
-
const
|
|
16
|
+
const targets = (options.targets || options.modules || "context")
|
|
17
|
+
.split(",")
|
|
18
|
+
.map((item) => item.trim())
|
|
19
|
+
.filter(Boolean);
|
|
21
20
|
const project = options.project || process.cwd();
|
|
22
21
|
const controllerMode = options.controllerMode === true;
|
|
23
22
|
|
|
24
23
|
console.log("=== ufoo init ===");
|
|
25
24
|
console.log(`Project directory: ${project}`);
|
|
26
|
-
console.log(`
|
|
25
|
+
console.log(`Targets: ${targets.join(", ")}`);
|
|
27
26
|
console.log();
|
|
28
27
|
|
|
29
28
|
if (!controllerMode) {
|
|
@@ -33,24 +32,17 @@ class UfooInit {
|
|
|
33
32
|
// 初始化核心
|
|
34
33
|
this.initCore(project, { controllerMode });
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// 初始化各模块
|
|
41
|
-
for (const module of modules) {
|
|
42
|
-
switch (module.trim()) {
|
|
35
|
+
// Initialize selected workspace features.
|
|
36
|
+
for (const target of targets) {
|
|
37
|
+
switch (target) {
|
|
43
38
|
case "context":
|
|
44
39
|
this.initContext(project);
|
|
45
40
|
break;
|
|
46
41
|
case "bus":
|
|
47
42
|
await this.initBus(project);
|
|
48
43
|
break;
|
|
49
|
-
case "resources":
|
|
50
|
-
this.initResources(project);
|
|
51
|
-
break;
|
|
52
44
|
default:
|
|
53
|
-
console.error(`Unknown
|
|
45
|
+
console.error(`Unknown init target: ${target}`);
|
|
54
46
|
}
|
|
55
47
|
}
|
|
56
48
|
|
|
@@ -121,106 +113,7 @@ class UfooInit {
|
|
|
121
113
|
}
|
|
122
114
|
|
|
123
115
|
/**
|
|
124
|
-
*
|
|
125
|
-
*/
|
|
126
|
-
injectAgentsTemplate(project) {
|
|
127
|
-
if (!fs.existsSync(this.agentsTemplate)) {
|
|
128
|
-
console.log("[template] AGENTS.template.md not found, skipping");
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const template = fs.readFileSync(this.agentsTemplate, "utf8");
|
|
133
|
-
const targets = this.resolveTemplateTargets(project);
|
|
134
|
-
if (targets.length === 0) {
|
|
135
|
-
console.log("[template] No target markdown files found, skipping");
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const labels = targets.map((file) => path.relative(project, file) || path.basename(file));
|
|
140
|
-
console.log(`[template] Injecting ufoo template into: ${labels.join(", ")}`);
|
|
141
|
-
|
|
142
|
-
for (const file of targets) {
|
|
143
|
-
this.injectTemplateIntoFile(file, template);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
console.log("[template] Done");
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
resolveTemplateTargets(project) {
|
|
150
|
-
const agentsFile = path.resolve(path.join(project, "AGENTS.md"));
|
|
151
|
-
const claudeFile = path.resolve(path.join(project, "CLAUDE.md"));
|
|
152
|
-
const targets = new Set();
|
|
153
|
-
|
|
154
|
-
if (fs.existsSync(agentsFile)) {
|
|
155
|
-
targets.add(agentsFile);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const claudeStat = this.safeLstat(claudeFile);
|
|
159
|
-
if (!claudeStat) return Array.from(targets);
|
|
160
|
-
|
|
161
|
-
if (claudeStat.isSymbolicLink()) {
|
|
162
|
-
try {
|
|
163
|
-
const rawTarget = fs.readlinkSync(claudeFile);
|
|
164
|
-
const sourceFile = path.resolve(path.dirname(claudeFile), rawTarget);
|
|
165
|
-
const projectRoot = path.resolve(project);
|
|
166
|
-
const inProject = sourceFile === projectRoot || sourceFile.startsWith(`${projectRoot}${path.sep}`);
|
|
167
|
-
if (inProject) {
|
|
168
|
-
targets.add(sourceFile);
|
|
169
|
-
} else {
|
|
170
|
-
console.warn(`[template] CLAUDE.md symlink target outside project, skipped: ${sourceFile}`);
|
|
171
|
-
}
|
|
172
|
-
} catch {
|
|
173
|
-
// ignore broken symlink
|
|
174
|
-
}
|
|
175
|
-
return Array.from(targets);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
targets.add(claudeFile);
|
|
179
|
-
return Array.from(targets);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
injectTemplateIntoFile(filePath, template) {
|
|
183
|
-
if (!fs.existsSync(filePath)) return;
|
|
184
|
-
|
|
185
|
-
let content = fs.readFileSync(filePath, "utf8");
|
|
186
|
-
const marker = "<!-- ufoo-template -->";
|
|
187
|
-
const block = `${marker}\n${template}\n${marker}`;
|
|
188
|
-
|
|
189
|
-
if (content.includes(marker)) {
|
|
190
|
-
const startIdx = content.indexOf(marker);
|
|
191
|
-
const endIdx = content.indexOf(marker, startIdx + marker.length);
|
|
192
|
-
if (endIdx !== -1) {
|
|
193
|
-
content = content.slice(0, startIdx) + block + content.slice(endIdx + marker.length);
|
|
194
|
-
} else {
|
|
195
|
-
content = content.slice(0, startIdx) + block + content.slice(startIdx + marker.length);
|
|
196
|
-
}
|
|
197
|
-
} else {
|
|
198
|
-
const headingEnd = this.findFirstHeadingEnd(content);
|
|
199
|
-
if (headingEnd !== -1) {
|
|
200
|
-
content = content.slice(0, headingEnd) + `\n${block}\n\n` + content.slice(headingEnd);
|
|
201
|
-
} else {
|
|
202
|
-
content = `${block}\n\n${content}`;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
fs.writeFileSync(filePath, content, "utf8");
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
findFirstHeadingEnd(content) {
|
|
209
|
-
const atxHeading = content.match(/^(?:[ \t]{0,3})#{1,6}[ \t]*[^\n]*(?:\n|$)/m);
|
|
210
|
-
const setextHeading = content.match(/^[^\n]+\n(?:=+|-+)[ \t]*(?:\n|$)/m);
|
|
211
|
-
|
|
212
|
-
let bestMatch = null;
|
|
213
|
-
if (atxHeading && setextHeading) {
|
|
214
|
-
bestMatch = atxHeading.index <= setextHeading.index ? atxHeading : setextHeading;
|
|
215
|
-
} else {
|
|
216
|
-
bestMatch = atxHeading || setextHeading;
|
|
217
|
-
}
|
|
218
|
-
if (!bestMatch) return -1;
|
|
219
|
-
return bestMatch.index + bestMatch[0].length;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* 初始化 context 模块
|
|
116
|
+
* 初始化 context
|
|
224
117
|
*/
|
|
225
118
|
initContext(project) {
|
|
226
119
|
console.log("[context] Initializing decision-only context...");
|
|
@@ -247,10 +140,10 @@ class UfooInit {
|
|
|
247
140
|
}
|
|
248
141
|
|
|
249
142
|
/**
|
|
250
|
-
* 初始化 bus
|
|
143
|
+
* 初始化 bus
|
|
251
144
|
*/
|
|
252
145
|
async initBus(project) {
|
|
253
|
-
console.log("[bus] Initializing bus
|
|
146
|
+
console.log("[bus] Initializing bus...");
|
|
254
147
|
|
|
255
148
|
const EventBus = require("../../../coordination/bus");
|
|
256
149
|
const bus = new EventBus(project);
|
|
@@ -263,78 +156,6 @@ class UfooInit {
|
|
|
263
156
|
}
|
|
264
157
|
}
|
|
265
158
|
|
|
266
|
-
/**
|
|
267
|
-
* 初始化 resources 模块
|
|
268
|
-
*/
|
|
269
|
-
initResources(project) {
|
|
270
|
-
if (!fs.existsSync(this.resourcesMod)) {
|
|
271
|
-
console.log("[resources] Module not found, skipping");
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
console.log("[resources] Initializing resources module...");
|
|
276
|
-
|
|
277
|
-
const targetDir = path.join(project, ".ufoo", "resources");
|
|
278
|
-
|
|
279
|
-
// 复制模块内容
|
|
280
|
-
this.copyModuleContent(this.resourcesMod, targetDir);
|
|
281
|
-
|
|
282
|
-
console.log("[resources] Done");
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* 复制模块内容
|
|
287
|
-
*/
|
|
288
|
-
copyModuleContent(src, dest) {
|
|
289
|
-
if (!fs.existsSync(dest)) {
|
|
290
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// 复制所有文件和目录(排除 .git、node_modules 等)
|
|
294
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
295
|
-
|
|
296
|
-
for (const entry of entries) {
|
|
297
|
-
// 跳过特殊目录
|
|
298
|
-
if (entry.name.startsWith(".") || entry.name === "node_modules") {
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const srcPath = path.join(src, entry.name);
|
|
303
|
-
const destPath = path.join(dest, entry.name);
|
|
304
|
-
|
|
305
|
-
if (entry.isDirectory()) {
|
|
306
|
-
this.copyRecursive(srcPath, destPath);
|
|
307
|
-
} else {
|
|
308
|
-
fs.copyFileSync(srcPath, destPath);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* 递归复制目录
|
|
315
|
-
*/
|
|
316
|
-
copyRecursive(src, dest) {
|
|
317
|
-
if (!fs.existsSync(dest)) {
|
|
318
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
322
|
-
|
|
323
|
-
for (const entry of entries) {
|
|
324
|
-
if (entry.name.startsWith(".") || entry.name === "node_modules") {
|
|
325
|
-
continue;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const srcPath = path.join(src, entry.name);
|
|
329
|
-
const destPath = path.join(dest, entry.name);
|
|
330
|
-
|
|
331
|
-
if (entry.isDirectory()) {
|
|
332
|
-
this.copyRecursive(srcPath, destPath);
|
|
333
|
-
} else {
|
|
334
|
-
fs.copyFileSync(srcPath, destPath);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
159
|
}
|
|
339
160
|
|
|
340
161
|
module.exports = UfooInit;
|
package/src/app/cli/run.js
CHANGED
|
@@ -535,7 +535,7 @@ async function runCli(argv) {
|
|
|
535
535
|
const chalk = requireOptional("chalk") || { cyan: (s) => s, red: (s) => s };
|
|
536
536
|
|
|
537
537
|
if (commander && commander.Command) {
|
|
538
|
-
const { Command } = commander;
|
|
538
|
+
const { Command, Option } = commander;
|
|
539
539
|
const program = new Command();
|
|
540
540
|
|
|
541
541
|
program
|
|
@@ -1074,10 +1074,10 @@ async function runCli(argv) {
|
|
|
1074
1074
|
}
|
|
1075
1075
|
});
|
|
1076
1076
|
|
|
1077
|
-
program
|
|
1077
|
+
const initCommand = program
|
|
1078
1078
|
.command("init")
|
|
1079
|
-
.description("Initialize
|
|
1080
|
-
.option("--
|
|
1079
|
+
.description("Initialize ufoo workspace state in a project")
|
|
1080
|
+
.option("--targets <list>", "Comma-separated init targets (context,bus)")
|
|
1081
1081
|
.option("--project <dir>", "Target project directory", process.cwd())
|
|
1082
1082
|
.action(async (opts) => {
|
|
1083
1083
|
const UfooInit = require("./features/init");
|
|
@@ -1090,6 +1090,11 @@ async function runCli(argv) {
|
|
|
1090
1090
|
process.exitCode = 1;
|
|
1091
1091
|
}
|
|
1092
1092
|
});
|
|
1093
|
+
if (Option && typeof Option === "function") {
|
|
1094
|
+
initCommand.addOption(new Option("--modules <list>", "Deprecated alias for --targets").hideHelp());
|
|
1095
|
+
} else {
|
|
1096
|
+
initCommand.option("--modules <list>", "Deprecated alias for --targets");
|
|
1097
|
+
}
|
|
1093
1098
|
|
|
1094
1099
|
const skills = program.command("skills").description("Manage skills templates");
|
|
1095
1100
|
skills
|
|
@@ -1713,7 +1718,7 @@ async function runCli(argv) {
|
|
|
1713
1718
|
console.log(" ufoo recover [list [target] | run <target>] [--json]");
|
|
1714
1719
|
console.log(" ufoo report <start|progress|done|error|list> [message] [--task <id>] [--agent <id>]");
|
|
1715
1720
|
console.log(" ufoo ucode [doctor|prepare|build] [--skip-install]");
|
|
1716
|
-
console.log(" ufoo init [--
|
|
1721
|
+
console.log(" ufoo init [--targets <list>] [--project <dir>]");
|
|
1717
1722
|
console.log(" ufoo skills list");
|
|
1718
1723
|
console.log(" ufoo skills install <name|all> [--target <dir> | --codex | --agents]");
|
|
1719
1724
|
console.log(" ufoo group templates [list|ls] [--json]");
|
|
@@ -2058,7 +2063,7 @@ async function runCli(argv) {
|
|
|
2058
2063
|
};
|
|
2059
2064
|
|
|
2060
2065
|
const opts = {
|
|
2061
|
-
|
|
2066
|
+
targets: getOpt("--targets", getOpt("--modules", "context")),
|
|
2062
2067
|
project: getOpt("--project", process.cwd()),
|
|
2063
2068
|
};
|
|
2064
2069
|
|
|
@@ -83,21 +83,17 @@ class ContextDoctor {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
|
-
* Lint
|
|
86
|
+
* Lint bundled context skill.
|
|
87
87
|
*/
|
|
88
88
|
lintProtocol() {
|
|
89
|
-
const moduleRoot = path.join(this.projectRoot, "modules", "context");
|
|
90
89
|
const repoSkill = path.join(this.projectRoot, "SKILLS", "uctx", "SKILL.md");
|
|
91
90
|
|
|
92
|
-
if (!fs.existsSync(
|
|
93
|
-
console.log("No
|
|
91
|
+
if (!fs.existsSync(repoSkill)) {
|
|
92
|
+
console.log("No bundled context skill found (skipping protocol lint)");
|
|
94
93
|
return true;
|
|
95
94
|
}
|
|
96
95
|
|
|
97
|
-
console.log(`Linting
|
|
98
|
-
|
|
99
|
-
// Check minimal module files
|
|
100
|
-
this.checkFile(path.join(moduleRoot, "README.md"), "README.md");
|
|
96
|
+
console.log(`Linting bundled context skill: ${repoSkill}`);
|
|
101
97
|
this.checkFile(repoSkill, "SKILLS/uctx/SKILL.md");
|
|
102
98
|
|
|
103
99
|
return !this.failed;
|
|
@@ -153,20 +149,6 @@ class ContextDoctor {
|
|
|
153
149
|
}
|
|
154
150
|
}
|
|
155
151
|
|
|
156
|
-
// Check global modules
|
|
157
|
-
const globalContext = path.join(
|
|
158
|
-
process.env.HOME,
|
|
159
|
-
".ufoo",
|
|
160
|
-
"modules",
|
|
161
|
-
"context"
|
|
162
|
-
);
|
|
163
|
-
if (!fs.existsSync(globalContext)) {
|
|
164
|
-
console.log("");
|
|
165
|
-
console.log(
|
|
166
|
-
`WARN: ${globalContext} not found (install via ufoo for best UX)`
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
152
|
console.log("");
|
|
171
153
|
if (this.failed) {
|
|
172
154
|
console.log("Status: FAILED");
|
|
@@ -1438,7 +1438,7 @@ function startDaemon({ projectRoot, provider, model, resumeMode = "auto" }) {
|
|
|
1438
1438
|
if (!fs.existsSync(targetPaths.ufooDir)) {
|
|
1439
1439
|
const repoRoot = path.join(__dirname, "..", "..", "..");
|
|
1440
1440
|
const init = new (require("../../app/cli/features/init"))(repoRoot);
|
|
1441
|
-
await init.init({
|
|
1441
|
+
await init.init({ targets: "context,bus", project: root });
|
|
1442
1442
|
}
|
|
1443
1443
|
if (!isRunning(root)) {
|
|
1444
1444
|
cleanupStaleState(root);
|
|
@@ -400,7 +400,7 @@ async function ensureGlobalControllerDaemon(options = {}) {
|
|
|
400
400
|
const UfooInit = require("../../app/cli/features/init");
|
|
401
401
|
const init = new UfooInit(PACKAGE_ROOT);
|
|
402
402
|
await suppressConsoleToStderr(() => init.init({
|
|
403
|
-
|
|
403
|
+
targets: "context,bus",
|
|
404
404
|
project: root,
|
|
405
405
|
controllerMode: true,
|
|
406
406
|
}));
|
package/src/ui/ink/ChatApp.js
CHANGED
|
@@ -3549,7 +3549,7 @@ async function runChatInk(projectRoot, options = {}) {
|
|
|
3549
3549
|
const repoRoot = path.join(__dirname, "..", "..", "..");
|
|
3550
3550
|
const init = new env.UfooInit(repoRoot);
|
|
3551
3551
|
await init.init({
|
|
3552
|
-
|
|
3552
|
+
targets: "context,bus",
|
|
3553
3553
|
project: projectRoot,
|
|
3554
3554
|
controllerMode: env.globalMode,
|
|
3555
3555
|
});
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<!-- ufoo -->
|
|
2
|
-
## ufoo Agent Protocol
|
|
3
|
-
|
|
4
|
-
> **Default: do not write a decision.** Record one only for important, plan-level knowledge that should constrain future work: architectural choices, trade-off analysis, cross-agent coordination, or precedent-setting integration contracts. NOT for routine findings, simple fixes, or because the user asked for a plan/evaluation/recommendation. Durable project facts belong in shared memory, not decisions. → `ufoo ctx decisions new "Title"` BEFORE acting only when that high bar is met.
|
|
5
|
-
> **Read shared memory before writing it.** Durable facts live in `.ufoo/memory/`; use `ufoo memory list/show`, `recall`, `search_memory`, or redacted `search_history` evidence before `remember` / `edit_memory`.
|
|
6
|
-
> **Auto-execute bus messages.** On `ubus`: execute tasks immediately, then `ufoo bus ack`. Never ask the user.
|
|
7
|
-
> **Full protocol**: `/ufoo` skill (auto-loaded on session start). Docs: `.ufoo/docs/`
|
|
8
|
-
<!-- /ufoo -->
|
package/modules/bus/README.md
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
# bus
|
|
2
|
-
|
|
3
|
-
File-system based Agent event bus for async communication between multiple AI Coding Agents.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
bus solves communication problems in multi-agent collaboration:
|
|
8
|
-
|
|
9
|
-
- Multiple Claude Code instances collaborating on the same project
|
|
10
|
-
- Communication between different AI tools (Claude Code, Cursor, Copilot)
|
|
11
|
-
- Task delegation and response
|
|
12
|
-
- Broadcast messages
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
15
|
-
|
|
16
|
-
Initialize via ufoo:
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
ufoo init --modules context,bus
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Directory Structure
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
.ufoo/
|
|
26
|
-
├── agent/
|
|
27
|
-
│ └── all-agents.json # Agent metadata + agent status
|
|
28
|
-
├── daemon/
|
|
29
|
-
│ ├── daemon.pid
|
|
30
|
-
│ ├── daemon.log
|
|
31
|
-
│ └── counts/
|
|
32
|
-
└── bus/
|
|
33
|
-
├── events/ # Event stream (JSONL, sharded by date)
|
|
34
|
-
├── offsets/ # Each Agent's consumption progress
|
|
35
|
-
└── queues/ # Targeted event queues
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Usage
|
|
39
|
-
|
|
40
|
-
### Join Bus
|
|
41
|
-
|
|
42
|
-
```bash
|
|
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)
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Check Pending Messages
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
ufoo bus check "$SUBSCRIBER"
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Send Messages
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
# Send to specific instance
|
|
58
|
-
ufoo bus send "claude-code:abc123" "Please help me review"
|
|
59
|
-
|
|
60
|
-
# Send to all instances of same type
|
|
61
|
-
ufoo bus send "claude-code" "Everyone please review"
|
|
62
|
-
|
|
63
|
-
# Broadcast to all
|
|
64
|
-
ufoo bus broadcast "I completed feature-x"
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### View Status
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
ufoo bus status
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Notifications/Alerts (no key injection, recommended)
|
|
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):
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
|
|
79
|
-
[ -n "$SUBSCRIBER" ] || SUBSCRIBER=$(ufoo bus join | tail -n 1)
|
|
80
|
-
|
|
81
|
-
# Background alert: title badge + bell + optional macOS notification center
|
|
82
|
-
ufoo bus alert "$SUBSCRIBER" 1 --notify --daemon
|
|
83
|
-
|
|
84
|
-
# Or: foreground continuous print of new messages (suitable for a side terminal)
|
|
85
|
-
ufoo bus listen "$SUBSCRIBER" --from-beginning
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Unattended Auto-Execute (recommended)
|
|
89
|
-
|
|
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:
|
|
91
|
-
|
|
92
|
-
1) Resolve subscriber in each terminal session first (records `tty`, also records `TMUX_PANE` if in tmux). Join only as fallback:
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
|
|
96
|
-
[ -n "$SUBSCRIBER" ] || SUBSCRIBER=$(ufoo bus join | tail -n 1)
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
2) Start the bus daemon in the project (runs as background daemon):
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
ufoo bus daemon --interval 1 --daemon
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
3) After sending a message, the daemon injects `/ubus` into the target session and presses Enter:
|
|
106
|
-
- tmux: `send-keys`
|
|
107
|
-
- Terminal.app (pure Automation): `do script` (no Accessibility needed, but requires Automation authorization; compatibility depends on whether target program accepts input)
|
|
108
|
-
- Terminal.app (Accessibility): System Events (needs Accessibility), injection sequence is Escape + paste + Return (avoids IME issues)
|
|
109
|
-
|
|
110
|
-
Tips:
|
|
111
|
-
- Terminal.app backend depends on `tty` in `.ufoo/agent/all-agents.json`. Execute `join` in the target terminal session (ensure `tty` is not `not a tty`).
|
|
112
|
-
- Pure Automation backend needs one-time authorization: System Preferences → Privacy & Security → Automation (allow script to control Terminal).
|
|
113
|
-
- Accessibility backend needs one-time authorization: System Preferences → Privacy & Security → Accessibility (for Terminal / script host).
|
|
114
|
-
|
|
115
|
-
Stop/view status:
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
ufoo bus daemon --status
|
|
119
|
-
ufoo bus daemon --stop
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Subscriber ID Format
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
{agent_type}:{instance_id}
|
|
126
|
-
|
|
127
|
-
Examples:
|
|
128
|
-
claude-code:a1b2c3
|
|
129
|
-
cursor-ai:main
|
|
130
|
-
copilot:session1
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## Relationship with context
|
|
134
|
-
|
|
135
|
-
| Module | Problem Solved |
|
|
136
|
-
|--------|----------------|
|
|
137
|
-
| context | Shared context, sparse decision log for major plan-level choices |
|
|
138
|
-
| bus | Real-time communication, task delegation, message passing |
|
|
139
|
-
|
|
140
|
-
Both are independent peer modules that can be used separately or together.
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# context
|
|
2
|
-
|
|
3
|
-
Decision-only context module for ufoo.
|
|
4
|
-
|
|
5
|
-
Purpose:
|
|
6
|
-
- Persist decisions in project workspaces
|
|
7
|
-
- Keep decision format canonical in `uctx` skill
|
|
8
|
-
|
|
9
|
-
Bus handles communication; context handles durable decision truth.
|
|
10
|
-
|
|
11
|
-
## Quick Start
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
# Install `ufoo` globally (once), then use it to install modules and init projects.
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
This repository is the `context` module. The recommended entrypoint is `ufoo`.
|
|
18
|
-
|
|
19
|
-
## Architecture
|
|
20
|
-
|
|
21
|
-
### Global: `~/.ufoo/` (read-only for agents, managed by humans)
|
|
22
|
-
|
|
23
|
-
Global modules live under `~/.ufoo/modules/`.
|
|
24
|
-
|
|
25
|
-
### Project: `<project>/.ufoo/context/` (writable)
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
.ufoo/context/
|
|
29
|
-
├── decisions/ # Append-only decision log (decision-only mode)
|
|
30
|
-
└── decisions.jsonl # Decision index (ts/type/file/author)
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Should be in the project workspace and writable by agents.
|
|
34
|
-
Versioning is optional but recommended for auditability.
|
|
35
|
-
|
|
36
|
-
## Module Structure
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
context/ # This repo
|
|
40
|
-
├── README.md # This file
|
|
41
|
-
├── ../../SKILLS/uctx/SKILL.md # Canonical decision format + workflow
|
|
42
|
-
└── .ufoo/context/ # Local project context for this repo (ignored; not part of protocol distribution)
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## For AI Agents
|
|
46
|
-
|
|
47
|
-
1. Read installed module from `~/.ufoo/modules/context/`
|
|
48
|
-
2. Read/write decisions in `<project>/.ufoo/context/decisions/`
|
|
49
|
-
3. **Never write to global** — only to project
|
|
50
|
-
4. Follow the decision format in the package-level `SKILLS/uctx/SKILL.md`
|
|
51
|
-
|
|
52
|
-
## Validate
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
# protocol repo
|
|
56
|
-
ufoo ctx lint
|
|
57
|
-
|
|
58
|
-
# project-local context (in a real project repo)
|
|
59
|
-
ufoo ctx lint --project <path-to-project-context>
|
|
60
|
-
```
|
package/modules/online/README.md
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# online
|
|
2
|
-
|
|
3
|
-
WebSocket relay module for cross-machine agent collaboration. Extends the local ufoo bus to work over the network.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
online enables agents on different machines to collaborate:
|
|
8
|
-
|
|
9
|
-
- Public channel chat (broadcast to all connected agents)
|
|
10
|
-
- Private room collaboration (bus/decisions/wake sync)
|
|
11
|
-
- Token-based authentication
|
|
12
|
-
- Auto-reconnect with exponential backoff
|
|
13
|
-
|
|
14
|
-
## Quick Start
|
|
15
|
-
|
|
16
|
-
### 1. Start a relay server
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
ufoo online server --port 8787
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### 2. Connect an agent
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# Join a public channel
|
|
26
|
-
ufoo online connect --nickname my-agent --join lobby
|
|
27
|
-
|
|
28
|
-
# Join a private room
|
|
29
|
-
ufoo online connect --nickname my-agent --room room_001 --room-password secret
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### 3. Send messages
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# To a channel
|
|
36
|
-
ufoo online send --nickname my-agent --channel lobby --text "hello everyone"
|
|
37
|
-
|
|
38
|
-
# To a room
|
|
39
|
-
ufoo online send --nickname my-agent --room room_001 --text "hello team"
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 4. Check inbox
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
ufoo online inbox my-agent # All messages
|
|
46
|
-
ufoo online inbox my-agent --unread # Unread only
|
|
47
|
-
ufoo online inbox my-agent --clear # Clear inbox
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Private Room Sync
|
|
51
|
-
|
|
52
|
-
In private room mode, agents automatically sync:
|
|
53
|
-
|
|
54
|
-
- **Bus messages** — local bus ↔ online relay, bidirectional
|
|
55
|
-
- **Decisions** — new `.md` files synced across team
|
|
56
|
-
- **Wake events** — remote agent can wake local agent via bus
|
|
57
|
-
|
|
58
|
-
## HTTP APIs (for web preview)
|
|
59
|
-
|
|
60
|
-
Auth-required management APIs:
|
|
61
|
-
|
|
62
|
-
- `GET/POST /ufoo/online/channels`
|
|
63
|
-
- `GET/POST /ufoo/online/rooms`
|
|
64
|
-
|
|
65
|
-
Public read-only preview APIs (no bearer token required):
|
|
66
|
-
|
|
67
|
-
- `GET /ufoo/online/public/channels`
|
|
68
|
-
- `GET /ufoo/online/public/rooms?type=private`
|
|
69
|
-
- `GET /ufoo/online/public/channels/:channel/messages?limit=120`
|
|
70
|
-
|
|
71
|
-
Notes:
|
|
72
|
-
|
|
73
|
-
- Channel history is in-memory (rolling buffer) on relay server.
|
|
74
|
-
- Private room public API only exposes metadata (`room_id`, `name`, `created_by`, `password_required`).
|
|
75
|
-
|
|
76
|
-
## Storage
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
~/.ufoo/online/
|
|
80
|
-
├── tokens.json # Auth tokens
|
|
81
|
-
├── inbox/<nickname>.jsonl # Incoming messages
|
|
82
|
-
└── outbox/<nickname>.jsonl # Queued outgoing messages
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Relationship with bus
|
|
86
|
-
|
|
87
|
-
| Module | Scope |
|
|
88
|
-
|--------|-------|
|
|
89
|
-
| bus | Local file-system based messaging within a single machine |
|
|
90
|
-
| online | Network relay extending bus across machines via WebSocket |
|
|
91
|
-
|
|
92
|
-
online builds on top of bus — local agents still communicate via the file-system bus, while online bridges messages to remote agents.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# Icon Context
|
|
2
|
-
|
|
3
|
-
Icons here define visual grammar, not shippable assets.
|
|
4
|
-
|
|
5
|
-
They are used as:
|
|
6
|
-
- Reference bases
|
|
7
|
-
- Modification sources
|
|
8
|
-
- Style constraints
|
|
9
|
-
|
|
10
|
-
Do not copy blindly into projects.
|
|
11
|
-
|
|
12
|
-
`libraries/` contains minimal subsets of third-party icon libraries (with licenses) as reference material.
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Icon Libraries (Minimal Subsets)
|
|
2
|
-
|
|
3
|
-
This directory vendors **small, representative subsets** of third-party icon libraries.
|
|
4
|
-
|
|
5
|
-
Purpose:
|
|
6
|
-
- Provide strong, unambiguous signal that icons are **canonical protocol context**
|
|
7
|
-
- Serve as reference bases and modification sources
|
|
8
|
-
|
|
9
|
-
Non-goals:
|
|
10
|
-
- Shipping assets for products
|
|
11
|
-
- Full icon sets
|
|
12
|
-
|
|
13
|
-
Each library subfolder contains:
|
|
14
|
-
- A minimal set of SVGs
|
|
15
|
-
- The upstream license text
|
|
16
|
-
- A short README with source info
|
|
17
|
-
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) Tailwind Labs, Inc.
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
22
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Heroicons (Minimal Subset)
|
|
2
|
-
|
|
3
|
-
Source:
|
|
4
|
-
- Repository: https://github.com/tailwindlabs/heroicons
|
|
5
|
-
- Path: `optimized/24/outline/`
|
|
6
|
-
- License: MIT (see `LICENSE`)
|
|
7
|
-
- Fetched: 2026-01-27
|
|
8
|
-
|
|
9
|
-
Included icons:
|
|
10
|
-
- `arrow-right.svg`
|
|
11
|
-
- `chevron-down.svg`
|
|
12
|
-
- `magnifying-glass.svg`
|
|
13
|
-
- `cog-6-tooth.svg`
|
|
14
|
-
- `check.svg`
|
|
15
|
-
- `x-mark.svg`
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
|
|
2
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"/>
|
|
3
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>
|
|
4
|
-
</svg>
|
|
5
|
-
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
|
|
2
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
|
|
3
|
-
</svg>
|
|
4
|
-
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
ISC License
|
|
2
|
-
|
|
3
|
-
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2026 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2026.
|
|
4
|
-
|
|
5
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
-
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
-
copyright notice and this permission notice appear in all copies.
|
|
8
|
-
|
|
9
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
-
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
-
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
-
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
-
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
-
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
-
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
The MIT License (MIT) (for portions derived from Feather)
|
|
20
|
-
|
|
21
|
-
Copyright (c) 2013-2026 Cole Bemis
|
|
22
|
-
|
|
23
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
24
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
25
|
-
in the Software without restriction, including without limitation the rights
|
|
26
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
27
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
28
|
-
furnished to do so, subject to the following conditions:
|
|
29
|
-
|
|
30
|
-
The above copyright notice and this permission notice shall be included in all
|
|
31
|
-
copies or substantial portions of the Software.
|
|
32
|
-
|
|
33
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
34
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
35
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
36
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
37
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
38
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
39
|
-
SOFTWARE.
|
|
40
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Lucide (Minimal Subset)
|
|
2
|
-
|
|
3
|
-
Source:
|
|
4
|
-
- Repository: https://github.com/lucide-icons/lucide
|
|
5
|
-
- Path: `icons/`
|
|
6
|
-
- License: ISC (see `LICENSE`)
|
|
7
|
-
- Fetched: 2026-01-27
|
|
8
|
-
|
|
9
|
-
Included icons:
|
|
10
|
-
- `arrow-right.svg`
|
|
11
|
-
- `chevron-down.svg`
|
|
12
|
-
- `search.svg`
|
|
13
|
-
- `settings.svg`
|
|
14
|
-
- `check.svg`
|
|
15
|
-
- `x.svg`
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<svg
|
|
2
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
3
|
-
width="24"
|
|
4
|
-
height="24"
|
|
5
|
-
viewBox="0 0 24 24"
|
|
6
|
-
fill="none"
|
|
7
|
-
stroke="currentColor"
|
|
8
|
-
stroke-width="2"
|
|
9
|
-
stroke-linecap="round"
|
|
10
|
-
stroke-linejoin="round"
|
|
11
|
-
>
|
|
12
|
-
<path d="M5 12h14" />
|
|
13
|
-
<path d="m12 5 7 7-7 7" />
|
|
14
|
-
</svg>
|
|
15
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<svg
|
|
2
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
3
|
-
width="24"
|
|
4
|
-
height="24"
|
|
5
|
-
viewBox="0 0 24 24"
|
|
6
|
-
fill="none"
|
|
7
|
-
stroke="currentColor"
|
|
8
|
-
stroke-width="2"
|
|
9
|
-
stroke-linecap="round"
|
|
10
|
-
stroke-linejoin="round"
|
|
11
|
-
>
|
|
12
|
-
<path d="m21 21-4.34-4.34" />
|
|
13
|
-
<circle cx="11" cy="11" r="8" />
|
|
14
|
-
</svg>
|
|
15
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<svg
|
|
2
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
3
|
-
width="24"
|
|
4
|
-
height="24"
|
|
5
|
-
viewBox="0 0 24 24"
|
|
6
|
-
fill="none"
|
|
7
|
-
stroke="currentColor"
|
|
8
|
-
stroke-width="2"
|
|
9
|
-
stroke-linecap="round"
|
|
10
|
-
stroke-linejoin="round"
|
|
11
|
-
>
|
|
12
|
-
<path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
|
|
13
|
-
<circle cx="12" cy="12" r="3" />
|
|
14
|
-
</svg>
|
|
15
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<svg
|
|
2
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
3
|
-
width="24"
|
|
4
|
-
height="24"
|
|
5
|
-
viewBox="0 0 24 24"
|
|
6
|
-
fill="none"
|
|
7
|
-
stroke="currentColor"
|
|
8
|
-
stroke-width="2"
|
|
9
|
-
stroke-linecap="round"
|
|
10
|
-
stroke-linejoin="round"
|
|
11
|
-
>
|
|
12
|
-
<path d="M18 6 6 18" />
|
|
13
|
-
<path d="m6 6 12 12" />
|
|
14
|
-
</svg>
|
|
15
|
-
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# resources
|
|
2
|
-
|
|
3
|
-
Optional resources for AI-assisted coding workflows.
|
|
4
|
-
|
|
5
|
-
This repository intentionally contains non-core materials such as:
|
|
6
|
-
- `UI/` tone + anti-pattern references
|
|
7
|
-
- `ICONS/` reference icon subsets (with licenses)
|
|
8
|
-
|
|
9
|
-
It is designed to be installed and managed by `ufoo` under `~/.ufoo/modules/resources`.
|