Repository navigation
Expand file tree
/
Copy pathget-swarm-forge
More file actions
executable file
·276 lines (248 loc) · 8.45 KB
/
Copy pathget-swarm-forge
File metadata and controls
executable file
·276 lines (248 loc) · 8.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#!/bin/zsh
set -euo pipefail
default_repo_url="https://github.com/unclebob/swarm-forge"
pack_names=(two-pack four-pack six-pack)
usage() {
echo "usage: get-swarm-forge <two-pack|four-pack|six-pack|project-manager|lieutenant>" >&2
echo " Packs (two-pack, four-pack, six-pack) compose into the current directory." >&2
echo " project-manager and lieutenant are forges (projects/, dashboard, host lieutenant)." >&2
}
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi
if [[ $# -ne 1 || "$1" == -* ]]; then
usage
exit 1
fi
product="$1"
case "$product" in
two-pack|four-pack|six-pack|project-manager|lieutenant) ;;
*)
echo "get-swarm-forge: unknown product '$product'" >&2
usage
exit 1
;;
esac
repo_url="${SWARMFORGE_REPO_URL:-$default_repo_url}"
tmp_dir="$(mktemp -d)"
cleanup() {
rm -rf "$tmp_dir"
}
trap cleanup EXIT
local_git_dir() {
if [[ -v SWARMFORGE_GIT_DIR ]]; then
echo "$SWARMFORGE_GIT_DIR"
elif [[ -d "$HOME/projects/swarm-forge/.git" ]]; then
echo "$HOME/projects/swarm-forge"
fi
}
try_download_branch() {
local ref="$1"
local dest="$2"
local url="${repo_url}/archive/refs/heads/${ref}.tar.gz"
local tar_path="$tmp_dir/${ref}.tgz"
local git_dir
mkdir -p "$dest"
if curl -fsSL "$url" -o "$tar_path"; then
tar -xz --strip-components=1 -C "$dest" -f "$tar_path"
return 0
fi
git_dir="$(local_git_dir)"
if [[ -n "$git_dir" && -d "$git_dir/.git" ]] \
&& git -C "$git_dir" show-ref --verify --quiet "refs/heads/$ref"; then
echo "get-swarm-forge: GitHub has no branch '$ref'; using $git_dir" >&2
git -C "$git_dir" archive --format=tar "$ref" | tar -x -C "$dest"
return 0
fi
return 1
}
download_branch() {
local ref="$1"
local dest="$2"
if try_download_branch "$ref" "$dest"; then
return
fi
echo "get-swarm-forge: branch '$ref' was not found at ${repo_url}/archive/refs/heads/${ref}.tar.gz" >&2
echo "Push the branch, or set SWARMFORGE_GIT_DIR to a local checkout that has it." >&2
exit 1
}
fail_missing() {
local missing_path="$1"
echo "get-swarm-forge: missing required file after composing swarm: $missing_path" >&2
exit 1
}
copy_shared_scripts_and_articles() {
local src="$1"
[[ -d "$src/swarmforge/scripts" ]] || fail_missing "swarmforge/scripts from $src"
[[ -d "$src/swarmforge/constitution/articles" ]] || fail_missing "swarmforge/constitution/articles from $src"
mkdir -p swarmforge/constitution/articles
rm -rf swarmforge/scripts
cp -R "$src/swarmforge/scripts" swarmforge/scripts
local shared_article
for shared_article in engineering.prompt workflow.prompt handoffs.prompt; do
[[ -f "$src/swarmforge/constitution/articles/$shared_article" ]] \
&& cp "$src/swarmforge/constitution/articles/$shared_article" \
"swarmforge/constitution/articles/$shared_article"
done
}
copy_host_files() {
local src="$1"
copy_shared_scripts_and_articles "$src"
[[ -f "$src/swarmforge/constitution.prompt" ]] \
&& cp "$src/swarmforge/constitution.prompt" swarmforge/constitution.prompt
mkdir -p swarmforge/roles
[[ -f "$src/swarmforge/roles/lieutenant.prompt" ]] \
&& cp "$src/swarmforge/roles/lieutenant.prompt" swarmforge/roles/lieutenant.prompt
if [[ ! -f swarmforge/swarmforge.conf && -f "$src/swarmforge/swarmforge.conf" ]]; then
cp "$src/swarmforge/swarmforge.conf" swarmforge/swarmforge.conf
fi
if [[ -f "$src/swarm" ]]; then
cp "$src/swarm" swarm
elif [[ ! -f swarm ]]; then
cat > swarm <<'WRAP'
#!/bin/zsh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
exec "$SCRIPT_DIR/swarmforge/scripts/swarmforge.sh" "$@"
WRAP
fi
chmod +x swarm
}
copy_pack_template() {
local src="$1"
local dest="$2"
mkdir -p "$dest/swarmforge"
[[ -f "$src/swarm" ]] && cp "$src/swarm" "$dest/swarm"
[[ -f "$src/swarmforge/swarmforge.conf" ]] || fail_missing "swarmforge/swarmforge.conf in pack"
[[ -d "$src/swarmforge/roles" ]] || fail_missing "swarmforge/roles in pack"
cp "$src/swarmforge/swarmforge.conf" "$dest/swarmforge/swarmforge.conf"
[[ -f "$src/swarmforge/constitution.prompt" ]] \
&& cp "$src/swarmforge/constitution.prompt" "$dest/swarmforge/constitution.prompt"
rm -rf "$dest/swarmforge/roles"
cp -R "$src/swarmforge/roles" "$dest/swarmforge/roles"
mkdir -p "$dest/swarmforge/constitution/articles"
if [[ -d "$src/swarmforge/constitution/articles" ]]; then
for pack_article in "$src/swarmforge/constitution/articles"/*; do
[[ -f "$pack_article" ]] || continue
article_name="${pack_article:t}"
case "$article_name" in
engineering.prompt|workflow.prompt|handoffs.prompt) continue ;;
esac
cp "$pack_article" "$dest/swarmforge/constitution/articles/$article_name"
done
fi
}
verify_pack_roles() {
local dest="$1"
[[ -f "$dest/swarmforge/swarmforge.conf" ]] || fail_missing "$dest/swarmforge/swarmforge.conf"
while IFS= read -r role; do
[[ -f "$dest/swarmforge/roles/${role}.prompt" ]] \
|| fail_missing "$dest/swarmforge/roles/${role}.prompt"
done < <(awk '/^window/ { print $2 }' "$dest/swarmforge/swarmforge.conf")
}
pack_tree() {
local name="$1"
if [[ -n "$packs_src" && -d "$packs_src/$name" ]]; then
echo "$packs_src/$name"
elif [[ -n "$packs_src" && -f "$packs_src/swarmforge/swarmforge.conf" ]]; then
echo "$packs_src"
else
echo ""
fi
}
install_project_pack() {
local pack_src
pack_src="$(pack_tree "$product")"
if [[ -z "$pack_src" && -d "$host_dir/.swarmforge/project-pack" ]]; then
pack_src="$host_dir/.swarmforge/project-pack"
fi
[[ -n "$pack_src" && -d "$pack_src" ]] || fail_missing ".swarmforge/project-pack"
mkdir -p .swarmforge/project-pack
copy_pack_template "$pack_src" ".swarmforge/project-pack"
verify_pack_roles ".swarmforge/project-pack"
}
install_named_packs() {
mkdir -p packs
local pack_name pack_src
for pack_name in "${pack_names[@]}"; do
pack_src="$(pack_tree "$pack_name")"
if [[ -z "$pack_src" ]]; then
pack_src="$tmp_dir/packs/$pack_name"
download_branch "$pack_name" "$pack_src"
fi
[[ -n "$pack_src" && -d "$pack_src" ]] || fail_missing "pack $pack_name"
copy_pack_template "$pack_src" "packs/$pack_name"
verify_pack_roles "packs/$pack_name"
done
}
require_files() {
local required_path
for required_path in "$@"; do
[[ -e "$required_path" ]] || fail_missing "$required_path"
done
}
host_dir="${SWARMFORGE_BASE_DIR:-$tmp_dir/host}"
host_from="$product"
packs_src="${SWARMFORGE_PACKS_DIR:-}"
main_dir="$tmp_dir/main"
if [[ "$product" == two-pack || "$product" == four-pack || "$product" == six-pack ]]; then
if [[ -z "${SWARMFORGE_BASE_DIR:-}" ]]; then
download_branch "main" "$main_dir"
host_dir="$main_dir"
fi
pack_src="$(pack_tree "$product")"
if [[ -z "$pack_src" ]]; then
pack_src="$tmp_dir/pack"
download_branch "$product" "$pack_src"
fi
[[ -n "$pack_src" && -d "$pack_src" ]] || fail_missing "pack $product"
copy_shared_scripts_and_articles "$host_dir"
copy_pack_template "$pack_src" "."
[[ -f swarm ]] || fail_missing "swarm from pack $product"
chmod +x swarm
verify_pack_roles "."
require_files \
swarm \
swarmforge/scripts/swarmforge.sh \
swarmforge/scripts/handoffd.bb \
swarmforge/scripts/done_with_current.sh \
swarmforge/constitution/articles/handoffs.prompt \
swarmforge/constitution/articles/workflow.prompt \
swarmforge/constitution/articles/engineering.prompt \
swarmforge/swarmforge.conf \
swarmforge/constitution.prompt
echo "SwarmForge pack installed from $product. Run ./swarm to start the pack."
exit 0
fi
if [[ -z "${SWARMFORGE_BASE_DIR:-}" ]]; then
download_branch "$product" "$host_dir"
fi
mkdir -p projects
copy_host_files "$host_dir"
if [[ "$product" == "lieutenant" ]]; then
install_project_pack
else
install_named_packs
fi
required_files=(
swarm
swarmforge/scripts/swarmforge.sh
swarmforge/scripts/handoffd.bb
swarmforge/scripts/done_with_current.sh
swarmforge/constitution/articles/handoffs.prompt
swarmforge/constitution/articles/workflow.prompt
swarmforge/constitution/articles/engineering.prompt
swarmforge/roles/lieutenant.prompt
)
if [[ "$product" == "lieutenant" ]]; then
required_files+=(.swarmforge/project-pack/swarmforge/swarmforge.conf)
else
required_files+=(
packs/two-pack/swarmforge/swarmforge.conf
packs/four-pack/swarmforge/swarmforge.conf
packs/six-pack/swarmforge/swarmforge.conf
)
fi
require_files "${required_files[@]}"
echo "SwarmForge forge installed from $host_from. Run ./swarm to start the dashboard."