diff --git a/scripts/README.md b/scripts/README.md index d0ae191..3fbf89f 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -74,9 +74,43 @@ 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) +## `validate-ai-workspace.sh` (AAASM-3944) -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. +Checks an installed workspace root (and, optionally, one or more repo +checkouts inside it) against `.claude/WORKSPACE.md`'s expected layout, +reporting missing files, broken symlinks, and visible local overrides. + +### Usage + +```bash +./scripts/validate-ai-workspace.sh ~/ai-agent-assembly +``` + +Add repo paths to also get a lightweight, informational check for each repo's +own `.claude/CLAUDE.md` and `AGENTS.md`: + +```bash +./scripts/validate-ai-workspace.sh ~/ai-agent-assembly ~/ai-agent-assembly/agent-assembly ~/ai-agent-assembly/python-sdk +``` + +### What it checks + +For each of `CLAUDE.md`, `AGENTS.md`, `.claude/rules/`, and `.claude/skills/` +at the workspace root, it reports one of: + +- **Missing** — the item does not exist. +- **Broken symlink** — the item is a symlink whose target does not exist. +- **Local override** — the item exists but isn't a symlink back to a + recognized `.github` clone (a contributor's intentional local override, per + the override model in `.claude/WORKSPACE.md`). Reported for visibility, not + as an error. +- **OK** — correctly symlinked back to a `.github` clone. + +It also sweeps the workspace's `.claude/` tree for any other broken symlinks +(e.g. under `.claude/commands/`). + +### Exit codes + +`0` if everything is present and either correctly symlinked or a recognized +local override (overrides do not fail validation). `1` if anything is missing +or a symlink is broken. Safe to wire into CI as a gate. diff --git a/scripts/validate-ai-workspace.sh b/scripts/validate-ai-workspace.sh new file mode 100755 index 0000000..0c92971 --- /dev/null +++ b/scripts/validate-ai-workspace.sh @@ -0,0 +1,216 @@ +#!/usr/bin/env bash +# +# validate-ai-workspace.sh — check an installed AI onboarding workspace root +# (and, optionally, one or more org repo checkouts) against the layout +# defined in .claude/WORKSPACE.md (AAASM-3939) and installed by +# bootstrap-ai-workspace.sh (AAASM-3943). +# +# Usage: +# ./scripts/validate-ai-workspace.sh [repo-path...] +# +# Checks the workspace root for CLAUDE.md, AGENTS.md, .claude/rules/, and +# .claude/skills/, classifying each as: missing, a broken symlink, a local +# override (present but not a symlink back to a recognized .github clone), +# or correctly symlinked (OK). Local overrides are reported for visibility +# but do not fail validation — see the override model in .claude/WORKSPACE.md. +# +# Any extra repo-path arguments get a lightweight, informational check for +# their own `.claude/CLAUDE.md` and `AGENTS.md` (repo-level context gaps do +# not affect the exit code — this script validates the workspace scaffold, +# not repo-level completeness). +# +# Exit codes: +# 0 - every expected workspace item is present and either correctly +# symlinked or a recognized local override. Safe to use as a CI gate. +# 1 - at least one expected item is missing, or a symlink is broken. +# +# This is a plain local script: no network calls, only filesystem checks +# and local (non-network) `git remote -v` reads. + +set -euo pipefail + +usage() { + echo "Usage: $(basename "${BASH_SOURCE[0]}") [repo-path...]" >&2 +} + +if [[ $# -lt 1 ]]; then + echo "Error: is required." >&2 + usage + exit 1 +fi + +case "$1" in + -h|--help) + usage + exit 0 + ;; +esac + +WORKSPACE_ROOT="$1" +shift +declare -a REPO_PATHS=("$@") + +# --- Report buckets ---------------------------------------------------------- +# bash 3.2 (macOS default) throws "unbound variable" under `set -u` when +# expanding an array that has zero elements, so every expansion of these is +# guarded with an `${#arr[@]} -gt 0` length check before use. +declare -a MISSING=() +declare -a BROKEN=() +declare -a OVERRIDES=() +declare -a OK=() +declare -a REPO_NOTES=() + +# --- Helpers ------------------------------------------------------------- + +# is_dot_github_clone +# True if is inside a git checkout whose configured remotes reference +# the ai-agent-assembly/.github repo. `git remote -v` only reads local repo +# config; it makes no network calls. +is_dot_github_clone() { + local dir="$1" + local git_root + git_root="$(cd "${dir}" 2>/dev/null && git rev-parse --show-toplevel 2>/dev/null)" || return 1 + [[ -n "${git_root}" ]] || return 1 + git -C "${git_root}" remote -v 2>/dev/null | grep -qiE 'ai-agent-assembly/\.github(\.git)?' +} + +# check_item