@seanyao/roll 2026.514.3 → 2026.514.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -0
- package/bin/roll +84 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -62,6 +62,7 @@ roll loop on # optional: let the agent work unattended
|
|
|
62
62
|
| Loop (autonomous executor) | [guide/en/loop.md](docs/guide/en/loop.md) | [guide/zh/loop.md](docs/guide/zh/loop.md) |
|
|
63
63
|
| Dream (nightly health scan) | [guide/en/dream.md](docs/guide/en/dream.md) | [guide/zh/dream.md](docs/guide/zh/dream.md) |
|
|
64
64
|
| Peer (cross-agent review) | [guide/en/peer.md](docs/guide/en/peer.md) | [guide/zh/peer.md](docs/guide/zh/peer.md) |
|
|
65
|
+
| Configuration (env vars) | [guide/en/configuration.md](docs/guide/en/configuration.md) | [guide/zh/configuration.md](docs/guide/zh/configuration.md) |
|
|
65
66
|
| Skill selection guide | [guide/en/skills.md](docs/guide/en/skills.md) | [guide/zh/skills.md](docs/guide/zh/skills.md) |
|
|
66
67
|
| Domain model (DDD) | [domain/context-map.md](docs/domain/context-map.md) | — |
|
|
67
68
|
| Engineering common sense | [practices/engineering-common-sense.md](docs/practices/engineering-common-sense.md) | — |
|
package/bin/roll
CHANGED
|
@@ -4,7 +4,7 @@ set -euo pipefail
|
|
|
4
4
|
# Roll — AI Agent Convention Manager
|
|
5
5
|
# Single source of truth for how all AI coding agents behave.
|
|
6
6
|
|
|
7
|
-
VERSION="2026.514.
|
|
7
|
+
VERSION="2026.514.5"
|
|
8
8
|
ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
|
|
9
9
|
ROLL_CONFIG="${ROLL_HOME}/config.yaml"
|
|
10
10
|
ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
|
|
@@ -465,7 +465,9 @@ _link_skills() {
|
|
|
465
465
|
local current_target
|
|
466
466
|
current_target="$(readlink "$skill_link")"
|
|
467
467
|
if [[ "$current_target" != "$skill_dir" ]]; then
|
|
468
|
-
ln -sf
|
|
468
|
+
# macOS ln -sf follows symlinks-to-dirs and creates inside instead of
|
|
469
|
+
# replacing — explicitly remove first to guarantee replacement.
|
|
470
|
+
rm -f "$skill_link" && ln -s "$skill_dir" "$skill_link"
|
|
469
471
|
repaired=$((repaired + 1))
|
|
470
472
|
fi
|
|
471
473
|
# correct symlink: skip silently
|
|
@@ -2123,6 +2125,9 @@ WT="\$(_worktree_path "${slug}" "cycle-\${CYCLE_ID}")"
|
|
|
2123
2125
|
BRANCH="loop/cycle-\${CYCLE_ID}"
|
|
2124
2126
|
_USE_WORKTREE=0
|
|
2125
2127
|
cd "${project_path}" 2>/dev/null || true
|
|
2128
|
+
# US-AUTO-038: snapshot orphan claude/* branches before claude runs so the
|
|
2129
|
+
# post-claude cleanup can diff and delete only this session's additions.
|
|
2130
|
+
CLAUDE_BRANCH_SNAPSHOT="\$(_claude_remote_snapshot "${project_path}")"
|
|
2126
2131
|
if _worktree_fetch_origin main \\
|
|
2127
2132
|
&& _worktree_create "\$WT" "\$BRANCH" "origin/main"; then
|
|
2128
2133
|
_USE_WORKTREE=1
|
|
@@ -2148,6 +2153,14 @@ for _attempt in 1 2 3; do
|
|
|
2148
2153
|
fi
|
|
2149
2154
|
done
|
|
2150
2155
|
|
|
2156
|
+
# US-AUTO-038: diff snapshot vs current and delete any claude/* branches this
|
|
2157
|
+
# session pushed to origin. Runs regardless of claude's exit code (cleanup is
|
|
2158
|
+
# orthogonal to success/failure) and is silent on non-GitHub / unreachable.
|
|
2159
|
+
_claude_cleanup_new_branches "\$CLAUDE_BRANCH_SNAPSHOT" "${project_path}" || true
|
|
2160
|
+
# REFACTOR-011: also prune local .claude/worktrees/ entries whose branch has
|
|
2161
|
+
# been merged to main (remote-branch cleanup above doesn't touch local worktrees).
|
|
2162
|
+
_claude_cleanup_stale_worktrees "${project_path}" || true
|
|
2163
|
+
|
|
2151
2164
|
# Post-claude: publish cycle branch. Doc-only changes (BACKLOG/docs) merge
|
|
2152
2165
|
# immediately via --admin; code changes use auto-merge (CI gate required).
|
|
2153
2166
|
# When \`gh\` is unavailable, fall back to the legacy ff-merge path.
|
|
@@ -3633,6 +3646,75 @@ _worktree_merge_back() {
|
|
|
3633
3646
|
return 0
|
|
3634
3647
|
}
|
|
3635
3648
|
|
|
3649
|
+
# _claude_remote_snapshot [repo]
|
|
3650
|
+
# Echo the current set of remote `claude/*` branch names (sans
|
|
3651
|
+
# refs/heads/), one per line, sorted. Silent on remote unreachable / no
|
|
3652
|
+
# remote / no matches — empty stdout, exit 0.
|
|
3653
|
+
_claude_remote_snapshot() {
|
|
3654
|
+
local repo="${1:-.}"
|
|
3655
|
+
git -C "$repo" ls-remote --heads origin 'refs/heads/claude/*' 2>/dev/null \
|
|
3656
|
+
| awk '{print $2}' \
|
|
3657
|
+
| sed 's|^refs/heads/||' \
|
|
3658
|
+
| sort
|
|
3659
|
+
}
|
|
3660
|
+
|
|
3661
|
+
# _claude_cleanup_new_branches <prior> [repo]
|
|
3662
|
+
# Delete remote `claude/*` branches present now but absent from <prior>
|
|
3663
|
+
# (newline-separated list, as emitted by _claude_remote_snapshot). Skips
|
|
3664
|
+
# silently when origin is not a GitHub remote. Each successful delete logs
|
|
3665
|
+
# one INFO line; failures are silently ignored so the loop's main flow is
|
|
3666
|
+
# never derailed.
|
|
3667
|
+
_claude_cleanup_new_branches() {
|
|
3668
|
+
local prior="$1"
|
|
3669
|
+
local repo="${2:-.}"
|
|
3670
|
+
local url; url=$(git -C "$repo" remote get-url origin 2>/dev/null)
|
|
3671
|
+
[[ "$url" == *github.com* ]] || return 0
|
|
3672
|
+
local current; current=$(_claude_remote_snapshot "$repo")
|
|
3673
|
+
[ -z "$current" ] && return 0
|
|
3674
|
+
local prior_sorted; prior_sorted=$(printf '%s\n' "$prior" | sort -u)
|
|
3675
|
+
local new_branches
|
|
3676
|
+
new_branches=$(comm -13 <(printf '%s\n' "$prior_sorted") <(printf '%s\n' "$current"))
|
|
3677
|
+
[ -z "$new_branches" ] && return 0
|
|
3678
|
+
while IFS= read -r branch; do
|
|
3679
|
+
[ -z "$branch" ] && continue
|
|
3680
|
+
if git -C "$repo" push origin --delete "$branch" 2>/dev/null; then
|
|
3681
|
+
echo "[loop] deleted stale claude branch: $branch"
|
|
3682
|
+
fi
|
|
3683
|
+
done <<< "$new_branches"
|
|
3684
|
+
return 0
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
# _claude_cleanup_stale_worktrees [project_path]
|
|
3688
|
+
# Remove local worktrees under <project_path>/.claude/worktrees/ whose
|
|
3689
|
+
# branch has been fully merged into main (merge-base --is-ancestor). Active
|
|
3690
|
+
# worktrees (branch ahead of main) are preserved. Runs `git worktree prune`
|
|
3691
|
+
# afterwards to clear stale metadata. Silent on missing directory or any
|
|
3692
|
+
# individual failure so the loop's main flow is never derailed.
|
|
3693
|
+
_claude_cleanup_stale_worktrees() {
|
|
3694
|
+
local project_path="${1:-.}"
|
|
3695
|
+
local wt_dir="${project_path}/.claude/worktrees"
|
|
3696
|
+
[ -d "$wt_dir" ] || return 0
|
|
3697
|
+
local entry branch
|
|
3698
|
+
for entry in "$wt_dir"/*/; do
|
|
3699
|
+
[ -d "$entry" ] || continue
|
|
3700
|
+
branch=$(git -C "$project_path" worktree list --porcelain 2>/dev/null \
|
|
3701
|
+
| awk -v p="${entry%/}" '
|
|
3702
|
+
/^worktree / { cur=$2; flag=(cur==p) }
|
|
3703
|
+
/^branch / && flag { sub(/^refs\/heads\//, "", $2); print $2; flag=0 }
|
|
3704
|
+
')
|
|
3705
|
+
[ -z "$branch" ] && branch=$(git -C "$entry" symbolic-ref --short HEAD 2>/dev/null)
|
|
3706
|
+
[ -z "$branch" ] && continue
|
|
3707
|
+
if git -C "$project_path" merge-base --is-ancestor "$branch" main 2>/dev/null; then
|
|
3708
|
+
git -C "$project_path" worktree remove --force "$entry" 2>/dev/null || true
|
|
3709
|
+
rm -rf "$entry" 2>/dev/null || true
|
|
3710
|
+
git -C "$project_path" branch -D "$branch" 2>/dev/null || true
|
|
3711
|
+
echo "[loop] removed stale worktree: $branch"
|
|
3712
|
+
fi
|
|
3713
|
+
done
|
|
3714
|
+
git -C "$project_path" worktree prune 2>/dev/null || true
|
|
3715
|
+
return 0
|
|
3716
|
+
}
|
|
3717
|
+
|
|
3636
3718
|
# US-AUTO-033: publish a loop cycle branch as a GitHub PR with auto-merge.
|
|
3637
3719
|
#
|
|
3638
3720
|
# _loop_publish_pr <branch> [title]
|