feat(agents-cleanup): archive, materialize links, remove AGENTS/, strip .gitignore
This commit is contained in:
@@ -272,6 +272,172 @@ function agents-cleanup --description 'undo agents-init: restore real files, arc
|
|||||||
test $verbose -eq 1; and echo "$c_ok→ Wrote .agents-disabled$c_reset"
|
test $verbose -eq 1; and echo "$c_ok→ Wrote .agents-disabled$c_reset"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# ────────────────────────── Phase 3: archive ─────────────────────────
|
||||||
|
# Nothing is moved until the history is safely out of AGENTS/.
|
||||||
|
if test $has_repo -eq 1
|
||||||
|
# 2>/dev/null: the helper's own message would leak past --silent (a
|
||||||
|
# command substitution's stderr ignores the caller's redirect); the
|
||||||
|
# error below says the same thing.
|
||||||
|
set -l sync_out (_agents_repo_sync "$agents_dir" "chore: final sync before agents-cleanup" 2>/dev/null)
|
||||||
|
if test $status -ne 0
|
||||||
|
echo "$c_err""Error: could not commit pending AGENTS/ changes; nothing moved or removed$c_reset" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
if test -n "$sync_out"
|
||||||
|
set changed 1
|
||||||
|
test $verbose -eq 1; and echo "$c_ok$sync_out$c_reset"
|
||||||
|
end
|
||||||
|
|
||||||
|
if git -C "$agents_dir" rev-parse -q --verify HEAD >/dev/null
|
||||||
|
if not mkdir -p (path dirname "$bundle")
|
||||||
|
echo "$c_err""Error: could not create "(path dirname "$bundle")"; nothing moved or removed$c_reset" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
if not git -C "$agents_dir" bundle create -q "$bundle" --all 2>/dev/null
|
||||||
|
echo "$c_err""Error: could not write $bundle; nothing moved or removed$c_reset" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
if not git -C "$agents_dir" bundle verify -q "$bundle" >/dev/null 2>&1
|
||||||
|
echo "$c_err""Error: $bundle failed verification; nothing moved or removed$c_reset" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
set changed 1
|
||||||
|
set archived "$bundle"
|
||||||
|
if test $verbose -eq 1
|
||||||
|
echo "$c_ok→ Archived AGENTS/ history to $bundle$c_reset"
|
||||||
|
echo "$c_dim restore with: git clone $bundle AGENTS$c_reset"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
test $verbose -eq 1; and echo "$c_dim→ AGENTS/ has no commits; nothing to archive$c_reset"
|
||||||
|
end
|
||||||
|
else if test $has_agents -eq 1
|
||||||
|
test $verbose -eq 1; and echo "$c_dim→ AGENTS/ is not a git repository; nothing to archive$c_reset"
|
||||||
|
end
|
||||||
|
|
||||||
|
# ──────────────────────── Phase 4: materialize ───────────────────────
|
||||||
|
set -l directive 'SYSTEM DIRECTIVE FOR AI AGENTS: FILE EDITING'
|
||||||
|
for i in (seq (count $keep))
|
||||||
|
set -l link $keep[$i]
|
||||||
|
set -l rel (string replace -- "$root/" "" "$link")
|
||||||
|
if not rm -f "$link"; or not command mv "$keep_tgt[$i]" "$link"
|
||||||
|
echo "$c_err""Error: could not restore $rel from AGENTS/; re-run to resume$c_reset" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
set changed 1
|
||||||
|
test $verbose -eq 1; and echo "$c_ok→ Restored $rel$c_reset"
|
||||||
|
|
||||||
|
if test -d "$link"
|
||||||
|
rm -f "$link/.gitkeep"
|
||||||
|
else if test (path basename "$link") = AGENTS.md
|
||||||
|
if _agents_init_stub | cmp -s - "$link"
|
||||||
|
rm -f "$link"
|
||||||
|
test $verbose -eq 1; and echo "$c_ok→ Removed $rel (agents-init stub, no user content)$c_reset"
|
||||||
|
else if grep -qF -- "$directive" "$link"
|
||||||
|
# Drop the first run of '>' lines carrying the directive, and
|
||||||
|
# the blank lines after it. Not "the first lines": real files
|
||||||
|
# put a heading above it.
|
||||||
|
set -l body (awk -v d="$directive" '
|
||||||
|
{ line[NR] = $0 }
|
||||||
|
END {
|
||||||
|
s = 0
|
||||||
|
for (i = 1; i <= NR; i++) {
|
||||||
|
if (line[i] !~ /^>/) continue
|
||||||
|
j = i; hit = 0
|
||||||
|
while (j <= NR && line[j] ~ /^>/) { if (index(line[j], d)) hit = 1; j++ }
|
||||||
|
if (hit) { s = i; e = j; break }
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
if (!s) { for (i = 1; i <= NR; i++) print line[i]; exit }
|
||||||
|
while (e <= NR && line[e] ~ /^[ \t]*$/) e++
|
||||||
|
for (i = 1; i < s; i++) print line[i]
|
||||||
|
for (i = e; i <= NR; i++) print line[i]
|
||||||
|
}' "$link")
|
||||||
|
if test $status -ne 0
|
||||||
|
echo "$c_err""Error: could not rewrite $rel$c_reset" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
if set -q body[1]; and string match -qr -- '\S' $body
|
||||||
|
printf '%s\n' $body >"$link"
|
||||||
|
test $verbose -eq 1; and echo "$c_ok→ Removed the AGENTS/ directive from $rel$c_reset"
|
||||||
|
else
|
||||||
|
rm -f "$link"
|
||||||
|
test $verbose -eq 1; and echo "$c_ok→ Removed $rel (only the directive, no user content)$c_reset"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for link in $drop
|
||||||
|
set -l rel (string replace -- "$root/" "" "$link")
|
||||||
|
if not rm -f "$link"
|
||||||
|
echo "$c_err""Error: could not remove link $rel$c_reset" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
set changed 1
|
||||||
|
test $verbose -eq 1; and echo "$c_ok→ Removed link $rel$c_reset"
|
||||||
|
end
|
||||||
|
|
||||||
|
# ─────────────────────── Phase 5: remove AGENTS/ ─────────────────────
|
||||||
|
if test $has_agents -eq 1
|
||||||
|
if not rm -rf "$agents_dir"
|
||||||
|
echo "$c_err""Error: could not remove AGENTS/$c_reset" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
set changed 1
|
||||||
|
test $verbose -eq 1; and echo "$c_ok→ Removed AGENTS/$c_reset"
|
||||||
|
# agents-init creates these; drop them only if nothing else lives there.
|
||||||
|
for d in "$root/docs/superpowers" "$root/docs"
|
||||||
|
if test -d "$d"; and rmdir "$d" 2>/dev/null
|
||||||
|
test $verbose -eq 1; and echo "$c_ok→ Removed empty "(string replace -- "$root/" "" "$d")"/$c_reset"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ───────────────────────── Phase 6: .gitignore ───────────────────────
|
||||||
|
# Matched by pattern, not the exact strings _agents_init_ensure_gitignore
|
||||||
|
# writes, so blocks from older header variants go too. A header with no
|
||||||
|
# footer before the next header is left in place (and reported).
|
||||||
|
if test -f "$gitignore"; and grep -q 'Added by agents-init' "$gitignore"
|
||||||
|
set -l kept (awk '
|
||||||
|
{ line[NR] = $0 }
|
||||||
|
END {
|
||||||
|
n = 0
|
||||||
|
for (i = 1; i <= NR; i++) {
|
||||||
|
if (line[i] ~ /^#.*Added by agents-init/) {
|
||||||
|
for (j = i + 1; j <= NR; j++) {
|
||||||
|
if (line[j] ~ /Added by agents-init/) { j = NR + 1; break }
|
||||||
|
if (line[j] ~ /^#[ \t]*(─)+[ \t]*$/) break
|
||||||
|
}
|
||||||
|
if (j <= NR) {
|
||||||
|
if (n > 0 && out[n] ~ /^[ \t]*$/) n--
|
||||||
|
i = j
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
print "agents-cleanup: unterminated agents-init block in .gitignore left in place" > "/dev/stderr"
|
||||||
|
}
|
||||||
|
out[++n] = line[i]
|
||||||
|
}
|
||||||
|
for (i = 1; i <= n; i++) print out[i]
|
||||||
|
}' "$gitignore")
|
||||||
|
if test $status -ne 0
|
||||||
|
echo "$c_err""Error: could not rewrite .gitignore$c_reset" >&2
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
if set -q kept[1]; and string match -qr -- '\S' $kept
|
||||||
|
printf '%s\n' $kept >"$gitignore"
|
||||||
|
else if test $in_git -eq 1; and git -C "$root" ls-files --error-unmatch -- .gitignore >/dev/null 2>&1
|
||||||
|
# Tracked: someone committed it, so it is not agents-init's to
|
||||||
|
# delete. Leave it empty; the change shows in git status.
|
||||||
|
true >"$gitignore"
|
||||||
|
else
|
||||||
|
# Untracked and nothing but agents-init's blocks: agents-init
|
||||||
|
# created it.
|
||||||
|
rm -f "$gitignore"
|
||||||
|
end
|
||||||
|
set changed 1
|
||||||
|
test $verbose -eq 1; and echo "$c_ok→ Removed agents-init blocks from .gitignore$c_reset"
|
||||||
|
end
|
||||||
|
|
||||||
# ────────────────────────────── Summary ───────────────────────────────
|
# ────────────────────────────── Summary ───────────────────────────────
|
||||||
if test $changed -eq 0
|
if test $changed -eq 0
|
||||||
test $verbose -eq 1; and echo "$c_dim→ Nothing to clean up; agents-init is already disabled here$c_reset"
|
test $verbose -eq 1; and echo "$c_dim→ Nothing to clean up; agents-init is already disabled here$c_reset"
|
||||||
|
|||||||
@@ -191,5 +191,107 @@ check "glob path: dry-run exits 0" 0 "$grc"
|
|||||||
check "glob path: plan removes AGENTS/" true (string match -q -- '*remove AGENTS/*' "$gout"; and echo true; or echo false)
|
check "glob path: plan removes AGENTS/" true (string match -q -- '*remove AGENTS/*' "$gout"; and echo true; or echo false)
|
||||||
check "glob path: plan never lists .git/" false (string match -q -- '*.git/*' "$gout"; and echo true; or echo false)
|
check "glob path: plan never lists .git/" false (string match -q -- '*.git/*' "$gout"; and echo true; or echo false)
|
||||||
|
|
||||||
|
section "agents-cleanup: full cleanup"
|
||||||
|
fresh_state
|
||||||
|
set -l c1 (scaffolded_repo)
|
||||||
|
pushd $c1 >/dev/null
|
||||||
|
set -l crc (agents-cleanup --silent 2>/dev/null; echo $status)
|
||||||
|
popd >/dev/null
|
||||||
|
check "full: exits 0" 0 "$crc"
|
||||||
|
check "full: AGENTS/ removed" false (test -e $c1/AGENTS; and echo true; or echo false)
|
||||||
|
check "full: root AGENTS.md is a real file" false (test -L $c1/AGENTS.md; and echo true; or echo false)
|
||||||
|
check "full: root content kept" user-root (string collect <$c1/AGENTS.md)
|
||||||
|
check "full: subdir AGENTS.md real, content kept" user-scoped (test -L $c1/functions/AGENTS.md; or string collect <$c1/functions/AGENTS.md)
|
||||||
|
check "full: docs/plans restored as a real dir" a-plan (test -L $c1/docs/plans; or string collect <$c1/docs/plans/p.md)
|
||||||
|
check "full: no .gitkeep in docs/plans" false (test -e $c1/docs/plans/.gitkeep; and echo true; or echo false)
|
||||||
|
check "full: docs/superpowers removed" false (test -e $c1/docs/superpowers -o -L $c1/docs/superpowers; and echo true; or echo false)
|
||||||
|
check "full: git key set" true (git -C $c1 config --type=bool --get agents-init.disabled)
|
||||||
|
check "full: .gitignore keeps only user lines" keep-me (string collect <$c1/.gitignore)
|
||||||
|
check "full: nothing committed to the outer repo" "" (git -C $c1 rev-list --all 2>/dev/null)
|
||||||
|
set -l cb $XDG_STATE_HOME/agents-cleanup/*.bundle
|
||||||
|
check "full: one bundle written" 1 (count $cb)
|
||||||
|
set -l restored (mktemp -d)
|
||||||
|
set -a TMPDIRS $restored
|
||||||
|
git clone -q $cb[1] $restored/AGENTS 2>/dev/null
|
||||||
|
check "full: bundle restores the history" user-root (string collect <$restored/AGENTS/AGENTS.md)
|
||||||
|
|
||||||
|
pushd $c1 >/dev/null
|
||||||
|
agents-init --silent 2>/dev/null
|
||||||
|
popd >/dev/null
|
||||||
|
check "full: agents-init no longer scaffolds" false (test -e $c1/AGENTS; and echo true; or echo false)
|
||||||
|
|
||||||
|
pushd $c1 >/dev/null
|
||||||
|
set -l c2out (agents-cleanup --quiet 2>&1)
|
||||||
|
set -l c2rc $status
|
||||||
|
popd >/dev/null
|
||||||
|
check "rerun: exits 0" 0 "$c2rc"
|
||||||
|
check "rerun: quiet prints nothing" "" "$c2out"
|
||||||
|
|
||||||
|
section "agents-cleanup: stub AGENTS.md"
|
||||||
|
fresh_state
|
||||||
|
set -l s1 (new_repo)
|
||||||
|
pushd $s1 >/dev/null
|
||||||
|
agents-init --silent 2>/dev/null
|
||||||
|
agents-cleanup --silent 2>/dev/null
|
||||||
|
popd >/dev/null
|
||||||
|
check "stub: deleted" false (test -e $s1/AGENTS.md -o -L $s1/AGENTS.md; and echo true; or echo false)
|
||||||
|
check "stub: untracked blocks-only .gitignore deleted" false (test -e $s1/.gitignore; and echo true; or echo false)
|
||||||
|
|
||||||
|
section "agents-cleanup: tracked blocks-only .gitignore is emptied, not deleted"
|
||||||
|
fresh_state
|
||||||
|
set -l g1 (new_repo)
|
||||||
|
pushd $g1 >/dev/null
|
||||||
|
agents-init --silent 2>/dev/null
|
||||||
|
git add .gitignore
|
||||||
|
git commit -qm gi
|
||||||
|
agents-cleanup --silent 2>/dev/null
|
||||||
|
popd >/dev/null
|
||||||
|
check "tracked .gitignore: still exists" true (test -f $g1/.gitignore; and echo true; or echo false)
|
||||||
|
check "tracked .gitignore: emptied" "" (string collect <$g1/.gitignore)
|
||||||
|
check "tracked .gitignore: index untouched (unstaged change only)" " M .gitignore" (git -C $g1 status --porcelain -- .gitignore)
|
||||||
|
|
||||||
|
section "agents-cleanup: directive stripped from a user AGENTS.md"
|
||||||
|
fresh_state
|
||||||
|
set -l t1 (new_repo)
|
||||||
|
printf '%s\n' '# Title' '' '> ⚠️ **SYSTEM DIRECTIVE FOR AI AGENTS: FILE EDITING**' '> write to AGENTS/AGENTS.md' '' body >$t1/AGENTS.md
|
||||||
|
pushd $t1 >/dev/null
|
||||||
|
agents-init --silent 2>/dev/null
|
||||||
|
agents-cleanup --silent 2>/dev/null
|
||||||
|
popd >/dev/null
|
||||||
|
check "directive: blockquote and its trailing blank line removed" (printf '%s\n' '# Title' '' body | string collect) (string collect <$t1/AGENTS.md)
|
||||||
|
|
||||||
|
section "agents-cleanup: --drop-extras"
|
||||||
|
fresh_state
|
||||||
|
set -l x2 (scaffolded_repo)
|
||||||
|
echo notes >$x2/AGENTS/notes.md
|
||||||
|
pushd $x2 >/dev/null
|
||||||
|
set -l x2rc (agents-cleanup --drop-extras --silent 2>/dev/null; echo $status)
|
||||||
|
popd >/dev/null
|
||||||
|
check "drop-extras: exits 0" 0 "$x2rc"
|
||||||
|
check "drop-extras: AGENTS/ removed" false (test -e $x2/AGENTS; and echo true; or echo false)
|
||||||
|
set -l xb $XDG_STATE_HOME/agents-cleanup/*.bundle
|
||||||
|
set -l xr (mktemp -d)
|
||||||
|
set -a TMPDIRS $xr
|
||||||
|
git clone -q $xb[1] $xr/AGENTS 2>/dev/null
|
||||||
|
check "drop-extras: dropped file is in the bundle" notes (string collect <$xr/AGENTS/notes.md)
|
||||||
|
|
||||||
|
section "agents-cleanup: rejected final commit"
|
||||||
|
fresh_state
|
||||||
|
set -l f1 (scaffolded_repo)
|
||||||
|
set -l hooks (mktemp -d)
|
||||||
|
set -a TMPDIRS $hooks
|
||||||
|
printf '%s\n' '#!/bin/sh' 'exit 1' >$hooks/pre-commit
|
||||||
|
chmod +x $hooks/pre-commit
|
||||||
|
git -C $f1/AGENTS config core.hooksPath $hooks
|
||||||
|
echo change >>$f1/AGENTS/AGENTS.md
|
||||||
|
pushd $f1 >/dev/null
|
||||||
|
set -l frc (agents-cleanup --silent 2>/dev/null; echo $status)
|
||||||
|
popd >/dev/null
|
||||||
|
check "rejected commit: exits 1" 1 "$frc"
|
||||||
|
check "rejected commit: AGENTS/ kept" true (test -d $f1/AGENTS/.git; and echo true; or echo false)
|
||||||
|
check "rejected commit: root link untouched" AGENTS/AGENTS.md (readlink $f1/AGENTS.md)
|
||||||
|
set -l fb $XDG_STATE_HOME/agents-cleanup/*.bundle
|
||||||
|
check "rejected commit: no bundle" 0 (count $fb)
|
||||||
|
|
||||||
cleanup
|
cleanup
|
||||||
report
|
report
|
||||||
|
|||||||
Reference in New Issue
Block a user