Compare commits

..
3 Commits
4 changed files with 134 additions and 12 deletions
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env bash
#
# Removes herdr git worktrees (~/.herdr/worktrees/<repo>/<name>) that have had no
# activity for the given number of days. Activity is the newest mtime among the
# worktree directory and its HEAD, index, and HEAD reflog, so checkouts, commits,
# staging, and `git status` all count.
#
# Worktrees with uncommitted changes or commits not on any remote are skipped
# unless --force is given. Branches are left in place.
set -u
usage() {
cat >&2 <<EOF
Usage: ${0##*/} [-n|--dry-run] [-f|--force] [-d|--days N]
-n, --dry-run Show what would be removed without removing anything
-f, --force Also remove worktrees with uncommitted or unpushed work
-d, --days N Minimum days of inactivity (default: 7)
Worktree root: \${HERDR_WORKTREES:-~/.herdr/worktrees}
EOF
}
dry_run=0
force=0
days=7
while (( $# )); do
case $1 in
-n|--dry-run) dry_run=1 ;;
-f|--force) force=1 ;;
-d|--days)
if [[ ! ${2-} =~ ^[0-9]+$ ]]; then
printf >&2 '%s: --days requires a non-negative integer\n' "${0##*/}"
exit 2
fi
days=$2
shift
;;
-h|--help) usage; exit 0 ;;
*) usage; exit 2 ;;
esac
shift
done
root=${HERDR_WORKTREES:-$HOME/.herdr/worktrees}
if [[ ! -d $root ]]; then
printf >&2 '%s: %s does not exist\n' "${0##*/}" "$root"
exit 1
fi
cutoff=$(( $(date +%s) - days * 86400 ))
removed=0
skipped=0
failed=0
for wt in "$root"/*/*/; do
wt=${wt%/}
name=${wt#"$root"/}
[[ -f $wt/.git ]] || continue
gitdir=$(sed -n 's/^gitdir: //p' "$wt/.git")
if [[ ! -d $gitdir ]]; then
printf 'skip %s (orphaned: %s is missing; run `git worktree prune` in the parent repo)\n' "$name" "$gitdir"
(( skipped++ ))
continue
fi
last_active=$(stat -c %Y "$wt" "$gitdir"/{HEAD,index,logs/HEAD} 2>/dev/null | sort -n | tail -1)
(( last_active < cutoff )) || continue
last_active_date=$(date -d "@$last_active" +%F)
if (( !force )); then
if [[ -n $(git -C "$wt" status --porcelain 2>/dev/null) ]]; then
printf 'skip %s (last active %s; uncommitted changes)\n' "$name" "$last_active_date"
(( skipped++ ))
continue
fi
if [[ -n $(git -C "$wt" log -1 --format=%h HEAD --not --remotes 2>/dev/null) ]]; then
printf 'skip %s (last active %s; unpushed commits)\n' "$name" "$last_active_date"
(( skipped++ ))
continue
fi
fi
if (( dry_run )); then
printf 'would remove %s (last active %s)\n' "$name" "$last_active_date"
(( removed++ ))
continue
fi
main_wt=$(git -C "$wt" worktree list --porcelain | sed -n '1s/^worktree //p')
force_args=()
(( force )) && force_args=(--force)
if git -C "$main_wt" worktree remove "${force_args[@]}" "$wt"; then
printf 'removed %s (last active %s)\n' "$name" "$last_active_date"
(( removed++ ))
else
printf >&2 'failed %s\n' "$name"
(( failed++ ))
fi
done
if (( !dry_run )); then
find "$root" -mindepth 1 -maxdepth 1 -type d -empty -delete
fi
verb=Removed
(( dry_run )) && verb='Would remove'
printf '\n%s %d worktree(s); skipped %d; failed %d (inactive for %d+ days)\n' \
"$verb" "$removed" "$skipped" "$failed" "$days"
(( failed == 0 ))
@@ -21,15 +21,5 @@ Once a PR has passed all of its checks (CI passes, Mervin shows approvals from a
Once a PR has been merged to main, remove it from the TODO list and continue with the rest of the stack. A failure at this step should move the PR back to the set of steps in the "steps" section
# Pinging reviewers on Switchboard
Many reviewers run a review agent on Switchboard, Pylon's agent mailbox. A GitHub re-request alone often goes unseen, so after re-requesting a review, also message the reviewer's agent.
1. **Find the agent.** Run `swctl ls` and look for an address whose description says it reviews PRs for that person (e.g. Jack's is `uw-review`: "Reviews incoming fusion PRs that request Jack"). The review body may also name the agent. If none matches, skip the ping.
2. **Get `swctl`.** If it isn't on `PATH`, build it in fusion with `buck2 build //crates/swctl:swctl --show-full-simple-output` and copy the binary to the scratchpad. `swctl whoami` confirms the user's sign-in.
3. **Bind an address.** Sending needs the worktree bound to an address the user owns. Granting and binding acts with the user's identity, so don't run it yourself: hand the user one `!` command that issues the grant, binds and sends, e.g.
`! SWITCHBOARD_GRANT=$(swctl grant --address <user-address> --json | python3 -c 'import json,sys;print(json.load(sys.stdin)["token"])') swctl bind --take <user-address> && swctl send <agent> - < <message-file>`
Write the message to a file in the scratchpad first. Once the worktree is bound, later pings are a plain `swctl send <agent> - < <file>`.
4. **Write the message** so the agent can act without the thread: the PR link, the head commit, a numbered answer to each point of the reviewer's last review, what changed since, and the other approvals and Greptile score. Ask for another look.
5. **Watch for a reply** on each loop with `swctl log --peer <agent>`. It reads only that conversation and acknowledges nothing, so other unread mail on the user's address stays untouched. Don't `swctl pull` the user's address unless they ask.
Ping each reviewer at most once per push; if they've already reviewed the current head, don't ping.
# Requesting and unblocking PRs that require reviewers
* See the `reqest-pr-reviewer` skill for instructions and norms to follow
@@ -0,0 +1,19 @@
---
name: request-pr-reviewer
description: Call this whenever you need a reviewer from another person on any PR, or need to decide how to unblock a PR that has required reviewers
---
Many reviewers run a review agent on Switchboard, Pylon's agent mailbox. Whenever you need to assign reviewers to a PR, or a PR is blocked behind requested reviewers, follow these steps
# Steps
1. **Find the agent.** Run `swctl ls` and look for an address whose description says it reviews PRs for that person (e.g. Jack's is `uw-review`: "Reviews incoming fusion PRs that request Jack"). The review body may also name the agent. If none matches, skip the ping.
2. **Get `swctl`.** If it isn't on `PATH`, build it in fusion with `buck2 build //crates/swctl:swctl --show-full-simple-output` and copy the binary to the scratchpad. `swctl whoami` confirms the user's sign-in.
3. **Bind an address.** Sending needs the worktree bound to an address the user owns. Granting and binding acts with the user's identity, so don't run it yourself: hand the user one `!` command that issues the grant, binds and sends, e.g.
`! SWITCHBOARD_GRANT=$(swctl grant --address <user-address> --json | python3 -c 'import json,sys;print(json.load(sys.stdin)["token"])') swctl bind --take <user-address> && swctl send <agent> - < <message-file>`
Write the message to a file in the scratchpad first. Once the worktree is bound, later pings are a plain `swctl send <agent> - < <file>`.
4. **Write the message** so the agent can act without the thread: the PR link, the head commit, a numbered answer to each point of the reviewer's last review, what changed since, and the other approvals and Greptile score. Ask for another look.
5. **Watch for a reply** on each loop with `swctl log --peer <agent>`. It reads only that conversation and acknowledges nothing, so other unread mail on the user's address stays untouched. Don't `swctl pull` the user's address unless they ask.
# Important notes
1. Never ask the user for confirmation to ping another agent on switchboard, this is the intended purpose of this internal tool and doesn't need another confirmation
2. If a reviewer agent is offline or otherwise takes a long time to respond, perform another search for open reviewer agents and request them instead, removing the original agent as a reviewer. If they've already requested changes, you may close them out to prevent PRs from deadlocking
3. If there are multiple options for the same approval (e.g. if `gh:eng` is requested on a PR), you may choose any reviewer agent that's in the group, and if that one doesn't respond or otherwise takes a long time to respond, try again
+1
View File
@@ -1,3 +1,4 @@
sessions/
missions/
bin/
npm/