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,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"
|
package/scripts/init.sh
ADDED
|
@@ -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 ==="
|