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.
Files changed (77) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +163 -0
  3. package/README.zh-CN.md +163 -0
  4. package/bin/uclaude +65 -0
  5. package/bin/ucodex +65 -0
  6. package/bin/ufoo +93 -0
  7. package/bin/ufoo.js +35 -0
  8. package/modules/AGENTS.template.md +87 -0
  9. package/modules/bus/README.md +132 -0
  10. package/modules/bus/SKILLS/ubus/SKILL.md +209 -0
  11. package/modules/bus/scripts/bus-alert.sh +185 -0
  12. package/modules/bus/scripts/bus-listen.sh +117 -0
  13. package/modules/context/ASSUMPTIONS.md +7 -0
  14. package/modules/context/CONSTRAINTS.md +7 -0
  15. package/modules/context/CONTEXT-STRUCTURE.md +49 -0
  16. package/modules/context/DECISION-PROTOCOL.md +62 -0
  17. package/modules/context/HANDOFF.md +33 -0
  18. package/modules/context/README.md +82 -0
  19. package/modules/context/RULES.md +15 -0
  20. package/modules/context/SKILLS/README.md +14 -0
  21. package/modules/context/SKILLS/uctx/SKILL.md +91 -0
  22. package/modules/context/SYSTEM.md +18 -0
  23. package/modules/context/TEMPLATES/assumptions.md +4 -0
  24. package/modules/context/TEMPLATES/constraints.md +4 -0
  25. package/modules/context/TEMPLATES/decision.md +16 -0
  26. package/modules/context/TEMPLATES/project-context-readme.md +6 -0
  27. package/modules/context/TEMPLATES/system.md +3 -0
  28. package/modules/context/TEMPLATES/terminology.md +4 -0
  29. package/modules/context/TERMINOLOGY.md +10 -0
  30. package/modules/resources/ICONS/README.md +12 -0
  31. package/modules/resources/ICONS/libraries/README.md +17 -0
  32. package/modules/resources/ICONS/libraries/heroicons/LICENSE +22 -0
  33. package/modules/resources/ICONS/libraries/heroicons/README.md +15 -0
  34. package/modules/resources/ICONS/libraries/heroicons/arrow-right.svg +4 -0
  35. package/modules/resources/ICONS/libraries/heroicons/check.svg +4 -0
  36. package/modules/resources/ICONS/libraries/heroicons/chevron-down.svg +4 -0
  37. package/modules/resources/ICONS/libraries/heroicons/cog-6-tooth.svg +5 -0
  38. package/modules/resources/ICONS/libraries/heroicons/magnifying-glass.svg +4 -0
  39. package/modules/resources/ICONS/libraries/heroicons/x-mark.svg +4 -0
  40. package/modules/resources/ICONS/libraries/lucide/LICENSE +40 -0
  41. package/modules/resources/ICONS/libraries/lucide/README.md +15 -0
  42. package/modules/resources/ICONS/libraries/lucide/arrow-right.svg +15 -0
  43. package/modules/resources/ICONS/libraries/lucide/check.svg +14 -0
  44. package/modules/resources/ICONS/libraries/lucide/chevron-down.svg +14 -0
  45. package/modules/resources/ICONS/libraries/lucide/search.svg +15 -0
  46. package/modules/resources/ICONS/libraries/lucide/settings.svg +15 -0
  47. package/modules/resources/ICONS/libraries/lucide/x.svg +15 -0
  48. package/modules/resources/ICONS/rules.md +7 -0
  49. package/modules/resources/README.md +9 -0
  50. package/modules/resources/UI/ANTI-PATTERNS.md +6 -0
  51. package/modules/resources/UI/TONE.md +6 -0
  52. package/package.json +40 -0
  53. package/scripts/banner.sh +89 -0
  54. package/scripts/bus-alert.sh +6 -0
  55. package/scripts/bus-autotrigger.sh +6 -0
  56. package/scripts/bus-daemon.sh +231 -0
  57. package/scripts/bus-inject.sh +144 -0
  58. package/scripts/bus-listen.sh +6 -0
  59. package/scripts/bus.sh +984 -0
  60. package/scripts/context-decisions.sh +167 -0
  61. package/scripts/context-doctor.sh +72 -0
  62. package/scripts/context-lint.sh +110 -0
  63. package/scripts/doctor.sh +22 -0
  64. package/scripts/init.sh +247 -0
  65. package/scripts/skills.sh +113 -0
  66. package/scripts/status.sh +125 -0
  67. package/src/agent/cliRunner.js +190 -0
  68. package/src/agent/internalRunner.js +212 -0
  69. package/src/agent/normalizeOutput.js +41 -0
  70. package/src/agent/ufooAgent.js +222 -0
  71. package/src/chat/index.js +1603 -0
  72. package/src/cli.js +349 -0
  73. package/src/config.js +37 -0
  74. package/src/daemon/index.js +501 -0
  75. package/src/daemon/ops.js +120 -0
  76. package/src/daemon/run.js +41 -0
  77. package/src/daemon/status.js +78 -0
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # context-decisions.sh
5
+ # Show latest decisions from project context
6
+
7
+ usage() {
8
+ cat <<'EOF'
9
+ context-decisions.sh
10
+
11
+ Show recent decisions from a decision log directory.
12
+
13
+ Default behavior:
14
+ - Use .ufoo/context/DECISIONS/ (project-local decision log)
15
+
16
+ Usage:
17
+ bash scripts/context-decisions.sh [options]
18
+
19
+ Options:
20
+ -n <num> Show last N decisions (default: 1)
21
+ -l List all decisions (titles only)
22
+ -a Show all decisions (full content)
23
+ -d <dir> Use a specific decisions directory
24
+ -s <status> Filter by status: open, resolved, wontfix (default: open)
25
+ Use -s all to show all statuses
26
+ --help Show this help
27
+
28
+ Frontmatter format:
29
+ ---
30
+ status: open | resolved | wontfix
31
+ resolved_by: <agent> # optional, who resolved it
32
+ resolved_at: <date> # optional, when resolved
33
+ ---
34
+ EOF
35
+ }
36
+
37
+ DECISIONS_DIR="${AI_CONTEXT_DECISIONS_DIR:-}"
38
+ NUM=1
39
+ LIST_ONLY=0
40
+ SHOW_ALL=0
41
+ STATUS_FILTER="open"
42
+
43
+ while [[ $# -gt 0 ]]; do
44
+ case "$1" in
45
+ -n)
46
+ NUM="$2"
47
+ shift 2
48
+ ;;
49
+ -l)
50
+ LIST_ONLY=1
51
+ shift
52
+ ;;
53
+ -a)
54
+ SHOW_ALL=1
55
+ shift
56
+ ;;
57
+ -d|--dir)
58
+ DECISIONS_DIR="$2"
59
+ shift 2
60
+ ;;
61
+ -s)
62
+ STATUS_FILTER="$2"
63
+ shift 2
64
+ ;;
65
+ --help|-h)
66
+ usage
67
+ exit 0
68
+ ;;
69
+ *)
70
+ echo "Unknown option: $1" >&2
71
+ usage >&2
72
+ exit 1
73
+ ;;
74
+ esac
75
+ done
76
+
77
+ # Default decisions directory
78
+ if [[ -z "${DECISIONS_DIR}" ]]; then
79
+ DECISIONS_DIR=".ufoo/context/DECISIONS"
80
+ fi
81
+
82
+ # Extract status from frontmatter (defaults to "open" if no frontmatter)
83
+ get_status() {
84
+ local file="$1"
85
+ local status
86
+ # Check if file starts with ---
87
+ if head -1 "$file" | grep -q '^---$'; then
88
+ status=$(awk '/^---$/{if(++c==2)exit} c==1 && /^status:/{print $2}' "$file")
89
+ fi
90
+ echo "${status:-open}"
91
+ }
92
+
93
+ # Check if file matches status filter
94
+ matches_status() {
95
+ local file="$1"
96
+ if [[ "$STATUS_FILTER" == "all" ]]; then
97
+ return 0
98
+ fi
99
+ local status
100
+ status=$(get_status "$file")
101
+ [[ "$status" == "$STATUS_FILTER" ]]
102
+ }
103
+
104
+ if [[ ! -d "$DECISIONS_DIR" ]]; then
105
+ echo "No decisions directory found at $DECISIONS_DIR" >&2
106
+ exit 0
107
+ fi
108
+
109
+ # Get sorted decision files (newest first by filename)
110
+ FILES=()
111
+ while IFS= read -r f; do
112
+ FILES+=("$f")
113
+ done < <(ls -1 "$DECISIONS_DIR"/*.md 2>/dev/null | sort -r)
114
+
115
+ if [[ ${#FILES[@]} -eq 0 ]]; then
116
+ echo "No decisions found."
117
+ exit 0
118
+ fi
119
+
120
+ if [[ $LIST_ONLY -eq 1 ]]; then
121
+ count=0
122
+ output=""
123
+ for f in "${FILES[@]}"; do
124
+ if matches_status "$f"; then
125
+ status=$(get_status "$f")
126
+ # Get title (skip frontmatter if present)
127
+ if head -1 "$f" | grep -q '^---$'; then
128
+ title=$(awk '/^---$/{if(++c==2){getline; print; exit}}' "$f" | sed 's/^# //')
129
+ else
130
+ title=$(head -1 "$f" | sed 's/^# //')
131
+ fi
132
+ output+=" [$status] $(basename "$f"): $title"$'\n'
133
+ ((count++))
134
+ fi
135
+ done
136
+ echo "=== Decisions (${count} ${STATUS_FILTER}, ${#FILES[@]} total) ==="
137
+ printf "%s" "$output"
138
+ exit 0
139
+ fi
140
+
141
+ # Filter files by status
142
+ FILTERED_FILES=()
143
+ for f in "${FILES[@]}"; do
144
+ if matches_status "$f"; then
145
+ FILTERED_FILES+=("$f")
146
+ fi
147
+ done
148
+
149
+ if [[ ${#FILTERED_FILES[@]} -eq 0 ]]; then
150
+ echo "No decisions with status '$STATUS_FILTER' found."
151
+ exit 0
152
+ fi
153
+
154
+ if [[ $SHOW_ALL -eq 1 ]]; then
155
+ NUM=${#FILTERED_FILES[@]}
156
+ fi
157
+
158
+ echo "=== Latest Decision(s) [${STATUS_FILTER}] ==="
159
+ echo ""
160
+
161
+ for ((i=0; i<NUM && i<${#FILTERED_FILES[@]}; i++)); do
162
+ f="${FILTERED_FILES[$i]}"
163
+ status=$(get_status "$f")
164
+ echo "--- $(basename "$f") [$status] ---"
165
+ cat "$f"
166
+ echo ""
167
+ done
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ cat <<'EOF'
6
+ context-doctor.sh
7
+
8
+ Quick diagnostics for context installations.
9
+
10
+ Usage:
11
+ bash scripts/context-doctor.sh
12
+ bash scripts/context-doctor.sh --project <path-to-project-context>
13
+
14
+ What it checks:
15
+ - Runs context-lint (protocol or project)
16
+ - Verifies decision listing works
17
+ - Warns if ~/.ufoo/modules/context is missing
18
+ EOF
19
+ }
20
+
21
+ MODE="protocol"
22
+ PROJECT_PATH=""
23
+ REPO_ROOT="$(pwd)"
24
+ CONTEXT_MODULE_PATH="$REPO_ROOT/modules/context"
25
+
26
+ while [[ $# -gt 0 ]]; do
27
+ case "$1" in
28
+ --project)
29
+ MODE="project"
30
+ PROJECT_PATH="$2"
31
+ shift 2
32
+ ;;
33
+ --help|-h)
34
+ usage
35
+ exit 0
36
+ ;;
37
+ *)
38
+ echo "Unknown option: $1" >&2
39
+ usage >&2
40
+ exit 1
41
+ ;;
42
+ esac
43
+ done
44
+
45
+ echo "=== context doctor ==="
46
+ echo "Reminder: If you provide evaluation/recommendation/plan, write a decision before replying."
47
+
48
+ if [[ "$MODE" == "project" ]]; then
49
+ if [[ -z "$PROJECT_PATH" ]]; then
50
+ echo "FAIL: --project requires a path" >&2
51
+ exit 1
52
+ fi
53
+ echo "Mode: project"
54
+ echo "Project: $PROJECT_PATH"
55
+ bash scripts/context-lint.sh --project "$PROJECT_PATH"
56
+ bash scripts/context-decisions.sh -n 1 -d "$PROJECT_PATH/DECISIONS" >/dev/null
57
+ else
58
+ echo "Mode: protocol"
59
+ # In the ufoo monorepo, the protocol module lives under modules/context.
60
+ if [[ -d "$CONTEXT_MODULE_PATH" && -f "$CONTEXT_MODULE_PATH/SYSTEM.md" ]]; then
61
+ bash "$CONTEXT_MODULE_PATH/scripts/context-lint.sh"
62
+ else
63
+ bash scripts/context-lint.sh
64
+ fi
65
+ bash scripts/context-decisions.sh -n 1 >/dev/null 2>&1 || true
66
+ fi
67
+
68
+ if [[ ! -d "${HOME}/.ufoo/modules/context" ]]; then
69
+ echo "WARN: ${HOME}/.ufoo/modules/context not found (install via ufoo for best UX)"
70
+ fi
71
+
72
+ echo "Status: OK"
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ MODULE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+
6
+ usage() {
7
+ cat <<'EOF'
8
+ context-lint.sh
9
+
10
+ Validate context protocol/project context structure.
11
+
12
+ Usage:
13
+ bash scripts/context-lint.sh
14
+ bash scripts/context-lint.sh --project <path-to-project-context>
15
+
16
+ Exit codes:
17
+ 0 OK
18
+ 1 Validation failed
19
+ EOF
20
+ }
21
+
22
+ fail() {
23
+ printf 'FAIL: %s\n' "$1" >&2
24
+ FAILED=1
25
+ }
26
+
27
+ check_file() {
28
+ local path="$1"
29
+ if [[ ! -f "$path" ]]; then
30
+ fail "Missing file: $path"
31
+ fi
32
+ }
33
+
34
+ check_dir() {
35
+ local path="$1"
36
+ if [[ ! -d "$path" ]]; then
37
+ fail "Missing directory: $path"
38
+ fi
39
+ }
40
+
41
+ check_any_glob() {
42
+ local pattern="$1"
43
+ shopt -s nullglob
44
+ local matches=($pattern)
45
+ shopt -u nullglob
46
+ if (( ${#matches[@]} == 0 )); then
47
+ fail "Missing: $pattern"
48
+ fi
49
+ }
50
+
51
+ lint_protocol_repo() {
52
+ check_file "$MODULE_ROOT/README.md"
53
+ check_file "$MODULE_ROOT/SYSTEM.md"
54
+ check_file "$MODULE_ROOT/RULES.md"
55
+ check_file "$MODULE_ROOT/CONSTRAINTS.md"
56
+ check_file "$MODULE_ROOT/ASSUMPTIONS.md"
57
+ check_file "$MODULE_ROOT/TERMINOLOGY.md"
58
+ check_file "$MODULE_ROOT/DECISION-PROTOCOL.md"
59
+ check_file "$MODULE_ROOT/CONTEXT-STRUCTURE.md"
60
+ check_file "$MODULE_ROOT/HANDOFF.md"
61
+ check_file "$MODULE_ROOT/.gitignore"
62
+
63
+ # This repo is a distributable protocol module. It should not ship a project-local .ufoo/context/.
64
+ if ! grep -qE '^[[:space:]]*\.ufoo/context/([[:space:]]*(#.*)?)?$' "$MODULE_ROOT/.gitignore"; then
65
+ fail ".gitignore must ignore .ufoo/context/ (project-local truth) for the protocol repo"
66
+ fi
67
+
68
+ # UI/ICONS are resources and must live outside this repo (see decisions); do not allow drift.
69
+ if [[ -d "$MODULE_ROOT/UI" || -d "$MODULE_ROOT/ICONS" ]]; then
70
+ fail "UI/ and ICONS/ must not exist in this repo; they belong in resources"
71
+ fi
72
+ }
73
+
74
+ lint_project_context() {
75
+ local root="$1"
76
+ check_dir "$root"
77
+ check_file "$root/README.md"
78
+ check_file "$root/SYSTEM.md"
79
+ check_file "$root/CONSTRAINTS.md"
80
+ check_file "$root/ASSUMPTIONS.md"
81
+ check_file "$root/TERMINOLOGY.md"
82
+ check_dir "$root/DECISIONS"
83
+ }
84
+
85
+ main() {
86
+ FAILED=0
87
+
88
+ if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
89
+ usage
90
+ exit 0
91
+ fi
92
+
93
+ if [[ "${1:-}" == "--project" ]]; then
94
+ if [[ $# -ne 2 ]]; then
95
+ usage >&2
96
+ exit 1
97
+ fi
98
+ lint_project_context "$2"
99
+ else
100
+ lint_protocol_repo
101
+ fi
102
+
103
+ if [[ "$FAILED" -ne 0 ]]; then
104
+ exit 1
105
+ fi
106
+
107
+ echo "OK"
108
+ }
109
+
110
+ main "$@"
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ context_mod="$repo_root/modules/context"
6
+
7
+ if [[ ! -d "$context_mod" ]]; then
8
+ echo "FAIL: missing $context_mod" >&2
9
+ exit 1
10
+ fi
11
+
12
+ bash "$context_mod/scripts/context-lint.sh" >/dev/null
13
+
14
+ echo "=== ufoo doctor ==="
15
+ echo "Monorepo: $repo_root"
16
+ echo "Modules:"
17
+ echo "- context: $context_mod"
18
+ if [[ -d "$repo_root/modules/resources" ]]; then
19
+ echo "- resources: $repo_root/modules/resources"
20
+ fi
21
+
22
+ echo "Status: OK"
@@ -0,0 +1,247 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ context_mod="$repo_root/modules/context"
6
+ bus_mod="$repo_root/modules/bus"
7
+ resources_mod="$repo_root/modules/resources"
8
+ agents_template="$repo_root/modules/AGENTS.template.md"
9
+
10
+ usage() {
11
+ cat <<'USAGE'
12
+ ufoo init
13
+
14
+ Usage:
15
+ ufoo init [--modules context[,bus,resources]] [--project <dir>]
16
+
17
+ Available modules:
18
+ context - Shared context protocol (.ufoo/context/)
19
+ bus - Agent event bus (.ufoo/bus/)
20
+ resources - UI/Icons resources
21
+
22
+ Defaults:
23
+ --modules context
24
+ --project <current directory>
25
+
26
+ Examples:
27
+ ufoo init # Initialize context only
28
+ ufoo init --modules context,bus # Initialize context + bus
29
+ ufoo init --modules context,bus,resources # All modules
30
+ USAGE
31
+ }
32
+
33
+ modules="context"
34
+ project="$(pwd)"
35
+
36
+ while [[ $# -gt 0 ]]; do
37
+ case "$1" in
38
+ --modules)
39
+ modules="$2"
40
+ shift 2
41
+ ;;
42
+ --project)
43
+ project="$2"
44
+ shift 2
45
+ ;;
46
+ --help|-h)
47
+ usage
48
+ exit 0
49
+ ;;
50
+ *)
51
+ echo "Unknown option: $1" >&2
52
+ usage >&2
53
+ exit 1
54
+ ;;
55
+ esac
56
+ done
57
+
58
+ # Parse modules list
59
+ IFS=',' read -ra MODULE_LIST <<< "$modules"
60
+
61
+ echo "=== ufoo init ==="
62
+ echo "Project directory: $project"
63
+ echo "Modules: ${MODULE_LIST[*]}"
64
+ echo ""
65
+
66
+ agents_file="$project/AGENTS.md"
67
+ claude_file="$project/CLAUDE.md"
68
+
69
+ # Ensure AGENTS.md exists
70
+ if [[ ! -f "$agents_file" ]]; then
71
+ printf '# Project Instructions\n\n`CLAUDE.md` points to this file. Please keep project instructions here (prefer edits in `AGENTS.md`).\n\n' >"$agents_file"
72
+ fi
73
+
74
+ # CLAUDE.md points to AGENTS.md
75
+ printf 'AGENTS.md\n' >"$claude_file"
76
+
77
+ # ============================================================================
78
+ # Core: Create .ufoo directory and docs symlink
79
+ # ============================================================================
80
+
81
+ init_core() {
82
+ echo "[core] Initializing .ufoo core..."
83
+
84
+ mkdir -p "$project/.ufoo"
85
+
86
+ # Create docs symlink to ufoo installation docs
87
+ local docs_link="$project/.ufoo/docs"
88
+ local docs_target="$repo_root/docs"
89
+
90
+ if [[ -d "$docs_target" ]]; then
91
+ rm -f "$docs_link"
92
+ ln -sf "$docs_target" "$docs_link"
93
+ echo "[core] Created docs symlink: .ufoo/docs -> $docs_target"
94
+ fi
95
+
96
+ echo "[core] Done"
97
+ }
98
+
99
+ # ============================================================================
100
+ # Inject ufoo template into AGENTS.md (always update)
101
+ # ============================================================================
102
+
103
+ inject_agents_template() {
104
+ echo "[template] Injecting ufoo template into AGENTS.md..."
105
+
106
+ if [[ ! -f "$agents_template" ]]; then
107
+ echo "[template] Warning: template file not found: $agents_template"
108
+ return
109
+ fi
110
+
111
+ local temp_file
112
+ temp_file=$(mktemp)
113
+
114
+ # Remove old ufoo block if exists (between <!-- ufoo --> and <!-- /ufoo -->)
115
+ if grep -q "<!-- ufoo -->" "$agents_file" 2>/dev/null; then
116
+ # Remove old block
117
+ sed '/<!-- ufoo -->/,/<!-- \/ufoo -->/d' "$agents_file" > "$temp_file"
118
+ mv "$temp_file" "$agents_file"
119
+ echo "[template] Removed old ufoo template block"
120
+ fi
121
+
122
+ # Also remove legacy blocks if they exist
123
+ for legacy in "<!-- ufoo-context -->" "<!-- ufoo-bus -->" "<!-- context -->" "<!-- bus -->"; do
124
+ local end_tag="${legacy/<!-- /<!-- \/}"
125
+ if grep -q "$legacy" "$agents_file" 2>/dev/null; then
126
+ sed "/$legacy/,/$end_tag/d" "$agents_file" > "$temp_file"
127
+ mv "$temp_file" "$agents_file"
128
+ echo "[template] Removed old $legacy block"
129
+ fi
130
+ done
131
+
132
+ # Append new template
133
+ echo "" >> "$agents_file"
134
+ cat "$agents_template" >> "$agents_file"
135
+
136
+ echo "[template] Template injected"
137
+ }
138
+
139
+ # ============================================================================
140
+ # Module: context
141
+ # ============================================================================
142
+
143
+ init_context() {
144
+ echo "[context] Initializing .ufoo/context..."
145
+
146
+ if [[ ! -d "$context_mod" ]]; then
147
+ echo "FAIL: missing context module at $context_mod" >&2
148
+ exit 1
149
+ fi
150
+
151
+ ufoo_context="$project/.ufoo/context"
152
+ mkdir -p "$ufoo_context/DECISIONS"
153
+
154
+ # Create symlinks to templates (not copies)
155
+ local templates_dir="$context_mod/TEMPLATES"
156
+ if [[ -d "$templates_dir" ]]; then
157
+ for tpl in "$templates_dir"/*.md; do
158
+ if [[ -f "$tpl" ]]; then
159
+ local basename=$(basename "$tpl")
160
+ local target="$ufoo_context/$basename"
161
+ # Only create if not exists (preserve user modifications)
162
+ if [[ ! -e "$target" ]]; then
163
+ ln -sf "$tpl" "$target"
164
+ fi
165
+ fi
166
+ done
167
+ fi
168
+
169
+ echo "[context] Done: $ufoo_context"
170
+ }
171
+
172
+ # ============================================================================
173
+ # Module: bus
174
+ # ============================================================================
175
+
176
+ init_bus() {
177
+ echo "[bus] Initializing .ufoo/bus..."
178
+
179
+ if [[ ! -d "$bus_mod" ]]; then
180
+ echo "FAIL: missing bus module at $bus_mod" >&2
181
+ exit 1
182
+ fi
183
+
184
+ ufoo_bus="$project/.ufoo/bus"
185
+ mkdir -p "$ufoo_bus"/{events,offsets,queues,logs,pids}
186
+
187
+ # Create bus.json if not exists
188
+ if [[ ! -f "$ufoo_bus/bus.json" ]]; then
189
+ local project_name
190
+ project_name=$(basename "$project")
191
+ cat > "$ufoo_bus/bus.json" << EOF
192
+ {
193
+ "bus_id": "${project_name}-bus",
194
+ "created_at": "$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")",
195
+ "subscribers": {},
196
+ "agent_types": {},
197
+ "config": {
198
+ "poll_interval_ms": 3000,
199
+ "heartbeat_timeout_ms": 30000
200
+ }
201
+ }
202
+ EOF
203
+ fi
204
+
205
+ echo "[bus] Done: $ufoo_bus"
206
+ }
207
+
208
+ # ============================================================================
209
+ # Module: resources
210
+ # ============================================================================
211
+
212
+ init_resources() {
213
+ echo "[resources] Initializing resources..."
214
+
215
+ if [[ ! -d "$resources_mod" ]]; then
216
+ echo "FAIL: missing resources module at $resources_mod" >&2
217
+ exit 1
218
+ fi
219
+
220
+ echo "[resources] Done (resources module needs no initialization, directly reference $resources_mod)"
221
+ }
222
+
223
+ # ============================================================================
224
+ # Execute
225
+ # ============================================================================
226
+
227
+ # Always init core first
228
+ init_core
229
+
230
+ # Execute selected modules
231
+ for mod in "${MODULE_LIST[@]}"; do
232
+ case "$mod" in
233
+ context) init_context ;;
234
+ bus) init_bus ;;
235
+ resources) init_resources ;;
236
+ *)
237
+ echo "Unknown module: $mod" >&2
238
+ exit 1
239
+ ;;
240
+ esac
241
+ done
242
+
243
+ # Always inject/update AGENTS.md template
244
+ inject_agents_template
245
+
246
+ echo ""
247
+ echo "=== Initialization complete ==="