ai-superpower/apply.sh
moilanik cd6f8b5c81 refactor: symlinks point directly to ai-superpower/.ai/ (revert copy approach)
- remove DEV_ROOT/.ai/ copy step — AI_TARGET = SCRIPT_DIR/.ai (repo)
- remove /dev/tty deleted copy folder from filesystem
- edits to .ai/ from any project land directly in git working tree
- docs/apply-requirements.md: FR-3 rewritten
- README.md: principles, install steps, directory structure updated
2026-03-03 11:49:35 +02:00

148 lines
6.0 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"
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
exec bash "$TARGET/apply.sh" --bootstrapped "$DEV_ROOT"
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="$SCRIPT_DIR/.ai"
TEMPLATE_MARKER="<!-- ai-superpower:template"
# 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"