u-foo 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +35 -0
- package/README.md +163 -0
- package/README.zh-CN.md +163 -0
- package/bin/uclaude +65 -0
- package/bin/ucodex +65 -0
- package/bin/ufoo +93 -0
- package/bin/ufoo.js +35 -0
- package/modules/AGENTS.template.md +87 -0
- package/modules/bus/README.md +132 -0
- package/modules/bus/SKILLS/ubus/SKILL.md +209 -0
- package/modules/bus/scripts/bus-alert.sh +185 -0
- package/modules/bus/scripts/bus-listen.sh +117 -0
- package/modules/context/ASSUMPTIONS.md +7 -0
- package/modules/context/CONSTRAINTS.md +7 -0
- package/modules/context/CONTEXT-STRUCTURE.md +49 -0
- package/modules/context/DECISION-PROTOCOL.md +62 -0
- package/modules/context/HANDOFF.md +33 -0
- package/modules/context/README.md +82 -0
- package/modules/context/RULES.md +15 -0
- package/modules/context/SKILLS/README.md +14 -0
- package/modules/context/SKILLS/uctx/SKILL.md +91 -0
- package/modules/context/SYSTEM.md +18 -0
- package/modules/context/TEMPLATES/assumptions.md +4 -0
- package/modules/context/TEMPLATES/constraints.md +4 -0
- package/modules/context/TEMPLATES/decision.md +16 -0
- package/modules/context/TEMPLATES/project-context-readme.md +6 -0
- package/modules/context/TEMPLATES/system.md +3 -0
- package/modules/context/TEMPLATES/terminology.md +4 -0
- package/modules/context/TERMINOLOGY.md +10 -0
- package/modules/resources/ICONS/README.md +12 -0
- package/modules/resources/ICONS/libraries/README.md +17 -0
- package/modules/resources/ICONS/libraries/heroicons/LICENSE +22 -0
- package/modules/resources/ICONS/libraries/heroicons/README.md +15 -0
- package/modules/resources/ICONS/libraries/heroicons/arrow-right.svg +4 -0
- package/modules/resources/ICONS/libraries/heroicons/check.svg +4 -0
- package/modules/resources/ICONS/libraries/heroicons/chevron-down.svg +4 -0
- package/modules/resources/ICONS/libraries/heroicons/cog-6-tooth.svg +5 -0
- package/modules/resources/ICONS/libraries/heroicons/magnifying-glass.svg +4 -0
- package/modules/resources/ICONS/libraries/heroicons/x-mark.svg +4 -0
- package/modules/resources/ICONS/libraries/lucide/LICENSE +40 -0
- package/modules/resources/ICONS/libraries/lucide/README.md +15 -0
- package/modules/resources/ICONS/libraries/lucide/arrow-right.svg +15 -0
- package/modules/resources/ICONS/libraries/lucide/check.svg +14 -0
- package/modules/resources/ICONS/libraries/lucide/chevron-down.svg +14 -0
- package/modules/resources/ICONS/libraries/lucide/search.svg +15 -0
- package/modules/resources/ICONS/libraries/lucide/settings.svg +15 -0
- package/modules/resources/ICONS/libraries/lucide/x.svg +15 -0
- package/modules/resources/ICONS/rules.md +7 -0
- package/modules/resources/README.md +9 -0
- package/modules/resources/UI/ANTI-PATTERNS.md +6 -0
- package/modules/resources/UI/TONE.md +6 -0
- package/package.json +40 -0
- package/scripts/banner.sh +89 -0
- package/scripts/bus-alert.sh +6 -0
- package/scripts/bus-autotrigger.sh +6 -0
- package/scripts/bus-daemon.sh +231 -0
- package/scripts/bus-inject.sh +144 -0
- package/scripts/bus-listen.sh +6 -0
- package/scripts/bus.sh +984 -0
- package/scripts/context-decisions.sh +167 -0
- package/scripts/context-doctor.sh +72 -0
- package/scripts/context-lint.sh +110 -0
- package/scripts/doctor.sh +22 -0
- package/scripts/init.sh +247 -0
- package/scripts/skills.sh +113 -0
- package/scripts/status.sh +125 -0
- package/src/agent/cliRunner.js +190 -0
- package/src/agent/internalRunner.js +212 -0
- package/src/agent/normalizeOutput.js +41 -0
- package/src/agent/ufooAgent.js +222 -0
- package/src/chat/index.js +1603 -0
- package/src/cli.js +349 -0
- package/src/config.js +37 -0
- package/src/daemon/index.js +501 -0
- package/src/daemon/ops.js +120 -0
- package/src/daemon/run.js +41 -0
- package/src/daemon/status.js +78 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<!-- ufoo -->
|
|
2
|
+
## ufoo Protocol
|
|
3
|
+
|
|
4
|
+
This project uses **ufoo** for agent coordination. Read the full documentation at `.ufoo/docs/` (symlinked from ufoo installation).
|
|
5
|
+
|
|
6
|
+
### Core Principles
|
|
7
|
+
|
|
8
|
+
1. **Agents are autonomous** - Execute tasks without asking for permission
|
|
9
|
+
2. **Communication via bus** - Use `ufoo bus` for inter-agent messaging
|
|
10
|
+
3. **Decisions are recorded** - Use `ufoo ctx` for decision tracking
|
|
11
|
+
4. **Context is shared** - All agents read from `.ufoo/context/`
|
|
12
|
+
|
|
13
|
+
### Available Commands
|
|
14
|
+
|
|
15
|
+
| Command | Description |
|
|
16
|
+
|---------|-------------|
|
|
17
|
+
| `uinit` | Initialize/repair .ufoo directory |
|
|
18
|
+
| `uctx` | Check context status and decisions |
|
|
19
|
+
| `ustatus` | Unified status view (banner, unread bus, open decisions) |
|
|
20
|
+
| `ubus` | Check bus messages and **auto-execute** them |
|
|
21
|
+
|
|
22
|
+
### Quick Reference
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Context
|
|
26
|
+
ufoo ctx decisions -l # List all decisions
|
|
27
|
+
ufoo ctx decisions -n 1 # Show latest decision
|
|
28
|
+
|
|
29
|
+
# Bus
|
|
30
|
+
ufoo bus join # Join bus (auto by uclaude/ucodex)
|
|
31
|
+
ufoo bus check $SUBSCRIBER # Check pending messages
|
|
32
|
+
ufoo bus send "<id>" "<msg>" # Send message
|
|
33
|
+
ufoo bus status # Show bus status
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## ufoo context Protocol
|
|
39
|
+
|
|
40
|
+
On session start, check context status:
|
|
41
|
+
```bash
|
|
42
|
+
ufoo ctx decisions -l
|
|
43
|
+
ufoo ctx decisions -n 1
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Key files in `.ufoo/context/`:
|
|
47
|
+
- `DECISIONS/` - Decision log (append-only)
|
|
48
|
+
- `SYSTEM.md` - System overview
|
|
49
|
+
- `CONSTRAINTS.md` - Non-negotiable rules
|
|
50
|
+
|
|
51
|
+
**Decision recording policy:**
|
|
52
|
+
- **Must record**: evaluations, architecture, naming, trade-offs
|
|
53
|
+
- Write decision **before replying** when applicable
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## ufoo bus Protocol
|
|
58
|
+
|
|
59
|
+
### CRITICAL: `ubus` Command Behavior
|
|
60
|
+
|
|
61
|
+
**When you receive `ubus`, you MUST:**
|
|
62
|
+
1. Check pending messages: `ufoo bus check $SUBSCRIBER`
|
|
63
|
+
2. **EXECUTE each task immediately** - Do NOT ask the user
|
|
64
|
+
3. Reply to sender: `ufoo bus send "<publisher>" "<result>"`
|
|
65
|
+
|
|
66
|
+
**Rules:**
|
|
67
|
+
- Execute tasks immediately without asking
|
|
68
|
+
- Always reply to the sender
|
|
69
|
+
- Do NOT ask "Want me to...?" or "Should I...?"
|
|
70
|
+
- Do NOT wait for user confirmation
|
|
71
|
+
|
|
72
|
+
### Message Format
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
@you from claude-code:abc123
|
|
76
|
+
Type: message/targeted
|
|
77
|
+
Content: {"message":"Please analyze the project structure"}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Extract sender ID from "from" field, use it to reply.
|
|
81
|
+
|
|
82
|
+
### Example
|
|
83
|
+
|
|
84
|
+
1. Receive: `@you from claude-code:bd36dda0 Content: {"message":"Please analyze the project structure"}`
|
|
85
|
+
2. Execute: Analyze the project structure
|
|
86
|
+
3. Reply: `ufoo bus send "claude-code:bd36dda0" "Project contains src/, scripts/, modules/"`
|
|
87
|
+
<!-- /ufoo -->
|
|
@@ -0,0 +1,132 @@
|
|
|
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
|
+
.bus/
|
|
26
|
+
├── bus.json # Bus metadata + subscriber status
|
|
27
|
+
├── events/ # Event stream (JSONL, sharded by date)
|
|
28
|
+
├── offsets/ # Each Agent's consumption progress
|
|
29
|
+
└── queues/ # Targeted event queues
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Join Bus
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
SUBSCRIBER=$(bash scripts/bus.sh join)
|
|
38
|
+
# Output: claude-code:a1b2c3
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Check Pending Messages
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bash scripts/bus.sh check $SUBSCRIBER
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Send Messages
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Send to specific instance
|
|
51
|
+
bash scripts/bus.sh send "claude-code:abc123" "Please help me review"
|
|
52
|
+
|
|
53
|
+
# Send to all instances of same type
|
|
54
|
+
bash scripts/bus.sh send "claude-code" "Everyone please review"
|
|
55
|
+
|
|
56
|
+
# Broadcast to all
|
|
57
|
+
bash scripts/bus.sh broadcast "I completed feature-x"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### View Status
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
bash scripts/bus.sh status
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Notifications/Alerts (no key injection, recommended)
|
|
67
|
+
|
|
68
|
+
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):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
SUBSCRIBER=$(bash scripts/bus.sh join | tail -n 1)
|
|
72
|
+
|
|
73
|
+
# Background alert: title badge + bell + optional macOS notification center
|
|
74
|
+
bash scripts/bus-alert.sh "$SUBSCRIBER" 1 --notify --daemon
|
|
75
|
+
|
|
76
|
+
# Or: foreground continuous print of new messages (suitable for a side terminal)
|
|
77
|
+
bash scripts/bus-listen.sh "$SUBSCRIBER" --from-beginning
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Unattended Auto-Execute (recommended)
|
|
81
|
+
|
|
82
|
+
If you need **Claude A to notify Claude B / Codex C and have the target auto-execute** (e.g., auto-trigger `/ubus`), use `autotrigger`:
|
|
83
|
+
|
|
84
|
+
1) First `join` in each terminal session (records `tty`, also records `TMUX_PANE` if in tmux):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
SUBSCRIBER=$(bash scripts/bus.sh join | tail -n 1)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
2) Start autotrigger in the project (runs as background daemon):
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# backend=auto prefers tmux (if available), otherwise tries Terminal.app do script (pure Automation), finally Accessibility
|
|
94
|
+
bash scripts/bus-autotrigger.sh start --interval 1 --command "/ubus" --backend auto
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
3) After sending a message, autotrigger injects `/ubus` into the target session and presses Enter:
|
|
98
|
+
- tmux: `send-keys`
|
|
99
|
+
- Terminal.app (pure Automation): `do script` (no Accessibility needed, but requires Automation authorization; compatibility depends on whether target program accepts input)
|
|
100
|
+
- Terminal.app (Accessibility): System Events (needs Accessibility), injection sequence is Escape + paste + Return (avoids IME issues)
|
|
101
|
+
|
|
102
|
+
Tips:
|
|
103
|
+
- Terminal.app backend depends on `tty` in `bus.json`. Execute `join` in the target terminal session (ensure `tty` is not `not a tty`).
|
|
104
|
+
- Pure Automation backend needs one-time authorization: System Preferences → Privacy & Security → Automation (allow script to control Terminal).
|
|
105
|
+
- Accessibility backend needs one-time authorization: System Preferences → Privacy & Security → Accessibility (for Terminal / script host).
|
|
106
|
+
|
|
107
|
+
Stop/view status:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
bash scripts/bus-autotrigger.sh status
|
|
111
|
+
bash scripts/bus-autotrigger.sh stop
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Subscriber ID Format
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
{agent_type}:{instance_id}
|
|
118
|
+
|
|
119
|
+
Examples:
|
|
120
|
+
claude-code:a1b2c3
|
|
121
|
+
cursor-ai:main
|
|
122
|
+
copilot:session1
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Relationship with context
|
|
126
|
+
|
|
127
|
+
| Module | Problem Solved |
|
|
128
|
+
|--------|----------------|
|
|
129
|
+
| context | Shared context, decision recording, knowledge persistence |
|
|
130
|
+
| bus | Real-time communication, task delegation, message passing |
|
|
131
|
+
|
|
132
|
+
Both are independent peer modules that can be used separately or together.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ubus
|
|
3
|
+
description: |
|
|
4
|
+
Poll event bus, check pending messages.
|
|
5
|
+
Use when: (1) check if other Agents sent messages, (2) view bus status, (3) periodic polling.
|
|
6
|
+
If not yet joined bus, will auto-join.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# /ubus - Event Bus Polling
|
|
10
|
+
|
|
11
|
+
Check pending messages on the event bus.
|
|
12
|
+
|
|
13
|
+
## Arguments
|
|
14
|
+
|
|
15
|
+
- `/ubus` - Check messages and show status
|
|
16
|
+
- `/ubus watch` - Start background auto-notification (title badge + bell + notification center)
|
|
17
|
+
- `/ubus stop` - Stop background auto-notification
|
|
18
|
+
- `/ubus listen` - Foreground continuous listener, print new messages (suitable for side terminal)
|
|
19
|
+
- `/ubus auto` - Unattended auto-execute (auto-inject `/ubus` and press Enter)
|
|
20
|
+
|
|
21
|
+
## Execution Flow
|
|
22
|
+
|
|
23
|
+
### 1. Check if .ufoo/bus exists
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
if [[ ! -d ".ufoo/bus" ]]; then
|
|
27
|
+
echo "Event bus not initialized, please run /uinit and select bus module"
|
|
28
|
+
exit
|
|
29
|
+
fi
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 2. Check if already joined bus
|
|
33
|
+
|
|
34
|
+
Read `.ufoo/bus/bus.json`, check if current session is registered.
|
|
35
|
+
|
|
36
|
+
If not, auto-join (will auto-generate a friendly nickname like "codex-1", "claude-1"):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
SUBSCRIBER=$(ufoo bus join | tail -n 1)
|
|
40
|
+
echo "Joined event bus: $SUBSCRIBER"
|
|
41
|
+
# Example output: codex:0e293156 (nickname: codex-1)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
To join with a custom nickname:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ufoo bus join [session-id] [agent-type] "your-nickname"
|
|
48
|
+
# Example: ufoo bus join abc123 claude-code "architect"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 3. Handle arguments
|
|
52
|
+
|
|
53
|
+
If argument is `watch`, use **Bash tool's `run_in_background: true`** to start background notification:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Title badge + bell + notification center (no accessibility permission needed)
|
|
57
|
+
ufoo bus alert "$SUBSCRIBER" 2 --notify --daemon
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
If argument is `listen`, foreground blocking listener (no background task tool needed):
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
ufoo bus listen "$SUBSCRIBER" --from-beginning
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If argument is `auto`, unattended auto-execute:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Start daemon (background resident), auto-inject /ubus + Enter on new message
|
|
70
|
+
ufoo bus daemon --daemon
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Tips:
|
|
74
|
+
- Need to use `uclaude`/`ucodex` wrapper to start Claude Code/Codex (auto-records tty)
|
|
75
|
+
- Terminal.app needs Accessibility permission (for keyboard input injection)
|
|
76
|
+
|
|
77
|
+
If argument is `stop`, stop background notification:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
ufoo bus alert "$SUBSCRIBER" --stop
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 4. Check pending events
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ufoo bus check "$SUBSCRIBER"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If pending events exist, show:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
=== Pending Messages ===
|
|
93
|
+
|
|
94
|
+
@you from claude-code:abc123
|
|
95
|
+
Type: task/delegate
|
|
96
|
+
Content: {"task":"review","file":"src/main.ts"}
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
Please handle the above messages, after completion you can reply:
|
|
100
|
+
ufoo bus send "claude-code:abc123" "Review completed, found 2 issues..."
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 5. IMPORTANT: Acknowledge messages after handling
|
|
104
|
+
|
|
105
|
+
After you have read and processed the messages, you MUST acknowledge them to prevent repeated notifications:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
ufoo bus ack "$SUBSCRIBER"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**This is critical** - if you don't ack, the daemon will keep injecting `/ubus` commands.
|
|
112
|
+
|
|
113
|
+
If there's nothing to do (no actionable task), just ack immediately without sending a reply.
|
|
114
|
+
|
|
115
|
+
### 6. Routing Override
|
|
116
|
+
|
|
117
|
+
If the message explicitly instructs you to report to a specific PM/DEV/TEST ID, **send the result to that ID instead of the publisher**.
|
|
118
|
+
|
|
119
|
+
### 5. Show bus status
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
ufoo bus status
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Output (now includes nicknames):
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
=== Event Bus Status ===
|
|
129
|
+
My identity: claude-code:xyz789
|
|
130
|
+
Online subscribers: 2
|
|
131
|
+
- claude-code:abc123 (architect)
|
|
132
|
+
- claude-code:xyz789 (dev-lead)
|
|
133
|
+
Recent events: 5
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Managing Nicknames
|
|
137
|
+
|
|
138
|
+
### View and Change Nicknames
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Change an agent's nickname
|
|
142
|
+
ufoo bus rename <subscriber-id> "new-nickname"
|
|
143
|
+
# Example: ufoo bus rename claude-code:47b1d525 "backend-dev"
|
|
144
|
+
|
|
145
|
+
# Nickname alias command
|
|
146
|
+
ufoo bus nick <subscriber-id> "new-nickname"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Important Notes:**
|
|
150
|
+
- Nicknames must be globally unique
|
|
151
|
+
- Cannot change nickname during join (use `rename` command instead)
|
|
152
|
+
- Re-joining with same subscriber ID will reuse existing nickname
|
|
153
|
+
- Auto-generated nicknames: `codex-1`, `codex-2`, `claude-1`, `claude-2`, etc.
|
|
154
|
+
|
|
155
|
+
## Handling Received Messages
|
|
156
|
+
|
|
157
|
+
When receiving targeted messages:
|
|
158
|
+
|
|
159
|
+
1. **Understand request** - Read message content
|
|
160
|
+
2. **Execute task** - If task delegation, execute it
|
|
161
|
+
3. **Reply to sender** - Send response after completion
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
ufoo bus send "<sender-id>" "<reply-content>"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Sending Messages
|
|
168
|
+
|
|
169
|
+
### Smart Routing (when you don't know the target ID)
|
|
170
|
+
|
|
171
|
+
If the user says "notify codex to do X" without specifying an ID, use smart routing:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Step 1: Find candidates
|
|
175
|
+
ufoo bus resolve "$SUBSCRIBER" codex
|
|
176
|
+
|
|
177
|
+
# Output shows:
|
|
178
|
+
# - If only 1 codex: directly shows the ID
|
|
179
|
+
# - If multiple: shows each with nickname and message history
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Based on the output:
|
|
183
|
+
- **Single match**: Use that ID directly
|
|
184
|
+
- **Multiple matches**: Analyze the message history to find the right target
|
|
185
|
+
- Look for context clues in previous conversations
|
|
186
|
+
- If still unclear, ask the user which one, or send to all of that type
|
|
187
|
+
|
|
188
|
+
### Direct Send
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Send to specific Agent by full ID
|
|
192
|
+
ufoo bus send "claude-code:abc123" "message content"
|
|
193
|
+
|
|
194
|
+
# Send to specific Agent by nickname (NEW!)
|
|
195
|
+
ufoo bus send "architect" "message content"
|
|
196
|
+
ufoo bus send "backend-dev" "message content"
|
|
197
|
+
|
|
198
|
+
# Send to all Agents of same type
|
|
199
|
+
ufoo bus send "codex" "message content"
|
|
200
|
+
|
|
201
|
+
# Broadcast to everyone
|
|
202
|
+
ufoo bus broadcast "message content"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Target Resolution Priority:**
|
|
206
|
+
1. Exact subscriber ID (e.g., `claude-code:abc123`)
|
|
207
|
+
2. Nickname match (e.g., `architect` → resolves to subscriber ID)
|
|
208
|
+
3. Agent type (e.g., `codex` → all codex agents)
|
|
209
|
+
4. Wildcard (`*` → all agents)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# bus-alert.sh
|
|
5
|
+
# Background notification daemon for a single subscriber.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# bash bus-alert.sh <subscriber> [interval] [options]
|
|
9
|
+
#
|
|
10
|
+
# Options:
|
|
11
|
+
# --notify Enable macOS Notification Center
|
|
12
|
+
# --daemon Run in background
|
|
13
|
+
# --stop Stop running alert for this subscriber
|
|
14
|
+
# --no-title Disable terminal title badge
|
|
15
|
+
# --no-bell Disable terminal bell
|
|
16
|
+
|
|
17
|
+
BUS_DIR=".ufoo/bus"
|
|
18
|
+
INTERVAL=2
|
|
19
|
+
DAEMON_MODE=0
|
|
20
|
+
USE_NOTIFY=0
|
|
21
|
+
USE_TITLE=1
|
|
22
|
+
USE_BELL=1
|
|
23
|
+
STOP_MODE=0
|
|
24
|
+
|
|
25
|
+
usage() {
|
|
26
|
+
cat <<'USAGE'
|
|
27
|
+
bus-alert.sh - Background notification daemon for a single subscriber
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
bus-alert.sh <subscriber> [interval] [options]
|
|
31
|
+
|
|
32
|
+
Options:
|
|
33
|
+
--notify Enable macOS Notification Center
|
|
34
|
+
--daemon Run in background
|
|
35
|
+
--stop Stop running alert for this subscriber
|
|
36
|
+
--no-title Disable terminal title badge
|
|
37
|
+
--no-bell Disable terminal bell
|
|
38
|
+
-h, --help Show this help
|
|
39
|
+
USAGE
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Handle --help first
|
|
43
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
44
|
+
usage
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
SUBSCRIBER="${1:-}"
|
|
49
|
+
shift || true
|
|
50
|
+
|
|
51
|
+
if [[ -z "$SUBSCRIBER" ]]; then
|
|
52
|
+
usage >&2
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Parse interval if numeric
|
|
57
|
+
if [[ "${1:-}" =~ ^[0-9]+$ ]]; then
|
|
58
|
+
INTERVAL="$1"
|
|
59
|
+
shift || true
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
while [[ $# -gt 0 ]]; do
|
|
63
|
+
case "$1" in
|
|
64
|
+
--notify)
|
|
65
|
+
USE_NOTIFY=1
|
|
66
|
+
shift
|
|
67
|
+
;;
|
|
68
|
+
--daemon)
|
|
69
|
+
DAEMON_MODE=1
|
|
70
|
+
shift
|
|
71
|
+
;;
|
|
72
|
+
--stop)
|
|
73
|
+
STOP_MODE=1
|
|
74
|
+
shift
|
|
75
|
+
;;
|
|
76
|
+
--no-title)
|
|
77
|
+
USE_TITLE=0
|
|
78
|
+
shift
|
|
79
|
+
;;
|
|
80
|
+
--no-bell)
|
|
81
|
+
USE_BELL=0
|
|
82
|
+
shift
|
|
83
|
+
;;
|
|
84
|
+
*)
|
|
85
|
+
shift
|
|
86
|
+
;;
|
|
87
|
+
esac
|
|
88
|
+
done
|
|
89
|
+
|
|
90
|
+
# Sanitize subscriber for filename: claude-code:abc123 -> claude-code_abc123
|
|
91
|
+
SAFE_SUB="${SUBSCRIBER//:/_}"
|
|
92
|
+
PID_FILE="$BUS_DIR/pids/alert-${SAFE_SUB}.pid"
|
|
93
|
+
QUEUE_FILE="$BUS_DIR/queues/${SAFE_SUB}/pending.jsonl"
|
|
94
|
+
|
|
95
|
+
mkdir -p "$BUS_DIR/pids"
|
|
96
|
+
|
|
97
|
+
# Stop mode
|
|
98
|
+
if [[ "$STOP_MODE" == "1" ]]; then
|
|
99
|
+
if [[ -f "$PID_FILE" ]]; then
|
|
100
|
+
pid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
101
|
+
if [[ -n "$pid" ]]; then
|
|
102
|
+
kill "$pid" 2>/dev/null && echo "[alert] Stopped $SUBSCRIBER (pid=$pid)" || echo "[alert] Not running"
|
|
103
|
+
fi
|
|
104
|
+
rm -f "$PID_FILE"
|
|
105
|
+
else
|
|
106
|
+
echo "[alert] Not running for $SUBSCRIBER"
|
|
107
|
+
fi
|
|
108
|
+
exit 0
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# Daemon mode - fork to background
|
|
112
|
+
if [[ "$DAEMON_MODE" == "1" ]]; then
|
|
113
|
+
# Check if already running
|
|
114
|
+
if [[ -f "$PID_FILE" ]]; then
|
|
115
|
+
existing="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
116
|
+
if [[ -n "$existing" ]] && kill -0 "$existing" 2>/dev/null; then
|
|
117
|
+
echo "[alert] Already running for $SUBSCRIBER (pid=$existing)"
|
|
118
|
+
exit 0
|
|
119
|
+
fi
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
LOG_FILE="$BUS_DIR/logs/alert-${SAFE_SUB}.log"
|
|
123
|
+
mkdir -p "$BUS_DIR/logs"
|
|
124
|
+
|
|
125
|
+
args=("$SUBSCRIBER" "$INTERVAL")
|
|
126
|
+
[[ "$USE_NOTIFY" == "1" ]] && args+=("--notify")
|
|
127
|
+
[[ "$USE_TITLE" == "0" ]] && args+=("--no-title")
|
|
128
|
+
[[ "$USE_BELL" == "0" ]] && args+=("--no-bell")
|
|
129
|
+
|
|
130
|
+
nohup bash "$0" "${args[@]}" >> "$LOG_FILE" 2>&1 &
|
|
131
|
+
echo $! > "$PID_FILE"
|
|
132
|
+
echo "[alert] Started for $SUBSCRIBER (pid=$!, log=$LOG_FILE)"
|
|
133
|
+
exit 0
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Record PID for foreground mode too
|
|
137
|
+
echo $$ > "$PID_FILE"
|
|
138
|
+
trap 'rm -f "$PID_FILE" 2>/dev/null || true' EXIT
|
|
139
|
+
|
|
140
|
+
echo "[alert] Watching $SUBSCRIBER (interval=${INTERVAL}s)"
|
|
141
|
+
|
|
142
|
+
LAST_COUNT=0
|
|
143
|
+
|
|
144
|
+
# Get initial count
|
|
145
|
+
if [[ -f "$QUEUE_FILE" ]] && [[ -s "$QUEUE_FILE" ]]; then
|
|
146
|
+
LAST_COUNT="$(wc -l < "$QUEUE_FILE" | tr -d ' ')"
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
while true; do
|
|
150
|
+
# Get current count
|
|
151
|
+
if [[ -f "$QUEUE_FILE" ]] && [[ -s "$QUEUE_FILE" ]]; then
|
|
152
|
+
count="$(wc -l < "$QUEUE_FILE" | tr -d ' ')"
|
|
153
|
+
else
|
|
154
|
+
count=0
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
# New messages arrived
|
|
158
|
+
if [[ "$count" -gt "$LAST_COUNT" ]]; then
|
|
159
|
+
new_count=$((count - LAST_COUNT))
|
|
160
|
+
echo "[alert] $(date '+%H:%M:%S') +${new_count} new message(s)"
|
|
161
|
+
|
|
162
|
+
# Terminal bell
|
|
163
|
+
if [[ "$USE_BELL" == "1" ]]; then
|
|
164
|
+
printf '\a'
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# Terminal title badge
|
|
168
|
+
if [[ "$USE_TITLE" == "1" ]]; then
|
|
169
|
+
printf '\033]0;[%d] %s\007' "$count" "$SUBSCRIBER"
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# macOS notification
|
|
173
|
+
if [[ "$USE_NOTIFY" == "1" ]]; then
|
|
174
|
+
osascript -e "display notification \"${new_count} new message(s)\" with title \"ufoo bus\" subtitle \"$SUBSCRIBER\"" 2>/dev/null || true
|
|
175
|
+
fi
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# Update title even if no new messages (show current count)
|
|
179
|
+
if [[ "$USE_TITLE" == "1" ]] && [[ "$count" -gt 0 ]]; then
|
|
180
|
+
printf '\033]0;[%d] %s\007' "$count" "$SUBSCRIBER"
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
LAST_COUNT="$count"
|
|
184
|
+
sleep "$INTERVAL"
|
|
185
|
+
done
|