@thebassclef/lite 1.0.1 → 1.0.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.
Files changed (46) hide show
  1. package/dist/cli.cjs +24 -1
  2. package/dist/cli.js +24 -1
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/lite/.claude/hooks/bassclef-sync.sh +98 -16
  7. package/dist/lite/.claude/hooks/longrun-prep-compounding-sequence-check.sh +3 -3
  8. package/dist/lite/.claude/hooks/session-reflection.d/00-bassclef-hook-connect.sh +63 -0
  9. package/dist/lite/.claude/hooks/session-reflection.d/05-active-bet.sh +113 -0
  10. package/dist/lite/.claude/hooks/session-reflection.d/08-settings-drift.sh +62 -0
  11. package/dist/lite/.claude/hooks/session-reflection.d/10-abrupt-stop-recovery.sh +78 -0
  12. package/dist/lite/.claude/hooks/session-reflection.d/12-session-lock.sh +63 -0
  13. package/dist/lite/.claude/hooks/session-reflection.d/15-substrate-config-validate.sh +91 -0
  14. package/dist/lite/.claude/hooks/session-reflection.d/20-artifact-staleness.sh +98 -0
  15. package/dist/lite/.claude/hooks/session-reflection.d/30-metrics-staleness.sh +61 -0
  16. package/dist/lite/.claude/hooks/session-reflection.d/35-post-merge-whereami-self-heal.sh +180 -0
  17. package/dist/lite/.claude/hooks/session-reflection.d/36-whereami-size-check.sh +117 -0
  18. package/dist/lite/.claude/hooks/session-reflection.d/40-gate-compliance.sh +108 -0
  19. package/dist/lite/.claude/hooks/session-reflection.d/45-auto-sync-status.sh +99 -0
  20. package/dist/lite/.claude/hooks/session-reflection.d/50-evolution-issues.sh +44 -0
  21. package/dist/lite/.claude/hooks/session-reflection.d/55-orientation-gate.sh +187 -0
  22. package/dist/lite/.claude/hooks/session-reflection.d/56-roadmap-reconcile-gate.sh +88 -0
  23. package/dist/lite/.claude/hooks/session-reflection.d/60-deferred-actions.sh +212 -0
  24. package/dist/lite/.claude/hooks/session-reflection.d/70-recent-strategic-artifacts.sh +101 -0
  25. package/dist/lite/.claude/hooks/session-reflection.d/75-prior-r8-findings.sh +100 -0
  26. package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +87 -0
  27. package/dist/lite/.claude/hooks/session-reflection.d/80-workflow-staleness.sh +141 -0
  28. package/dist/lite/.claude/hooks/session-reflection.d/81-hook-manifest-staleness.sh +67 -0
  29. package/dist/lite/.claude/hooks/session-reflection.d/90-release-backlog.sh +140 -0
  30. package/dist/lite/.claude/hooks/session-reflection.d/90-secrets-expiry.sh +104 -0
  31. package/dist/lite/.claude/hooks/session-reflection.d/95-settings-hook-verify.sh +131 -0
  32. package/dist/lite/.claude/hooks/session-reflection.d/95-telemetry-emit.sh +66 -0
  33. package/dist/lite/.claude/hooks/session-reflection.d/97-install-class-dispatch.sh +57 -0
  34. package/dist/lite/.claude/hooks/session-reflection.d/tests/00-bassclef-hook-connect.test.sh +492 -0
  35. package/dist/lite/.claude/hooks/session-reflection.d/tests/05-active-bet.test.sh +336 -0
  36. package/dist/lite/.claude/hooks/session-reflection.d/tests/12-session-lock.test.sh +216 -0
  37. package/dist/lite/.claude/hooks/session-reflection.d/tests/15-substrate-config-validate.test.sh +50 -0
  38. package/dist/lite/.claude/hooks/session-reflection.d/tests/20-artifact-staleness.test.sh +348 -0
  39. package/dist/lite/.claude/hooks/session-reflection.d/tests/36-whereami-size-check.test.sh +189 -0
  40. package/dist/lite/.claude/hooks/session-reflection.d/tests/45-auto-sync-status.test.sh +263 -0
  41. package/dist/lite/.claude/hooks/session-reflection.d/tests/55-orientation-gate.test.sh +595 -0
  42. package/dist/lite/.claude/hooks/session-reflection.d/tests/56-roadmap-reconcile-gate.test.sh +227 -0
  43. package/dist/lite/.claude/hooks/session-reflection.d/tests/75-prior-r8-findings.test.sh +142 -0
  44. package/dist/lite/.claude/hooks/session-reflection.d/tests/90-release-backlog.test.sh +44 -0
  45. package/dist/lite/.claude/hooks/trace-helper.sh +22 -0
  46. package/package.json +1 -1
