@thebassclef/lite 1.9.1 → 1.9.3
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/README.md +37 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/hooks/bassclef-sync.sh +795 -749
- package/dist/lite/.claude/hooks/session-reflection.d/tests/01-adopter-message-channel.test.sh +168 -0
- package/dist/lite/bassclef-version.json +5 -5
- package/dist/lite/presence/install/bassclef-hook-connect.sh +124 -2
- package/dist/lite/standards/lite-manifest.json +2 -2
- package/package.json +2 -2
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: upstream
|
|
3
|
+
# fixtures: mock-git-repos-inline
|
|
4
|
+
# Tier 0 test for adopter message channel (bassclef-upstream#1926).
|
|
5
|
+
#
|
|
6
|
+
# Per @luminary alan-cooper Sam+Louis persona + @luminary donald-norman signifier
|
|
7
|
+
# + @luminary linus-torvalds adopter contract.
|
|
8
|
+
#
|
|
9
|
+
# Source under test: .claude/hooks/session-reflection.d/01-adopter-message-channel.sh
|
|
10
|
+
#
|
|
11
|
+
# test-list (per .claude/rules/test-list-discipline.md — RED first, GREEN as they land):
|
|
12
|
+
#
|
|
13
|
+
# Fragment exists + executable
|
|
14
|
+
# [ ] T00: fragment exists at expected path and is executable
|
|
15
|
+
#
|
|
16
|
+
# Fresh install gate — all 3 files absent → banner fires
|
|
17
|
+
# [ ] T01: fresh install (no whereami, no substrate.config, no .claude/settings) → welcome text
|
|
18
|
+
#
|
|
19
|
+
# Onboarded gate — any of the 3 files present → silent
|
|
20
|
+
# [ ] T02: whereami.md present → silent
|
|
21
|
+
# [ ] T03: substrate.config.md present → silent
|
|
22
|
+
# [ ] T04: .claude/settings.json present → silent
|
|
23
|
+
# [ ] T08: bassclef itself (all 3 files present) → silent (Torvalds T1 fold)
|
|
24
|
+
#
|
|
25
|
+
# Override
|
|
26
|
+
# [ ] T05: SKIP_ADOPTER_MESSAGES=1 → silent (bypass on fresh install)
|
|
27
|
+
#
|
|
28
|
+
# Message body (Cooper C2 fold)
|
|
29
|
+
# [ ] T06: welcome text contains "Welcome to bassclef"
|
|
30
|
+
# [ ] T07: welcome text contains "/onboard-repo"
|
|
31
|
+
|
|
32
|
+
set -u
|
|
33
|
+
|
|
34
|
+
REPO_ROOT="$(cd "$(dirname "$0")/../../../.." && pwd)"
|
|
35
|
+
HOOK="${REPO_ROOT}/.claude/hooks/session-reflection.d/01-adopter-message-channel.sh"
|
|
36
|
+
PASS=0
|
|
37
|
+
FAIL=0
|
|
38
|
+
FAILED_TESTS=()
|
|
39
|
+
|
|
40
|
+
assert() {
|
|
41
|
+
local name="$1"
|
|
42
|
+
local ok="$2"
|
|
43
|
+
local why="$3"
|
|
44
|
+
if [ "$ok" = "1" ]; then
|
|
45
|
+
PASS=$((PASS + 1))
|
|
46
|
+
echo " PASS: $name"
|
|
47
|
+
else
|
|
48
|
+
FAIL=$((FAIL + 1))
|
|
49
|
+
FAILED_TESTS+=("$name — $why")
|
|
50
|
+
echo " FAIL: $name — $why"
|
|
51
|
+
fi
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
TMPROOT="$(mktemp -d -t adopter-message-channel.XXXXXX)"
|
|
55
|
+
cleanup() { rm -rf "$TMPROOT"; }
|
|
56
|
+
trap cleanup EXIT
|
|
57
|
+
|
|
58
|
+
# --- T00: fragment exists + executable ---
|
|
59
|
+
if [ -x "$HOOK" ]; then
|
|
60
|
+
assert "T00: fragment exists + executable" 1 ""
|
|
61
|
+
else
|
|
62
|
+
assert "T00: fragment exists + executable" 0 "not found or not executable: $HOOK"
|
|
63
|
+
echo ""
|
|
64
|
+
echo "=== Results ==="
|
|
65
|
+
echo "PASS: $PASS"
|
|
66
|
+
echo "FAIL: $FAIL"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# --- Fixture builder ---
|
|
71
|
+
run_hook_in() {
|
|
72
|
+
local tmpdir="$1"
|
|
73
|
+
(cd "$tmpdir" && bash "$HOOK" 2>&1)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# --- T01: fresh install (no state files) → welcome text ---
|
|
77
|
+
SC_FRESH="$TMPROOT/fresh"
|
|
78
|
+
mkdir -p "$SC_FRESH"
|
|
79
|
+
OUT_T01=$(run_hook_in "$SC_FRESH")
|
|
80
|
+
if echo "$OUT_T01" | grep -qE "Welcome to bassclef"; then
|
|
81
|
+
assert "T01: fresh install emits welcome text" 1 ""
|
|
82
|
+
else
|
|
83
|
+
assert "T01: fresh install emits welcome text" 0 "no welcome text in: $OUT_T01"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# --- T02: whereami.md present → silent ---
|
|
87
|
+
SC_WHEREAMI="$TMPROOT/whereami-present"
|
|
88
|
+
mkdir -p "$SC_WHEREAMI/docs"
|
|
89
|
+
echo "---" > "$SC_WHEREAMI/docs/whereami.md"
|
|
90
|
+
OUT_T02=$(run_hook_in "$SC_WHEREAMI")
|
|
91
|
+
if [ -z "$OUT_T02" ]; then
|
|
92
|
+
assert "T02: whereami.md present → silent" 1 ""
|
|
93
|
+
else
|
|
94
|
+
assert "T02: whereami.md present → silent" 0 "unexpected output: $OUT_T02"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# --- T03: substrate.config.md present → silent ---
|
|
98
|
+
SC_CONFIG="$TMPROOT/config-present"
|
|
99
|
+
mkdir -p "$SC_CONFIG"
|
|
100
|
+
echo "---" > "$SC_CONFIG/substrate.config.md"
|
|
101
|
+
OUT_T03=$(run_hook_in "$SC_CONFIG")
|
|
102
|
+
if [ -z "$OUT_T03" ]; then
|
|
103
|
+
assert "T03: substrate.config.md present → silent" 1 ""
|
|
104
|
+
else
|
|
105
|
+
assert "T03: substrate.config.md present → silent" 0 "unexpected output: $OUT_T03"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# --- T04: .claude/settings.json present → silent ---
|
|
109
|
+
SC_SETTINGS="$TMPROOT/settings-present"
|
|
110
|
+
mkdir -p "$SC_SETTINGS/.claude"
|
|
111
|
+
echo '{}' > "$SC_SETTINGS/.claude/settings.json"
|
|
112
|
+
OUT_T04=$(run_hook_in "$SC_SETTINGS")
|
|
113
|
+
if [ -z "$OUT_T04" ]; then
|
|
114
|
+
assert "T04: .claude/settings.json present → silent" 1 ""
|
|
115
|
+
else
|
|
116
|
+
assert "T04: .claude/settings.json present → silent" 0 "unexpected output: $OUT_T04"
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# --- T05: SKIP_ADOPTER_MESSAGES=1 bypass on fresh install ---
|
|
120
|
+
SC_SKIP="$TMPROOT/skip"
|
|
121
|
+
mkdir -p "$SC_SKIP"
|
|
122
|
+
OUT_T05=$(SKIP_ADOPTER_MESSAGES=1 run_hook_in "$SC_SKIP")
|
|
123
|
+
if [ -z "$OUT_T05" ] || echo "$OUT_T05" | grep -qE "SKIP_ADOPTER_MESSAGES"; then
|
|
124
|
+
assert "T05: SKIP_ADOPTER_MESSAGES=1 bypasses" 1 ""
|
|
125
|
+
else
|
|
126
|
+
assert "T05: SKIP_ADOPTER_MESSAGES=1 bypasses" 0 "banner fired: $OUT_T05"
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# --- T06: welcome text contains "Welcome to bassclef" ---
|
|
130
|
+
if echo "$OUT_T01" | grep -qE "Welcome to bassclef"; then
|
|
131
|
+
assert "T06: welcome contains 'Welcome to bassclef'" 1 ""
|
|
132
|
+
else
|
|
133
|
+
assert "T06: welcome contains 'Welcome to bassclef'" 0 "missing in: $OUT_T01"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# --- T07: welcome text contains "/onboard-repo" ---
|
|
137
|
+
if echo "$OUT_T01" | grep -qE "/onboard-repo"; then
|
|
138
|
+
assert "T07: welcome contains '/onboard-repo'" 1 ""
|
|
139
|
+
else
|
|
140
|
+
assert "T07: welcome contains '/onboard-repo'" 0 "missing in: $OUT_T01"
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# --- T08: bassclef itself (all 3 files present) → silent ---
|
|
144
|
+
SC_BASSCLEF="$TMPROOT/bassclef"
|
|
145
|
+
mkdir -p "$SC_BASSCLEF/docs" "$SC_BASSCLEF/.claude"
|
|
146
|
+
echo "---" > "$SC_BASSCLEF/docs/whereami.md"
|
|
147
|
+
echo "---" > "$SC_BASSCLEF/substrate.config.md"
|
|
148
|
+
echo '{}' > "$SC_BASSCLEF/.claude/settings.json"
|
|
149
|
+
OUT_T08=$(run_hook_in "$SC_BASSCLEF")
|
|
150
|
+
if [ -z "$OUT_T08" ]; then
|
|
151
|
+
assert "T08: bassclef itself (all 3 present) → silent" 1 ""
|
|
152
|
+
else
|
|
153
|
+
assert "T08: bassclef itself (all 3 present) → silent" 0 "unexpected: $OUT_T08"
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# --- Summary ---
|
|
157
|
+
echo ""
|
|
158
|
+
echo "=== Results ==="
|
|
159
|
+
echo "PASS: $PASS"
|
|
160
|
+
echo "FAIL: $FAIL"
|
|
161
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
162
|
+
echo ""
|
|
163
|
+
echo "Failed tests:"
|
|
164
|
+
for t in "${FAILED_TESTS[@]}"; do
|
|
165
|
+
echo " - $t"
|
|
166
|
+
done
|
|
167
|
+
exit 1
|
|
168
|
+
fi
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "v1.5.
|
|
3
|
-
"marker_tag": "release-2026-09-
|
|
4
|
-
"release_date": "2026-09-
|
|
5
|
-
"release_sha": "
|
|
6
|
-
"release_tag": "release-2026-09-
|
|
2
|
+
"version": "v1.5.2",
|
|
3
|
+
"marker_tag": "release-2026-09-25-26328f36",
|
|
4
|
+
"release_date": "2026-09-25T17:01:41Z",
|
|
5
|
+
"release_sha": "26328f36",
|
|
6
|
+
"release_tag": "release-2026-09-25-26328f36"
|
|
7
7
|
}
|
|
@@ -31,6 +31,102 @@
|
|
|
31
31
|
# If this list changes, update sync.template.sh L777 in the same PR.
|
|
32
32
|
LANE_3_HOOKS=(vendored-dispatcher-refresh.sh session-reflection.sh session-start-recap-inject.sh)
|
|
33
33
|
|
|
34
|
+
# _bhc_resolve_adopter_tier <adopter_cwd> — private helper (per RFC Ousterhout 3a).
|
|
35
|
+
# Precedence: BASSCLEF_TIER env → .bassclef-source.json `tier` field →
|
|
36
|
+
# .claude/bassclef-configs.jsonc `adopter.tier` → empty.
|
|
37
|
+
# Prints tier value to stdout; empty on no-tier-declared.
|
|
38
|
+
_bhc_resolve_adopter_tier() {
|
|
39
|
+
local adopter_cwd="$1"
|
|
40
|
+
|
|
41
|
+
# 1. Env override (test-only + peer coordination)
|
|
42
|
+
if [ -n "${BASSCLEF_TIER:-}" ]; then
|
|
43
|
+
echo "$BASSCLEF_TIER"
|
|
44
|
+
return 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# 2. .bassclef-source.json tier field
|
|
48
|
+
local source_json="$adopter_cwd/.bassclef-source.json"
|
|
49
|
+
if [ -f "$source_json" ] && command -v jq >/dev/null 2>&1; then
|
|
50
|
+
local t
|
|
51
|
+
t=$(jq -r '.tier // empty' "$source_json" 2>/dev/null)
|
|
52
|
+
if [ -n "$t" ]; then
|
|
53
|
+
echo "$t"
|
|
54
|
+
return 0
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# 3. .claude/bassclef-configs.jsonc adopter.tier — reuse lib/tier-check.sh
|
|
59
|
+
# precedent: strip comments + jsonc → json parse via python3.
|
|
60
|
+
local configs="$adopter_cwd/.claude/bassclef-configs.jsonc"
|
|
61
|
+
if [ -f "$configs" ] && command -v python3 >/dev/null 2>&1; then
|
|
62
|
+
local t
|
|
63
|
+
t=$(python3 - "$configs" 2>/dev/null <<'PYEOF'
|
|
64
|
+
import json, re, sys
|
|
65
|
+
try:
|
|
66
|
+
with open(sys.argv[1]) as f:
|
|
67
|
+
content = f.read()
|
|
68
|
+
content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
|
|
69
|
+
content = re.sub(r'//[^\n]*', '', content)
|
|
70
|
+
d = json.loads(content)
|
|
71
|
+
t = d.get('adopter', {}).get('tier', '') or d.get('tier', '')
|
|
72
|
+
if t:
|
|
73
|
+
print(t)
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
76
|
+
PYEOF
|
|
77
|
+
)
|
|
78
|
+
if [ -n "$t" ]; then
|
|
79
|
+
echo "$t"
|
|
80
|
+
return 0
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# 4. No tier declared
|
|
85
|
+
return 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# _bhc_tier_rank <tier> — bassclef tier ordering per ADR-052 D6.
|
|
89
|
+
# Higher rank = broader hook set. lite < standard < ultra < upstream.
|
|
90
|
+
# Prints rank integer to stdout; 0 on unknown.
|
|
91
|
+
_bhc_tier_rank() {
|
|
92
|
+
case "$1" in
|
|
93
|
+
lite) echo 1 ;;
|
|
94
|
+
standard) echo 2 ;;
|
|
95
|
+
ultra) echo 3 ;;
|
|
96
|
+
upstream) echo 4 ;;
|
|
97
|
+
*) echo 0 ;;
|
|
98
|
+
esac
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# _bhc_load_tier_hook_basenames <bassclef_root> <tier> — private helper (per RFC Ousterhout 3a).
|
|
102
|
+
# Prints newline-delimited set of hook basenames in the tier's manifest.
|
|
103
|
+
# Two source patterns per adopter-simulator-lite Assertion 7 pattern:
|
|
104
|
+
# 1. Direct hook — `.claude/hooks/<name>.sh` → basename `<name>.sh`
|
|
105
|
+
# 2. Install template — `presence/install/<name>.template.sh` → basename `<name>.sh`
|
|
106
|
+
# Empty output when manifest missing or malformed (fail-soft per Nygard).
|
|
107
|
+
_bhc_load_tier_hook_basenames() {
|
|
108
|
+
local bassclef_root="$1"
|
|
109
|
+
local tier="$2"
|
|
110
|
+
local manifest="$bassclef_root/${tier}-manifest.json"
|
|
111
|
+
|
|
112
|
+
if [ ! -f "$manifest" ]; then
|
|
113
|
+
return 0
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
117
|
+
return 0
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
if ! jq -e . "$manifest" >/dev/null 2>&1; then
|
|
121
|
+
return 0
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
{
|
|
125
|
+
jq -r '.entries[]? | .path | select(test("^\\.claude/hooks/[^/]+\\.sh$"))' "$manifest" 2>/dev/null | sed -E 's#^\.claude/hooks/##'
|
|
126
|
+
jq -r '.entries[]? | .path | select(test("^presence/install/[^/]+\\.template\\.sh$"))' "$manifest" 2>/dev/null | sed -E 's#^presence/install/##; s#\.template\.sh$#.sh#'
|
|
127
|
+
} | sort -u
|
|
128
|
+
}
|
|
129
|
+
|
|
34
130
|
# bassclef_hook_connect_apply <adopter_cwd> [<bassclef_root>]
|
|
35
131
|
#
|
|
36
132
|
# Reads $bassclef_root/.claude/settings.json (or $BASSCLEF_SOURCE_ROOT if
|
|
@@ -125,12 +221,33 @@ bassclef_hook_connect_apply() {
|
|
|
125
221
|
local lane3_re
|
|
126
222
|
lane3_re=$(printf '%s\n' "${LANE_3_HOOKS[@]}" | sed 's/\.sh$//' | paste -sd'|' -)
|
|
127
223
|
|
|
224
|
+
# === Tier filter (bassclef-upstream#1946) ===
|
|
225
|
+
# Resolve adopter tier + load in-tier hook basenames. Empty tier OR empty
|
|
226
|
+
# basename set → fallback merge-all (grace preserved per ADR-031); stderr
|
|
227
|
+
# signal for observability.
|
|
228
|
+
local adopter_tier tier_basenames tier_re
|
|
229
|
+
adopter_tier=$(_bhc_resolve_adopter_tier "$adopter_cwd" 2>/dev/null || true)
|
|
230
|
+
tier_basenames=""
|
|
231
|
+
tier_re=""
|
|
232
|
+
if [ -n "$adopter_tier" ]; then
|
|
233
|
+
tier_basenames=$(_bhc_load_tier_hook_basenames "$bassclef_root" "$adopter_tier" 2>/dev/null || true)
|
|
234
|
+
if [ -z "$tier_basenames" ]; then
|
|
235
|
+
echo "[bassclef-hook-connect] tier=$adopter_tier but manifest missing/empty — merging all wires" >&2
|
|
236
|
+
else
|
|
237
|
+
# Build regex alternation from basenames (strip .sh for pattern building).
|
|
238
|
+
tier_re=$(printf '%s\n' "$tier_basenames" | sed 's/\.sh$//' | paste -sd'|' -)
|
|
239
|
+
fi
|
|
240
|
+
else
|
|
241
|
+
echo "[bassclef-hook-connect] no tier declared in .bassclef-source.json — merging all wires (grace window). Set tier: lite to filter." >&2
|
|
242
|
+
fi
|
|
243
|
+
|
|
128
244
|
# Merge every hook entry from every event type. Preserves operator-owned
|
|
129
245
|
# entries. Adds bassclef entries under matching (event, matcher) pairs
|
|
130
246
|
# without duplicating commands. Skips Lane 3 hooks at $CLAUDE_PROJECT_DIR
|
|
131
|
-
# per <consumer-repo>#1262.
|
|
247
|
+
# per <consumer-repo>#1262. Filters off-tier entries when tier_re non-empty
|
|
248
|
+
# per bassclef-upstream#1946.
|
|
132
249
|
local merged
|
|
133
|
-
merged=$(jq --slurpfile bassclef "$bassclef_settings" --arg lane3re "$lane3_re" '
|
|
250
|
+
merged=$(jq --slurpfile bassclef "$bassclef_settings" --arg lane3re "$lane3_re" --arg tierre "$tier_re" '
|
|
134
251
|
. as $adopter
|
|
135
252
|
| ($bassclef[0].hooks // {}) as $bhooks
|
|
136
253
|
| .hooks //= {}
|
|
@@ -142,6 +259,11 @@ bassclef_hook_connect_apply() {
|
|
|
142
259
|
($bentry.matcher // "") as $bmatcher |
|
|
143
260
|
($bentry.hooks // [] | map(select(
|
|
144
261
|
(.command // "") | test("\\$CLAUDE_PROJECT_DIR/\\.claude/hooks/(" + $lane3re + ")\\.sh") | not
|
|
262
|
+
)) | map(select(
|
|
263
|
+
# Tier filter: keep only entries whose command basename is in the
|
|
264
|
+
# tier manifest set. Empty tier_re means no filter (fallback).
|
|
265
|
+
($tierre == "") or
|
|
266
|
+
((.command // "") | test("\\.claude/hooks/(" + $tierre + ")\\.sh"))
|
|
145
267
|
))) as $bhookslist |
|
|
146
268
|
if ($bhookslist | length) == 0 then .
|
|
147
269
|
elif any(.hooks[$event][]; (.matcher // "") == $bmatcher) then
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"tier": "lite",
|
|
3
3
|
"manifest_version": "1.11.1",
|
|
4
|
-
"generated_at": "2026-09-
|
|
4
|
+
"generated_at": "2026-09-25T15:57:21Z",
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
7
7
|
"slug": "029-release-pipeline",
|
|
@@ -1303,7 +1303,7 @@
|
|
|
1303
1303
|
"type": "presence-template",
|
|
1304
1304
|
"path": "presence/install/bassclef-hook-connect.sh",
|
|
1305
1305
|
"tier": "lite",
|
|
1306
|
-
"content_hash": "sha256:
|
|
1306
|
+
"content_hash": "sha256:7ac3148028a5f2fd1ca35b84812c664d2ff10b475026f278c78c490f6b12fa21",
|
|
1307
1307
|
"description": "description: Dynamic-derive hook connect — closes bassclef-upstream#625."
|
|
1308
1308
|
},
|
|
1309
1309
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thebassclef/lite",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
4
|
"description": "Bassclef CLI — install and upgrade bassclef in your project with two commands.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bassclef",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"cli",
|
|
9
9
|
"scaffolding"
|
|
10
10
|
],
|
|
11
|
-
"homepage": "https://
|
|
11
|
+
"homepage": "https://docs.bassclef.dev",
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
14
|
"url": "git+https://github.com/sunj-labs/bassclef-cli.git"
|