@thebassclef/lite 1.9.2 → 1.9.4
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 +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/bassclef-version.json +5 -5
- package/dist/lite/lib/bassclef-dir-resolver.sh +20 -14
- package/dist/lite/presence/install/bassclef-hook-connect.sh +178 -2
- package/dist/lite/standards/lite-manifest.json +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ your browser.
|
|
|
63
63
|
|
|
64
64
|
## Current release
|
|
65
65
|
|
|
66
|
-
<!-- version-start -->1.9.
|
|
66
|
+
<!-- version-start -->1.9.4<!-- version-end -->
|
|
67
67
|
|
|
68
68
|
See [CHANGELOG.md](CHANGELOG.md) for the full changelog. Adopter-facing
|
|
69
69
|
release notes live at [docs.bassclef.dev/docs/whats-new/releases](https://docs.bassclef.dev/docs/whats-new/releases).
|
package/dist/index.cjs
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version: "1.9.
|
|
1
|
+
export declare const version: "1.9.4";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "v1.
|
|
3
|
-
"marker_tag": "release-2026-09-
|
|
4
|
-
"release_date": "2026-09-
|
|
5
|
-
"release_sha": "
|
|
6
|
-
"release_tag": "release-2026-09-
|
|
2
|
+
"version": "v1.6.0",
|
|
3
|
+
"marker_tag": "release-2026-09-26-db89d305",
|
|
4
|
+
"release_date": "2026-09-26T12:19:56Z",
|
|
5
|
+
"release_sha": "db89d305",
|
|
6
|
+
"release_tag": "release-2026-09-26-db89d305"
|
|
7
7
|
}
|
|
@@ -8,14 +8,17 @@
|
|
|
8
8
|
# file at $HOME/.claude/hooks/, not a symlink to a checkout).
|
|
9
9
|
#
|
|
10
10
|
# The resolver probes four paths in order per Chain-of-Responsibility:
|
|
11
|
-
# 1.
|
|
12
|
-
#
|
|
13
|
-
#
|
|
11
|
+
# 1. Bundled path — walk up from $HOOK_REAL_PATH with sentinel present
|
|
12
|
+
# (was Check 3 pre-#1954; moved to Check 1 for #1953 shadow class cure —
|
|
13
|
+
# adopters with adjacent bassclef checkouts now get the npm install path)
|
|
14
|
+
# 2. Peer-checkout — $CWD/../bassclef with sentinel file present
|
|
15
|
+
# 3. $HOME/bassclef — tarball adopter path with sentinel file present
|
|
14
16
|
# 4. Fallback — $CWD (adopter repo); safer than $HOME because the files
|
|
15
17
|
# fragments want ship into the repo via bassclef init
|
|
16
18
|
#
|
|
17
19
|
# The sentinel file is presence/install/bassclef-hook-connect.sh. Every
|
|
18
20
|
# probe requires it — an empty peer dir or empty $HOME/bassclef does NOT win.
|
|
21
|
+
# Nygard R1 fold — bundled probe fail-through preserves peer-checkout adopters.
|
|
19
22
|
#
|
|
20
23
|
# Silent contract: never crashes, never writes to stderr on the resolve path.
|
|
21
24
|
# Consumers (13 fragments plus session-reflection.sh coordinator) treat the
|
|
@@ -42,17 +45,9 @@ resolve_bassclef_dir() {
|
|
|
42
45
|
local home_dir="${HOME_OVERRIDE:-${HOME:-/nonexistent}}"
|
|
43
46
|
local sentinel="presence/install/bassclef-hook-connect.sh"
|
|
44
47
|
|
|
45
|
-
# 1.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
# 2. $HOME/bassclef — tarball adopter path
|
|
51
|
-
if [ -d "$home_dir/bassclef" ] && [ -f "$home_dir/bassclef/$sentinel" ]; then
|
|
52
|
-
printf '%s\n' "$home_dir/bassclef" && return 0
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
# 3. Bundled path — walk up two levels from dispatcher
|
|
48
|
+
# 1. Bundled path — walk up two levels from dispatcher (SWAPPED to Check 1
|
|
49
|
+
# per #1954 Layer 1a — closes #1953 shadow class where an adjacent
|
|
50
|
+
# bassclef checkout at $CWD/../bassclef was winning over the npm install).
|
|
56
51
|
if [ -n "$hook_path" ]; then
|
|
57
52
|
local candidate
|
|
58
53
|
candidate="$( cd "$(dirname "$hook_path")/../.." 2>/dev/null && pwd )" || candidate=""
|
|
@@ -61,6 +56,17 @@ resolve_bassclef_dir() {
|
|
|
61
56
|
fi
|
|
62
57
|
fi
|
|
63
58
|
|
|
59
|
+
# 2. Peer-checkout — sibling bassclef dir next to $CWD (Nygard R1 fold —
|
|
60
|
+
# preserves peer-checkout adopters when bundled probe fails).
|
|
61
|
+
if [ -d "$cwd/../bassclef" ] && [ -f "$cwd/../bassclef/$sentinel" ]; then
|
|
62
|
+
( cd "$cwd/../bassclef" 2>/dev/null && pwd ) && return 0
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# 3. $HOME/bassclef — tarball adopter path
|
|
66
|
+
if [ -d "$home_dir/bassclef" ] && [ -f "$home_dir/bassclef/$sentinel" ]; then
|
|
67
|
+
printf '%s\n' "$home_dir/bassclef" && return 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
64
70
|
# 4. Fallback — $CWD (adopter repo). Safer than $HOME because the files
|
|
65
71
|
# fragments want (init manifest, presence/install/*) ship into the repo.
|
|
66
72
|
( cd "$cwd" 2>/dev/null && pwd ) || printf '%s\n' "$cwd"
|
|
@@ -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,78 @@ 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
|
+
|
|
244
|
+
# === Filter-on-read (bassclef-upstream#1954) ===
|
|
245
|
+
# For each bassclef hook command, verify the target file exists at
|
|
246
|
+
# $bassclef_root/.claude/hooks/<basename>. Skip any entry whose target is
|
|
247
|
+
# missing. Log every skip to stderr. Emit warn banner over threshold.
|
|
248
|
+
# Saltzer-Schroeder R4 fold — matches both $CLAUDE_PROJECT_DIR- and
|
|
249
|
+
# $HOME-prefixed commands via basename extraction from .claude/hooks/ path.
|
|
250
|
+
# Nygard R2 mitigation — [ -f ] with symlink follow.
|
|
251
|
+
local present_re=""
|
|
252
|
+
local skip_count=0
|
|
253
|
+
local skipped_targets_file="$adopter_cwd/.bassclef-hook-connect-skipped.$$.tmp"
|
|
254
|
+
: > "$skipped_targets_file"
|
|
255
|
+
local present_basenames_file="$adopter_cwd/.bassclef-hook-connect-present.$$.tmp"
|
|
256
|
+
: > "$present_basenames_file"
|
|
257
|
+
local bhc_cmd bhc_base bhc_target
|
|
258
|
+
while IFS= read -r bhc_cmd; do
|
|
259
|
+
[ -z "$bhc_cmd" ] && continue
|
|
260
|
+
bhc_base=$(echo "$bhc_cmd" | grep -oE '\.claude/hooks/[^"'"'"'[:space:]\\]+\.sh' | head -1 | sed 's#\.claude/hooks/##')
|
|
261
|
+
if [ -z "$bhc_base" ]; then
|
|
262
|
+
continue
|
|
263
|
+
fi
|
|
264
|
+
bhc_target="$bassclef_root/.claude/hooks/$bhc_base"
|
|
265
|
+
if [ -f "$bhc_target" ] || [ -L "$bhc_target" ]; then
|
|
266
|
+
echo "$bhc_base" >> "$present_basenames_file"
|
|
267
|
+
else
|
|
268
|
+
echo "$bhc_target" >> "$skipped_targets_file"
|
|
269
|
+
skip_count=$((skip_count + 1))
|
|
270
|
+
echo "[bassclef-hook-connect] skip — target missing at $bhc_target" >&2
|
|
271
|
+
fi
|
|
272
|
+
done < <(jq -r '.hooks // {} | to_entries[] | .value[]? | .hooks[]? | .command // empty' "$bassclef_settings" 2>/dev/null)
|
|
273
|
+
|
|
274
|
+
if [ -s "$present_basenames_file" ]; then
|
|
275
|
+
present_re=$(sort -u "$present_basenames_file" | sed 's/\.sh$//' | paste -sd'|' -)
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
# Warn banner over threshold. Threshold=0 disables banner but per-skip
|
|
279
|
+
# stderr above still fires.
|
|
280
|
+
local warn_threshold="${BASSCLEF_HOOK_CONNECT_SKIP_WARN:-5}"
|
|
281
|
+
if [ "$skip_count" -gt 0 ] && [ "$warn_threshold" -gt 0 ] && [ "$skip_count" -gt "$warn_threshold" ]; then
|
|
282
|
+
echo "[bassclef-hook-connect] WARN — $skip_count entries skipped (target files missing). Top 3:" >&2
|
|
283
|
+
sort -u "$skipped_targets_file" | head -3 | sed 's/^/ - /' >&2
|
|
284
|
+
echo " Set BASSCLEF_HOOK_CONNECT_SKIP_WARN=N to change threshold, or 0 to disable banner." >&2
|
|
285
|
+
fi
|
|
286
|
+
rm -f "$present_basenames_file" "$skipped_targets_file"
|
|
287
|
+
|
|
128
288
|
# Merge every hook entry from every event type. Preserves operator-owned
|
|
129
289
|
# entries. Adds bassclef entries under matching (event, matcher) pairs
|
|
130
290
|
# without duplicating commands. Skips Lane 3 hooks at $CLAUDE_PROJECT_DIR
|
|
131
|
-
# per <consumer-repo>#1262.
|
|
291
|
+
# per <consumer-repo>#1262. Filters off-tier entries when tier_re non-empty
|
|
292
|
+
# per bassclef-upstream#1946. Filters missing-target entries when
|
|
293
|
+
# present_re non-empty per bassclef-upstream#1954.
|
|
132
294
|
local merged
|
|
133
|
-
merged=$(jq --slurpfile bassclef "$bassclef_settings" --arg lane3re "$lane3_re" '
|
|
295
|
+
merged=$(jq --slurpfile bassclef "$bassclef_settings" --arg lane3re "$lane3_re" --arg tierre "$tier_re" --arg presentre "$present_re" '
|
|
134
296
|
. as $adopter
|
|
135
297
|
| ($bassclef[0].hooks // {}) as $bhooks
|
|
136
298
|
| .hooks //= {}
|
|
@@ -142,6 +304,20 @@ bassclef_hook_connect_apply() {
|
|
|
142
304
|
($bentry.matcher // "") as $bmatcher |
|
|
143
305
|
($bentry.hooks // [] | map(select(
|
|
144
306
|
(.command // "") | test("\\$CLAUDE_PROJECT_DIR/\\.claude/hooks/(" + $lane3re + ")\\.sh") | not
|
|
307
|
+
)) | map(select(
|
|
308
|
+
# Tier filter: keep only entries whose command basename is in the
|
|
309
|
+
# tier manifest set. Empty tier_re means no filter (fallback).
|
|
310
|
+
($tierre == "") or
|
|
311
|
+
((.command // "") | test("\\.claude/hooks/(" + $tierre + ")\\.sh"))
|
|
312
|
+
)) | map(select(
|
|
313
|
+
# Filter-on-read (bassclef-upstream#1954): keep only entries whose
|
|
314
|
+
# target hook file exists at $bassclef_root/.claude/hooks/<basename>.
|
|
315
|
+
# Empty present_re means no filter (fallback merge-all — matches
|
|
316
|
+
# the tier filter fallback shape). Non-.claude/hooks commands pass
|
|
317
|
+
# through (not our target class).
|
|
318
|
+
($presentre == "") or
|
|
319
|
+
((.command // "") | test("\\.claude/hooks/[a-zA-Z0-9._-]+\\.sh") | not) or
|
|
320
|
+
((.command // "") | test("\\.claude/hooks/(" + $presentre + ")\\.sh"))
|
|
145
321
|
))) as $bhookslist |
|
|
146
322
|
if ($bhookslist | length) == 0 then .
|
|
147
323
|
elif any(.hooks[$event][]; (.matcher // "") == $bmatcher) then
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"tier": "lite",
|
|
3
|
-
"manifest_version": "1.11.
|
|
4
|
-
"generated_at": "2026-09-
|
|
3
|
+
"manifest_version": "1.11.2",
|
|
4
|
+
"generated_at": "2026-09-26T11:18:11Z",
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
7
7
|
"slug": "029-release-pipeline",
|
|
@@ -736,7 +736,7 @@
|
|
|
736
736
|
"type": "lib",
|
|
737
737
|
"path": "lib/bassclef-dir-resolver.sh",
|
|
738
738
|
"tier": "lite",
|
|
739
|
-
"content_hash": "sha256:
|
|
739
|
+
"content_hash": "sha256:7b0f95e282983cca1edcf27083526edabc964e72cd0ef3596c7c8df915e24f92",
|
|
740
740
|
"description": "bassclef-dir-resolver.sh — resolve BASSCLEF_DIR across install layouts."
|
|
741
741
|
},
|
|
742
742
|
{
|
|
@@ -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:a50c3ca715f4f786e2bcbbd804923711f34fe29924aa7a487808934612993ec6",
|
|
1307
1307
|
"description": "description: Dynamic-derive hook connect — closes bassclef-upstream#625."
|
|
1308
1308
|
},
|
|
1309
1309
|
{
|