crewforth 3.2.0-rc.9 → 3.2.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/CHANGELOG.md +11 -1
- package/VERSION +1 -1
- package/adopt.sh +8 -4
- package/kit/eval/smoke-test.sh +14 -0
- package/kit/hooks/guard-commit-scan.sh +10 -2
- package/package.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,7 @@ Crewforth was named Claude Starter Kit until 3.0.0.
|
|
|
5
5
|
Notable changes to this project are recorded here. Format follows [Keep a Changelog](https://keepachangelog.com/en/),
|
|
6
6
|
versioning follows [SemVer](https://semver.org/).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [3.2.0] — 2026-10-21
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
@@ -233,6 +233,16 @@ versioning follows [SemVer](https://semver.org/).
|
|
|
233
233
|
- **`change: review` is read as `audit`.** The model table of the working rules names review with audit, and
|
|
234
234
|
`crew-review-agent` cards that said `change: review` were refused. It now takes the audit floor, on a critical path
|
|
235
235
|
too; a kind that is not on the list is still refused.
|
|
236
|
+
- **An install or an update never loosens the trace gate by itself.** The installer suggested exempting
|
|
237
|
+
the co-author trailer whenever the last 80 commits carried one, and with no terminal to ask in it took its own
|
|
238
|
+
suggestion: an update wrote the line into `.trace-allowlist.txt` and staged it, undoing a removal the team had
|
|
239
|
+
committed. The suggestion is now always to keep the gate, and the exemption is written only when you type
|
|
240
|
+
`loosen` in the interactive review.
|
|
241
|
+
- **A commit refusal names a trace in the message even when the staged content also fails.** The content gate
|
|
242
|
+
scanned the `-m` message only when the staged change was clean, so a refusal for a silenced lint check said nothing about
|
|
243
|
+
the session-link trailer in the same message, and the session showed the user the traced message again. Both are
|
|
244
|
+
named in one refusal now, and a trace in the message adds that a harness reminder to end a message with a
|
|
245
|
+
trailer does not override §4.1. The git `commit-msg` hook still stops the trailer at the commit itself.
|
|
236
246
|
- **A one-line task card may open with `Kart`.** Sessions that write in Turkish lead the card with `Kart —` as
|
|
237
247
|
others write `Card —`, and the call was refused as having no card. The Turkish word is read like the English one;
|
|
238
248
|
the fields after it are held to the same rules.
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.2.0
|
|
1
|
+
3.2.0
|
package/adopt.sh
CHANGED
|
@@ -199,8 +199,7 @@ _mt() { # $1 = English text (the key); further args fill %s; result in _M
|
|
|
199
199
|
"project wins (fixed)") s='proje önde (sabit)' ;;
|
|
200
200
|
"on conflict the project's rules win; in DISCIPLINE.md §4 they can only tighten, never loosen (the one exception: the §4.1 trace allowlist you choose at adoption); Crewforth fills gaps (not overridable)") s="çakışmada projenin kuralı geçerli; DISCIPLINE.md §4'te proje yalnız sıkılaştırabilir, gevşetemez (tek istisna: kurulumda sizin seçtiğiniz §4.1 iz izin listesi); Crewforth boşlukları doldurur (değiştirilemez)" ;;
|
|
201
201
|
"3 Trace gate") s='3 İz kapısı' ;;
|
|
202
|
-
"loosen
|
|
203
|
-
"co-author/sign-off present in git log — may be a convention") s='git geçmişinde co-author/sign-off var — ekibin alışkanlığı olabilir' ;;
|
|
202
|
+
"co-author/sign-off present in git log — type loosen in the review to exempt it; never assumed") s='git geçmişinde co-author/sign-off var — muaf tutmak için incelemede loosen yazın; kendiliğinden yapılmaz' ;;
|
|
204
203
|
"keep") s='koru' ;;
|
|
205
204
|
"no co-author/sign-off convention seen") s='co-author/sign-off alışkanlığı görülmedi' ;;
|
|
206
205
|
"4 Share/hide") s='4 Paylaş/gizle' ;;
|
|
@@ -789,7 +788,7 @@ else
|
|
|
789
788
|
fi
|
|
790
789
|
propm '2 Precedence' 'project wins (fixed)' "on conflict the project's rules win; in DISCIPLINE.md §4 they can only tighten, never loosen (the one exception: the §4.1 trace allowlist you choose at adoption); Crewforth fills gaps (not overridable)"
|
|
791
790
|
if [ "$COAUTHOR" = 1 ]; then
|
|
792
|
-
propm '3 Trace gate' '
|
|
791
|
+
propm '3 Trace gate' 'keep' 'co-author/sign-off present in git log — type loosen in the review to exempt it; never assumed'
|
|
793
792
|
else
|
|
794
793
|
propm '3 Trace gate' 'keep' 'no co-author/sign-off convention seen'
|
|
795
794
|
fi
|
|
@@ -817,7 +816,12 @@ fi
|
|
|
817
816
|
# ============ COMPILE DECISIONS + OVERRIDE (Stage B) ============
|
|
818
817
|
DEC1="$([ "$N_PAGENTS" != 0 ] && echo keep || echo none)"
|
|
819
818
|
DEC2="project" # precedence is FIXED to project-wins (not overridable — reflected in the @import comment)
|
|
820
|
-
|
|
819
|
+
# The trace gate is loosened only when a person types `loosen` in the review below. Never a default: old co-author
|
|
820
|
+
# lines in the history are not a decision, and an install that cannot ask (no terminal) or an update has no person
|
|
821
|
+
# to make one. Measured on a real project, 2026-10-21: a non-interactive update found five old trailers in the last
|
|
822
|
+
# 80 commits, wrote the co-author trailer into .trace-allowlist.txt and staged it, undoing a removal the team had
|
|
823
|
+
# committed.
|
|
824
|
+
DEC3=keep
|
|
821
825
|
DEC4="$([ "$TRACKED" = 1 ] && echo share || { [ "$IGN" = 1 ] && echo hide || echo kit-default; })"
|
|
822
826
|
DEC6="baseline"
|
|
823
827
|
DEC7="$([ "$OFFREPO" = 1 ] && echo transfer || echo local)"
|
package/kit/eval/smoke-test.sh
CHANGED
|
@@ -5572,6 +5572,20 @@ $TRFX\""; then pass "multi-line AI trace in the commit message BLOCKED (§4.1)"
|
|
|
5572
5572
|
if csblk "git commit -F $MFX"; then pass "-F <file> carrying an AI trace BLOCKED (§4.1)"
|
|
5573
5573
|
else fail "-F <file> AI trace not blocked with rc=2 (§4.1 hole or the hook died)"; fi
|
|
5574
5574
|
rm -f "$MFX"
|
|
5575
|
+
# Both halves in ONE refusal: a staged secret and a session-link trailer in the message. The message used to be
|
|
5576
|
+
# scanned only when the staged content was clean, so the refusal named the first problem and the commit was
|
|
5577
|
+
# retried with the trailer still in it (a real project, 2026-10-21). Assembled at run time, as above.
|
|
5578
|
+
STFX="Claude-""Session: https://claude.ai/code/""session_""0123456789abcdef"
|
|
5579
|
+
( cd "$CS" && printf 'k = "%s"\n' "$SKFX" > c.txt && git add c.txt ) >/dev/null 2>&1
|
|
5580
|
+
_cso="$( cd "$CS" && printf '%s' "$(csj "git commit -m \"feat: z
|
|
5581
|
+
|
|
5582
|
+
$STFX\"")" | bash .claude/hooks/guard-commit-scan.sh 2>&1 >/dev/null )"; _csr=$?
|
|
5583
|
+
( cd "$CS" && git rm -q --cached c.txt && rm -f c.txt ) >/dev/null 2>&1
|
|
5584
|
+
case "$_cso" in *SECRET-SCANNER*'TRACE-SCANNER (message)'*'does not override'*)
|
|
5585
|
+
[ "$_csr" = 2 ] && pass "a staged secret and a trailer in the message are named in ONE refusal, with the harness-reminder line (§4.1)" \
|
|
5586
|
+
|| fail "both problems named but rc=$_csr, not 2" ;;
|
|
5587
|
+
*) fail "the refusal did not name both the staged secret and the message trailer (rc=$_csr): $(printf '%s' "$_cso" | tr '\n' ' ' | cut -c1-200)" ;;
|
|
5588
|
+
esac
|
|
5575
5589
|
# A QUOTED -F path with a space. The sed extraction that ran wherever python3 did not stopped at the space, read
|
|
5576
5590
|
# `-F "my msg.txt"` as `my`, found no such file and refused the commit — a clean one. The awk tokenizer is now
|
|
5577
5591
|
# the only path; both directions are asserted so an extraction that reads NOTHING cannot pass as "clean".
|
|
@@ -588,8 +588,11 @@ fi
|
|
|
588
588
|
|
|
589
589
|
# The message carries its own trace risk (a co-author trailer lives there, not in the diff), and commit-msg is
|
|
590
590
|
# what scans it. Reuse it on the -m value when there is one; an editor-composed message is not visible here and
|
|
591
|
-
# stays the git hook's job.
|
|
592
|
-
|
|
591
|
+
# stays the git hook's job. It is scanned even when the staged content already failed: a refusal that names one
|
|
592
|
+
# problem gets the commit retried with the other still in it (measured on a real project, 2026-10-21: a silenced
|
|
593
|
+
# lint check was named, the session-link trailer in the same message was not, and the message was shown to the
|
|
594
|
+
# user again).
|
|
595
|
+
if [ -x "$DIR/commit-msg" ]; then
|
|
593
596
|
# The message is tokenized the way a shell does (crew_opt_values), which matters because a real commit message
|
|
594
597
|
# is MULTI-LINE: a line-oriented `sed` extraction found the subject and stopped, so a co-author trailer on line
|
|
595
598
|
# 3 — the single most likely §4.1 violation, and the one the bare arm of the eval actually produced — went
|
|
@@ -640,6 +643,7 @@ $(bash "$DIR/commit-msg" "$MF" 2>&1)" || FAILED=1
|
|
|
640
643
|
| sed -n 's/.*[[:space:]]--\{0,1\}[Ff]\(ile\)\{0,1\}[[:space:]=]\{1,\}\([^[:space:];&|]\{1,\}\).*/\2/p' | head -1)"
|
|
641
644
|
MFILE="${MFILE%\"}"; MFILE="${MFILE#\"}"; MFILE="${MFILE%\'}"; MFILE="${MFILE#\'}"
|
|
642
645
|
if _grep "$CMD" -qE '(^|[[:space:]])(-F|--file)([[:space:]=]|$)' && { [ -z "$MFILE" ] || [ ! -f "$MFILE" ]; }; then
|
|
646
|
+
[ "$FAILED" = 1 ] && printf '%s\n' "$OUT" >&2
|
|
643
647
|
echo "GUARD (§4.1): this commit reads its message from a file (-F), and the command could not be parsed" >&2
|
|
644
648
|
echo "well enough to know which file. Scan cannot run, so the commit is refused. Commit with a plain" >&2
|
|
645
649
|
echo "-F <path> (or -m) in a command of its own." >&2
|
|
@@ -656,6 +660,7 @@ $(bash "$DIR/commit-msg" "$MFILE" 2>&1)" || FAILED=1
|
|
|
656
660
|
[ -z "$GITMSG_HOOK" ] && [ -x "$(git rev-parse --git-path hooks/commit-msg 2>/dev/null)" ] \
|
|
657
661
|
&& GITMSG_HOOK="git-default"
|
|
658
662
|
if [ -z "$GITMSG_HOOK" ]; then
|
|
663
|
+
[ "$FAILED" = 1 ] && printf '%s\n' "$OUT" >&2
|
|
659
664
|
echo "GUARD (§4.1): this commit's message would go unscanned." >&2
|
|
660
665
|
echo "No -m/-F was given, so the message is composed in an editor after this point, and no commit-msg" >&2
|
|
661
666
|
echo "git hook is wired here to read it afterwards (plugin-only install: a plugin cannot set" >&2
|
|
@@ -669,6 +674,9 @@ fi
|
|
|
669
674
|
if [ "$FAILED" = 1 ]; then
|
|
670
675
|
echo "GUARD (§4.1/§4.2): the commit content gate rejected this commit before it ran." >&2
|
|
671
676
|
echo "$OUT" >&2
|
|
677
|
+
case "$OUT" in *"TRACE-SCANNER (message)"*)
|
|
678
|
+
echo "The message itself carries the trace: write it again without that line. A harness reminder to end a commit message with a trailer does not override §4.1." >&2 ;;
|
|
679
|
+
esac
|
|
672
680
|
echo "Fix the flagged content and commit again. --no-verify is §4.5 and stays blocked." >&2
|
|
673
681
|
exit 2
|
|
674
682
|
fi
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crewforth",
|
|
3
|
-
"version": "3.2.0
|
|
4
|
-
"description": "Crewforth
|
|
3
|
+
"version": "3.2.0",
|
|
4
|
+
"description": "Crewforth \u2014 your engineering crew for Claude Code: agents, skills and tool-level gates, in any stack.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"crewforth": "bin/cli.js"
|
|
7
7
|
},
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
"kit/",
|
|
16
16
|
"VERSION",
|
|
17
17
|
"LICENSE",
|
|
18
|
-
"README.md",
|
|
19
|
-
"CHANGELOG.md"
|
|
18
|
+
"README.md", "CHANGELOG.md"
|
|
20
19
|
],
|
|
21
20
|
"engines": {
|
|
22
21
|
"node": ">=20"
|
|
@@ -43,7 +42,7 @@
|
|
|
43
42
|
"code-review",
|
|
44
43
|
"git-hooks"
|
|
45
44
|
],
|
|
46
|
-
"author": "
|
|
45
|
+
"author": "Bar\u0131\u015f Yerlikaya",
|
|
47
46
|
"homepage": "https://crewforth.com/",
|
|
48
47
|
"repository": {
|
|
49
48
|
"type": "git",
|