u-foo 1.0.2 → 1.0.6
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 +67 -8
- package/README.zh-CN.md +9 -7
- package/SKILLS/ufoo/SKILL.md +117 -0
- package/SKILLS/uinit/SKILL.md +73 -0
- package/SKILLS/ustatus/SKILL.md +36 -0
- package/bin/uclaude.js +13 -0
- package/bin/ucodex.js +13 -0
- package/bin/ufoo +9 -31
- package/bin/ufoo.js +13 -0
- package/modules/AGENTS.template.md +15 -7
- package/modules/bus/README.md +28 -23
- package/modules/bus/SKILLS/ubus/SKILL.md +18 -8
- package/modules/context/README.md +18 -40
- package/modules/context/SKILLS/uctx/SKILL.md +61 -1
- package/package.json +16 -4
- package/scripts/.archived/bash-to-js-migration/README.md +46 -0
- package/scripts/.archived/bash-to-js-migration/banner.sh +89 -0
- package/scripts/{bus-inject.sh → .archived/bash-to-js-migration/bus-inject.sh} +35 -3
- package/scripts/{bus.sh → .archived/bash-to-js-migration/bus.sh} +3 -1
- package/scripts/banner.sh +2 -89
- package/scripts/postinstall.js +59 -0
- package/src/agent/cliRunner.js +33 -5
- package/src/agent/internalRunner.js +78 -51
- package/src/agent/launcher.js +702 -0
- package/src/agent/notifier.js +200 -0
- package/src/agent/ptyRunner.js +377 -0
- package/src/agent/ptyWrapper.js +354 -0
- package/src/agent/readyDetector.js +159 -0
- package/src/agent/ufooAgent.js +37 -42
- package/src/bus/API_DESIGN.md +204 -0
- package/src/bus/activate.js +156 -0
- package/src/bus/daemon.js +308 -0
- package/src/bus/index.js +785 -0
- package/src/bus/inject.js +285 -0
- package/src/bus/message.js +302 -0
- package/src/bus/nickname.js +86 -0
- package/src/bus/queue.js +131 -0
- package/src/bus/shake.js +26 -0
- package/src/bus/subscriber.js +296 -0
- package/src/bus/utils.js +357 -0
- package/src/chat/index.js +1995 -263
- package/src/cli.js +658 -95
- package/src/config.js +23 -4
- package/src/context/decisions.js +314 -0
- package/src/context/doctor.js +183 -0
- package/src/context/index.js +38 -0
- package/src/daemon/index.js +749 -94
- package/src/daemon/ops.js +395 -51
- package/src/daemon/providerSessions.js +291 -0
- package/src/daemon/run.js +38 -3
- package/src/daemon/status.js +24 -7
- package/src/doctor/index.js +50 -0
- package/src/init/index.js +264 -0
- package/src/skills/index.js +159 -0
- package/src/status/index.js +252 -0
- package/src/terminal/detect.js +64 -0
- package/src/terminal/index.js +8 -0
- package/src/terminal/iterm2.js +126 -0
- package/src/ufoo/agentsStore.js +41 -0
- package/src/ufoo/paths.js +46 -0
- package/src/utils/banner.js +73 -0
- package/bin/uclaude +0 -65
- package/bin/ucodex +0 -65
- package/modules/bus/scripts/bus-alert.sh +0 -185
- package/modules/bus/scripts/bus-listen.sh +0 -117
- package/modules/context/ASSUMPTIONS.md +0 -7
- package/modules/context/CONSTRAINTS.md +0 -7
- package/modules/context/CONTEXT-STRUCTURE.md +0 -49
- package/modules/context/DECISION-PROTOCOL.md +0 -62
- package/modules/context/HANDOFF.md +0 -33
- package/modules/context/RULES.md +0 -15
- package/modules/context/SKILLS/README.md +0 -14
- package/modules/context/SYSTEM.md +0 -18
- package/modules/context/TEMPLATES/assumptions.md +0 -4
- package/modules/context/TEMPLATES/constraints.md +0 -4
- package/modules/context/TEMPLATES/decision.md +0 -16
- package/modules/context/TEMPLATES/project-context-readme.md +0 -6
- package/modules/context/TEMPLATES/system.md +0 -3
- package/modules/context/TEMPLATES/terminology.md +0 -4
- package/modules/context/TERMINOLOGY.md +0 -10
- /package/scripts/{bus-alert.sh → .archived/bash-to-js-migration/bus-alert.sh} +0 -0
- /package/scripts/{bus-autotrigger.sh → .archived/bash-to-js-migration/bus-autotrigger.sh} +0 -0
- /package/scripts/{bus-daemon.sh → .archived/bash-to-js-migration/bus-daemon.sh} +0 -0
- /package/scripts/{bus-listen.sh → .archived/bash-to-js-migration/bus-listen.sh} +0 -0
- /package/scripts/{context-decisions.sh → .archived/bash-to-js-migration/context-decisions.sh} +0 -0
- /package/scripts/{context-doctor.sh → .archived/bash-to-js-migration/context-doctor.sh} +0 -0
- /package/scripts/{context-lint.sh → .archived/bash-to-js-migration/context-lint.sh} +0 -0
- /package/scripts/{doctor.sh → .archived/bash-to-js-migration/doctor.sh} +0 -0
- /package/scripts/{init.sh → .archived/bash-to-js-migration/init.sh} +0 -0
- /package/scripts/{skills.sh → .archived/bash-to-js-migration/skills.sh} +0 -0
- /package/scripts/{status.sh → .archived/bash-to-js-migration/status.sh} +0 -0
|
@@ -29,18 +29,28 @@ if [[ ! -d ".ufoo/bus" ]]; then
|
|
|
29
29
|
fi
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
### 2.
|
|
32
|
+
### 2. Get or create subscriber ID
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
If not, auto-join (will auto-generate a friendly nickname like "codex-1", "claude-1"):
|
|
34
|
+
**IMPORTANT**: Always check for existing subscriber ID first to avoid creating duplicates.
|
|
37
35
|
|
|
38
36
|
```bash
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
# Check environment variable first (set by launcher/daemon)
|
|
38
|
+
if [ -n "$UFOO_SUBSCRIBER_ID" ]; then
|
|
39
|
+
SUBSCRIBER="$UFOO_SUBSCRIBER_ID"
|
|
40
|
+
echo "Using existing subscriber ID: $SUBSCRIBER"
|
|
41
|
+
else
|
|
42
|
+
# Not launched via uclaude/ucodex, need to join manually
|
|
43
|
+
SUBSCRIBER=$(ufoo bus join | tail -n 1)
|
|
44
|
+
echo "Joined event bus: $SUBSCRIBER"
|
|
45
|
+
# Example output: codex:0e293156 (nickname: codex-1)
|
|
46
|
+
fi
|
|
42
47
|
```
|
|
43
48
|
|
|
49
|
+
**Why this matters**:
|
|
50
|
+
- `uclaude`/`ucodex` automatically set `UFOO_SUBSCRIBER_ID` during launch
|
|
51
|
+
- Re-joining creates a new ID, causing message loss
|
|
52
|
+
- Always reuse existing ID when available
|
|
53
|
+
|
|
44
54
|
To join with a custom nickname:
|
|
45
55
|
|
|
46
56
|
```bash
|
|
@@ -127,7 +137,7 @@ Output (now includes nicknames):
|
|
|
127
137
|
```
|
|
128
138
|
=== Event Bus Status ===
|
|
129
139
|
My identity: claude-code:xyz789
|
|
130
|
-
Online
|
|
140
|
+
Online agents: 2
|
|
131
141
|
- claude-code:abc123 (architect)
|
|
132
142
|
- claude-code:xyz789 (dev-lead)
|
|
133
143
|
Recent events: 5
|
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
# context
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Decision-only context module for ufoo.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
This protocol solves it by treating decisions and context as files:
|
|
12
|
-
- **Decisions as files** — Persistent, versioned, reviewable. All agents read the same truth.
|
|
13
|
-
|
|
14
|
-
## Core Principle
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
Global context defines the law.
|
|
18
|
-
Project context defines the truth.
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
**Files are truth, not memory.**
|
|
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.
|
|
22
10
|
|
|
23
11
|
## Quick Start
|
|
24
12
|
|
|
@@ -34,42 +22,32 @@ This repository is the `context` module. The recommended entrypoint is `ufoo`.
|
|
|
34
22
|
|
|
35
23
|
Global modules live under `~/.ufoo/modules/`.
|
|
36
24
|
|
|
37
|
-
### Project: `<project>/.context/` (writable)
|
|
25
|
+
### Project: `<project>/.ufoo/context/` (writable)
|
|
38
26
|
|
|
39
27
|
```
|
|
40
|
-
.context/
|
|
41
|
-
├──
|
|
42
|
-
|
|
43
|
-
├── CONSTRAINTS.md # Non-negotiable rules
|
|
44
|
-
├── ASSUMPTIONS.md # Current assumptions
|
|
45
|
-
├── TERMINOLOGY.md # Shared vocabulary
|
|
46
|
-
└── DECISIONS/ # Append-only log
|
|
28
|
+
.ufoo/context/
|
|
29
|
+
├── decisions/ # Append-only decision log (decision-only mode)
|
|
30
|
+
└── decisions.jsonl # Decision index (ts/type/file/author)
|
|
47
31
|
```
|
|
48
32
|
|
|
49
|
-
|
|
33
|
+
Should be in the project workspace and writable by agents.
|
|
34
|
+
Versioning is optional but recommended for auditability.
|
|
50
35
|
|
|
51
|
-
##
|
|
36
|
+
## Module Structure
|
|
52
37
|
|
|
53
38
|
```
|
|
54
39
|
context/ # This repo
|
|
55
|
-
├──
|
|
56
|
-
├──
|
|
57
|
-
|
|
58
|
-
├── DECISION-PROTOCOL.md # How to write decisions
|
|
59
|
-
├── HANDOFF.md # Session handoff rules
|
|
60
|
-
├── CONTEXT-STRUCTURE.md # Project structure spec
|
|
61
|
-
├── TEMPLATES/ # AI behavior constraint
|
|
62
|
-
├── SKILLS/ # tool skill docs (module-local)
|
|
63
|
-
└── .context/ # Local project context for this repo (ignored; not part of protocol distribution)
|
|
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)
|
|
64
43
|
```
|
|
65
44
|
|
|
66
45
|
## For AI Agents
|
|
67
46
|
|
|
68
47
|
1. Read installed module from `~/.ufoo/modules/context/`
|
|
69
|
-
2. Read/write
|
|
48
|
+
2. Read/write decisions in `<project>/.ufoo/context/decisions/`
|
|
70
49
|
3. **Never write to global** — only to project
|
|
71
|
-
4.
|
|
72
|
-
5. Do not modify TEMPLATES/ without a decision
|
|
50
|
+
4. Follow the decision format in `SKILLS/uctx/SKILL.md`
|
|
73
51
|
|
|
74
52
|
## Validate
|
|
75
53
|
|
|
@@ -14,12 +14,72 @@ Fast context check for daily use. Run at session start or anytime.
|
|
|
14
14
|
|
|
15
15
|
Pre-flight reminder:
|
|
16
16
|
- If the user is asking for evaluation/recommendation/plan, write a decision before replying.
|
|
17
|
+
Use: `ufoo ctx decisions new "<Title>"`
|
|
18
|
+
|
|
19
|
+
## Decision format (canonical)
|
|
20
|
+
|
|
21
|
+
Project context is decision-only. Decisions live at:
|
|
22
|
+
`<project>/.ufoo/context/decisions/`
|
|
23
|
+
|
|
24
|
+
Decision index (JSONL):
|
|
25
|
+
`<project>/.ufoo/context/decisions.jsonl`
|
|
26
|
+
|
|
27
|
+
Generate/update the index:
|
|
28
|
+
```bash
|
|
29
|
+
ufoo ctx decisions index
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Each JSONL row includes:
|
|
33
|
+
- `ts` (ISO timestamp)
|
|
34
|
+
- `type` (`decision` or `decision_status`)
|
|
35
|
+
- `file` (decision filename)
|
|
36
|
+
- `author` (decision author or resolver)
|
|
37
|
+
|
|
38
|
+
Create a new decision (recommended before replying when required):
|
|
39
|
+
```bash
|
|
40
|
+
ufoo ctx decisions new "Short Title"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**File naming:** `NNNN-short-title.md` (4-digit prefix + kebab-case slug).
|
|
44
|
+
|
|
45
|
+
**Template for new decisions:**
|
|
46
|
+
```yaml
|
|
47
|
+
---
|
|
48
|
+
status: open
|
|
49
|
+
---
|
|
50
|
+
# DECISION NNNN: <Title>
|
|
51
|
+
|
|
52
|
+
Date: YYYY-MM-DD
|
|
53
|
+
Author: <agent>
|
|
54
|
+
|
|
55
|
+
Context:
|
|
56
|
+
What led to this decision?
|
|
57
|
+
|
|
58
|
+
Decision:
|
|
59
|
+
What is now considered true?
|
|
60
|
+
|
|
61
|
+
Implications:
|
|
62
|
+
What must follow from this?
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Status updates (only edit frontmatter):**
|
|
66
|
+
```yaml
|
|
67
|
+
---
|
|
68
|
+
status: resolved
|
|
69
|
+
resolved_by: <agent>
|
|
70
|
+
resolved_at: YYYY-MM-DD
|
|
71
|
+
---
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Rules:
|
|
75
|
+
- Decisions are append-only. Do not rewrite past content.
|
|
76
|
+
- Only update the frontmatter when changing status.
|
|
17
77
|
|
|
18
78
|
## Workflow
|
|
19
79
|
|
|
20
80
|
### 1. Verify structure exists
|
|
21
81
|
|
|
22
|
-
Check `.ufoo/context/` exists. If missing, tell user to run `
|
|
82
|
+
Check `.ufoo/context/decisions/` exists. If missing, tell user to run `ufoo init`.
|
|
23
83
|
|
|
24
84
|
### 2. List all decisions
|
|
25
85
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u-foo",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
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",
|
|
@@ -17,13 +17,14 @@
|
|
|
17
17
|
],
|
|
18
18
|
"bin": {
|
|
19
19
|
"ufoo": "bin/ufoo.js",
|
|
20
|
-
"uclaude": "bin/uclaude",
|
|
21
|
-
"ucodex": "bin/ucodex"
|
|
20
|
+
"uclaude": "bin/uclaude.js",
|
|
21
|
+
"ucodex": "bin/ucodex.js"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"bin/",
|
|
25
25
|
"src/",
|
|
26
26
|
"scripts/",
|
|
27
|
+
"SKILLS/",
|
|
27
28
|
"modules/",
|
|
28
29
|
"LICENSE",
|
|
29
30
|
"README.md"
|
|
@@ -32,9 +33,20 @@
|
|
|
32
33
|
"engines": {
|
|
33
34
|
"node": ">=18"
|
|
34
35
|
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"postinstall": "node scripts/postinstall.js",
|
|
38
|
+
"test": "jest",
|
|
39
|
+
"test:watch": "jest --watch",
|
|
40
|
+
"test:coverage": "jest --coverage"
|
|
41
|
+
},
|
|
35
42
|
"dependencies": {
|
|
36
43
|
"blessed": "^0.1.81",
|
|
37
44
|
"chalk": "^4.1.2",
|
|
38
|
-
"commander": "^13.1.0"
|
|
45
|
+
"commander": "^13.1.0",
|
|
46
|
+
"gray-matter": "^4.0.3",
|
|
47
|
+
"node-pty": "^1.1.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"jest": "^30.2.0"
|
|
39
51
|
}
|
|
40
52
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 已迁移到 JavaScript 的脚本
|
|
2
|
+
|
|
3
|
+
这些 bash 脚本已经完全迁移到 JavaScript 模块。
|
|
4
|
+
|
|
5
|
+
## 迁移对照表
|
|
6
|
+
|
|
7
|
+
| Bash 脚本 | JavaScript 模块 | 状态 |
|
|
8
|
+
|----------|----------------|------|
|
|
9
|
+
| `bus.sh` | `src/bus/index.js` + 7个子模块 | ✅ 完全替换 |
|
|
10
|
+
| `bus-daemon.sh` | `src/bus/daemon.js` | ✅ 完全替换 |
|
|
11
|
+
| `bus-inject.sh` | `src/bus/inject.js` | ✅ 完全替换 |
|
|
12
|
+
| `status.sh` | `src/status/index.js` | ✅ 完全替换 |
|
|
13
|
+
| `skills.sh` | `src/skills/index.js` | ✅ 完全替换 |
|
|
14
|
+
| `init.sh` | `src/init/index.js` | ✅ 完全替换 |
|
|
15
|
+
|
|
16
|
+
## 迁移完成日期
|
|
17
|
+
|
|
18
|
+
2026-02-04
|
|
19
|
+
|
|
20
|
+
## 使用方式
|
|
21
|
+
|
|
22
|
+
所有命令接口保持不变,直接使用即可:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
ufoo bus status
|
|
26
|
+
ufoo status
|
|
27
|
+
ufoo skills list
|
|
28
|
+
ufoo init
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 保留原因
|
|
32
|
+
|
|
33
|
+
这些脚本被归档保留用于:
|
|
34
|
+
1. 历史参考
|
|
35
|
+
2. 性能对比
|
|
36
|
+
3. 回退备份(如有必要)
|
|
37
|
+
|
|
38
|
+
## 性能对比
|
|
39
|
+
|
|
40
|
+
| 指标 | Bash | JavaScript | 差异 |
|
|
41
|
+
|------|------|------------|------|
|
|
42
|
+
| 消息延迟 | 45ms | 51ms | +13% |
|
|
43
|
+
| 并发安全 | ✅ | ✅ | 相同 |
|
|
44
|
+
| 功能完整 | 100% | 100% | 相同 |
|
|
45
|
+
|
|
46
|
+
性能差异在可接受范围内(<15%),换取更好的可维护性和跨平台支持。
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# banner.sh - TUI startup banner for ufoo agents
|
|
3
|
+
|
|
4
|
+
# Colors
|
|
5
|
+
RST='\033[0m'
|
|
6
|
+
BLD='\033[1m'
|
|
7
|
+
DIM='\033[2m'
|
|
8
|
+
CYN='\033[0;36m'
|
|
9
|
+
GRN='\033[0;32m'
|
|
10
|
+
MAG='\033[0;35m'
|
|
11
|
+
WHT='\033[1;37m'
|
|
12
|
+
YLW='\033[0;33m'
|
|
13
|
+
|
|
14
|
+
show_banner() {
|
|
15
|
+
local agent_type="${1:-claude}"
|
|
16
|
+
local session_id="${2:-unknown}"
|
|
17
|
+
local subscriber="${3:-}"
|
|
18
|
+
local daemon_status="${4:-}"
|
|
19
|
+
|
|
20
|
+
local ACOL
|
|
21
|
+
if [[ "$agent_type" == "codex" ]]; then
|
|
22
|
+
ACOL="$GRN"
|
|
23
|
+
else
|
|
24
|
+
ACOL="$MAG"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Width matches Codex CLI (inner content = 52)
|
|
28
|
+
local INNER=52
|
|
29
|
+
local W=$INNER
|
|
30
|
+
|
|
31
|
+
# Helper: print line with exact padding
|
|
32
|
+
line() {
|
|
33
|
+
local prefix="$1"
|
|
34
|
+
local value="$2"
|
|
35
|
+
local color="$3"
|
|
36
|
+
local prefix_len=${#prefix}
|
|
37
|
+
local value_len=${#value}
|
|
38
|
+
local pad_len=$((INNER - prefix_len - value_len))
|
|
39
|
+
printf "${DIM}│${RST}${prefix}${color}${value}${RST}"
|
|
40
|
+
printf "%${pad_len}s" ""
|
|
41
|
+
printf "${DIM}│${RST}\n"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
echo ""
|
|
45
|
+
printf "${DIM}╭"; printf '─%.0s' $(seq 1 $W); printf "╮${RST}\n"
|
|
46
|
+
|
|
47
|
+
# Title line with icon (compute padding from plain text lengths)
|
|
48
|
+
local icon_plain="<○>"
|
|
49
|
+
local title="UFOO · Multi-Agent Protocol"
|
|
50
|
+
local title_pad=$((INNER - 1 - ${#icon_plain} - 1 - ${#title}))
|
|
51
|
+
printf "${DIM}│${RST} ${WHT}${icon_plain}${RST} ${CYN}${BLD}UFOO${RST}${DIM} · Multi-Agent Protocol${RST}"
|
|
52
|
+
printf "%${title_pad}s" ""
|
|
53
|
+
printf "${DIM}│${RST}\n"
|
|
54
|
+
|
|
55
|
+
printf "${DIM}├"; printf '─%.0s' $(seq 1 $W); printf "┤${RST}\n"
|
|
56
|
+
|
|
57
|
+
# Agent
|
|
58
|
+
if [[ -n "$subscriber" ]]; then
|
|
59
|
+
local sub="$subscriber"
|
|
60
|
+
[[ ${#sub} -gt 36 ]] && sub="${sub:0:33}..."
|
|
61
|
+
line " Agent " "$sub" "${ACOL}${BLD}"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Daemon
|
|
65
|
+
if [[ -n "$daemon_status" ]]; then
|
|
66
|
+
line " Daemon " "$daemon_status" "${GRN}"
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Online agents (daemon handles cleanup of dead agents)
|
|
70
|
+
if [[ -f ".ufoo/bus/bus.json" ]]; then
|
|
71
|
+
local online
|
|
72
|
+
online=$(jq -r '.subscribers | to_entries[] | select(.value.status == "active") | .key' .ufoo/bus/bus.json 2>/dev/null | grep -v "^${subscriber}$" 2>/dev/null | head -5 || true)
|
|
73
|
+
if [[ -n "$online" ]]; then
|
|
74
|
+
printf "${DIM}├"; printf '─%.0s' $(seq 1 $W); printf "┤${RST}\n"
|
|
75
|
+
line " Online " "" ""
|
|
76
|
+
while IFS= read -r agent; do
|
|
77
|
+
[[ -z "$agent" ]] && continue
|
|
78
|
+
local a="$agent"
|
|
79
|
+
[[ ${#a} -gt 44 ]] && a="${a:0:41}..."
|
|
80
|
+
line " · " "$a" "${YLW}"
|
|
81
|
+
done <<< "$online"
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
printf "${DIM}╰"; printf '─%.0s' $(seq 1 $W); printf "╯${RST}\n"
|
|
86
|
+
echo ""
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export -f show_banner 2>/dev/null || true
|
|
@@ -54,18 +54,50 @@ else
|
|
|
54
54
|
fi
|
|
55
55
|
|
|
56
56
|
# Detect terminal type from tty path or environment
|
|
57
|
-
#
|
|
57
|
+
# Priority: 1) tmux (via stored pane ID), 2) iTerm2, 3) Terminal.app
|
|
58
|
+
|
|
59
|
+
# Try to get tmux pane info from bus.json (more reliable than tty lookup)
|
|
60
|
+
TMUX_PANE=""
|
|
61
|
+
if command -v jq &>/dev/null && [[ -f "$BUS_DIR/bus.json" ]]; then
|
|
62
|
+
TMUX_PANE=$(jq -r --arg id "$SUBSCRIBER" '.subscribers[$id].tmux_pane // ""' "$BUS_DIR/bus.json" 2>/dev/null || echo "")
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Check if this is a tmux session
|
|
66
|
+
USE_TMUX=0
|
|
67
|
+
if [[ -n "$TMUX_PANE" ]] && command -v tmux &>/dev/null; then
|
|
68
|
+
# Verify the pane still exists
|
|
69
|
+
if tmux list-panes -a -F '#{pane_id}' 2>/dev/null | grep -q "^${TMUX_PANE}$"; then
|
|
70
|
+
USE_TMUX=1
|
|
71
|
+
else
|
|
72
|
+
echo "[inject] Warning: tmux pane $TMUX_PANE no longer exists" >&2
|
|
73
|
+
# Fallback: try to find pane by tty
|
|
74
|
+
TMUX_PANE=$(tmux list-panes -a -F '#{pane_id} #{pane_tty}' 2>/dev/null | grep " ${TARGET_TTY}$" | awk '{print $1}' | head -1)
|
|
75
|
+
if [[ -n "$TMUX_PANE" ]]; then
|
|
76
|
+
USE_TMUX=1
|
|
77
|
+
echo "[inject] Found alternative tmux pane by tty: $TMUX_PANE" >&2
|
|
78
|
+
fi
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
58
81
|
|
|
59
82
|
# Check if iTerm2 is running and has this tty
|
|
60
83
|
USE_ITERM=0
|
|
61
|
-
if pgrep -q "iTerm2"; then
|
|
84
|
+
if [[ "$USE_TMUX" == "0" ]] && pgrep -q "iTerm2"; then
|
|
62
85
|
# Check if iTerm2 has a session with this tty
|
|
63
86
|
if osascript -e 'tell application "iTerm2" to get tty of current session of current window' 2>/dev/null | grep -q "$TARGET_TTY"; then
|
|
64
87
|
USE_ITERM=1
|
|
65
88
|
fi
|
|
66
89
|
fi
|
|
67
90
|
|
|
68
|
-
if [[ "$
|
|
91
|
+
if [[ "$USE_TMUX" == "1" ]]; then
|
|
92
|
+
echo "[inject] Using tmux send-keys method for pane: $TMUX_PANE"
|
|
93
|
+
# Only send C-c if UFOO_INJECT_INTERRUPT is set (to avoid disrupting running agents)
|
|
94
|
+
if [[ "${UFOO_INJECT_INTERRUPT:-0}" == "1" ]]; then
|
|
95
|
+
echo "[inject] Sending interrupt (C-c) before command"
|
|
96
|
+
tmux send-keys -t "$TMUX_PANE" C-c 2>/dev/null || true
|
|
97
|
+
sleep 0.1
|
|
98
|
+
fi
|
|
99
|
+
tmux send-keys -t "$TMUX_PANE" "$INJECT_CMD" Enter
|
|
100
|
+
elif [[ "$USE_ITERM" == "1" ]]; then
|
|
69
101
|
echo "[inject] Using iTerm2 write text method"
|
|
70
102
|
osascript <<EOF
|
|
71
103
|
tell application "iTerm2"
|
|
@@ -295,6 +295,7 @@ cmd_join() {
|
|
|
295
295
|
--arg ts "$(get_timestamp)" \
|
|
296
296
|
--arg pid "${UFOO_PARENT_PID:-$PPID}" \
|
|
297
297
|
--arg cwd "$(pwd)" \
|
|
298
|
+
--arg launch_mode "${UFOO_LAUNCH_MODE:-unknown}" \
|
|
298
299
|
'
|
|
299
300
|
.subscribers[$name] = {
|
|
300
301
|
"agent_type": $agent_type,
|
|
@@ -306,7 +307,8 @@ cmd_join() {
|
|
|
306
307
|
"pid": ($pid | tonumber),
|
|
307
308
|
"joined_at": $ts,
|
|
308
309
|
"status": "active",
|
|
309
|
-
"last_heartbeat": $ts
|
|
310
|
+
"last_heartbeat": $ts,
|
|
311
|
+
"launch_mode": $launch_mode
|
|
310
312
|
}
|
|
311
313
|
|
|
|
312
314
|
.agent_types[$agent_type].instances = (
|
package/scripts/banner.sh
CHANGED
|
@@ -1,89 +1,2 @@
|
|
|
1
|
-
#!/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
# Colors
|
|
5
|
-
RST='\033[0m'
|
|
6
|
-
BLD='\033[1m'
|
|
7
|
-
DIM='\033[2m'
|
|
8
|
-
CYN='\033[0;36m'
|
|
9
|
-
GRN='\033[0;32m'
|
|
10
|
-
MAG='\033[0;35m'
|
|
11
|
-
WHT='\033[1;37m'
|
|
12
|
-
YLW='\033[0;33m'
|
|
13
|
-
|
|
14
|
-
show_banner() {
|
|
15
|
-
local agent_type="${1:-claude}"
|
|
16
|
-
local session_id="${2:-unknown}"
|
|
17
|
-
local subscriber="${3:-}"
|
|
18
|
-
local daemon_status="${4:-}"
|
|
19
|
-
|
|
20
|
-
local ACOL
|
|
21
|
-
if [[ "$agent_type" == "codex" ]]; then
|
|
22
|
-
ACOL="$GRN"
|
|
23
|
-
else
|
|
24
|
-
ACOL="$MAG"
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
# Width matches Codex CLI (inner content = 52)
|
|
28
|
-
local INNER=52
|
|
29
|
-
local W=$INNER
|
|
30
|
-
|
|
31
|
-
# Helper: print line with exact padding
|
|
32
|
-
line() {
|
|
33
|
-
local prefix="$1"
|
|
34
|
-
local value="$2"
|
|
35
|
-
local color="$3"
|
|
36
|
-
local prefix_len=${#prefix}
|
|
37
|
-
local value_len=${#value}
|
|
38
|
-
local pad_len=$((INNER - prefix_len - value_len))
|
|
39
|
-
printf "${DIM}│${RST}${prefix}${color}${value}${RST}"
|
|
40
|
-
printf "%${pad_len}s" ""
|
|
41
|
-
printf "${DIM}│${RST}\n"
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
echo ""
|
|
45
|
-
printf "${DIM}╭"; printf '─%.0s' $(seq 1 $W); printf "╮${RST}\n"
|
|
46
|
-
|
|
47
|
-
# Title line with icon (compute padding from plain text lengths)
|
|
48
|
-
local icon_plain="<○>"
|
|
49
|
-
local title="UFOO · Multi-Agent Protocol"
|
|
50
|
-
local title_pad=$((INNER - 1 - ${#icon_plain} - 1 - ${#title}))
|
|
51
|
-
printf "${DIM}│${RST} ${WHT}${icon_plain}${RST} ${CYN}${BLD}UFOO${RST}${DIM} · Multi-Agent Protocol${RST}"
|
|
52
|
-
printf "%${title_pad}s" ""
|
|
53
|
-
printf "${DIM}│${RST}\n"
|
|
54
|
-
|
|
55
|
-
printf "${DIM}├"; printf '─%.0s' $(seq 1 $W); printf "┤${RST}\n"
|
|
56
|
-
|
|
57
|
-
# Agent
|
|
58
|
-
if [[ -n "$subscriber" ]]; then
|
|
59
|
-
local sub="$subscriber"
|
|
60
|
-
[[ ${#sub} -gt 36 ]] && sub="${sub:0:33}..."
|
|
61
|
-
line " Agent " "$sub" "${ACOL}${BLD}"
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# Daemon
|
|
65
|
-
if [[ -n "$daemon_status" ]]; then
|
|
66
|
-
line " Daemon " "$daemon_status" "${GRN}"
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
# Online agents (daemon handles cleanup of dead agents)
|
|
70
|
-
if [[ -f ".ufoo/bus/bus.json" ]]; then
|
|
71
|
-
local online
|
|
72
|
-
online=$(jq -r '.subscribers | to_entries[] | select(.value.status == "active") | .key' .ufoo/bus/bus.json 2>/dev/null | grep -v "^${subscriber}$" 2>/dev/null | head -5 || true)
|
|
73
|
-
if [[ -n "$online" ]]; then
|
|
74
|
-
printf "${DIM}├"; printf '─%.0s' $(seq 1 $W); printf "┤${RST}\n"
|
|
75
|
-
line " Online " "" ""
|
|
76
|
-
while IFS= read -r agent; do
|
|
77
|
-
[[ -z "$agent" ]] && continue
|
|
78
|
-
local a="$agent"
|
|
79
|
-
[[ ${#a} -gt 44 ]] && a="${a:0:41}..."
|
|
80
|
-
line " · " "$a" "${YLW}"
|
|
81
|
-
done <<< "$online"
|
|
82
|
-
fi
|
|
83
|
-
fi
|
|
84
|
-
|
|
85
|
-
printf "${DIM}╰"; printf '─%.0s' $(seq 1 $W); printf "╯${RST}\n"
|
|
86
|
-
echo ""
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export -f show_banner 2>/dev/null || true
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
exit 1
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const { spawnSync } = require("child_process");
|
|
5
|
+
|
|
6
|
+
function run(args) {
|
|
7
|
+
const bin = path.join(__dirname, "..", "bin", "ufoo.js");
|
|
8
|
+
const res = spawnSync(process.execPath, [bin, ...args], {
|
|
9
|
+
stdio: "ignore",
|
|
10
|
+
});
|
|
11
|
+
return res.status === 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function tryInstall(args, label) {
|
|
15
|
+
try {
|
|
16
|
+
run(args);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.warn(`[postinstall] ${label} failed: ${err.message || String(err)}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Fix node-pty spawn-helper permissions on macOS (both arm64 and x64)
|
|
23
|
+
function fixNodePtyPermissions() {
|
|
24
|
+
const platforms = ["darwin-arm64", "darwin-x64"];
|
|
25
|
+
|
|
26
|
+
for (const platform of platforms) {
|
|
27
|
+
try {
|
|
28
|
+
const spawnHelperPath = path.join(
|
|
29
|
+
__dirname,
|
|
30
|
+
"..",
|
|
31
|
+
"node_modules",
|
|
32
|
+
"node-pty",
|
|
33
|
+
"prebuilds",
|
|
34
|
+
platform,
|
|
35
|
+
"spawn-helper"
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (fs.existsSync(spawnHelperPath)) {
|
|
39
|
+
const stats = fs.statSync(spawnHelperPath);
|
|
40
|
+
// Check if executable bit is missing
|
|
41
|
+
if ((stats.mode & 0o111) === 0) {
|
|
42
|
+
fs.chmodSync(spawnHelperPath, 0o755);
|
|
43
|
+
console.log(`[postinstall] Fixed node-pty spawn-helper permissions (${platform})`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
// Silently ignore errors - not critical for non-macOS or if node-pty not installed
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fixNodePtyPermissions();
|
|
53
|
+
|
|
54
|
+
const skills = ["ufoo", "ubus", "uctx"];
|
|
55
|
+
|
|
56
|
+
for (const skill of skills) {
|
|
57
|
+
tryInstall(["skills", "install", skill, "--codex"], `install ${skill} skill (codex)`);
|
|
58
|
+
tryInstall(["skills", "install", skill], `install ${skill} skill (claude)`);
|
|
59
|
+
}
|