@biffo/cli 0.283.3 → 0.284.0
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/_skeletons/plugin-template/AGENTS.md +18 -3
- package/_skeletons/sibling-template/AGENTS.md +18 -3
- package/dist/index.js +424 -178
- package/package.json +1 -1
- package/scripts/claim.sh +119 -9
package/package.json
CHANGED
package/scripts/claim.sh
CHANGED
|
@@ -39,20 +39,52 @@
|
|
|
39
39
|
#
|
|
40
40
|
# ## Usage
|
|
41
41
|
#
|
|
42
|
-
# sh scripts/claim.sh 1234
|
|
43
|
-
# sh scripts/claim.sh 1234 --check
|
|
44
|
-
# sh scripts/claim.sh 1234 -R owner/repo
|
|
45
|
-
# sh scripts/claim.sh --
|
|
42
|
+
# sh scripts/claim.sh 1234 --as <token> # check, and claim if free
|
|
43
|
+
# sh scripts/claim.sh 1234 --as <token> --check # report only, change nothing
|
|
44
|
+
# sh scripts/claim.sh 1234 --as <token> -R owner/repo
|
|
45
|
+
# sh scripts/claim.sh 1234 --release <token> # only the holder may clear it
|
|
46
|
+
# sh scripts/claim.sh --guard <branch> # pre-push gate — see below
|
|
46
47
|
#
|
|
47
48
|
# 0 free — and claimed, unless --check
|
|
48
49
|
# 1 taken, or already closed — the reason is printed
|
|
49
|
-
# 2 cannot tell — issue unreadable, gh unauthenticated
|
|
50
|
+
# 2 cannot tell — issue unreadable, gh unauthenticated, or no --as token
|
|
50
51
|
#
|
|
51
52
|
# 2 is deliberately not 0, matching `wait-for-checks.sh` and `branch-health.sh`.
|
|
52
53
|
# A check that cannot see its input must not report "free".
|
|
53
54
|
#
|
|
54
55
|
# Requires `gh`, authenticated. Uses gh's embedded jq, so no jq binary needed.
|
|
55
56
|
#
|
|
57
|
+
# ## `--as <token>` is REQUIRED on the claim and `--check` paths (#1562)
|
|
58
|
+
#
|
|
59
|
+
# It used to be optional, and `${HOLDER:+ …}` in the claim comment simply
|
|
60
|
+
# omitted the slug when it was absent — no flag, no slug, no warning, exit 0.
|
|
61
|
+
# So the safe form existed and nothing steered anyone to it: measured on
|
|
62
|
+
# 2026-08-13, `--as` appeared **zero** times in the AGENTS.md of every satellite
|
|
63
|
+
# in the estate, because the flag shipped in #1279 reached the template's own
|
|
64
|
+
# ruleset and neither skeleton. Two concurrent sessions in one plugin repo then
|
|
65
|
+
# produced four claims that read `Claimed at … by \`Keiran Holloway\`` and could
|
|
66
|
+
# not be told apart — ownership had to be reconstructed from a local command log,
|
|
67
|
+
# and for one pair could not be established at all.
|
|
68
|
+
#
|
|
69
|
+
# An optional flag that records the deciding information is a fail-open: the
|
|
70
|
+
# default loses exactly what the mechanism exists to keep. So a claim that
|
|
71
|
+
# cannot be proved to be yours is now refused rather than made.
|
|
72
|
+
#
|
|
73
|
+
# **`--guard` and `--release` are deliberately exempt.** `--guard` is invoked by
|
|
74
|
+
# `.githooks/pre-push` on EVERY push in every repo, with no token and no issue
|
|
75
|
+
# argument, and it never compares identity by design (see below) — requiring one
|
|
76
|
+
# there would break `git push` estate-wide to enforce a rule that path does not
|
|
77
|
+
# use. `--release` carries the token in its own value, so it already cannot run
|
|
78
|
+
# untokened.
|
|
79
|
+
#
|
|
80
|
+
# **The token must identify a session, not a role.** A mandatory field that
|
|
81
|
+
# everybody satisfies with `--as agent` is worse than an optional one, because it
|
|
82
|
+
# manufactures a column that looks authoritative and distinguishes nobody — the
|
|
83
|
+
# 2026-08-13 measurement above is what that looks like. So the shape is checked
|
|
84
|
+
# (at least two `-`-separated parts, 6+ characters), and the refusal prints a
|
|
85
|
+
# ready-made suggestion derived from the branch — which is already unique per
|
|
86
|
+
# unit of work — so the cheapest thing to type is also a good token.
|
|
87
|
+
#
|
|
56
88
|
# ## `--guard <branch>` — the pre-push gate (#1231 instance 2)
|
|
57
89
|
#
|
|
58
90
|
# The four-signal check above is advisory: nothing ever runs it for you, so a
|
|
@@ -98,7 +130,12 @@ CHECK_ONLY=""
|
|
|
98
130
|
GUARD_BRANCH=""
|
|
99
131
|
|
|
100
132
|
usage() {
|
|
101
|
-
|
|
133
|
+
# The whole header comment, found rather than hardcoded: the previous fixed
|
|
134
|
+
# `2,84p` was already truncating the last five lines of the `--guard` section
|
|
135
|
+
# mid-sentence, and any edit to the header silently moves where the real end
|
|
136
|
+
# is. Derived from `set -u`, which is always the first line of code.
|
|
137
|
+
_usage_end=$(grep -n '^set -u' "$0" | head -1 | cut -d: -f1)
|
|
138
|
+
sed -n "2,$((_usage_end - 2))p" "$0" | sed 's/^# \{0,1\}//'
|
|
102
139
|
exit 2
|
|
103
140
|
}
|
|
104
141
|
|
|
@@ -429,11 +466,77 @@ fi
|
|
|
429
466
|
|
|
430
467
|
case "$ISSUE" in
|
|
431
468
|
'' | *[!0-9]*)
|
|
432
|
-
echo "claim: give an issue number, e.g. sh scripts/claim.sh 1234" >&2
|
|
469
|
+
echo "claim: give an issue number, e.g. sh scripts/claim.sh 1234 --as <token>" >&2
|
|
433
470
|
exit 2
|
|
434
471
|
;;
|
|
435
472
|
esac
|
|
436
473
|
|
|
474
|
+
# --- --as <token> is required from here on (#1562) ---------------------------
|
|
475
|
+
#
|
|
476
|
+
# Placed AFTER the `--guard` and `--release` short-circuits above, which is the
|
|
477
|
+
# whole exemption: those two paths exit before reaching this line. Everything
|
|
478
|
+
# below is the four-signal check and the claim it writes — the two things that
|
|
479
|
+
# need to know whose session is asking.
|
|
480
|
+
#
|
|
481
|
+
# A suggestion, not a lecture. An agent that hits this must be able to fix it by
|
|
482
|
+
# copying one line, so the refusal derives a token instead of describing one:
|
|
483
|
+
#
|
|
484
|
+
# <slug>-<MMDD>-<pid>
|
|
485
|
+
#
|
|
486
|
+
# `<slug>` comes from the current branch when it names the work, because a
|
|
487
|
+
# branch is already unique per unit of work (AGENTS.md: one worktree per unit).
|
|
488
|
+
# Claiming usually happens BEFORE the worktree exists, though — you claim from
|
|
489
|
+
# the primary checkout, on `dev` — so on an integration branch the slug falls
|
|
490
|
+
# back to the issue, and the pid keeps two sessions that both did that apart.
|
|
491
|
+
suggest_token() {
|
|
492
|
+
_b=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '')
|
|
493
|
+
case "$_b" in
|
|
494
|
+
'' | HEAD | dev | main | master | staging) _slug="issue$ISSUE" ;;
|
|
495
|
+
*/*) _slug=$(printf '%s' "${_b#*/}" | tr 'A-Z' 'a-z' | tr -c 'a-z0-9-' '-' | cut -c1-24) ;;
|
|
496
|
+
*) _slug=$(printf '%s' "$_b" | tr 'A-Z' 'a-z' | tr -c 'a-z0-9-' '-' | cut -c1-24) ;;
|
|
497
|
+
esac
|
|
498
|
+
_slug=$(printf '%s' "$_slug" | sed 's/-*$//')
|
|
499
|
+
printf '%s-%s-%04x' "$_slug" "$(date -u +%m%d)" "$$"
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
# Two `-`-separated parts, 6+ characters. Deliberately shape-only: the token is
|
|
503
|
+
# opaque by design, so there is nothing else to validate — but `agent`, `me`,
|
|
504
|
+
# `bot`, `session` and every other role word a whole estate would share fail it,
|
|
505
|
+
# and that is the failure this rule is for. See the header.
|
|
506
|
+
token_is_identifying() {
|
|
507
|
+
case "$1" in
|
|
508
|
+
*[!A-Za-z0-9._-]*) return 1 ;; # opaque-token characters only
|
|
509
|
+
-* | *-) return 1 ;; # no leading or trailing separator
|
|
510
|
+
*-*) [ "${#1}" -ge 6 ] ;; # two parts, long enough to be a session
|
|
511
|
+
*) return 1 ;; # a single word: 'agent', 'bot', 'me'
|
|
512
|
+
esac
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if [ -z "$HOLDER" ]; then
|
|
516
|
+
_s=$(suggest_token)
|
|
517
|
+
echo "${RED}claim: --as <token> is required.${OFF}" >&2
|
|
518
|
+
echo "${DIM} A claim with no token cannot be proved to be yours: every session on this${OFF}" >&2
|
|
519
|
+
echo "${DIM} workstation claims under the same GitHub actor, so --release has nothing to${OFF}" >&2
|
|
520
|
+
echo "${DIM} check and a delegated agent cannot tell your reservation from a stranger's.${OFF}" >&2
|
|
521
|
+
echo >&2
|
|
522
|
+
echo " sh scripts/biffo.sh claim $ISSUE --as $_s" >&2
|
|
523
|
+
echo >&2
|
|
524
|
+
echo "${DIM} Give that same token to every agent you dispatch onto #$ISSUE, and release it${OFF}" >&2
|
|
525
|
+
echo "${DIM} with: sh scripts/biffo.sh claim $ISSUE --release $_s${OFF}" >&2
|
|
526
|
+
exit 2
|
|
527
|
+
fi
|
|
528
|
+
|
|
529
|
+
if ! token_is_identifying "$HOLDER"; then
|
|
530
|
+
_s=$(suggest_token)
|
|
531
|
+
echo "${RED}claim: --as '$HOLDER' does not identify a session.${OFF}" >&2
|
|
532
|
+
echo "${DIM} Needs two '-'-separated parts and 6+ characters, e.g. <what>-<MMDD>-<unique>.${OFF}" >&2
|
|
533
|
+
echo "${DIM} A token every session shares — 'agent', 'bot', 'me' — is worse than none:${OFF}" >&2
|
|
534
|
+
echo "${DIM} it fills the field that decides ownership with a value that decides nothing.${OFF}" >&2
|
|
535
|
+
echo >&2
|
|
536
|
+
echo " sh scripts/biffo.sh claim $ISSUE --as $_s" >&2
|
|
537
|
+
exit 2
|
|
538
|
+
fi
|
|
539
|
+
|
|
437
540
|
# --- 0. Does the issue exist, and is it still open? --------------------------
|
|
438
541
|
|
|
439
542
|
meta=$(gh_issue view "$ISSUE" --json state,title,labels \
|
|
@@ -578,14 +681,21 @@ gh_label create "$LABEL" \
|
|
|
578
681
|
>/dev/null 2>&1 || true
|
|
579
682
|
|
|
580
683
|
# Claim it. Label AND comment together: the label is what other sessions filter
|
|
581
|
-
# on, the comment is what dates it so a stale claim can be recognised later
|
|
684
|
+
# on, the comment is what dates it so a stale claim can be recognised later, and
|
|
685
|
+
# the holder token is what makes it answerable.
|
|
686
|
+
#
|
|
687
|
+
# The token is interpolated unconditionally (#1562). It used to be
|
|
688
|
+
# `${HOLDER:+ ${HOLDER_MARK}${HOLDER}}` — present only when `--as` was passed —
|
|
689
|
+
# which is why a claim could be written with nothing to identify it. That branch
|
|
690
|
+
# is now unreachable (the requirement above exits 2 first), so the conditional
|
|
691
|
+
# would only be a place for the old behaviour to come back.
|
|
582
692
|
gh_issue edit "$ISSUE" --add-label "$LABEL" >/dev/null 2>&1 || {
|
|
583
693
|
echo "${RED}claim: could not apply the '$LABEL' label.${OFF}" >&2
|
|
584
694
|
echo "${DIM} Not claimed. Do not start work on the assumption that it worked.${OFF}" >&2
|
|
585
695
|
exit 2
|
|
586
696
|
}
|
|
587
697
|
gh_issue comment "$ISSUE" \
|
|
588
|
-
--body "Claimed at $(date -u +%FT%TZ) by \`$(git config user.name 2>/dev/null || echo agent)
|
|
698
|
+
--body "Claimed at $(date -u +%FT%TZ) by \`$(git config user.name 2>/dev/null || echo agent)\`. ${HOLDER_MARK}${HOLDER} Release it — remove the label — on merge, or if you stop." \
|
|
589
699
|
>/dev/null 2>&1
|
|
590
700
|
|
|
591
701
|
echo "${GREEN}Claimed.${OFF}"
|