@laitszkin/apollo-toolkit 3.2.1 → 3.3.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/AGENTS.md +11 -4
- package/CHANGELOG.md +19 -1
- package/README.md +35 -8
- package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
- package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
- package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
- package/implement-specs-with-worktree/SKILL.md +3 -1
- package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
- package/lib/cli.js +190 -9
- package/lib/installer.js +147 -8
- package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
- package/package.json +1 -1
- package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
- package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
- package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
- package/scripts/install_skills.ps1 +488 -108
- package/scripts/install_skills.sh +429 -15
- package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
|
@@ -4,7 +4,8 @@ set -euo pipefail
|
|
|
4
4
|
usage() {
|
|
5
5
|
cat <<"USAGE"
|
|
6
6
|
Usage:
|
|
7
|
-
./scripts/install_skills.sh [codex|openclaw|trae|agents|claude-code|all]...
|
|
7
|
+
./scripts/install_skills.sh [install] [codex|openclaw|trae|agents|claude-code|all]...
|
|
8
|
+
./scripts/install_skills.sh uninstall [codex|openclaw|trae|agents|claude-code|all]...
|
|
8
9
|
|
|
9
10
|
Modes:
|
|
10
11
|
codex Copy skills into ~/.codex/skills (includes ./codex/ agent-specific skills)
|
|
@@ -14,6 +15,10 @@ Modes:
|
|
|
14
15
|
claude-code Copy skills into ~/.claude/skills
|
|
15
16
|
all Install all supported targets
|
|
16
17
|
|
|
18
|
+
Options:
|
|
19
|
+
--symlink Install skills as symlinks (recommended; auto-update via git pull)
|
|
20
|
+
--copy Install skills as file copies (manual reinstall for updates)
|
|
21
|
+
|
|
17
22
|
Optional environment overrides:
|
|
18
23
|
CODEX_SKILLS_DIR Override codex skills destination path
|
|
19
24
|
OPENCLAW_HOME Override openclaw home path
|
|
@@ -27,6 +32,8 @@ USAGE
|
|
|
27
32
|
|
|
28
33
|
SCRIPT_SOURCE="${BASH_SOURCE[0]-}"
|
|
29
34
|
TOOLKIT_REPO_URL="${APOLLO_TOOLKIT_REPO_URL:-https://github.com/LaiTszKin/apollo-toolkit.git}"
|
|
35
|
+
MANIFEST_FILENAME=".apollo-toolkit-manifest.json"
|
|
36
|
+
LINK_MODE=""
|
|
30
37
|
|
|
31
38
|
expand_user_path() {
|
|
32
39
|
local raw_path="${1-}"
|
|
@@ -77,10 +84,14 @@ else
|
|
|
77
84
|
fi
|
|
78
85
|
SCRIPT_DIR="$REPO_ROOT/scripts"
|
|
79
86
|
fi
|
|
87
|
+
|
|
88
|
+
# ---- State variables ----
|
|
80
89
|
SELECTED_MODES=()
|
|
81
90
|
SHARED_SKILL_PATHS=()
|
|
82
91
|
CODEX_SKILL_PATHS=()
|
|
83
92
|
|
|
93
|
+
# ---- Skill collection ----
|
|
94
|
+
|
|
84
95
|
collect_skills() {
|
|
85
96
|
local dir
|
|
86
97
|
SHARED_SKILL_PATHS=()
|
|
@@ -91,7 +102,6 @@ collect_skills() {
|
|
|
91
102
|
fi
|
|
92
103
|
done < <(find "$REPO_ROOT" -mindepth 1 -maxdepth 1 -type d | sort)
|
|
93
104
|
|
|
94
|
-
# For codex mode, also include codex-specific skills
|
|
95
105
|
if [[ " ${SELECTED_MODES[*]} " =~ " codex " ]]; then
|
|
96
106
|
local codex_dir="$REPO_ROOT/codex"
|
|
97
107
|
if [[ -d "$codex_dir" ]]; then
|
|
@@ -109,6 +119,150 @@ collect_skills() {
|
|
|
109
119
|
fi
|
|
110
120
|
}
|
|
111
121
|
|
|
122
|
+
# Get skill names from paths (basename only, deduplicated)
|
|
123
|
+
get_skill_names() {
|
|
124
|
+
local -a paths=("$@")
|
|
125
|
+
local -a names=()
|
|
126
|
+
local name
|
|
127
|
+
for path in "${paths[@]}"; do
|
|
128
|
+
name="$(basename "$path")"
|
|
129
|
+
names+=("$name")
|
|
130
|
+
done
|
|
131
|
+
printf '%s\n' "${names[@]}" | sort -u
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# ---- Manifest management ----
|
|
135
|
+
|
|
136
|
+
read_manifest_skills() {
|
|
137
|
+
local target_root="$1"
|
|
138
|
+
local manifest_file="$target_root/$MANIFEST_FILENAME"
|
|
139
|
+
if [[ -f "$manifest_file" ]]; then
|
|
140
|
+
# Extract skill names from JSON manifest (historical + current, deduplicated)
|
|
141
|
+
# Use python3 if available, otherwise fall back to simple grep
|
|
142
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
143
|
+
python3 -c "
|
|
144
|
+
import json, sys
|
|
145
|
+
try:
|
|
146
|
+
with open('$manifest_file') as f:
|
|
147
|
+
m = json.load(f)
|
|
148
|
+
skills = set(m.get('historicalSkills', []) + m.get('skills', []))
|
|
149
|
+
for s in sorted(skills):
|
|
150
|
+
print(s)
|
|
151
|
+
except: pass
|
|
152
|
+
" 2>/dev/null || true
|
|
153
|
+
else
|
|
154
|
+
# Fallback: grep for skill names in JSON array
|
|
155
|
+
grep -E '^\s*"' "$manifest_file" 2>/dev/null | \
|
|
156
|
+
sed 's/.*"\([^"]*\)".*/\1/' | \
|
|
157
|
+
grep -v 'version\|installedAt\|linkMode\|skills\|historicalSkills\|source' | \
|
|
158
|
+
sort -u || true
|
|
159
|
+
fi
|
|
160
|
+
fi
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
write_manifest() {
|
|
164
|
+
local target_root="$1"
|
|
165
|
+
local version="${2:-unknown}"
|
|
166
|
+
local link_mode="$3"
|
|
167
|
+
shift 3
|
|
168
|
+
local -a skill_names=("$@")
|
|
169
|
+
|
|
170
|
+
local manifest_file="$target_root/$MANIFEST_FILENAME"
|
|
171
|
+
|
|
172
|
+
# Read existing manifest for historical skills
|
|
173
|
+
local -a historical_skills=()
|
|
174
|
+
if [[ -f "$manifest_file" ]]; then
|
|
175
|
+
while IFS= read -r name; do
|
|
176
|
+
[[ -n "$name" ]] && historical_skills+=("$name")
|
|
177
|
+
done < <(read_manifest_skills "$target_root")
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# Merge current + historical, deduplicate
|
|
181
|
+
local -a merged=()
|
|
182
|
+
local name
|
|
183
|
+
for name in "${historical_skills[@]}" "${skill_names[@]}"; do
|
|
184
|
+
merged+=("$name")
|
|
185
|
+
done
|
|
186
|
+
|
|
187
|
+
local -a all_skills_sorted
|
|
188
|
+
while IFS= read -r name; do
|
|
189
|
+
[[ -n "$name" ]] && all_skills_sorted+=("$name")
|
|
190
|
+
done < <(printf '%s\n' "${merged[@]}" | sort -u)
|
|
191
|
+
|
|
192
|
+
local now
|
|
193
|
+
now="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
194
|
+
|
|
195
|
+
# Write JSON manifest manually (no jq dependency)
|
|
196
|
+
mkdir -p "$target_root"
|
|
197
|
+
{
|
|
198
|
+
printf '{\n'
|
|
199
|
+
printf ' "version": "%s",\n' "$version"
|
|
200
|
+
printf ' "installedAt": "%s",\n' "$now"
|
|
201
|
+
printf ' "linkMode": "%s",\n' "$link_mode"
|
|
202
|
+
printf ' "skills": [\n'
|
|
203
|
+
local i=0
|
|
204
|
+
for name in "${skill_names[@]}"; do
|
|
205
|
+
if [[ $i -gt 0 ]]; then printf ',\n'; fi
|
|
206
|
+
printf ' "%s"' "$name"
|
|
207
|
+
i=$((i + 1))
|
|
208
|
+
done
|
|
209
|
+
printf '\n ],\n'
|
|
210
|
+
printf ' "historicalSkills": [\n'
|
|
211
|
+
i=0
|
|
212
|
+
for name in "${all_skills_sorted[@]}"; do
|
|
213
|
+
if [[ $i -gt 0 ]]; then printf ',\n'; fi
|
|
214
|
+
printf ' "%s"' "$name"
|
|
215
|
+
i=$((i + 1))
|
|
216
|
+
done
|
|
217
|
+
printf '\n ]\n'
|
|
218
|
+
printf '}\n'
|
|
219
|
+
} > "$manifest_file"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# List all known skill names (current + historical from all manifests, deduplicated)
|
|
223
|
+
list_all_known_skill_names() {
|
|
224
|
+
local -a target_dirs=()
|
|
225
|
+
local target_dir
|
|
226
|
+
|
|
227
|
+
# Collect all potential target directories
|
|
228
|
+
for mode in "${SELECTED_MODES[@]}"; do
|
|
229
|
+
case "$mode" in
|
|
230
|
+
codex) target_dirs+=("$(expand_user_path "${CODEX_SKILLS_DIR:-$HOME/.codex/skills}")") ;;
|
|
231
|
+
trae) target_dirs+=("$(expand_user_path "${TRAE_SKILLS_DIR:-$HOME/.trae/skills}")") ;;
|
|
232
|
+
agents) target_dirs+=("$(expand_user_path "${AGENTS_SKILLS_DIR:-$HOME/.agents/skills}")") ;;
|
|
233
|
+
claude-code) target_dirs+=("$(expand_user_path "${CLAUDE_CODE_SKILLS_DIR:-$HOME/.claude/skills}")") ;;
|
|
234
|
+
openclaw)
|
|
235
|
+
local openclaw_home oc_workspace
|
|
236
|
+
openclaw_home="$(expand_user_path "${OPENCLAW_HOME:-$HOME/.openclaw}")"
|
|
237
|
+
if [[ -d "$openclaw_home" ]]; then
|
|
238
|
+
for oc_workspace in "$openclaw_home"/workspace*; do
|
|
239
|
+
[[ -d "$oc_workspace/skills" ]] && target_dirs+=("$oc_workspace/skills")
|
|
240
|
+
done
|
|
241
|
+
fi
|
|
242
|
+
;;
|
|
243
|
+
esac
|
|
244
|
+
done
|
|
245
|
+
|
|
246
|
+
local -a all_names=()
|
|
247
|
+
|
|
248
|
+
# Current skill names from repo
|
|
249
|
+
local name
|
|
250
|
+
while IFS= read -r name; do
|
|
251
|
+
[[ -n "$name" ]] && all_names+=("$name")
|
|
252
|
+
done < <(get_skill_names "${SHARED_SKILL_PATHS[@]}" "${CODEX_SKILL_PATHS[@]}")
|
|
253
|
+
|
|
254
|
+
# Historical from manifests
|
|
255
|
+
for target_dir in "${target_dirs[@]}"; do
|
|
256
|
+
while IFS= read -r name; do
|
|
257
|
+
[[ -n "$name" ]] && all_names+=("$name")
|
|
258
|
+
done < <(read_manifest_skills "$target_dir")
|
|
259
|
+
done
|
|
260
|
+
|
|
261
|
+
printf '%s\n' "${all_names[@]}" | sort -u
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
# ---- Install operations ----
|
|
265
|
+
|
|
112
266
|
replace_with_copy() {
|
|
113
267
|
local src="$1"
|
|
114
268
|
local target_root="$2"
|
|
@@ -125,14 +279,41 @@ replace_with_copy() {
|
|
|
125
279
|
echo "[copied] $src -> $target"
|
|
126
280
|
}
|
|
127
281
|
|
|
282
|
+
replace_with_symlink() {
|
|
283
|
+
local src="$1"
|
|
284
|
+
local target_root="$2"
|
|
285
|
+
local name target
|
|
286
|
+
|
|
287
|
+
name="$(basename "$src")"
|
|
288
|
+
target="$target_root/$name"
|
|
289
|
+
|
|
290
|
+
mkdir -p "$target_root"
|
|
291
|
+
if [[ -e "$target" || -L "$target" ]]; then
|
|
292
|
+
rm -rf "$target"
|
|
293
|
+
fi
|
|
294
|
+
ln -s "$src" "$target"
|
|
295
|
+
echo "[symlink] $src -> $target"
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
do_replace() {
|
|
299
|
+
if [[ "$LINK_MODE" == "symlink" ]]; then
|
|
300
|
+
replace_with_symlink "$@"
|
|
301
|
+
else
|
|
302
|
+
replace_with_copy "$@"
|
|
303
|
+
fi
|
|
304
|
+
}
|
|
305
|
+
|
|
128
306
|
install_codex() {
|
|
129
307
|
local codex_skills_dir src
|
|
130
308
|
codex_skills_dir="$(expand_user_path "${CODEX_SKILLS_DIR:-$HOME/.codex/skills}")"
|
|
131
309
|
|
|
132
|
-
echo "Installing to codex: $codex_skills_dir"
|
|
310
|
+
echo "Installing to codex: $codex_skills_dir (mode: $LINK_MODE)"
|
|
311
|
+
local -a skill_names=()
|
|
133
312
|
for src in "${SHARED_SKILL_PATHS[@]}" "${CODEX_SKILL_PATHS[@]}"; do
|
|
134
|
-
|
|
313
|
+
do_replace "$src" "$codex_skills_dir"
|
|
314
|
+
skill_names+=("$(basename "$src")")
|
|
135
315
|
done
|
|
316
|
+
write_manifest "$codex_skills_dir" "${VERSION:-unknown}" "$LINK_MODE" "${skill_names[@]}"
|
|
136
317
|
}
|
|
137
318
|
|
|
138
319
|
install_openclaw() {
|
|
@@ -153,10 +334,13 @@ install_openclaw() {
|
|
|
153
334
|
|
|
154
335
|
for workspace in "${workspaces[@]}"; do
|
|
155
336
|
skills_dir="$workspace/skills"
|
|
156
|
-
echo "Installing to openclaw workspace: $skills_dir"
|
|
337
|
+
echo "Installing to openclaw workspace: $skills_dir (mode: $LINK_MODE)"
|
|
338
|
+
local -a skill_names=()
|
|
157
339
|
for src in "${SHARED_SKILL_PATHS[@]}"; do
|
|
158
|
-
|
|
340
|
+
do_replace "$src" "$skills_dir"
|
|
341
|
+
skill_names+=("$(basename "$src")")
|
|
159
342
|
done
|
|
343
|
+
write_manifest "$skills_dir" "${VERSION:-unknown}" "$LINK_MODE" "${skill_names[@]}"
|
|
160
344
|
done
|
|
161
345
|
}
|
|
162
346
|
|
|
@@ -164,32 +348,133 @@ install_trae() {
|
|
|
164
348
|
local trae_skills_dir src
|
|
165
349
|
trae_skills_dir="$(expand_user_path "${TRAE_SKILLS_DIR:-$HOME/.trae/skills}")"
|
|
166
350
|
|
|
167
|
-
echo "Installing to trae: $trae_skills_dir"
|
|
351
|
+
echo "Installing to trae: $trae_skills_dir (mode: $LINK_MODE)"
|
|
352
|
+
local -a skill_names=()
|
|
168
353
|
for src in "${SHARED_SKILL_PATHS[@]}"; do
|
|
169
|
-
|
|
354
|
+
do_replace "$src" "$trae_skills_dir"
|
|
355
|
+
skill_names+=("$(basename "$src")")
|
|
170
356
|
done
|
|
357
|
+
write_manifest "$trae_skills_dir" "${VERSION:-unknown}" "$LINK_MODE" "${skill_names[@]}"
|
|
171
358
|
}
|
|
172
359
|
|
|
173
360
|
install_agents() {
|
|
174
361
|
local agents_skills_dir src
|
|
175
362
|
agents_skills_dir="$(expand_user_path "${AGENTS_SKILLS_DIR:-$HOME/.agents/skills}")"
|
|
176
363
|
|
|
177
|
-
echo "Installing to agents: $agents_skills_dir"
|
|
364
|
+
echo "Installing to agents: $agents_skills_dir (mode: $LINK_MODE)"
|
|
365
|
+
local -a skill_names=()
|
|
178
366
|
for src in "${SHARED_SKILL_PATHS[@]}"; do
|
|
179
|
-
|
|
367
|
+
do_replace "$src" "$agents_skills_dir"
|
|
368
|
+
skill_names+=("$(basename "$src")")
|
|
180
369
|
done
|
|
370
|
+
write_manifest "$agents_skills_dir" "${VERSION:-unknown}" "$LINK_MODE" "${skill_names[@]}"
|
|
181
371
|
}
|
|
182
372
|
|
|
183
373
|
install_claude_code() {
|
|
184
374
|
local claude_code_skills_dir src
|
|
185
375
|
claude_code_skills_dir="$(expand_user_path "${CLAUDE_CODE_SKILLS_DIR:-$HOME/.claude/skills}")"
|
|
186
376
|
|
|
187
|
-
echo "Installing to claude-code: $claude_code_skills_dir"
|
|
377
|
+
echo "Installing to claude-code: $claude_code_skills_dir (mode: $LINK_MODE)"
|
|
378
|
+
local -a skill_names=()
|
|
188
379
|
for src in "${SHARED_SKILL_PATHS[@]}"; do
|
|
189
|
-
|
|
380
|
+
do_replace "$src" "$claude_code_skills_dir"
|
|
381
|
+
skill_names+=("$(basename "$src")")
|
|
190
382
|
done
|
|
383
|
+
write_manifest "$claude_code_skills_dir" "${VERSION:-unknown}" "$LINK_MODE" "${skill_names[@]}"
|
|
191
384
|
}
|
|
192
385
|
|
|
386
|
+
# ---- Uninstall operations ----
|
|
387
|
+
|
|
388
|
+
uninstall_target() {
|
|
389
|
+
local target_root="$1"
|
|
390
|
+
local target_label="$2"
|
|
391
|
+
|
|
392
|
+
local manifest_file="$target_root/$MANIFEST_FILENAME"
|
|
393
|
+
if [[ ! -f "$manifest_file" ]]; then
|
|
394
|
+
echo "[skip] No manifest found in: $target_root" >&2
|
|
395
|
+
return
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
local -a skills=()
|
|
399
|
+
local name
|
|
400
|
+
while IFS= read -r name; do
|
|
401
|
+
[[ -n "$name" ]] && skills+=("$name")
|
|
402
|
+
done < <(read_manifest_skills "$target_root")
|
|
403
|
+
|
|
404
|
+
if [[ ${#skills[@]} -eq 0 ]]; then
|
|
405
|
+
echo "[skip] No skills in manifest: $target_root" >&2
|
|
406
|
+
rm -f "$manifest_file"
|
|
407
|
+
return
|
|
408
|
+
fi
|
|
409
|
+
|
|
410
|
+
echo "Uninstalling from $target_label: $target_root"
|
|
411
|
+
for name in "${skills[@]}"; do
|
|
412
|
+
local skill_path="$target_root/$name"
|
|
413
|
+
if [[ -e "$skill_path" || -L "$skill_path" ]]; then
|
|
414
|
+
rm -rf "$skill_path"
|
|
415
|
+
echo " [removed] $skill_path"
|
|
416
|
+
fi
|
|
417
|
+
done
|
|
418
|
+
|
|
419
|
+
rm -f "$manifest_file"
|
|
420
|
+
echo " [removed manifest] $manifest_file"
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
run_uninstall() {
|
|
424
|
+
local mode
|
|
425
|
+
|
|
426
|
+
if [[ ${#SELECTED_MODES[@]} -eq 0 ]]; then
|
|
427
|
+
# Uninstall from all known targets
|
|
428
|
+
SELECTED_MODES=(codex openclaw trae agents claude-code)
|
|
429
|
+
fi
|
|
430
|
+
|
|
431
|
+
echo "Uninstalling Apollo Toolkit skills..."
|
|
432
|
+
echo "Target modes: ${SELECTED_MODES[*]}"
|
|
433
|
+
echo
|
|
434
|
+
|
|
435
|
+
# Show all known skills (current + historical, deduplicated)
|
|
436
|
+
collect_skills
|
|
437
|
+
echo "All known skills (current + historical):"
|
|
438
|
+
list_all_known_skill_names | while read -r name; do
|
|
439
|
+
[[ -n "$name" ]] && echo " - $name"
|
|
440
|
+
done
|
|
441
|
+
echo
|
|
442
|
+
|
|
443
|
+
for mode in "${SELECTED_MODES[@]}"; do
|
|
444
|
+
case "$mode" in
|
|
445
|
+
codex)
|
|
446
|
+
local dir="$(expand_user_path "${CODEX_SKILLS_DIR:-$HOME/.codex/skills}")"
|
|
447
|
+
uninstall_target "$dir" "codex"
|
|
448
|
+
;;
|
|
449
|
+
openclaw)
|
|
450
|
+
local openclaw_home oc_workspace
|
|
451
|
+
openclaw_home="$(expand_user_path "${OPENCLAW_HOME:-$HOME/.openclaw}")"
|
|
452
|
+
if [[ -d "$openclaw_home" ]]; then
|
|
453
|
+
for oc_workspace in "$openclaw_home"/workspace*; do
|
|
454
|
+
uninstall_target "$oc_workspace/skills" "openclaw"
|
|
455
|
+
done
|
|
456
|
+
fi
|
|
457
|
+
;;
|
|
458
|
+
trae)
|
|
459
|
+
local dir="$(expand_user_path "${TRAE_SKILLS_DIR:-$HOME/.trae/skills}")"
|
|
460
|
+
uninstall_target "$dir" "trae"
|
|
461
|
+
;;
|
|
462
|
+
agents)
|
|
463
|
+
local dir="$(expand_user_path "${AGENTS_SKILLS_DIR:-$HOME/.agents/skills}")"
|
|
464
|
+
uninstall_target "$dir" "agents"
|
|
465
|
+
;;
|
|
466
|
+
claude-code)
|
|
467
|
+
local dir="$(expand_user_path "${CLAUDE_CODE_SKILLS_DIR:-$HOME/.claude/skills}")"
|
|
468
|
+
uninstall_target "$dir" "claude-code"
|
|
469
|
+
;;
|
|
470
|
+
esac
|
|
471
|
+
done
|
|
472
|
+
|
|
473
|
+
echo "Done."
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
# ---- Mode management ----
|
|
477
|
+
|
|
193
478
|
add_mode_once() {
|
|
194
479
|
local mode="$1"
|
|
195
480
|
local existing
|
|
@@ -242,6 +527,89 @@ read_choice_from_user() {
|
|
|
242
527
|
printf '%s' "$result"
|
|
243
528
|
}
|
|
244
529
|
|
|
530
|
+
read_yes_no() {
|
|
531
|
+
local prompt="$1"
|
|
532
|
+
local default_yes="${2:-true}"
|
|
533
|
+
local hint result
|
|
534
|
+
|
|
535
|
+
if [[ "$default_yes" == "true" ]]; then
|
|
536
|
+
hint="[Y/n]"
|
|
537
|
+
else
|
|
538
|
+
hint="[y/N]"
|
|
539
|
+
fi
|
|
540
|
+
|
|
541
|
+
result="$(read_choice_from_user "$prompt $hint ")"
|
|
542
|
+
result="${result,,}" # lowercase
|
|
543
|
+
|
|
544
|
+
if [[ -z "$result" ]]; then
|
|
545
|
+
if [[ "$default_yes" == "true" ]]; then
|
|
546
|
+
return 0
|
|
547
|
+
else
|
|
548
|
+
return 1
|
|
549
|
+
fi
|
|
550
|
+
fi
|
|
551
|
+
|
|
552
|
+
[[ "$result" == "y" || "$result" == "yes" ]]
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
# Prompt user to choose symlink or copy mode
|
|
556
|
+
prompt_link_mode() {
|
|
557
|
+
echo
|
|
558
|
+
echo "Symlink mode:"
|
|
559
|
+
echo " Pro: Skills auto-update when you 'git pull' in ~/.apollo-toolkit"
|
|
560
|
+
echo " Pro: No need to re-run installer after patch updates"
|
|
561
|
+
echo " Con: Changes pushed to the repo automatically reflect in your skills -"
|
|
562
|
+
echo " you may receive updates you did not intend to accept"
|
|
563
|
+
echo
|
|
564
|
+
|
|
565
|
+
if read_yes_no "Install skills as symlinks (recommended)?" "true"; then
|
|
566
|
+
LINK_MODE="symlink"
|
|
567
|
+
else
|
|
568
|
+
LINK_MODE="copy"
|
|
569
|
+
fi
|
|
570
|
+
echo "Using: $LINK_MODE"
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
# Prompt whether to include codex-exclusive skills in non-codex targets
|
|
574
|
+
prompt_include_exclusive() {
|
|
575
|
+
if [[ ${#CODEX_SKILL_PATHS[@]} -eq 0 ]]; then
|
|
576
|
+
return
|
|
577
|
+
fi
|
|
578
|
+
|
|
579
|
+
local has_non_codex=false
|
|
580
|
+
local mode
|
|
581
|
+
for mode in "${SELECTED_MODES[@]}"; do
|
|
582
|
+
if [[ "$mode" != "codex" ]]; then
|
|
583
|
+
has_non_codex=true
|
|
584
|
+
break
|
|
585
|
+
fi
|
|
586
|
+
done
|
|
587
|
+
|
|
588
|
+
if [[ "$has_non_codex" != "true" ]]; then
|
|
589
|
+
return
|
|
590
|
+
fi
|
|
591
|
+
|
|
592
|
+
local -a codex_only_names=()
|
|
593
|
+
local codex_path name
|
|
594
|
+
for codex_path in "${CODEX_SKILL_PATHS[@]}"; do
|
|
595
|
+
name="$(basename "$codex_path")"
|
|
596
|
+
codex_only_names+=("$name")
|
|
597
|
+
done
|
|
598
|
+
|
|
599
|
+
echo
|
|
600
|
+
echo "Exclusive skills detected:"
|
|
601
|
+
echo " The following skills are exclusive to codex: ${codex_only_names[*]}"
|
|
602
|
+
echo " Your selected non-codex targets: $(printf '%s\n' "${SELECTED_MODES[@]}" | grep -v codex | tr '\n' ' ')"
|
|
603
|
+
|
|
604
|
+
if read_yes_no "Install codex-exclusive skills to non-codex targets as well?" "false"; then
|
|
605
|
+
# Add codex skills to shared paths so they get installed everywhere
|
|
606
|
+
for codex_path in "${CODEX_SKILL_PATHS[@]}"; do
|
|
607
|
+
SHARED_SKILL_PATHS+=("$codex_path")
|
|
608
|
+
done
|
|
609
|
+
echo "Will include codex-exclusive skills in all targets."
|
|
610
|
+
fi
|
|
611
|
+
}
|
|
612
|
+
|
|
245
613
|
choose_modes_interactive() {
|
|
246
614
|
local choice raw_choice
|
|
247
615
|
local -a choices
|
|
@@ -293,18 +661,64 @@ resolve_modes() {
|
|
|
293
661
|
done
|
|
294
662
|
}
|
|
295
663
|
|
|
664
|
+
# ---- Main ----
|
|
665
|
+
|
|
296
666
|
main() {
|
|
297
|
-
local
|
|
298
|
-
SELECTED_MODES=()
|
|
667
|
+
local first_arg="${1:-}"
|
|
299
668
|
|
|
300
|
-
if [[ "$
|
|
669
|
+
if [[ "$first_arg" == "-h" || "$first_arg" == "--help" ]]; then
|
|
301
670
|
usage
|
|
302
671
|
exit 0
|
|
303
672
|
fi
|
|
304
673
|
|
|
674
|
+
# Parse --symlink / --copy flags
|
|
675
|
+
local -a filtered_args=()
|
|
676
|
+
for arg in "$@"; do
|
|
677
|
+
case "$arg" in
|
|
678
|
+
--symlink) LINK_MODE="symlink" ;;
|
|
679
|
+
--copy) LINK_MODE="copy" ;;
|
|
680
|
+
*) filtered_args+=("$arg") ;;
|
|
681
|
+
esac
|
|
682
|
+
done
|
|
683
|
+
set -- "${filtered_args[@]}"
|
|
684
|
+
|
|
685
|
+
first_arg="${1:-}"
|
|
686
|
+
|
|
687
|
+
# Uninstall path
|
|
688
|
+
if [[ "$first_arg" == "uninstall" ]]; then
|
|
689
|
+
shift
|
|
690
|
+
SELECTED_MODES=()
|
|
691
|
+
if [[ $# -gt 0 ]]; then
|
|
692
|
+
resolve_modes "$@"
|
|
693
|
+
fi
|
|
694
|
+
run_uninstall
|
|
695
|
+
exit 0
|
|
696
|
+
fi
|
|
697
|
+
|
|
698
|
+
# Install path (default). Skip "install" verb if present.
|
|
699
|
+
if [[ "$first_arg" == "install" ]]; then
|
|
700
|
+
shift
|
|
701
|
+
fi
|
|
702
|
+
|
|
703
|
+
SELECTED_MODES=()
|
|
305
704
|
resolve_modes "$@"
|
|
306
705
|
collect_skills
|
|
307
706
|
|
|
707
|
+
# Prompt for link mode if not set via CLI
|
|
708
|
+
if [[ -z "$LINK_MODE" ]]; then
|
|
709
|
+
prompt_link_mode
|
|
710
|
+
fi
|
|
711
|
+
|
|
712
|
+
# Prompt for exclusive skills inclusion
|
|
713
|
+
prompt_include_exclusive
|
|
714
|
+
|
|
715
|
+
# Show summary and confirm
|
|
716
|
+
echo
|
|
717
|
+
echo "Apollo Toolkit repo: $REPO_ROOT"
|
|
718
|
+
echo "Install mode: $LINK_MODE"
|
|
719
|
+
echo "Targets: ${SELECTED_MODES[*]}"
|
|
720
|
+
echo
|
|
721
|
+
|
|
308
722
|
for mode in "${SELECTED_MODES[@]}"; do
|
|
309
723
|
case "$mode" in
|
|
310
724
|
codex) install_codex ;;
|