@@ -0,0 +1,104 @@
1
+ # tier: lite
2
+ # Secrets-expiry scan: parse substrate.secrets.md, surface BLOCKED
3
+ # at session start when any secret is within alert_threshold_days.
4
+ #
5
+ # Schema: standards/state-spine/schemas/substrate-secrets.schema.json
6
+ # Standard: standards/secrets-lifecycle.md
7
+ # Rule: (paired with .claude/rules/substrate-config-schema.md cross-reference)
8
+ # Refs: bassclef#381
9
+ #
10
+ # Behavior:
11
+ # - Silent when no substrate.secrets.md (graceful in repos without secrets)
12
+ # - Silent when all secrets in OK state
13
+ # - Surfaces BLOCKED block listing every WARN / ALERT / EXPIRED secret
14
+ # with rotation procedure path
15
+ # - Override per-session: SKIP_SECRETS_EXPIRY_CHECK=1 (logged to stderr)
16
+
17
+ # Per-session override
18
+ if [ "${SKIP_SECRETS_EXPIRY_CHECK:-0}" = "1" ]; then
19
+ echo "[secrets-expiry: SKIP_SECRETS_EXPIRY_CHECK=1, skipping]" >&2
20
+ return 0 2>/dev/null || true
21
+ fi
22
+
23
+ SECRETS_FILE="${CWD:-$(pwd)}/substrate.secrets.md"
24
+
25
+ # No file → graceful exit (consumer repo without secrets is fine)
26
+ if [ ! -f "$SECRETS_FILE" ]; then
27
+ return 0 2>/dev/null || true
28
+ fi
29
+
30
+ # Locate the status script. Prefer consumer-repo path; fall back to
31
+ # bassclef-vendored path if available.
32
+ STATUS_SCRIPT="${CWD:-$(pwd)}/scripts/secrets-status.sh"
33
+ if [ ! -x "$STATUS_SCRIPT" ] && [ -n "${BASSCLEF_DIR:-}" ]; then
34
+ STATUS_SCRIPT="${BASSCLEF_DIR}/scripts/secrets-status.sh"
35
+ fi
36
+
37
+ if [ ! -x "$STATUS_SCRIPT" ]; then
38
+ # Script missing — surface as BLOCKED with installation hint.
39
+ blocked_banner "secrets-expiry — scripts/secrets-status.sh not found
40
+
41
+ substrate.secrets.md exists at $SECRETS_FILE but the status script is
42
+ missing. The hook can't check expiry without it.
43
+
44
+ Resolution:
45
+ - If this is bassclef: ensure scripts/secrets-status.sh is committed
46
+ - If this is a consumer repo: re-run /onboard-repo to vendor the script
47
+
48
+ Override: SKIP_SECRETS_EXPIRY_CHECK=1 <command>
49
+ Standard: standards/secrets-lifecycle.md"
50
+ return 0 2>/dev/null || true
51
+ fi
52
+
53
+ # Run status script in JSON mode for parseable output
54
+ STATUS_JSON="$(SECRETS_FILE="$SECRETS_FILE" bash "$STATUS_SCRIPT" --json 2>/dev/null || echo '')"
55
+
56
+ if [ -z "$STATUS_JSON" ]; then
57
+ return 0 2>/dev/null || true
58
+ fi
59
+
60
+ # Extract WARN / ALERT / EXPIRED entries via python3 (jq optional).
61
+ # Each line of output is "STATE|NAME|EXPIRES|DAYS|PROCEDURE"
62
+ PROBLEM_ENTRIES="$(echo "$STATUS_JSON" | python3 -c "
63
+ import sys, json
64
+ try:
65
+ d = json.load(sys.stdin)
66
+ except Exception:
67
+ sys.exit(0)
68
+ for s in d.get('secrets', []):
69
+ state = s.get('state', '')
70
+ if state in ('WARN', 'ALERT', 'EXPIRED'):
71
+ print(f\"{state}|{s.get('name','')}|{s.get('expires_at','')}|{s.get('days_remaining','')}|{s.get('rotation_procedure','')}\")
72
+ " 2>/dev/null || echo '')"
73
+
74
+ if [ -z "$PROBLEM_ENTRIES" ]; then
75
+ return 0 2>/dev/null || true
76
+ fi
77
+
78
+ # Build BLOCKED banner content
79
+ MSG="secrets-expiry — at least one credential within alert window
80
+
81
+ "
82
+
83
+ while IFS='|' read -r state name expires days proc; do
84
+ case "$state" in
85
+ EXPIRED) glyph="💀 EXPIRED" ;;
86
+ ALERT) glyph="🛑 ALERT " ;;
87
+ WARN) glyph="⚠️ WARN " ;;
88
+ *) glyph="$state" ;;
89
+ esac
90
+ MSG="$MSG $glyph $name expires $expires ($days days)
91
+ rotate: $proc
92
+ "
93
+ done <<< "$PROBLEM_ENTRIES"
94
+
95
+ MSG="$MSG
96
+ Resolution paths:
97
+ 1. Rotate the credential per its rotation_procedure runbook
98
+ 2. Update substrate.secrets.md (last_rotated + expires_at) after rotation
99
+ 3. Verify with: bash scripts/secrets-status.sh
100
+
101
+ Override (per-session): SKIP_SECRETS_EXPIRY_CHECK=1 <command>
102
+ Standard: standards/secrets-lifecycle.md"
103
+
104
+ blocked_banner "$MSG"
@@ -0,0 +1,131 @@
1
+ # tier: lite
2
+ # Settings hook path verifier — fires at every SessionStart via
3
+ # session-reflection.sh. Reads project settings.json (and user
4
+ # settings.json when reachable), enumerates every hook command path,
5
+ # checks each file exists on disk. Fires BLOCKED banner when any wired
6
+ # hook path resolves to a missing file.
7
+ #
8
+ # Closes the silent staleness class per bassclef-upstream#888. When a
9
+ # hook path in settings.json points at a file that got renamed, deleted,
10
+ # or never installed, SessionStart today prints a non-blocking error and
11
+ # the session continues. Adopters miss the signal. Substrate discipline
12
+ # quietly disappears from that session's context.
13
+ #
14
+ # Sourced (not exec'd) by session-reflection.sh. Inherits:
15
+ # - blocked_banner() function
16
+ # - trace_log() function
17
+ # - BASSCLEF_DIR environment variable
18
+ # - CWD (project root; the harness sets it before source)
19
+ #
20
+ # Per @luminary saltzer-schroeder — complete mediation. Every settings
21
+ # entry gets checked, not sampled.
22
+ # Per @luminary linus-torvalds — we do not break adopters. A missing hook
23
+ # is a silent break of the adopter contract; this check surfaces it.
24
+ #
25
+ # Override path — operator can bypass per-session.
26
+ if [ "${SKIP_SETTINGS_HOOK_VERIFY:-0}" = "1" ]; then
27
+ trace_log "skip" "settings-hook-verify" "SKIP_SETTINGS_HOOK_VERIFY=1" 2>/dev/null || true
28
+ return 0 2>/dev/null || true
29
+ fi
30
+
31
+ # Require jq for JSON parsing. Skip silently when absent (rare).
32
+ if ! command -v jq >/dev/null 2>&1; then
33
+ trace_log "skip" "settings-hook-verify" "jq not available" 2>/dev/null || true
34
+ return 0 2>/dev/null || true
35
+ fi
36
+
37
+ __shv_settings_files=""
38
+ __shv_project_settings="${CWD:-${PWD}}/.claude/settings.json"
39
+ if [ -f "$__shv_project_settings" ]; then
40
+ __shv_settings_files="$__shv_project_settings"
41
+ fi
42
+ __shv_user_settings="${HOME}/.claude/settings.json"
43
+ if [ -f "$__shv_user_settings" ]; then
44
+ __shv_settings_files="${__shv_settings_files:+${__shv_settings_files}$'\n'}${__shv_user_settings}"
45
+ fi
46
+
47
+ if [ -z "$__shv_settings_files" ]; then
48
+ trace_log "skip" "settings-hook-verify" "no settings.json readable" 2>/dev/null || true
49
+ unset __shv_settings_files __shv_project_settings __shv_user_settings
50
+ return 0 2>/dev/null || true
51
+ fi
52
+
53
+ # Extract the first token of every hook command across every event class.
54
+ # The command string may be plain path OR `path && next` OR `env=X path`.
55
+ # We take the first whitespace-separated token.
56
+ __shv_missing=""
57
+ __shv_check_file() {
58
+ local raw_cmd="$1"
59
+ local source_file="$2"
60
+ # First token — everything up to the first space, tab, or ampersand.
61
+ local first_token
62
+ first_token=$(printf '%s' "$raw_cmd" | awk '{print $1}')
63
+ # Skip if empty or does not look like a path (starts with quote or brace).
64
+ case "$first_token" in
65
+ ''|'{'*|'['*) return 0 ;;
66
+ esac
67
+ # Expand known env vars. Leave others as-is (best effort).
68
+ local expanded="$first_token"
69
+ expanded="${expanded//\$HOME/$HOME}"
70
+ expanded="${expanded//\$\{HOME\}/$HOME}"
71
+ expanded="${expanded//\$CLAUDE_PROJECT_DIR/${CWD:-${PWD}}}"
72
+ expanded="${expanded//\$\{CLAUDE_PROJECT_DIR\}/${CWD:-${PWD}}}"
73
+ # If still has an unexpanded $VAR, skip (cannot verify with confidence).
74
+ case "$expanded" in
75
+ *'$'*) return 0 ;;
76
+ esac
77
+ # Absolute path required for the check.
78
+ case "$expanded" in
79
+ /*) : ;;
80
+ *) return 0 ;;
81
+ esac
82
+ if [ ! -f "$expanded" ]; then
83
+ __shv_missing="${__shv_missing:+${__shv_missing}$'\n'} - ${expanded} (from ${source_file})"
84
+ fi
85
+ }
86
+
87
+ __shv_scan_settings() {
88
+ local settings="$1"
89
+ local raw
90
+ # Extract every .hooks.*[].hooks[].command as one line per command.
91
+ raw=$(jq -r '
92
+ .hooks // {}
93
+ | to_entries[]?
94
+ | .value[]?
95
+ | .hooks[]?
96
+ | select(.command != null)
97
+ | .command
98
+ ' "$settings" 2>/dev/null)
99
+ if [ -z "$raw" ]; then
100
+ return 0
101
+ fi
102
+ local line
103
+ while IFS= read -r line; do
104
+ [ -n "$line" ] && __shv_check_file "$line" "$settings"
105
+ done <<< "$raw"
106
+ }
107
+
108
+ # Scan each settings file.
109
+ while IFS= read -r __shv_file; do
110
+ [ -n "$__shv_file" ] && __shv_scan_settings "$__shv_file"
111
+ done <<< "$__shv_settings_files"
112
+
113
+ if [ -n "$__shv_missing" ]; then
114
+ blocked_banner "settings-hook-verify" \
115
+ "Hooks wired in settings.json but missing on disk:" \
116
+ "$__shv_missing" \
117
+ "" \
118
+ "Cure paths:" \
119
+ " 1. Re-run bassclef-sync.sh to restore vendored/symlinked hooks" \
120
+ " 2. Edit the settings file and remove the stale entry" \
121
+ " 3. Override this session: SKIP_SETTINGS_HOOK_VERIFY=1" \
122
+ "" \
123
+ "Per bassclef-upstream#888."
124
+ trace_log "fired" "settings-hook-verify" "missing hooks detected" 2>/dev/null || true
125
+ else
126
+ trace_log "pass" "settings-hook-verify" "all wired hooks resolve" 2>/dev/null || true
127
+ fi
128
+
129
+ unset __shv_settings_files __shv_project_settings __shv_user_settings \
130
+ __shv_missing __shv_file
131
+ unset -f __shv_check_file __shv_scan_settings
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env bash
2
+ # .claude/hooks/session-reflection.d/95-telemetry-emit.sh
3
+ # tier: lite
4
+ # install-class: dual
5
+ #
6
+ # Session-start telemetry emit fragment (bet 2026-07-31f Step 4).
7
+ #
8
+ # Emits `session_start` event when opt-in is ON. If no prior events file
9
+ # exists, also emits an `install_probe` event once — this is the first-
10
+ # session marker used to correlate install source.
11
+ #
12
+ # Silent-fail per @luminary michael-nygard R1 — never crashes session start.
13
+
14
+ # Coordinator-side helpers are provided by the parent session-reflection loop.
15
+ # When sourced from that context, `blocked_banner` + `trace_log` + `BASSCLEF_DIR`
16
+ # are already in scope. When run standalone, we no-op the helpers to stay safe.
17
+
18
+ if ! declare -F trace_log >/dev/null 2>&1; then
19
+ trace_log() { :; }
20
+ fi
21
+ BASSCLEF_DIR="${BASSCLEF_DIR:-${CWD:-$PWD}}"
22
+
23
+ # Probe bundled lib path first (dist/lite/ ship path per #1631 goal
24
+ # 2026-09-14b), then $BASSCLEF_DIR fallback for synced adopters.
25
+ # Pattern matches session-reflection.sh:13.
26
+ _sd="$(dirname "${BASH_SOURCE[0]:-$0}")"
27
+ TL_LIB=""
28
+ for _c in "${_sd}/../../../lib/telemetry.sh" \
29
+ "$BASSCLEF_DIR/lib/telemetry.sh"; do
30
+ [ -f "$_c" ] && TL_LIB="$_c" && break
31
+ done
32
+
33
+ # Fire in a silent-fail block so any error here does not crash session start.
34
+ {
35
+ # Only fire when the lib exists (adopter has telemetry enabled)
36
+ if [ -n "$TL_LIB" ]; then
37
+ # shellcheck source=/dev/null
38
+ source "$TL_LIB" 2>/dev/null || return 0 2>/dev/null || exit 0
39
+
40
+ if telemetry_is_enabled; then
41
+ sid=$(telemetry_session_id)
42
+ today=$(date -u +%Y%m%d)
43
+ events_dir="$BASSCLEF_DIR/state/telemetry"
44
+
45
+ # Install probe fires once per install — check if any prior events exist
46
+ if ! compgen -G "$events_dir/events-*.jsonl" >/dev/null 2>&1; then
47
+ # First session ever — emit install_probe (source: unknown for MVP)
48
+ install_source="unknown"
49
+ [ -d "$BASSCLEF_DIR/node_modules/@thebassclef" ] && install_source="npm"
50
+ [ -f "$BASSCLEF_DIR/.git/HEAD" ] && install_source="github-clone"
51
+
52
+ uid=$(uuidgen 2>/dev/null | tr '[:upper:]' '[:lower:]' || echo "00000000-0000-4000-8000-000000000000")
53
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
54
+ telemetry_emit "{\"event_type\":\"install_probe\",\"event_id\":\"$uid\",\"session_id\":\"$sid\",\"timestamp\":\"$ts\",\"tier\":\"lite\",\"payload\":{\"install_source\":\"$install_source\"}}"
55
+ fi
56
+
57
+ # Emit session_start
58
+ uid=$(uuidgen 2>/dev/null | tr '[:upper:]' '[:lower:]' || echo "00000000-0000-4000-8000-000000000001")
59
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
60
+ telemetry_emit "{\"event_type\":\"session_start\",\"event_id\":\"$uid\",\"session_id\":\"$sid\",\"timestamp\":\"$ts\",\"tier\":\"lite\",\"payload\":{}}"
61
+ trace_log "95-telemetry-emit: session_start emitted for session $sid"
62
+ fi
63
+ fi
64
+ } 2>/dev/null || true
65
+
66
+ return 0 2>/dev/null || exit 0
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # tier: lite
3
+ # install-class: dual
4
+ #
5
+ # 97-install-class-dispatch.sh — fragment that sources the install-class
6
+ # dispatch libs so the source-graph closure walker discovers them at
7
+ # build time (per scripts/build-adopter-tree.sh).
8
+ #
9
+ # Behavior today (V0 — goal 14c Steps 0-4 landed):
10
+ # - Sources lib/hook-header-parser.sh + lib/hook-installer.sh + lib/source-graph-walker.sh
11
+ # - Does NOT call install_by_class yet — Steps 5-6 extend this fragment
12
+ # to iterate substrate hooks and dispatch
13
+ #
14
+ # Why source-without-call:
15
+ # - Walker discovers libs via source-graph closure (per canvas Stage 2)
16
+ # - LITE_LIBS ships the 3 libs to dist/lite/lib/
17
+ # - Walker+LITE_LIBS sync check requires walker to find every LITE_LIBS entry
18
+ # - Sourcing without calling makes the libs walker-discoverable without
19
+ # changing SessionStart behavior — belt-and-braces for the Step 5-6 wire
20
+ #
21
+ # Silent-fail per ADR-050 R7 — never crash SessionStart on lib source failure
22
+
23
+ _ICD_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
+
25
+ # Source each dispatch lib. Bundled path first (dist/lite/lib/), $BASSCLEF_DIR fallback.
26
+ # Static .sh literals so scripts/build-adopter-tree.sh source-graph walker discovers them.
27
+
28
+ # hook-header-parser.sh
29
+ if [ -f "$_ICD_SCRIPT_DIR/../../../lib/hook-header-parser.sh" ]; then
30
+ # shellcheck source=/dev/null
31
+ { source "$_ICD_SCRIPT_DIR/../../../lib/hook-header-parser.sh"; } 2>/dev/null || true
32
+ elif [ -f "${BASSCLEF_DIR:-}/lib/hook-header-parser.sh" ]; then
33
+ # shellcheck source=/dev/null
34
+ { source "${BASSCLEF_DIR:-}/lib/hook-header-parser.sh"; } 2>/dev/null || true
35
+ fi
36
+
37
+ # hook-installer.sh
38
+ if [ -f "$_ICD_SCRIPT_DIR/../../../lib/hook-installer.sh" ]; then
39
+ # shellcheck source=/dev/null
40
+ { source "$_ICD_SCRIPT_DIR/../../../lib/hook-installer.sh"; } 2>/dev/null || true
41
+ elif [ -f "${BASSCLEF_DIR:-}/lib/hook-installer.sh" ]; then
42
+ # shellcheck source=/dev/null
43
+ { source "${BASSCLEF_DIR:-}/lib/hook-installer.sh"; } 2>/dev/null || true
44
+ fi
45
+
46
+ # source-graph-walker.sh
47
+ if [ -f "$_ICD_SCRIPT_DIR/../../../lib/source-graph-walker.sh" ]; then
48
+ # shellcheck source=/dev/null
49
+ { source "$_ICD_SCRIPT_DIR/../../../lib/source-graph-walker.sh"; } 2>/dev/null || true
50
+ elif [ -f "${BASSCLEF_DIR:-}/lib/source-graph-walker.sh" ]; then
51
+ # shellcheck source=/dev/null
52
+ { source "${BASSCLEF_DIR:-}/lib/source-graph-walker.sh"; } 2>/dev/null || true
53
+ fi
54
+
55
+ # Steps 5-6 will extend below with actual install_by_class calls per
56
+ # goal 2026-09-14c step sequencing (docs/iteration-bets/2026-09-14c-*.md).
57
+ # Today the fragment sources-without-calls to satisfy walker discovery.