@thebassclef/lite 1.1.1 → 1.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/dist/cli.cjs +23 -6
- package/dist/cli.js +23 -6
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/bassclef-configs.jsonc +56 -0
- package/dist/lite/.claude/bassclef-orientation.md +120 -0
- package/dist/lite/.claude/hooks/adr-discipline-check.sh +132 -0
- package/dist/lite/.claude/hooks/atomic-pr-check.sh +169 -0
- package/dist/lite/.claude/hooks/auto-save-idle.sh +51 -0
- package/dist/lite/.claude/hooks/bassclef-sync.sh +51 -13
- package/dist/lite/.claude/hooks/bet-doc-gate.sh +283 -0
- package/dist/lite/.claude/hooks/bug-diagnosis.sh +64 -0
- package/dist/lite/.claude/hooks/destructive-command-guard.sh +136 -0
- package/dist/lite/.claude/hooks/file-rename-discipline-check.sh +147 -0
- package/dist/lite/.claude/hooks/hook-classifier-canary.sh +158 -0
- package/dist/lite/.claude/hooks/kiss-default-check.sh +129 -0
- package/dist/lite/.claude/hooks/multi-tenancy-check.sh +83 -0
- package/dist/lite/.claude/hooks/oo-ad-entry-point-check.sh +216 -0
- package/dist/lite/.claude/hooks/operator-private-path-check.sh +203 -0
- package/dist/lite/.claude/hooks/pattern-annotation-validate.sh +142 -0
- package/dist/lite/.claude/hooks/plain-english-steering.sh +15 -1
- package/dist/lite/.claude/hooks/post-write-trace.sh +66 -0
- package/dist/lite/.claude/hooks/pr-body-scrub-check.sh +446 -0
- package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +137 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-body-scrub.sh +192 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-title-scrub.sh +172 -0
- package/dist/lite/.claude/hooks/role-entry-gate.sh +115 -0
- package/dist/lite/.claude/hooks/save-state.sh +268 -0
- package/dist/lite/.claude/hooks/session-reflection.d/08-settings-drift.sh +8 -1
- package/dist/lite/.claude/hooks/session-reflection.d/10-abrupt-stop-recovery.sh +52 -0
- package/dist/lite/.claude/hooks/session-reflection.d/55-orientation-gate.sh +40 -4
- package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +12 -5
- package/dist/lite/.claude/hooks/session-reflection.d/81-hook-manifest-staleness.sh +18 -2
- package/dist/lite/.claude/hooks/session-reflection.d/91-npm-version-drift.sh +54 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/10-abrupt-stop-recovery.test.sh +95 -0
- package/dist/lite/.claude/hooks/session-reflection.sh +62 -15
- package/dist/lite/.claude/hooks/tool-failure-diagnosis.sh +50 -0
- package/dist/lite/.claude/luminaries/andrej-karpathy.md +165 -0
- package/dist/lite/.claude/luminaries/dave-gray.md +155 -0
- package/dist/lite/.claude/luminaries/gary-klein.md +166 -0
- package/dist/lite/.claude/luminaries/kaoru-ishikawa.md +162 -0
- package/dist/lite/.claude/luminaries/lean-product-canvas.md +33 -0
- package/dist/lite/.claude/luminaries/luke-wroblewski.md +224 -0
- package/dist/lite/.claude/luminaries/ryan-singer.md +113 -0
- package/dist/lite/.claude/luminaries/steve-krug.md +168 -0
- package/dist/lite/.claude/luminaries/tony-ulwick.md +134 -0
- package/dist/lite/.claude/rules/arc-level-orientation-before-leaf.md +60 -0
- package/dist/lite/.claude/rules/architect-review-discipline.md +71 -0
- package/dist/lite/.claude/rules/auto-fix-criteria.md +126 -0
- package/dist/lite/.claude/rules/bassclef-hook-drift-blocker.md +144 -0
- package/dist/lite/.claude/rules/bassclef-models-discovery.md +107 -0
- package/dist/lite/.claude/rules/bassclef-workflow-parity.md +104 -0
- package/dist/lite/.claude/rules/cost-governance.md +154 -0
- package/dist/lite/.claude/rules/file-rename-discipline.md +95 -0
- package/dist/lite/.claude/rules/mechanism-fidelity.md +88 -0
- package/dist/lite/.claude/rules/prototype-production-coupling.md +125 -0
- package/dist/lite/.claude/rules/quick-fix-criteria.md +90 -0
- package/dist/lite/.claude/rules/session-artifacts.md +30 -0
- package/dist/lite/.claude/rules/sibling-smoke-after-substrate-change.md +107 -0
- package/dist/lite/.claude/rules/substrate-as-system.md +67 -0
- package/dist/lite/.claude/rules/usability.md +22 -0
- package/dist/lite/.claude/settings.json +20 -0
- package/dist/lite/.claude/skills/agent-research-spawn/SKILL.md +100 -0
- package/dist/lite/.claude/skills/architect-review/SKILL.md +201 -0
- package/dist/lite/.claude/skills/auto-fix/SKILL.md +297 -0
- package/dist/lite/.claude/skills/autonomous/SKILL.md +300 -0
- package/dist/lite/.claude/skills/bind-subdomain/SKILL.md +232 -0
- package/dist/lite/.claude/skills/canvas/SKILL.md +41 -0
- package/dist/lite/.claude/skills/deploy-prod/SKILL.md +106 -0
- package/dist/lite/.claude/skills/docs-sync/SKILL.md +188 -0
- package/dist/lite/.claude/skills/eisenhower/SKILL.md +273 -0
- package/dist/lite/.claude/skills/empathy-map/SKILL.md +168 -0
- package/dist/lite/.claude/skills/frontend-design/SKILL.md +53 -0
- package/dist/lite/.claude/skills/interview-me/SKILL.md +171 -0
- package/dist/lite/.claude/skills/journal-export/SKILL.md +293 -0
- package/dist/lite/.claude/skills/jtbd-tasks/SKILL.md +203 -0
- package/dist/lite/.claude/skills/objectory-decompose/SKILL.md +256 -0
- package/dist/lite/.claude/skills/pick-luminaries/SKILL.md +100 -0
- package/dist/lite/.claude/skills/pre-mortem/SKILL.md +180 -0
- package/dist/lite/.claude/skills/prototype-gallery/SKILL.md +264 -0
- package/dist/lite/.claude/skills/provision-deploy-host/SKILL.md +257 -0
- package/dist/lite/.claude/skills/quick-fix/SKILL.md +273 -0
- package/dist/lite/.claude/skills/release/SKILL.md +311 -0
- package/dist/lite/.claude/skills/release-notes/SKILL.md +202 -0
- package/dist/lite/.claude/skills/rfc/SKILL.md +222 -0
- package/dist/lite/.claude/skills/riff-prototypes/SKILL.md +637 -0
- package/dist/lite/.claude/skills/secrets-bootstrap/SKILL.md +209 -0
- package/dist/lite/.claude/skills/skill/SKILL.md +304 -0
- package/dist/lite/.claude/skills/smoke-test/SKILL.md +100 -0
- package/dist/lite/.claude/skills/state/SKILL.md +204 -0
- package/dist/lite/.claude/skills/substrate-check/SKILL.md +218 -0
- package/dist/lite/.claude/skills/synthetic-user/SKILL.md +381 -0
- package/dist/lite/.claude/skills/task-scenarios/SKILL.md +105 -0
- package/dist/lite/.claude/skills/tier-dependency-audit/SKILL.md +139 -0
- package/dist/lite/.claude/skills/ux-migration/SKILL.md +424 -0
- package/dist/lite/.claude/skills/value-prop-canvas/SKILL.md +297 -0
- package/dist/lite/README.md +5 -1
- package/dist/lite/gitignore +6 -0
- package/dist/lite/lib/bassclef-dir-resolver.sh +85 -0
- package/dist/lite/lib/clone-failure-classifier.sh +59 -0
- package/dist/lite/lib/fixture-builder.sh +190 -0
- package/dist/lite/lib/fresh-install-check.sh +49 -0
- package/dist/lite/lib/hook-heartbeat.sh +0 -1
- package/dist/lite/lib/mechanism-fidelity.sh +569 -0
- package/dist/lite/lib/npm-version-check.sh +112 -0
- package/dist/lite/lib/preview-state.sh +229 -0
- package/dist/lite/lib/session-rescue.sh +0 -1
- package/dist/lite/scripts/generate-lite-manifest.sh +145 -4
- package/dist/lite/scripts/lite-manifest-drift-check.sh +5 -1
- package/dist/lite/standards/architect-review-discipline.md +222 -0
- package/dist/lite/standards/bassclef-platform.md +249 -0
- package/dist/lite/standards/bassclef-test-repos.md +97 -0
- package/dist/lite/standards/bassclef-wiring-manifest.json +25 -1
- package/dist/lite/standards/cold-adopter-harness.md +205 -0
- package/dist/lite/standards/data-management.md +135 -0
- package/dist/lite/standards/frontend-stack/nextjs-prisma.md +124 -0
- package/dist/lite/standards/lite-manifest-schema-changes.md +27 -0
- package/dist/lite/standards/lite-manifest.json +1456 -14
- package/dist/lite/standards/mechanism-fidelity.md +205 -0
- package/dist/lite/standards/operator-private-discipline.md +200 -0
- package/dist/lite/standards/operator-to-public-flow.md +195 -0
- package/dist/lite/standards/prototype-gallery.md +251 -0
- package/dist/lite/standards/pseudonym-key-schema.md +109 -0
- package/dist/lite/standards/sibling-validation-checklist.md +181 -0
- package/dist/lite/standards/slack-notifications.md +161 -0
- package/dist/lite/standards/synthetic-user-testing.md +173 -0
- package/dist/lite/standards/vendor-bindings.md +165 -0
- package/dist/lite/standards/vendor-plugins/frontend-design.md +66 -0
- package/dist/lite/standards/vpc-fit-validation.md +167 -0
- package/dist/lite/templates/gallery-yml.md +113 -0
- package/dist/lite/templates/product-canvas.md +169 -0
- package/dist/lite/templates/value-prop-canvas.md +180 -0
- package/package.json +3 -3
- /package/dist/lite/{whereami.md → docs/whereami.md} +0 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# install-class: project
|
|
4
|
+
# file-rename-discipline-check — PreToolUse hook that blocks `git commit`
|
|
5
|
+
# Bash invocations when the staged changes delete a file under the
|
|
6
|
+
# rule's scope without a matching entry in standards/file-renames.json.
|
|
7
|
+
#
|
|
8
|
+
# Per .claude/rules/file-rename-discipline.md — every file rename in
|
|
9
|
+
# bassclef registers a map entry AND ships a fixture test. The rule's
|
|
10
|
+
# methodology layer says do it; this hook is the mechanical layer that
|
|
11
|
+
# fires on every commit.
|
|
12
|
+
#
|
|
13
|
+
# Why this exists: the 2026-06-21 rename of canonical-source-config-validate.sh
|
|
14
|
+
# shipped without registering itself anywhere. POA's session on 2026-06-29
|
|
15
|
+
# surfaced the gap eight days later. Bet 29e WU-9 shipped the cure; this
|
|
16
|
+
# hook prevents the next rename from repeating the failure.
|
|
17
|
+
#
|
|
18
|
+
# Exit codes:
|
|
19
|
+
# 0 — allow (non-matching Bash, no scoped deletions, all deletions registered, override)
|
|
20
|
+
# 2 — block (one or more scoped deletions missing from the rename map)
|
|
21
|
+
#
|
|
22
|
+
# Override: SKIP_FILE_RENAME_DISCIPLINE=1 (logged via trace-helper)
|
|
23
|
+
|
|
24
|
+
set +e
|
|
25
|
+
|
|
26
|
+
INPUT=$(cat 2>/dev/null)
|
|
27
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null)
|
|
28
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null)
|
|
29
|
+
|
|
30
|
+
# Only fire on Bash tool calls
|
|
31
|
+
[ "$TOOL_NAME" = "Bash" ] || exit 0
|
|
32
|
+
|
|
33
|
+
# Only fire on `git commit` invocations (not git status, not git diff, etc)
|
|
34
|
+
case "$COMMAND" in
|
|
35
|
+
*"git commit"*) : ;;
|
|
36
|
+
*) exit 0 ;;
|
|
37
|
+
esac
|
|
38
|
+
|
|
39
|
+
# Skip via override (logged)
|
|
40
|
+
if [ "${SKIP_FILE_RENAME_DISCIPLINE:-0}" = "1" ]; then
|
|
41
|
+
if [ -x ".claude/hooks/trace-helper.sh" ]; then
|
|
42
|
+
bash .claude/hooks/trace-helper.sh "file-rename-discipline" "skipped via SKIP_FILE_RENAME_DISCIPLINE=1" 2>/dev/null || true
|
|
43
|
+
fi
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Resolve the rename map (env-var override for tests + curl-pipe scenarios)
|
|
48
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
49
|
+
RENAME_MAP="${BASSCLEF_RENAME_MAP:-$REPO_ROOT/standards/file-renames.json}"
|
|
50
|
+
|
|
51
|
+
# If the rename map does not exist on this repo, the hook is dormant.
|
|
52
|
+
# (Adopter repos don't ship the map; only bassclef-upstream does.)
|
|
53
|
+
[ -f "$RENAME_MAP" ] || exit 0
|
|
54
|
+
|
|
55
|
+
# Read staged deletions. `git diff --cached --diff-filter=D --name-only`
|
|
56
|
+
# lists files deleted in the staged change.
|
|
57
|
+
DELETIONS=$(git diff --cached --diff-filter=D --name-only 2>/dev/null)
|
|
58
|
+
[ -n "$DELETIONS" ] || exit 0
|
|
59
|
+
|
|
60
|
+
# Scope filter — only check deletions under the rule's path list.
|
|
61
|
+
SCOPED_PATHS=(
|
|
62
|
+
".claude/hooks/"
|
|
63
|
+
".claude/skills/"
|
|
64
|
+
".claude/rules/"
|
|
65
|
+
".claude/luminaries/"
|
|
66
|
+
".claude/agents/"
|
|
67
|
+
"scripts/"
|
|
68
|
+
"lib/"
|
|
69
|
+
"standards/"
|
|
70
|
+
"presence/"
|
|
71
|
+
"architecture/decisions/"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Build the list of registered old_names from the rename map.
|
|
75
|
+
# Both the renames array AND the compat_shims_preserved list are
|
|
76
|
+
# considered "registered" — the rule allows both shapes.
|
|
77
|
+
REGISTERED_NAMES=$(jq -r '
|
|
78
|
+
((.renames // [])[]?.old_name),
|
|
79
|
+
((.compat_shims_preserved // [])[]?.old_name)
|
|
80
|
+
' "$RENAME_MAP" 2>/dev/null | sort -u)
|
|
81
|
+
|
|
82
|
+
# Walk the deletions; flag any scoped deletion whose basename is missing
|
|
83
|
+
# from the registered list.
|
|
84
|
+
UNREGISTERED=""
|
|
85
|
+
while IFS= read -r path; do
|
|
86
|
+
[ -n "$path" ] || continue
|
|
87
|
+
# Scope filter: skip deletions outside the rule's paths
|
|
88
|
+
in_scope=0
|
|
89
|
+
for scope in "${SCOPED_PATHS[@]}"; do
|
|
90
|
+
case "$path" in
|
|
91
|
+
"$scope"*) in_scope=1; break ;;
|
|
92
|
+
esac
|
|
93
|
+
done
|
|
94
|
+
[ "$in_scope" = "1" ] || continue
|
|
95
|
+
|
|
96
|
+
base=$(basename "$path")
|
|
97
|
+
if ! echo "$REGISTERED_NAMES" | grep -qFx "$base"; then
|
|
98
|
+
UNREGISTERED="$UNREGISTERED - $path (basename: $base)"$'\n'
|
|
99
|
+
fi
|
|
100
|
+
done <<< "$DELETIONS"
|
|
101
|
+
|
|
102
|
+
# No unregistered scoped deletions → allow
|
|
103
|
+
[ -z "$UNREGISTERED" ] && exit 0
|
|
104
|
+
|
|
105
|
+
# Block with a structured remediation message
|
|
106
|
+
cat <<MSG >&2
|
|
107
|
+
============================================
|
|
108
|
+
🛑 FILE-RENAME DISCIPLINE — BLOCKED 🛑
|
|
109
|
+
============================================
|
|
110
|
+
|
|
111
|
+
The staged commit deletes one or more files under the rule's scope without
|
|
112
|
+
a matching entry in standards/file-renames.json. Per .claude/rules/file-rename-discipline.md
|
|
113
|
+
every file rename registers a map entry plus a fixture test.
|
|
114
|
+
|
|
115
|
+
Unregistered deletions:
|
|
116
|
+
$UNREGISTERED
|
|
117
|
+
|
|
118
|
+
Three required moves:
|
|
119
|
+
|
|
120
|
+
1. Add an entry under \`.renames\` in standards/file-renames.json:
|
|
121
|
+
|
|
122
|
+
{
|
|
123
|
+
"old_name": "<basename-being-deleted>",
|
|
124
|
+
"new_name": "<basename-of-the-successor-file>",
|
|
125
|
+
"renamed_at": "$(date +%Y-%m-%d)",
|
|
126
|
+
"category": "<hook|skill|rule|agent|luminary|script|lib|standard|adr>"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
2. Ship a fixture under .claude/hooks/tests/fixtures/adopter-$(date +%Y-%m-%d)-rename/
|
|
130
|
+
that mirrors the adopter state after the rename ships. The migrate
|
|
131
|
+
script's test suite reads the fixture; assertions pin the cure.
|
|
132
|
+
|
|
133
|
+
3. Cite the rename in the goal doc or chronicle for this commit.
|
|
134
|
+
|
|
135
|
+
Compat-shim case — when the old name keeps working via a different
|
|
136
|
+
mechanism (e.g., dispatcher's canonical-sync.sh via ADR-032 two-stage rocket):
|
|
137
|
+
add the entry under \`.compat_shims_preserved\` instead, with a reason field.
|
|
138
|
+
|
|
139
|
+
Override (logged via trace-helper): SKIP_FILE_RENAME_DISCIPLINE=1 git commit ...
|
|
140
|
+
|
|
141
|
+
Use only for migrating bulk content or emergency rescue; routine renames
|
|
142
|
+
register the entry.
|
|
143
|
+
|
|
144
|
+
============================================
|
|
145
|
+
MSG
|
|
146
|
+
|
|
147
|
+
exit 2
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# install-class: project
|
|
4
|
+
# hook-classifier-canary.sh — advises author when a new hook lands
|
|
5
|
+
# without any recognized wiring (settings.json / .github/workflows /
|
|
6
|
+
# scripts/release-to-bassclef.sh / launchd header / sourced-lib
|
|
7
|
+
# header). Fires at PreToolUse on Edit|Write against paths under
|
|
8
|
+
# .claude/hooks/*.sh.
|
|
9
|
+
#
|
|
10
|
+
# Layer 4 cure for sunj-labs/bassclef-upstream#1163. Same class as
|
|
11
|
+
# the hook-wire-on-author rule; extends discipline to catch mislabels
|
|
12
|
+
# at author time — not release time.
|
|
13
|
+
#
|
|
14
|
+
# Anchors: @luminary saltzer-schroeder (complete mediation at earliest
|
|
15
|
+
# cheap gate), @luminary david-parnas (canary hides classifier
|
|
16
|
+
# vocabulary from author), @luminary michael-feathers (characterization
|
|
17
|
+
# tests pin behavior at author boundary).
|
|
18
|
+
#
|
|
19
|
+
# Reads stdin JSON per Claude Code hook contract:
|
|
20
|
+
# { "tool_name": "Edit|Write", "tool_input": { "file_path": "..." } }
|
|
21
|
+
#
|
|
22
|
+
# Exit codes:
|
|
23
|
+
# 0 — pass (V1 ADVISORY only writes to stderr; V2 may flip to BLOCK)
|
|
24
|
+
# 2 — reserved for V2 BLOCK escalation
|
|
25
|
+
#
|
|
26
|
+
# Override:
|
|
27
|
+
# SKIP_HOOK_CLASSIFIER_CANARY=1 bypass; logged via trace-helper
|
|
28
|
+
|
|
29
|
+
set -uo pipefail
|
|
30
|
+
|
|
31
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
32
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
33
|
+
|
|
34
|
+
# Override
|
|
35
|
+
if [ "${SKIP_HOOK_CLASSIFIER_CANARY:-}" = "1" ]; then
|
|
36
|
+
TRACE_HELPER="$SCRIPT_DIR/trace-helper.sh"
|
|
37
|
+
if [ -f "$TRACE_HELPER" ]; then
|
|
38
|
+
# shellcheck disable=SC1090
|
|
39
|
+
source "$TRACE_HELPER" 2>/dev/null || true
|
|
40
|
+
trace_record "hook-classifier-canary" "skipped via SKIP_HOOK_CLASSIFIER_CANARY=1" 2>/dev/null || true
|
|
41
|
+
fi
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Read stdin JSON. If jq not available, degrade gracefully.
|
|
46
|
+
INPUT=$(cat)
|
|
47
|
+
if [ -z "$INPUT" ]; then
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if command -v jq >/dev/null 2>&1; then
|
|
52
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null || echo "")
|
|
53
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || echo "")
|
|
54
|
+
else
|
|
55
|
+
# Fallback — crude sed parse
|
|
56
|
+
TOOL_NAME=$(echo "$INPUT" | sed -n 's/.*"tool_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
|
|
57
|
+
FILE_PATH=$(echo "$INPUT" | sed -n 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Pass-through — non-Edit/Write tools
|
|
61
|
+
case "$TOOL_NAME" in
|
|
62
|
+
Edit|Write|MultiEdit) ;;
|
|
63
|
+
*) exit 0 ;;
|
|
64
|
+
esac
|
|
65
|
+
|
|
66
|
+
# Pass-through — no file path
|
|
67
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
68
|
+
|
|
69
|
+
# Pass-through — path outside .claude/hooks/*.sh
|
|
70
|
+
case "$FILE_PATH" in
|
|
71
|
+
*/.claude/hooks/*.sh) ;;
|
|
72
|
+
*) exit 0 ;;
|
|
73
|
+
esac
|
|
74
|
+
|
|
75
|
+
# Pass-through — path is under .claude/hooks/tests/ or fixtures
|
|
76
|
+
case "$FILE_PATH" in
|
|
77
|
+
*/.claude/hooks/tests/*|*/.claude/hooks/*/tests/*) exit 0 ;;
|
|
78
|
+
esac
|
|
79
|
+
|
|
80
|
+
# Get bare hook name for classifier lookup
|
|
81
|
+
HOOK_NAME=$(basename "$FILE_PATH")
|
|
82
|
+
|
|
83
|
+
# Pass-through — this file exists on disk already (edit, not new)
|
|
84
|
+
if [ -f "$FILE_PATH" ]; then
|
|
85
|
+
exit 0
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Load classifier
|
|
89
|
+
LIB="$REPO_ROOT/lib/mechanism-fidelity.sh"
|
|
90
|
+
if [ ! -f "$LIB" ]; then
|
|
91
|
+
# Classifier lib missing — cannot advise; pass silently
|
|
92
|
+
exit 0
|
|
93
|
+
fi
|
|
94
|
+
# shellcheck disable=SC1090
|
|
95
|
+
source "$LIB"
|
|
96
|
+
|
|
97
|
+
# Ask classifier what pattern it detects for this hook name
|
|
98
|
+
PATTERN=$(classify_hook_install_pattern "$HOOK_NAME" "$REPO_ROOT" 2>/dev/null)
|
|
99
|
+
|
|
100
|
+
# Recognized patterns → pass
|
|
101
|
+
case "$PATTERN" in
|
|
102
|
+
ci|launchd|lib|release)
|
|
103
|
+
exit 0
|
|
104
|
+
;;
|
|
105
|
+
esac
|
|
106
|
+
|
|
107
|
+
# Standard means classifier could not place it AND file does not exist yet.
|
|
108
|
+
# Check settings.json — hook may be wired for PreToolUse there.
|
|
109
|
+
SETTINGS="$REPO_ROOT/.claude/settings.json"
|
|
110
|
+
if [ -f "$SETTINGS" ] && grep -qF "$HOOK_NAME" "$SETTINGS" 2>/dev/null; then
|
|
111
|
+
exit 0
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# No recognized wiring signal found. Emit ADVISORY.
|
|
115
|
+
cat >&2 <<EOF
|
|
116
|
+
|
|
117
|
+
============================================
|
|
118
|
+
🚨 hook-classifier-canary — ADVISORY
|
|
119
|
+
============================================
|
|
120
|
+
|
|
121
|
+
New hook detected: $HOOK_NAME
|
|
122
|
+
Author path: $FILE_PATH
|
|
123
|
+
|
|
124
|
+
The classifier at lib/mechanism-fidelity.sh does not recognize any
|
|
125
|
+
wiring signal for this hook. If this ships as-is, the cold-adopter
|
|
126
|
+
harness Shape d checks will flag it as DEAD-LETTER.
|
|
127
|
+
|
|
128
|
+
Five remediation paths (pick one before commit):
|
|
129
|
+
|
|
130
|
+
1. settings.json wiring — add hook entry to .claude/settings.json
|
|
131
|
+
under PreToolUse/PostToolUse/SessionStart/Stop event matcher.
|
|
132
|
+
For hooks that fire on tool calls.
|
|
133
|
+
|
|
134
|
+
2. CI workflow — reference the hook name in a .github/workflows/*.yml
|
|
135
|
+
file. For hooks that fire on push/PR events.
|
|
136
|
+
|
|
137
|
+
3. Release script — reference the hook name in
|
|
138
|
+
scripts/release-to-bassclef.sh. For hooks that gate on /release
|
|
139
|
+
dispatch. Classifier returns "release".
|
|
140
|
+
|
|
141
|
+
4. launchd header — add "# Runs via launchd" (or "scheduled job" /
|
|
142
|
+
"cron job") to the hook's top-of-file comment block. For
|
|
143
|
+
scheduled jobs. Classifier returns "launchd".
|
|
144
|
+
|
|
145
|
+
5. Sourced-library header — add "# sourced by <caller>" to the
|
|
146
|
+
hook's top-of-file comment block. For helper libraries loaded
|
|
147
|
+
via source. Classifier returns "lib".
|
|
148
|
+
|
|
149
|
+
If genuinely bypassing (rare): SKIP_HOOK_CLASSIFIER_CANARY=1 <command>
|
|
150
|
+
|
|
151
|
+
Per rule: .claude/rules/hook-wire-on-author.md
|
|
152
|
+
Per Layer 4 of: bassclef-upstream#1163
|
|
153
|
+
============================================
|
|
154
|
+
|
|
155
|
+
EOF
|
|
156
|
+
|
|
157
|
+
# V1 — ADVISORY only. V2 may flip to exit 2 (BLOCK).
|
|
158
|
+
exit 0
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# install-class: project
|
|
4
|
+
# kiss-default-check.sh — pre-push /kiss enforcement check
|
|
5
|
+
# Per bassclef#907 Part 4.
|
|
6
|
+
#
|
|
7
|
+
# Reads a draft body file. Checks for BLOCK-tier bassclef-internal jargon
|
|
8
|
+
# per standards/bassclef-internal-jargon.md. BLOCKs if jargon present and
|
|
9
|
+
# no opt-out signal.
|
|
10
|
+
#
|
|
11
|
+
# Composes with .claude/hooks/pr-body-scrub-check.sh (CI-time enforcement).
|
|
12
|
+
# This hook runs pre-push; the scrub hook runs at CI. Both read the same
|
|
13
|
+
# BLOCK list from the standards source-of-truth.
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# bash kiss-default-check.sh <body-file>
|
|
17
|
+
#
|
|
18
|
+
# Optional env:
|
|
19
|
+
# PR_LABELS=label1,label2,... — labels on the PR (newline or comma separated)
|
|
20
|
+
# SKIP_KISS=1 — operator opt-out for one invocation
|
|
21
|
+
# KISS_STANDARDS_FILE=<path> — override standards file location (testing)
|
|
22
|
+
#
|
|
23
|
+
# Exit codes:
|
|
24
|
+
# 0 — pass (no BLOCK terms found, OR opt-out signal present)
|
|
25
|
+
# 1 — block (BLOCK terms found + no opt-out)
|
|
26
|
+
# 2 — usage error
|
|
27
|
+
|
|
28
|
+
set +e
|
|
29
|
+
|
|
30
|
+
BODY_FILE="${1:-}"
|
|
31
|
+
if [ -z "$BODY_FILE" ] || [ ! -f "$BODY_FILE" ]; then
|
|
32
|
+
echo "usage: $0 <body-file>" >&2
|
|
33
|
+
exit 2
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Find the repo root + standards file
|
|
37
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
38
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
39
|
+
STANDARDS_FILE="${KISS_STANDARDS_FILE:-$REPO_ROOT/standards/bassclef-internal-jargon.md}"
|
|
40
|
+
|
|
41
|
+
if [ ! -f "$STANDARDS_FILE" ]; then
|
|
42
|
+
# Try the bassclef-sync'd path (when run from a consumer repo)
|
|
43
|
+
STANDARDS_FILE="${HOME}/src/sunj-labs/bassclef/standards/bassclef-internal-jargon.md"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [ ! -f "$STANDARDS_FILE" ]; then
|
|
47
|
+
echo "kiss-default-check: standards file not found at $STANDARDS_FILE" >&2
|
|
48
|
+
echo " set KISS_STANDARDS_FILE=<path> or skip this check" >&2
|
|
49
|
+
exit 2
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Opt-out — env var
|
|
53
|
+
if [ "${SKIP_KISS:-0}" = "1" ]; then
|
|
54
|
+
echo "kiss-default-check: SKIP_KISS=1 — skipped (logged)" >&2
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Opt-out — label
|
|
59
|
+
PR_LABELS="${PR_LABELS:-}"
|
|
60
|
+
LABELS_NORMALIZED=$(echo "$PR_LABELS" | tr ',' '\n' | sed 's/^ *//;s/ *$//' | grep -v '^$' || true)
|
|
61
|
+
if echo "$LABELS_NORMALIZED" | grep -qx "bassclef-internal-prose-ok"; then
|
|
62
|
+
echo "kiss-default-check: bassclef-internal-prose-ok label — advisory mode" >&2
|
|
63
|
+
ADVISORY=1
|
|
64
|
+
else
|
|
65
|
+
ADVISORY=0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Extract BLOCK terms from the standards table
|
|
69
|
+
# The standards table has lines like: | term | plain-english | BLOCK |
|
|
70
|
+
# Allows digits in the first column so terms like "12-pattern matcher"
|
|
71
|
+
# (added under bassclef-upstream#101) are picked up.
|
|
72
|
+
BLOCK_TERMS=$(awk -F'|' '
|
|
73
|
+
/^\| [a-zA-Z0-9-]+ / && /BLOCK *\|$/ {
|
|
74
|
+
gsub(/^ +| +$/, "", $2)
|
|
75
|
+
print $2
|
|
76
|
+
}
|
|
77
|
+
' "$STANDARDS_FILE")
|
|
78
|
+
|
|
79
|
+
if [ -z "$BLOCK_TERMS" ]; then
|
|
80
|
+
echo "kiss-default-check: no BLOCK terms found in standards file" >&2
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# Strip code blocks + inline backtick spans + URLs so they don't trigger false positives
|
|
85
|
+
SCRUBBED=$(awk '
|
|
86
|
+
BEGIN { in_code = 0 }
|
|
87
|
+
/^```/ { in_code = !in_code; next }
|
|
88
|
+
in_code { next }
|
|
89
|
+
{ print }
|
|
90
|
+
' "$BODY_FILE" | sed -E 's/`[^`]*`//g; s|https?://[^ )]+||g')
|
|
91
|
+
|
|
92
|
+
# Check each BLOCK term
|
|
93
|
+
FINDINGS=""
|
|
94
|
+
FINDING_COUNT=0
|
|
95
|
+
while IFS= read -r term; do
|
|
96
|
+
[ -z "$term" ] && continue
|
|
97
|
+
count=$(echo "$SCRUBBED" | grep -ioE "\\b${term}\\b" | wc -l | tr -d ' ')
|
|
98
|
+
if [ "$count" -gt 0 ]; then
|
|
99
|
+
FINDINGS="${FINDINGS}- \`${term}\` (${count}x)"$'\n'
|
|
100
|
+
FINDING_COUNT=$((FINDING_COUNT + count))
|
|
101
|
+
fi
|
|
102
|
+
done <<< "$BLOCK_TERMS"
|
|
103
|
+
|
|
104
|
+
if [ "$FINDING_COUNT" -eq 0 ]; then
|
|
105
|
+
echo "kiss-default-check: pass — no BLOCK terms in draft" >&2
|
|
106
|
+
exit 0
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# Report findings
|
|
110
|
+
{
|
|
111
|
+
echo ""
|
|
112
|
+
echo "kiss-default-check: $FINDING_COUNT BLOCK-tier jargon term(s) found in draft body:"
|
|
113
|
+
echo ""
|
|
114
|
+
echo "$FINDINGS"
|
|
115
|
+
echo "Plain-English alternatives at: $STANDARDS_FILE"
|
|
116
|
+
echo ""
|
|
117
|
+
echo "Options:"
|
|
118
|
+
echo " 1. Run /kiss --grade11 on the draft and re-push"
|
|
119
|
+
echo " 2. Add label 'bassclef-internal-prose-ok' for advisory-only"
|
|
120
|
+
echo " 3. Set SKIP_KISS=1 for one-time bypass (logged)"
|
|
121
|
+
echo ""
|
|
122
|
+
} >&2
|
|
123
|
+
|
|
124
|
+
if [ "$ADVISORY" = "1" ]; then
|
|
125
|
+
echo "kiss-default-check: ADVISORY (label) — passing with warnings" >&2
|
|
126
|
+
exit 0
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
exit 1
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# Multi-Tenancy Check — bassclef template.
|
|
4
|
+
# Fires on Edit/Write of source files.
|
|
5
|
+
# Warns when tenant-scoped patterns are missing.
|
|
6
|
+
#
|
|
7
|
+
# App repos should customize:
|
|
8
|
+
# TENANT_MODELS — pipe-separated list of tenant-scoped model names
|
|
9
|
+
# TENANT_CONTEXT_FN — function name that resolves tenant context
|
|
10
|
+
# TENANT_PARAMS_FN — function name for tenant-specific parameters
|
|
11
|
+
#
|
|
12
|
+
# Override by placing your own multi-tenancy-check.sh in .claude/hooks/
|
|
13
|
+
|
|
14
|
+
INPUT=$(cat)
|
|
15
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.file_path // ""')
|
|
16
|
+
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // ""')
|
|
17
|
+
|
|
18
|
+
# Skip non-source files
|
|
19
|
+
if echo "$FILE_PATH" | grep -qE '\.(sh|json|md)$|/\.claude/|/memory/|/docs/|/scripts/|__tests__'; then
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# === Customizable per project (override these in your app's copy) ===
|
|
24
|
+
TENANT_CONTEXT_FN="${TENANT_CONTEXT_FN:-getTenantContext}"
|
|
25
|
+
TENANT_PARAMS_FN="${TENANT_PARAMS_FN:-getTenantParams}"
|
|
26
|
+
# Default: no models (app must define its own)
|
|
27
|
+
TENANT_MODELS="${TENANT_MODELS:-}"
|
|
28
|
+
|
|
29
|
+
# Skip if no tenant models defined (not a multi-tenant project)
|
|
30
|
+
if [ -z "$TENANT_MODELS" ]; then
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
WARNINGS=""
|
|
35
|
+
|
|
36
|
+
# Check 1: Hardcoded numeric thresholds that should use params facade
|
|
37
|
+
if echo "$CONTENT" | grep -qE '[0-9]{3,}\.?[0-9]*\.?[0-9]*' ; then
|
|
38
|
+
if echo "$CONTENT" | grep -qE '_FLOOR|_CEILING|_THRESHOLD|_LIMIT|_MIN|_MAX'; then
|
|
39
|
+
if ! echo "$CONTENT" | grep -q "$TENANT_PARAMS_FN"; then
|
|
40
|
+
WARNINGS="${WARNINGS}
|
|
41
|
+
MULTI-TENANCY: Possible hardcoded threshold detected.
|
|
42
|
+
Use ${TENANT_PARAMS_FN}() instead of hardcoded values.
|
|
43
|
+
Financial and geographic thresholds must flow through
|
|
44
|
+
the tenant params facade for multi-tenant readiness."
|
|
45
|
+
fi
|
|
46
|
+
fi
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Check 2: Prisma queries on tenant-scoped models without context
|
|
50
|
+
if echo "$CONTENT" | grep -qiE "prisma\.(${TENANT_MODELS})\.(findMany|findFirst|create|update|delete)" ; then
|
|
51
|
+
if ! echo "$CONTENT" | grep -q "$TENANT_CONTEXT_FN"; then
|
|
52
|
+
WARNINGS="${WARNINGS}
|
|
53
|
+
MULTI-TENANCY: Query on tenant-scoped model without ${TENANT_CONTEXT_FN}().
|
|
54
|
+
Tenant-scoped models must be filtered by tenant/user context.
|
|
55
|
+
Use ${TENANT_CONTEXT_FN}() and filter appropriately."
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Check 3: API routes missing tenant context
|
|
60
|
+
if echo "$FILE_PATH" | grep -qE 'src/app/api/.*route\.ts'; then
|
|
61
|
+
if echo "$CONTENT" | grep -qE 'export async function (GET|POST|PATCH|DELETE|PUT)'; then
|
|
62
|
+
if ! echo "$CONTENT" | grep -q "$TENANT_CONTEXT_FN"; then
|
|
63
|
+
WARNINGS="${WARNINGS}
|
|
64
|
+
MULTI-TENANCY: API route handler without ${TENANT_CONTEXT_FN}().
|
|
65
|
+
All API routes touching tenant-scoped data must resolve
|
|
66
|
+
tenant context. Even GET routes need this for data isolation."
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
if [ -n "$WARNINGS" ]; then
|
|
72
|
+
echo "============================================"
|
|
73
|
+
echo "MULTI-TENANCY GUARDRAIL"
|
|
74
|
+
echo "============================================"
|
|
75
|
+
echo "$WARNINGS"
|
|
76
|
+
echo ""
|
|
77
|
+
echo "These are WARNs, not BLOCKs. If this is intentional"
|
|
78
|
+
echo "(e.g., platform-wide data), proceed."
|
|
79
|
+
echo "If tenant-scoped, add the missing context/facade."
|
|
80
|
+
echo "============================================"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
exit 0
|