diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index e444dd9..0000000 --- a/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -# Explicitly exclude agents-tools scripts from Git LFS auto-tracking. -# These are small shell scripts that must be stored as regular git objects. -scripts/agents-tools/** !filter !diff !merge text eol=lf diff --git a/.gitignore b/.gitignore index ca1a0a1..c71108e 100644 --- a/.gitignore +++ b/.gitignore @@ -293,3 +293,8 @@ pyrightconfig.json # agents-init --agents AGENTS/ # ──────────────────────────────────────────────────────── + +# ──────────────── Added by agents-init ────────────────── +# agents-init --plugins +docs/devlogs +# ──────────────────────────────────────────────────────── diff --git a/functions/agents-init.fish b/functions/agents-init.fish index a2ee4d5..10f0c7b 100644 --- a/functions/agents-init.fish +++ b/functions/agents-init.fish @@ -17,7 +17,18 @@ # or symlink → AGENTS.md (single-source case) # /AGENTS.md → AGENTS/AGENTS.md # /CLAUDE.md → AGENTS/CLAUDE.md -# docs/ → ../AGENTS/plugins/ +# AGENTS/plans superpowers plans (real dir, .gitkeep) +# AGENTS/specs superpowers specs (real dir, .gitkeep) +# AGENTS/devlogs agent development logs (real dir, .gitkeep) +# docs/superpowers/plans → ../../AGENTS/plans (always) +# docs/superpowers/specs → ../../AGENTS/specs (always) +# docs/plans → ../AGENTS/plans (only if docs/plans existed) +# docs/specs → ../AGENTS/specs (only if docs/specs existed) +# docs/devlogs → ../AGENTS/devlogs (only if docs/devlogs existed) +# +# plans/ and specs/ are merged from every legacy location (docs/, +# docs/superpowers/, and the old AGENTS/plugins/ layout) into the +# canonical AGENTS/; the AGENTS/plugins/ layer is removed. # # With no flags, runs both --agents and --plugins setup. At the end of # every invocation, commits any uncommitted changes in the AGENTS/ sub-repo @@ -25,7 +36,7 @@ # # ARGUMENTS # -a, --agents Set up AGENTS/ repo + AGENTS.md / CLAUDE.md symlinks only -# -p, --plugins Set up AGENTS/ repo + plugins dirs + docs/ symlinks only +# -p, --plugins Set up AGENTS/ repo + plans/specs/devlogs dirs + docs/ symlinks only # -v, --verbose Print all per-step output (default) # -q, --quiet Print one summary line only if changes were made # -s, --silent Suppress all output; errors only (standard UNIX convention) @@ -61,7 +72,7 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi echo "$c_head""Options:$c_reset" echo " $c_flag-h$c_reset, $c_flag--help$c_reset Show this help message" echo " $c_flag-a$c_reset, $c_flag--agents$c_reset Set up AGENTS.md / CLAUDE.md symlinks only" - echo " $c_flag-p$c_reset, $c_flag--plugins$c_reset Set up plugins dirs and docs/ symlinks only" + echo " $c_flag-p$c_reset, $c_flag--plugins$c_reset Set up plans/specs/devlogs dirs and docs/ symlinks only" echo " $c_flag-v$c_reset, $c_flag--verbose$c_reset Print all per-step output (default)" echo " $c_flag-q$c_reset, $c_flag--quiet$c_reset Print one summary line only if changes were made" echo " $c_flag-s$c_reset, $c_flag--silent$c_reset Suppress all output; only errors are printed" @@ -248,19 +259,6 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi # ─────────────────────────── --plugins mode ────────────────────────────── if test $do_plugins -eq 1 - # Ensure AGENTS/plugins/ dirs exist with .gitkeep - for dir in "$plugins_dir" "$plugins_dir/superpowers" "$plugins_dir/plans" "$plugins_dir/specs" - if not test -d "$dir" - if not mkdir -p "$dir" - echo "$c_err""Error: could not create "(string replace "$root/" "" "$dir")"/$c_reset" >&2 - return 1 - end - set changed 1 - test $verbose -eq 1; and echo "$c_ok→ Created "(string replace "$root/" "" "$dir")"/$c_reset" - end - test -f "$dir/.gitkeep"; or touch "$dir/.gitkeep" - end - set -l docs_dir "$root/docs" if not test -d "$docs_dir" @@ -272,39 +270,146 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi test $verbose -eq 1; and echo "$c_ok→ Created docs/$c_reset" end - for plug in superpowers plans specs - set -l docs_target "$docs_dir/$plug" - set -l agents_target "$plugins_dir/$plug" + # Consolidate plans/ and specs/ directly under AGENTS/ (no plugins/ layer). + # Real source dirs from any known layout are merged into the canonical + # AGENTS/ and removed: docs/, docs/superpowers/, + # AGENTS/plugins/, AGENTS/plugins/superpowers/ (legacy). + # Symlinks: docs/superpowers/ always (superpowers skills expect it); + # docs/ only when it had existed as a real directory. + for tgt in plans specs + set -l canonical "$agents_dir/$tgt" - # If a real directory exists in docs/, migrate its contents to AGENTS/plugins/ - if test -d "$docs_target"; and not test -L "$docs_target" - set -l contents (ls -A "$docs_target" 2>/dev/null) - if test (count $contents) -gt 0 - if not command cp -rn "$docs_target/." "$agents_target/" - echo "$c_err""Error: could not copy docs/$plug → AGENTS/plugins/$plug$c_reset" >&2 - return 1 - end - end - if not rm -rf "$docs_target" - echo "$c_err""Error: could not remove docs/$plug after copy$c_reset" >&2 - return 1 - end - set changed 1 - test $verbose -eq 1; and echo "$c_ok→ Moved docs/$plug → AGENTS/plugins/$plug$c_reset" + # Remember whether docs/ was a real dir (drives its symlink below) + set -l docs_tgt_was_real 0 + if test -d "$docs_dir/$tgt"; and not test -L "$docs_dir/$tgt" + set docs_tgt_was_real 1 end - # Create symlink docs/ → ../AGENTS/plugins/ - if not test -L "$docs_target" - if not ln -s "../AGENTS/plugins/$plug" "$docs_target" - echo "$c_err""Error: could not create docs/$plug symlink$c_reset" >&2 - return 1 + test -d "$canonical"; or mkdir -p "$canonical" + + # Candidate real source dirs to merge into the canonical dir + set -l srcs "$docs_dir/$tgt" "$plugins_dir/$tgt" "$plugins_dir/superpowers/$tgt" + # Only follow docs/superpowers/ when docs/superpowers is a real + # dir; if it is a legacy symlink, its target is already covered by the + # AGENTS/plugins/superpowers/ source above. + if not test -L "$docs_dir/superpowers" + set -a srcs "$docs_dir/superpowers/$tgt" + end + + for src in $srcs + if test -d "$src"; and not test -L "$src" + set -l rel (string replace "$root/" "" "$src") + set -l contents (command ls -A "$src" 2>/dev/null) + if test (count $contents) -gt 0 + if not command cp -rn "$src/." "$canonical/" + echo "$c_err""Error: could not merge $rel → AGENTS/$tgt$c_reset" >&2 + return 1 + end + end + if not rm -rf "$src" + echo "$c_err""Error: could not remove $rel after merge$c_reset" >&2 + return 1 + end + set changed 1 + test $verbose -eq 1; and echo "$c_ok→ Merged $rel → AGENTS/$tgt$c_reset" end + end + + test -f "$canonical/.gitkeep"; or touch "$canonical/.gitkeep" + + # docs/ symlink: only recreated when it had been a real directory + set -l docs_link "$docs_dir/$tgt" + if test $docs_tgt_was_real -eq 1 + if not test -L "$docs_link" + if not ln -s "../AGENTS/$tgt" "$docs_link" + echo "$c_err""Error: could not create docs/$tgt symlink$c_reset" >&2 + return 1 + end + set changed 1 + test $verbose -eq 1; and echo "$c_ok→ Linked docs/$tgt → AGENTS/$tgt$c_reset" + end + else if test -L "$docs_link"; and test (readlink "$docs_link") != "../AGENTS/$tgt" + # Stale legacy symlink (pointed into AGENTS/plugins) → drop it + rm -f "$docs_link" set changed 1 - test $verbose -eq 1; and echo "$c_ok→ Linked docs/$plug → AGENTS/plugins/$plug$c_reset" + test $verbose -eq 1; and echo "$c_warn→ Removed stale docs/$tgt symlink$c_reset" end end - set -l _gi (_agents_init_ensure_gitignore "$root" "agents-init --plugins" "docs/superpowers" "docs/plans" "docs/specs") + # docs/superpowers/ must be a real dir holding the plans/ + specs/ symlinks. + # Replace any legacy docs/superpowers symlink (content merged above). + if test -L "$docs_dir/superpowers" + rm -f "$docs_dir/superpowers" + set changed 1 + test $verbose -eq 1; and echo "$c_warn→ Removed legacy docs/superpowers symlink$c_reset" + end + if not test -d "$docs_dir/superpowers" + if not mkdir -p "$docs_dir/superpowers" + echo "$c_err""Error: could not create docs/superpowers/$c_reset" >&2 + return 1 + end + end + + # docs/superpowers/ symlinks — always present (superpowers default) + for tgt in plans specs + set -l sp_link "$docs_dir/superpowers/$tgt" + if not test -L "$sp_link" + if not ln -s "../../AGENTS/$tgt" "$sp_link" + echo "$c_err""Error: could not create docs/superpowers/$tgt symlink$c_reset" >&2 + return 1 + end + set changed 1 + test $verbose -eq 1; and echo "$c_ok→ Linked docs/superpowers/$tgt → AGENTS/$tgt$c_reset" + else if test (readlink "$sp_link") != "../../AGENTS/$tgt" + rm -f "$sp_link" + ln -s "../../AGENTS/$tgt" "$sp_link" + set changed 1 + test $verbose -eq 1; and echo "$c_ok→ Relinked docs/superpowers/$tgt → AGENTS/$tgt$c_reset" + end + end + + # Drop the now-empty legacy AGENTS/plugins/ layer entirely + if test -d "$plugins_dir" + rm -rf "$plugins_dir" + set changed 1 + test $verbose -eq 1; and echo "$c_warn→ Removed legacy AGENTS/plugins/$c_reset" + end + + # ── AGENTS/devlogs (standard location for agent dev logs) ───────────── + set -l devlogs_dir "$agents_dir/devlogs" + set -l docs_devlogs "$docs_dir/devlogs" + + if test -d "$docs_devlogs"; and not test -L "$docs_devlogs" + # Migrate an existing docs/devlogs into AGENTS/devlogs, then symlink + test -d "$devlogs_dir"; or mkdir -p "$devlogs_dir" + set -l contents (command ls -A "$docs_devlogs" 2>/dev/null) + if test (count $contents) -gt 0 + if not command cp -rn "$docs_devlogs/." "$devlogs_dir/" + echo "$c_err""Error: could not copy docs/devlogs → AGENTS/devlogs$c_reset" >&2 + return 1 + end + end + if not rm -rf "$docs_devlogs" + echo "$c_err""Error: could not remove docs/devlogs after copy$c_reset" >&2 + return 1 + end + if not ln -s "../AGENTS/devlogs" "$docs_devlogs" + echo "$c_err""Error: could not create docs/devlogs symlink$c_reset" >&2 + return 1 + end + set changed 1 + test $verbose -eq 1; and echo "$c_ok→ Moved docs/devlogs → AGENTS/devlogs$c_reset" + else if not test -d "$devlogs_dir" + if not mkdir -p "$devlogs_dir" + echo "$c_err""Error: could not create AGENTS/devlogs$c_reset" >&2 + return 1 + end + set changed 1 + test $verbose -eq 1; and echo "$c_ok→ Created AGENTS/devlogs/$c_reset" + end + test -f "$devlogs_dir/.gitkeep"; or touch "$devlogs_dir/.gitkeep" + + set -l _gi (_agents_init_ensure_gitignore "$root" "agents-init --plugins" "docs/superpowers" "docs/plans" "docs/specs" "docs/devlogs") if test -n "$_gi" set changed 1 test $verbose -eq 1; and echo $_gi diff --git a/scripts/agents-tools/test-version-bump.sh b/scripts/agents-tools/test-version-bump.sh index 942643d..c8f78ad 100644 --- a/scripts/agents-tools/test-version-bump.sh +++ b/scripts/agents-tools/test-version-bump.sh @@ -4,6 +4,9 @@ # # Self-contained tests for version-bump. Run: bash test-version-bump.sh set -u +# Neutralize an inherited CDPATH: bash `cd` echoes the resolved path when a +# target is found via CDPATH, which would corrupt the command substitutions below. +CDPATH= HERE="$(cd "$(dirname "$0")" && pwd)" SCRIPT="$HERE/version-bump" fail() { echo "FAIL: $1"; exit 1; } @@ -14,6 +17,9 @@ new_repo() { git -C "$d" config user.email t@t git -C "$d" config user.name t git -C "$d" config commit.gpgsign false + # Isolate from any global core.hooksPath so the test is hermetic. The e2e + # block re-points hooksPath at the repo's own .agents-tools/hooks. + git -C "$d" config core.hooksPath /dev/null printf '%s\n' "$d" }