ai-superpower/apply.sh
moilanik 8d3758cd6c feat: interactive docs/ creation per project
- 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
2026-03-03 11:28:01 +02:00

157 lines
6.3 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
# 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"