crewforth 3.1.0-rc.8 → 3.1.1
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 +14 -1
- package/VERSION +1 -1
- package/adopt.sh +80 -11
- package/kit/owned-blobs.tsv +4 -1
- package/package.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,20 @@ 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.1.1] — 2026-10-12
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **An update could write this machine's path into `.claude/git-shim`, a file projects commit.** The updater knew
|
|
13
|
+
the kit's own hooks directory only when `core.hooksPath` was spelled `.claude/hooks`. Spelled as an absolute path
|
|
14
|
+
(seen in a real project) it was taken for a hook chain of the project's own, and the shim named
|
|
15
|
+
`$ROOT//Users/<name>/…/.claude/hooks`: a path that does not exist, so nothing ran twice, but the user name and
|
|
16
|
+
the folder layout went into the repository. `core.hooksPath` is now read for what it names (the same directory,
|
|
17
|
+
whatever the spelling), a chain inside the repository is named from the root, and a chain outside it is kept in
|
|
18
|
+
the clone's git config (`crewforth.hookschain`) and read by the shim when it runs. A shim an earlier update
|
|
19
|
+
wrote for that case is removed by the next update.
|
|
20
|
+
|
|
21
|
+
## [3.1.0] — 2026-10-12
|
|
9
22
|
|
|
10
23
|
### Security
|
|
11
24
|
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.1.
|
|
1
|
+
3.1.1
|
package/adopt.sh
CHANGED
|
@@ -524,12 +524,53 @@ fi
|
|
|
524
524
|
rowm 'git' "$GITKIND" # every GITKIND value is a literal with its own table row
|
|
525
525
|
|
|
526
526
|
# existing hook system (decision #5 — single-hooksPath clash with husky/lefthook)
|
|
527
|
-
|
|
527
|
+
# WHAT core.hooksPath NAMES, however it is spelled. It was matched as text against `.claude/hooks`, so the same
|
|
528
|
+
# directory written as an absolute path (seen in a real project) was read as a hook chain of the project's own:
|
|
529
|
+
# the shim then named `$ROOT//Users/<name>/…/.claude/hooks`, a path that does not exist and that carries the
|
|
530
|
+
# machine's user name into a file projects commit; had it existed, every hook would have run twice.
|
|
531
|
+
# Asked of the file system and of git, not of the spelling: `-ef` says whether two names are one directory, and
|
|
532
|
+
# `git rev-parse --show-prefix` says where a directory is inside the repository.
|
|
533
|
+
_hp_class(){ # $1 = a hooks path as configured -> HP_KIND: none | own | shim | in (HP_REL, from the root) | out (HP_ABS)
|
|
534
|
+
local p="$1" top pfx r
|
|
535
|
+
HP_KIND=none; HP_REL=""; HP_ABS=""
|
|
536
|
+
[ -n "$p" ] || return 0
|
|
537
|
+
case "$p" in '~/'*) p="$HOME/${p#\~/}" ;; esac
|
|
538
|
+
while :; do case "$p" in ?*/) p="${p%/}" ;; *) break ;; esac; done
|
|
539
|
+
case "$p" in
|
|
540
|
+
/*|[A-Za-z]:[/\\]*)
|
|
541
|
+
if [ -d "$p" ]; then
|
|
542
|
+
if [ -d .claude/hooks ] && [ "$p" -ef .claude/hooks ]; then HP_KIND=own; return 0; fi
|
|
543
|
+
if [ -d .claude/git-shim ] && [ "$p" -ef .claude/git-shim ]; then HP_KIND=shim; return 0; fi
|
|
544
|
+
top="$(git -C "$p" rev-parse --show-toplevel 2>/dev/null || true)"
|
|
545
|
+
if [ -n "$top" ] && [ "$top" -ef . ]; then
|
|
546
|
+
pfx="$(git -C "$p" rev-parse --show-prefix 2>/dev/null || true)"; pfx="${pfx%/}"
|
|
547
|
+
if [ -n "$pfx" ]; then HP_KIND=in; HP_REL="$pfx"; return 0; fi
|
|
548
|
+
fi
|
|
549
|
+
fi
|
|
550
|
+
# Not there, or git cannot place it (.git/hooks is not in the work tree): the spelling is what is left.
|
|
551
|
+
for r in "$PWD" "$(pwd -P 2>/dev/null || true)"; do
|
|
552
|
+
[ -n "$r" ] || continue
|
|
553
|
+
case "$p" in "$r"/?*) p="${p#"$r"/}"
|
|
554
|
+
case "$p" in .claude/hooks) HP_KIND=own ;; .claude/git-shim) HP_KIND=shim ;; *) HP_KIND=in; HP_REL="$p" ;; esac
|
|
555
|
+
return 0 ;;
|
|
556
|
+
esac
|
|
557
|
+
done
|
|
558
|
+
HP_KIND=out; HP_ABS="$p" ;;
|
|
559
|
+
*)
|
|
560
|
+
while :; do case "$p" in ./?*) p="${p#./}" ;; *) break ;; esac; done
|
|
561
|
+
case "$p" in .claude/hooks) HP_KIND=own; return 0 ;; .claude/git-shim) HP_KIND=shim; return 0 ;; esac
|
|
562
|
+
if [ -d "$p" ] && [ -d .claude/hooks ] && [ "$p" -ef .claude/hooks ]; then HP_KIND=own; return 0; fi
|
|
563
|
+
if [ -d "$p" ] && [ -d .claude/git-shim ] && [ "$p" -ef .claude/git-shim ]; then HP_KIND=shim; return 0; fi
|
|
564
|
+
HP_KIND=in; HP_REL="$p" ;;
|
|
565
|
+
esac
|
|
566
|
+
}
|
|
567
|
+
HOOKSYS="none"; SHIM_CHAIN=""; SHIM_OUT=0; CUR_OUT=0
|
|
528
568
|
CURHP="$(git config --get core.hooksPath 2>/dev/null || true)"
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
569
|
+
_hp_class "$CURHP"
|
|
570
|
+
case "$HP_KIND" in
|
|
571
|
+
none) : ;;
|
|
572
|
+
own) HOOKSYS="Crewforth (already armed)"; CURHP="" ;; # kit's OWN path — not a foreign chain (re-adopt must not shim itself)
|
|
573
|
+
shim)
|
|
533
574
|
# Crewforth's shim, from an earlier run: git is pointed at it, and IT holds the project's own chain. That chain
|
|
534
575
|
# is read back from the shim, or the next lines would see "no chain" and point git straight at .claude/hooks:
|
|
535
576
|
# measured, the second update of a project with core.hooksPath=myhooks left its own pre-commit never running
|
|
@@ -540,14 +581,23 @@ case "$CURHP" in
|
|
|
540
581
|
[ -f "$_sf" ] || continue
|
|
541
582
|
while IFS= read -r _sl || [ -n "$_sl" ]; do
|
|
542
583
|
_sl="${_sl%$'\r'}"
|
|
584
|
+
# (A shim written before this was fixed can hold `P="$ROOT//abs/path/$H"`: what is left after `$ROOT/` is
|
|
585
|
+
# the absolute path, and it is classed like any other.)
|
|
543
586
|
case "$_sl" in 'P="'*'/$H"') _sl="${_sl#P=\"}"; _sl="${_sl%/\$H\"}"; _sl="${_sl#\$ROOT/}"
|
|
544
|
-
|
|
587
|
+
_hp_class "$_sl"
|
|
588
|
+
case "$HP_KIND" in in) SHIM_CHAIN="$HP_REL"; SHIM_OUT=0 ;; out) SHIM_CHAIN="$HP_ABS"; SHIM_OUT=1 ;; esac ;;
|
|
545
589
|
esac
|
|
546
590
|
done < "$_sf"
|
|
547
591
|
[ -n "$SHIM_CHAIN" ] && break
|
|
548
592
|
done
|
|
593
|
+
# A chain outside the repository is not written in the shim: it is in this clone's git config.
|
|
594
|
+
if [ -z "$SHIM_CHAIN" ]; then
|
|
595
|
+
_hp_class "$(git config --get crewforth.hookschain 2>/dev/null || true)"
|
|
596
|
+
case "$HP_KIND" in in) SHIM_CHAIN="$HP_REL"; SHIM_OUT=0 ;; out) SHIM_CHAIN="$HP_ABS"; SHIM_OUT=1 ;; esac
|
|
597
|
+
fi
|
|
549
598
|
[ -n "$SHIM_CHAIN" ] && HOOKSYS="core.hooksPath=$SHIM_CHAIN" ;;
|
|
550
|
-
|
|
599
|
+
in) CURHP="$HP_REL"; HOOKSYS="core.hooksPath=$CURHP" ;;
|
|
600
|
+
out) CURHP="$HP_ABS"; CUR_OUT=1; HOOKSYS="core.hooksPath=$CURHP" ;;
|
|
551
601
|
esac
|
|
552
602
|
[ -d .husky ] && HOOKSYS="husky (.husky/)"
|
|
553
603
|
{ [ -f lefthook.yml ] || [ -f .lefthook.yml ]; } && HOOKSYS="lefthook"
|
|
@@ -1627,9 +1677,9 @@ else HAND_SET="hook-aware merge (Crewforth hooks REFRESHED to current — new ev
|
|
|
1627
1677
|
h1m 'Stage 4 — arm the git gates (SHIM via husky) + PROOF'
|
|
1628
1678
|
|
|
1629
1679
|
# 4a) location of the existing hook chain (the shim calls this too)
|
|
1630
|
-
ORIG_HOOKS=""
|
|
1631
|
-
if [ -n "$CURHP" ]; then ORIG_HOOKS="$CURHP"
|
|
1632
|
-
elif [ -n "$SHIM_CHAIN" ]; then ORIG_HOOKS="$SHIM_CHAIN" # the chain the shim of an earlier run already runs
|
|
1680
|
+
ORIG_HOOKS=""; ORIG_OUT=0
|
|
1681
|
+
if [ -n "$CURHP" ]; then ORIG_HOOKS="$CURHP"; ORIG_OUT="$CUR_OUT"
|
|
1682
|
+
elif [ -n "$SHIM_CHAIN" ]; then ORIG_HOOKS="$SHIM_CHAIN"; ORIG_OUT="$SHIM_OUT" # the chain the shim of an earlier run already runs
|
|
1633
1683
|
elif [ -d .husky ]; then ORIG_HOOKS=".husky"
|
|
1634
1684
|
elif [ -x .git/hooks/pre-commit ] || [ -x .git/hooks/commit-msg ]; then ORIG_HOOKS=".git/hooks"; fi
|
|
1635
1685
|
# never shim the kit onto its OWN hooks (re-adopt) — the shim would exec itself and recurse on every commit
|
|
@@ -1637,9 +1687,28 @@ case "$ORIG_HOOKS" in .claude/hooks|.claude/git-shim) ORIG_HOOKS="" ;; esac
|
|
|
1637
1687
|
|
|
1638
1688
|
if [ -z "$ORIG_HOOKS" ]; then
|
|
1639
1689
|
git config core.hooksPath .claude/hooks
|
|
1690
|
+
git config --unset-all crewforth.hookschain 2>/dev/null || true
|
|
1691
|
+
# A shim of an earlier run has no chain to run any more. It is Crewforth's own generated file (its second line
|
|
1692
|
+
# says so), and one written before the fix above can hold a path of this machine: it goes, and nothing else does.
|
|
1693
|
+
for hk in pre-commit commit-msg; do
|
|
1694
|
+
if [ -f ".claude/git-shim/$hk" ] && grep -q '^# kit git-shim:' ".claude/git-shim/$hk" 2>/dev/null; then rm -f ".claude/git-shim/$hk"; fi
|
|
1695
|
+
done
|
|
1696
|
+
[ -d .claude/git-shim ] && rmdir .claude/git-shim 2>/dev/null || true
|
|
1640
1697
|
say 'core.hooksPath -> .claude/hooks (no existing hook chain)'
|
|
1641
1698
|
else
|
|
1642
1699
|
mkdir -p .claude/git-shim
|
|
1700
|
+
# THE SHIM HOLDS NO PATH OF THIS MACHINE. A chain inside the repository is named from the root. One outside it
|
|
1701
|
+
# (a hooks directory in the user's home) is kept in this clone's git config, which is not committed, and the shim
|
|
1702
|
+
# reads it when it runs.
|
|
1703
|
+
if [ "$ORIG_OUT" = 1 ]; then
|
|
1704
|
+
git config crewforth.hookschain "$ORIG_HOOKS"
|
|
1705
|
+
_shp='C="$(git config --get crewforth.hookschain 2>/dev/null || true)"
|
|
1706
|
+
P=""
|
|
1707
|
+
[ -z "$C" ] || P="$C/$H"'
|
|
1708
|
+
else
|
|
1709
|
+
git config --unset-all crewforth.hookschain 2>/dev/null || true
|
|
1710
|
+
_shp="P=\"\$ROOT/$ORIG_HOOKS/\$H\""
|
|
1711
|
+
fi
|
|
1643
1712
|
for hk in pre-commit commit-msg; do
|
|
1644
1713
|
cat > ".claude/git-shim/$hk" <<SHIM
|
|
1645
1714
|
#!/usr/bin/env bash
|
|
@@ -1648,7 +1717,7 @@ set -e
|
|
|
1648
1717
|
H="\$(basename "\$0")"
|
|
1649
1718
|
ROOT="\$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
1650
1719
|
K="\$ROOT/.claude/hooks/\$H"; [ -x "\$K" ] && "\$K" "\$@"
|
|
1651
|
-
|
|
1720
|
+
$_shp
|
|
1652
1721
|
if [ -x "\$P" ]; then "\$P" "\$@"
|
|
1653
1722
|
elif [ -f "\$P" ]; then bash "\$P" "\$@"; fi
|
|
1654
1723
|
exit 0
|
package/kit/owned-blobs.tsv
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
# Generated by packaging/gen-legacy-blobs.sh from the final release tags v1.0.0..v3.0
|
|
1
|
+
# Generated by packaging/gen-legacy-blobs.sh from the final release tags v1.0.0..v3.1.0 (below VERSION 3.1.1) — do not edit by hand.
|
|
2
2
|
# The flat files Crewforth owns: a copy whose bytes (CR aside) are one of these ids was never edited.
|
|
3
3
|
# install_path blob_sha1 first_tag last_tag
|
|
4
4
|
.claude/AGENT_TEMPLATE.md 075e2c26706c06f56af01652cd24c36918513bee v3.0.0 v3.0.4
|
|
5
5
|
.claude/AGENT_TEMPLATE.md 3fdc551d221ee3665b21ef8cc3563de861a9abe6 v1.4.0 v1.6.3
|
|
6
6
|
.claude/AGENT_TEMPLATE.md 686fed306c873b45c245c77a95a8e6de6ee383d5 v1.7.0 v2.11.0
|
|
7
|
+
.claude/AGENT_TEMPLATE.md 7ab19417b6ff3ac732e2c894be17ffb3ec091f7d v3.1.0 v3.1.0
|
|
7
8
|
.claude/AGENT_TEMPLATE.md 981df9ea27b6f1c5ab5cb509d40875a07603858d v2.12.0 v2.13.0
|
|
8
9
|
.claude/AGENT_TEMPLATE.md b38b9c9b799156e07280ca886230524563acc75c v1.0.5 v1.3.0
|
|
9
10
|
.claude/AGENT_TEMPLATE.md faec09e45543b75763c43bb24114a7b6355649ec v1.0.0 v1.0.4
|
|
@@ -25,6 +26,7 @@
|
|
|
25
26
|
.claude/DISCIPLINE.md 76d73a4e25c559ea361618862cde8480b1a6d8c2 v3.0.0 v3.0.0
|
|
26
27
|
.claude/DISCIPLINE.md b1ab7549b571dd2fc80d5777e0ae3185d29bd7b6 v2.13.0 v2.13.0
|
|
27
28
|
.claude/DISCIPLINE.md b7e1066451a8590e0a2607ad0a6fe80b21599c53 v1.1.3 v1.1.8
|
|
29
|
+
.claude/DISCIPLINE.md b90a067ee323a08146862b889293f8feb49f8e7e v3.1.0 v3.1.0
|
|
28
30
|
.claude/DISCIPLINE.md bf062c2b51a9cf3cc4d0caac389c148cf6fdc977 v2.12.0 v2.12.0
|
|
29
31
|
.claude/DISCIPLINE.md c693f1bd5832ce570058c6eedcacb5dff13d89e8 v1.10.0 v1.10.0
|
|
30
32
|
.claude/DISCIPLINE.md d63a01a183c9a80d739c840a511d338dc2765820 v1.4.0 v1.4.4
|
|
@@ -41,3 +43,4 @@
|
|
|
41
43
|
.claude/README.md 97d4dd1f7f1695052cc1d06cc43f5bcdc603c989 v1.0.0 v1.0.4
|
|
42
44
|
.claude/README.md a3419891cfda7385564008e4befc1007bc5364cb v1.1.1 v1.7.0
|
|
43
45
|
.claude/README.md a3b76e0fa31267610d28b742c122a962f6db59c4 v3.0.1 v3.0.4
|
|
46
|
+
.claude/README.md b6c9b57a67d25b7e4781e4eb61ff9637e6abe5ef v3.1.0 v3.1.0
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crewforth",
|
|
3
|
-
"version": "3.1.
|
|
4
|
-
"description": "Crewforth
|
|
3
|
+
"version": "3.1.1",
|
|
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",
|