fix(agents-init): only scaffold inside a git repository

Resolving the root to (pwd) meant running an agent CLI anywhere created an
AGENTS/ git repo, an AGENTS.md, two root symlinks, and a docs/ tree in that
directory. Scaffolding now requires a git repo or a pre-existing agent file
(AGENTS.md, CLAUDE.md, or AGENTS/); elsewhere it is a no-op.
This commit is contained in:
2026-09-16 19:06:43 -04:00
parent 6aebf5e189
commit 63288d9fd6
2 changed files with 40 additions and 2 deletions
+18 -2
View File
@@ -17,6 +17,11 @@
# agent-related files into it, and replaces them with symlinks so the outer # agent-related files into it, and replaces them with symlinks so the outer
# project never tracks agent files directly. # project never tracks agent files directly.
# #
# Scaffolding runs only inside a git repository, or in a directory that
# already has an AGENTS.md, CLAUDE.md, or AGENTS/. Elsewhere it is a
# no-op, so running an agent CLI in an arbitrary directory does not
# create a repository there.
#
# File layout after setup: # File layout after setup:
# AGENTS/AGENTS.md canonical agent spec (real file) # AGENTS/AGENTS.md canonical agent spec (real file)
# AGENTS/CLAUDE.md real file (if CLAUDE.md existed separately) # AGENTS/CLAUDE.md real file (if CLAUDE.md existed separately)
@@ -127,9 +132,20 @@ function agents-init --description 'scaffold AGENTS/ sub-repo with agent spec fi
end end
# --verbose is explicit default; no-op but accepted for completeness # --verbose is explicit default; no-op but accepted for completeness
# Resolve target root: git root if available, otherwise cwd # Only scaffold inside a git repository, or where an agent file already
# exists. Falling back to (pwd) meant `claude --version` in any
# directory created an AGENTS/ repo, two root symlinks, and a docs/
# tree there.
set -l root (git rev-parse --show-toplevel 2>/dev/null) set -l root (git rev-parse --show-toplevel 2>/dev/null)
test -z "$root"; and set root (pwd) if test -z "$root"
if test -e (pwd)/AGENTS.md -o -e (pwd)/CLAUDE.md -o -d (pwd)/AGENTS
set root (pwd)
else
test $verbose -eq 1
and echo "$c_dim→ Not a git repository; skipping AGENTS/ scaffolding$c_reset"
return 0
end
end
set -l agents_dir "$root/AGENTS" set -l agents_dir "$root/AGENTS"
set -l plugins_dir "$agents_dir/plugins" set -l plugins_dir "$agents_dir/plugins"
+22
View File
@@ -2053,6 +2053,28 @@ check "agents-init: a rejected commit returns non-zero" 1 "$ibrc"
check "agents-init: a rejected commit says nothing was recorded" true (string match -q '*nothing recorded*' -- (cat $ierr); and echo true; or echo false) check "agents-init: a rejected commit says nothing was recorded" true (string match -q '*nothing recorded*' -- (cat $ierr); and echo true; or echo false)
check "agents-init: a rejected commit really recorded nothing" $ibhead (git -C $ip/AGENTS rev-list --count HEAD) check "agents-init: a rejected commit really recorded nothing" $ibhead (git -C $ip/AGENTS rev-list --count HEAD)
# ─────────────────────── scaffolding guard ──────────────────────────────
# Resolving the root to (pwd) meant running an agent CLI in any directory
# created an AGENTS/ repo, root symlinks and a docs/ tree there.
echo ""
echo "== agents-init scaffolding guard =="
set -l plain (mktemp -d)
set -ga TMPDIRS $plain
pushd $plain >/dev/null
agents-init --silent 2>/dev/null
popd >/dev/null
check "no scaffold in a non-git dir" false (test -e $plain/AGENTS; and echo true; or echo false)
# A pre-existing agent file still opts the directory in.
set -l plainagents (mktemp -d)
set -ga TMPDIRS $plainagents
touch $plainagents/AGENTS.md
pushd $plainagents >/dev/null
agents-init --silent 2>/dev/null
popd >/dev/null
check "an existing AGENTS.md still scaffolds" true (test -d $plainagents/AGENTS; and echo true; or echo false)
# ──────────────────────── hermeticity assertion ──────────────────────── # ──────────────────────── hermeticity assertion ────────────────────────
# The whole suite must never have touched the real global agent state. The # The whole suite must never have touched the real global agent state. The
# failure this guards is specific: a global-memory sync with no test # failure this guards is specific: a global-memory sync with no test