diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..d0ae191 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,82 @@ +# `scripts/` + +Org-level AI onboarding scripts for the `ai-agent-assembly` org (Epic AAASM-3938). +See `.claude/WORKSPACE.md` for the workspace layout these scripts install and +validate against. + +## `bootstrap-ai-workspace.sh` (AAASM-3943) + +Installs the org AI baseline — `CLAUDE.md`, `AGENTS.md`, `.claude/rules/`, +`.claude/skills/` — from this repo into a contributor's local multi-repo +workspace root, so Claude Code and Codex see shared org context regardless of +which repo they're invoked from. + +### Usage + +```bash +./scripts/bootstrap-ai-workspace.sh ~/ai-agent-assembly +``` + +Run it from inside a local clone of `ai-agent-assembly/.github` (the script +locates its own repo checkout automatically, via its own path). The workspace +root path does not need to exist yet — it's created if missing. + +### `--dry-run` + +Prints exactly what would be created, updated, or skipped without touching the +filesystem: + +```bash +./scripts/bootstrap-ai-workspace.sh ~/ai-agent-assembly --dry-run +``` + +Use this before the first real run, or any time you want to check the current +state of a workspace root against this repo's baseline. + +### Symlinks, not copies + +The script **symlinks** (does not copy) `CLAUDE.md`, `AGENTS.md`, +`.claude/rules/`, and `.claude/skills/` into the workspace root. This means a +`git pull` in your `.github` checkout is picked up immediately everywhere it's +installed — no need to re-run the script after every org-baseline change. + +### Re-running is safe (idempotent) + +Running the script again after it already succeeded is a no-op for anything +already correctly installed — those items are reported as `skipped (up to +date)`. If a previously-installed symlink is broken (e.g. you moved or +re-cloned this `.github` repo), the script detects that and relinks it, +reported as `updated`. + +### "skipped (local override)" + +The script **never overwrites** a file, directory, or symlink at the target +path that isn't already the exact symlink this script would create. If you see +`skipped (local override)` in the summary, it means something already exists +there that doesn't point back at this repo — most commonly because you +intentionally replaced the installed copy with your own local version. + +If that's a mistake and you actually want the script to (re-)install the org +baseline there, remove the stale file/symlink yourself first, then re-run the +script: + +```bash +rm ~/ai-agent-assembly/CLAUDE.md # or whichever path was reported +./scripts/bootstrap-ai-workspace.sh ~/ai-agent-assembly +``` + +The script will not do this removal for you — that's a deliberate safety +choice so a contributor's local override is never silently clobbered. + +### Summary output + +Every run ends with a per-item report line (`[created]`, `[updated]`, +`[skipped]`, or `[failed]`) plus aggregate counts. The script exits non-zero +if any item failed. + +## `validate-ai-workspace.sh` (AAASM-3944, not yet built) + +Planned companion script that will check an installed workspace (and each +selected repo inside it) against `.claude/WORKSPACE.md`'s expected layout, +reporting missing files, broken symlinks, and visible repo-local overrides. It +will live alongside `bootstrap-ai-workspace.sh` in this directory once built. diff --git a/scripts/bootstrap-ai-workspace.sh b/scripts/bootstrap-ai-workspace.sh new file mode 100755 index 0000000..67fef55 --- /dev/null +++ b/scripts/bootstrap-ai-workspace.sh @@ -0,0 +1,205 @@ +#!/usr/bin/env bash +# +# bootstrap-ai-workspace.sh — install the ai-agent-assembly org AI baseline +# (CLAUDE.md, AGENTS.md, .claude/rules/, .claude/skills/) into a contributor's +# local multi-repo workspace root, per the layout defined in +# .claude/WORKSPACE.md (AAASM-3939) and tracked as AAASM-3943. +# +# Usage: +# ./scripts/bootstrap-ai-workspace.sh [--dry-run] +# +# This is a plain local script: no network calls, no eval, nothing piped from +# the network. It only reads from this repo checkout and writes into the +# workspace root you pass in. + +set -euo pipefail + +# --- Why symlinks instead of copies ----------------------------------------- +# Symlinks mean an edit to CLAUDE.md/AGENTS.md/.claude/rules/.claude/skills in +# this repo (after a `git pull`) is picked up immediately by every workspace +# that installed it, with no need to re-run this script. Copies would silently +# drift out of date the moment this repo's source-of-truth files change. +# ----------------------------------------------------------------------------- + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +usage() { + echo "Usage: $(basename "${BASH_SOURCE[0]}") [--dry-run]" >&2 +} + +DRY_RUN=0 +WORKSPACE_ROOT="" + +for arg in "$@"; do + case "${arg}" in + --dry-run) + DRY_RUN=1 + ;; + -h|--help) + usage + exit 0 + ;; + -*) + echo "Unknown flag: ${arg}" >&2 + usage + exit 1 + ;; + *) + if [[ -n "${WORKSPACE_ROOT}" ]]; then + echo "Unexpected extra argument: ${arg}" >&2 + usage + exit 1 + fi + WORKSPACE_ROOT="${arg}" + ;; + esac +done + +if [[ -z "${WORKSPACE_ROOT}" ]]; then + echo "Error: is required." >&2 + usage + exit 1 +fi + +# Counters for the final summary. +CREATED=0 +UPDATED=0 +SKIPPED=0 +FAILED=0 + +# One human-readable line per item, appended as we go, printed at the end. +declare -a REPORT_LINES=() + +record() { + # record + local status="$1" + local message="$2" + case "${status}" in + created) CREATED=$((CREATED + 1)) ;; + updated) UPDATED=$((UPDATED + 1)) ;; + skipped) SKIPPED=$((SKIPPED + 1)) ;; + failed) FAILED=$((FAILED + 1)) ;; + esac + REPORT_LINES+=("[${status}] ${message}") +} + +ensure_directory() { + # ensure_directory