- ask [y/N] per project when docs/ folder is missing - bootstrap reopens stdin from /dev/tty so prompts work through curl | bash - fallback to silent skip when stdin is not a TTY (non-interactive) - docs/apply-requirements.md: FR-1.4 and FR-5.1 updated
157 lines
6.3 KiB
Bash
Executable File
157 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
REPO_URL="https://gitea.nikos-dev.keskikuja.site/niko/ai-superpower.git"
|
||
REPO_NAME="ai-superpower"
|
||
RAW_URL="https://gitea.nikos-dev.keskikuja.site/niko/ai-superpower/raw/branch/main/apply.sh"
|
||
|
||
# ── Bootstrap mode (curl | bash) ─────────────────────────────────────────────
|
||
# BASH_SOURCE[0] is empty when piped through bash
|
||
if [[ -z "${BASH_SOURCE[0]:-}" ]]; then
|
||
DEV_ROOT="$PWD" # capture before exec replaces the process
|
||
TARGET="$DEV_ROOT/$REPO_NAME"
|
||
if [[ -d "$TARGET" ]]; then
|
||
echo "→ updating $REPO_NAME ..."
|
||
git -C "$TARGET" pull || { echo "✗ git pull failed"; exit 1; }
|
||
else
|
||
echo "→ cloning $REPO_NAME into $TARGET ..."
|
||
git clone "$REPO_URL" "$TARGET" || { echo "✗ git clone failed"; exit 1; }
|
||
fi
|
||
# Pass DEV_ROOT explicitly — local mode must not guess it from script location
|
||
# Reopen stdin from /dev/tty so local mode can prompt interactively
|
||
exec bash "$TARGET/apply.sh" --bootstrapped "$DEV_ROOT" </dev/tty
|
||
fi
|
||
|
||
# ── Guard — block direct invocation ──────────────────────────────────────────
|
||
if [[ "${1:-}" != "--bootstrapped" ]]; then
|
||
echo "✗ do not run this script directly."
|
||
echo " run from your dev root folder:"
|
||
echo ""
|
||
echo " curl -fsSL $RAW_URL | bash"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
|
||
# ── Local mode ────────────────────────────────────────────────────────────────
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
DEV_ROOT="${2:?DEV_ROOT not passed — re-run via curl}"
|
||
AI_TARGET="$DEV_ROOT/.ai"
|
||
TEMPLATE_MARKER="<!-- ai-superpower:template"
|
||
|
||
# Counters
|
||
cnt_found=0
|
||
cnt_no_docs=0
|
||
cnt_templated=0
|
||
cnt_refreshed=0
|
||
|
||
# ── Install .ai/ into dev root (real folder, refreshed on every run) ─────────
|
||
echo "→ installing .ai/ into $DEV_ROOT ..."
|
||
rm -rf "$AI_TARGET"
|
||
cp -r "$SCRIPT_DIR/.ai" "$AI_TARGET"
|
||
if ! grep -qxF '.ai' "$DEV_ROOT/.gitignore" 2>/dev/null; then
|
||
echo '.ai' >> "$DEV_ROOT/.gitignore"
|
||
fi
|
||
|
||
setup_project() {
|
||
local project="$1"
|
||
local name
|
||
name="$(basename "$project")"
|
||
echo ""
|
||
echo "▸ $name"
|
||
|
||
# FR-3: symlink
|
||
local ai_link="$project/.ai"
|
||
if [[ -L "$ai_link" && "$(readlink "$ai_link")" == "$AI_TARGET" ]]; then
|
||
: # correct — skip silently
|
||
else
|
||
ln -sfn "$AI_TARGET" "$ai_link"
|
||
echo " ✓ .ai symlinked"
|
||
fi
|
||
|
||
# FR-4: .gitignore
|
||
local gitignore="$project/.gitignore"
|
||
if ! grep -qxF '.ai' "$gitignore" 2>/dev/null; then
|
||
echo '.ai' >> "$gitignore"
|
||
echo " ✓ .ai added to .gitignore"
|
||
fi
|
||
|
||
# FR-5 + FR-6: docs
|
||
local docs="$project/docs"
|
||
if [[ ! -d "$docs" ]]; then
|
||
if [[ -t 0 ]]; then
|
||
printf " ? no docs/ folder — create it? [y/N] "
|
||
read -r answer
|
||
if [[ "${answer,,}" == "y" ]]; then
|
||
mkdir -p "$docs"
|
||
echo " ✓ created docs/"
|
||
else
|
||
echo " – skipped"
|
||
(( cnt_no_docs++ )) || true
|
||
return
|
||
fi
|
||
else
|
||
echo " ⚠ no docs/ folder — skipping context setup"
|
||
(( cnt_no_docs++ )) || true
|
||
return
|
||
fi
|
||
fi
|
||
|
||
local got_template=0
|
||
|
||
if [[ ! -f "$docs/ai-context.md" ]]; then
|
||
cp "$SCRIPT_DIR/templates/ai-context.md" "$docs/ai-context.md"
|
||
echo " ✓ created docs/ai-context.md"
|
||
got_template=1
|
||
elif head -1 "$docs/ai-context.md" | grep -qF "$TEMPLATE_MARKER"; then
|
||
cp "$SCRIPT_DIR/templates/ai-context.md" "$docs/ai-context.md"
|
||
echo " ✓ refreshed docs/ai-context.md (was factory template)"
|
||
(( cnt_refreshed++ )) || true
|
||
fi
|
||
|
||
if [[ ! -f "$docs/architecture.md" ]]; then
|
||
cp "$SCRIPT_DIR/templates/architecture.md" "$docs/architecture.md"
|
||
echo " ✓ created docs/architecture.md"
|
||
got_template=1
|
||
elif head -1 "$docs/architecture.md" | grep -qF "$TEMPLATE_MARKER"; then
|
||
cp "$SCRIPT_DIR/templates/architecture.md" "$docs/architecture.md"
|
||
echo " ✓ refreshed docs/architecture.md (was factory template)"
|
||
(( cnt_refreshed++ )) || true
|
||
fi
|
||
|
||
(( got_template )) && (( cnt_templated++ )) || true
|
||
}
|
||
|
||
# ── Scan and run ──────────────────────────────────────────────────────────────
|
||
echo "→ scanning $DEV_ROOT for projects ..."
|
||
|
||
while IFS= read -r gitdir; do
|
||
project="$(cd "$(dirname "$gitdir")" && pwd)"
|
||
[[ -f "$project/.ai-superpower" ]] && continue
|
||
setup_project "$project"
|
||
(( cnt_found++ )) || true
|
||
done < <(find "$DEV_ROOT" -mindepth 2 -maxdepth 4 -name ".git" -type d 2>/dev/null || true)
|
||
|
||
# ── Summary ───────────────────────────────────────────────────────────────────
|
||
COMMIT="$(git -C "$SCRIPT_DIR" rev-parse --short HEAD 2>/dev/null || echo "unknown")"
|
||
PREV_COMMIT="$(grep '^commit:' "$DEV_ROOT/.ai-superpower.version" 2>/dev/null | awk '{print $2}' || echo "")"
|
||
|
||
echo ""
|
||
echo "────────────────────────────────────────"
|
||
if [[ -n "$PREV_COMMIT" && "$PREV_COMMIT" != "$COMMIT" ]]; then
|
||
echo " version: $PREV_COMMIT → $COMMIT"
|
||
elif [[ -n "$PREV_COMMIT" ]]; then
|
||
echo " version: $COMMIT (no change)"
|
||
else
|
||
echo " version: $COMMIT (first run)"
|
||
fi
|
||
echo " projects: $cnt_found found"
|
||
(( cnt_no_docs > 0 )) && echo " no docs/: $cnt_no_docs project(s) skipped (no docs/ folder)" || true
|
||
(( cnt_templated > 0 )) && echo " templated: $cnt_templated project(s) received new docs templates" || true
|
||
(( cnt_refreshed > 0 )) && echo " refreshed: $cnt_refreshed template file(s) updated (were still factory default)" || true
|
||
echo "────────────────────────────────────────"
|
||
(( cnt_found > 0 )) && echo "✅ done" || echo "⚠ no projects found — are you in the right directory?"
|
||
|
||
# FR-8: write version file
|
||
printf '# Written by apply.sh on every run — shows when it was last run and which version of ai-superpower was used.\ndate: %s\ncommit: %s\n' \
|
||
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$COMMIT" > "$DEV_ROOT/.ai-superpower.version"
|