ai-superpower/apply.sh
moilanik 93b859f508 feat: add --update flag to refresh instructions without full setup
curl ... | bash -s -- --update pulls the latest .ai/ files and exits
without scanning projects or touching docs templates.

Bootstrap captures the pre-pull commit hash and forwards it to local
mode; local mode compares with HEAD and either reports "already up to
date" or prints new commit messages with git log --oneline.

FR-1.5 added to apply-requirements.md. README and apply.md updated.
2026-03-04 10:00:11 +02:00

167 lines
6.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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"
EXTRA_FLAGS="${1:-}" # pass through --update if provided
if [[ -d "$TARGET" ]]; then
PREV_COMMIT="$(git -C "$TARGET" rev-parse --short HEAD 2>/dev/null || echo "")"
echo "\u2192 updating $REPO_NAME ..."
git -C "$TARGET" pull || { echo "\u2717 git pull failed"; exit 1; }
else
PREV_COMMIT=""
echo "\u2192 cloning $REPO_NAME into $TARGET ..."
git clone "$REPO_URL" "$TARGET" || { echo "\u2717 git clone failed"; exit 1; }
fi
# Pass DEV_ROOT and PREV_COMMIT explicitly
exec bash "$TARGET/apply.sh" --bootstrapped "$DEV_ROOT" ${EXTRA_FLAGS:+"$EXTRA_FLAGS"} "$PREV_COMMIT"
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 # full setup"
echo " curl -fsSL $RAW_URL | bash -s -- --update # update instructions only"
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="$SCRIPT_DIR/.ai"
TEMPLATE_MARKER="<!-- ai-superpower:template"
# ── --update mode: instructions only, skip project setup ────────────────────
# git pull already ran in bootstrap; compare commits and report changes
if [[ "${3:-}" == "--update" ]]; then
COMMIT="$(git -C "$SCRIPT_DIR" rev-parse --short HEAD 2>/dev/null || echo "unknown")"
PREV_COMMIT="${4:-}"
echo ""
if [[ -z "$PREV_COMMIT" || "$PREV_COMMIT" == "$COMMIT" ]]; then
echo " version: $COMMIT (already up to date)"
else
echo " version: $PREV_COMMIT$COMMIT"
echo " new commits:"
git -C "$SCRIPT_DIR" log --oneline "${PREV_COMMIT}..HEAD" | sed 's/^/ /'
fi
echo ""
exit 0
fi
# Counters
cnt_found=0
cnt_no_docs=0
cnt_templated=0
cnt_refreshed=0
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 ]] || [[ -e /dev/tty ]]; then
printf " ? no docs/ folder — create it? [y/N] "
read -r answer </dev/tty
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"