@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.
Files changed (135) hide show
  1. package/dist/cli.cjs +23 -6
  2. package/dist/cli.js +23 -6
  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/bassclef-configs.jsonc +56 -0
  7. package/dist/lite/.claude/bassclef-orientation.md +120 -0
  8. package/dist/lite/.claude/hooks/adr-discipline-check.sh +132 -0
  9. package/dist/lite/.claude/hooks/atomic-pr-check.sh +169 -0
  10. package/dist/lite/.claude/hooks/auto-save-idle.sh +51 -0
  11. package/dist/lite/.claude/hooks/bassclef-sync.sh +51 -13
  12. package/dist/lite/.claude/hooks/bet-doc-gate.sh +283 -0
  13. package/dist/lite/.claude/hooks/bug-diagnosis.sh +64 -0
  14. package/dist/lite/.claude/hooks/destructive-command-guard.sh +136 -0
  15. package/dist/lite/.claude/hooks/file-rename-discipline-check.sh +147 -0
  16. package/dist/lite/.claude/hooks/hook-classifier-canary.sh +158 -0
  17. package/dist/lite/.claude/hooks/kiss-default-check.sh +129 -0
  18. package/dist/lite/.claude/hooks/multi-tenancy-check.sh +83 -0
  19. package/dist/lite/.claude/hooks/oo-ad-entry-point-check.sh +216 -0
  20. package/dist/lite/.claude/hooks/operator-private-path-check.sh +203 -0
  21. package/dist/lite/.claude/hooks/pattern-annotation-validate.sh +142 -0
  22. package/dist/lite/.claude/hooks/plain-english-steering.sh +15 -1
  23. package/dist/lite/.claude/hooks/post-write-trace.sh +66 -0
  24. package/dist/lite/.claude/hooks/pr-body-scrub-check.sh +446 -0
  25. package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +137 -0
  26. package/dist/lite/.claude/hooks/pre-gh-pr-body-scrub.sh +192 -0
  27. package/dist/lite/.claude/hooks/pre-gh-pr-title-scrub.sh +172 -0
  28. package/dist/lite/.claude/hooks/role-entry-gate.sh +115 -0
  29. package/dist/lite/.claude/hooks/save-state.sh +268 -0
  30. package/dist/lite/.claude/hooks/session-reflection.d/08-settings-drift.sh +8 -1
  31. package/dist/lite/.claude/hooks/session-reflection.d/10-abrupt-stop-recovery.sh +52 -0
  32. package/dist/lite/.claude/hooks/session-reflection.d/55-orientation-gate.sh +40 -4
  33. package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +12 -5
  34. package/dist/lite/.claude/hooks/session-reflection.d/81-hook-manifest-staleness.sh +18 -2
  35. package/dist/lite/.claude/hooks/session-reflection.d/91-npm-version-drift.sh +54 -0
  36. package/dist/lite/.claude/hooks/session-reflection.d/tests/10-abrupt-stop-recovery.test.sh +95 -0
  37. package/dist/lite/.claude/hooks/session-reflection.sh +62 -15
  38. package/dist/lite/.claude/hooks/tool-failure-diagnosis.sh +50 -0
  39. package/dist/lite/.claude/luminaries/andrej-karpathy.md +165 -0
  40. package/dist/lite/.claude/luminaries/dave-gray.md +155 -0
  41. package/dist/lite/.claude/luminaries/gary-klein.md +166 -0
  42. package/dist/lite/.claude/luminaries/kaoru-ishikawa.md +162 -0
  43. package/dist/lite/.claude/luminaries/lean-product-canvas.md +33 -0
  44. package/dist/lite/.claude/luminaries/luke-wroblewski.md +224 -0
  45. package/dist/lite/.claude/luminaries/ryan-singer.md +113 -0
  46. package/dist/lite/.claude/luminaries/steve-krug.md +168 -0
  47. package/dist/lite/.claude/luminaries/tony-ulwick.md +134 -0
  48. package/dist/lite/.claude/rules/arc-level-orientation-before-leaf.md +60 -0
  49. package/dist/lite/.claude/rules/architect-review-discipline.md +71 -0
  50. package/dist/lite/.claude/rules/auto-fix-criteria.md +126 -0
  51. package/dist/lite/.claude/rules/bassclef-hook-drift-blocker.md +144 -0
  52. package/dist/lite/.claude/rules/bassclef-models-discovery.md +107 -0
  53. package/dist/lite/.claude/rules/bassclef-workflow-parity.md +104 -0
  54. package/dist/lite/.claude/rules/cost-governance.md +154 -0
  55. package/dist/lite/.claude/rules/file-rename-discipline.md +95 -0
  56. package/dist/lite/.claude/rules/mechanism-fidelity.md +88 -0
  57. package/dist/lite/.claude/rules/prototype-production-coupling.md +125 -0
  58. package/dist/lite/.claude/rules/quick-fix-criteria.md +90 -0
  59. package/dist/lite/.claude/rules/session-artifacts.md +30 -0
  60. package/dist/lite/.claude/rules/sibling-smoke-after-substrate-change.md +107 -0
  61. package/dist/lite/.claude/rules/substrate-as-system.md +67 -0
  62. package/dist/lite/.claude/rules/usability.md +22 -0
  63. package/dist/lite/.claude/settings.json +20 -0
  64. package/dist/lite/.claude/skills/agent-research-spawn/SKILL.md +100 -0
  65. package/dist/lite/.claude/skills/architect-review/SKILL.md +201 -0
  66. package/dist/lite/.claude/skills/auto-fix/SKILL.md +297 -0
  67. package/dist/lite/.claude/skills/autonomous/SKILL.md +300 -0
  68. package/dist/lite/.claude/skills/bind-subdomain/SKILL.md +232 -0
  69. package/dist/lite/.claude/skills/canvas/SKILL.md +41 -0
  70. package/dist/lite/.claude/skills/deploy-prod/SKILL.md +106 -0
  71. package/dist/lite/.claude/skills/docs-sync/SKILL.md +188 -0
  72. package/dist/lite/.claude/skills/eisenhower/SKILL.md +273 -0
  73. package/dist/lite/.claude/skills/empathy-map/SKILL.md +168 -0
  74. package/dist/lite/.claude/skills/frontend-design/SKILL.md +53 -0
  75. package/dist/lite/.claude/skills/interview-me/SKILL.md +171 -0
  76. package/dist/lite/.claude/skills/journal-export/SKILL.md +293 -0
  77. package/dist/lite/.claude/skills/jtbd-tasks/SKILL.md +203 -0
  78. package/dist/lite/.claude/skills/objectory-decompose/SKILL.md +256 -0
  79. package/dist/lite/.claude/skills/pick-luminaries/SKILL.md +100 -0
  80. package/dist/lite/.claude/skills/pre-mortem/SKILL.md +180 -0
  81. package/dist/lite/.claude/skills/prototype-gallery/SKILL.md +264 -0
  82. package/dist/lite/.claude/skills/provision-deploy-host/SKILL.md +257 -0
  83. package/dist/lite/.claude/skills/quick-fix/SKILL.md +273 -0
  84. package/dist/lite/.claude/skills/release/SKILL.md +311 -0
  85. package/dist/lite/.claude/skills/release-notes/SKILL.md +202 -0
  86. package/dist/lite/.claude/skills/rfc/SKILL.md +222 -0
  87. package/dist/lite/.claude/skills/riff-prototypes/SKILL.md +637 -0
  88. package/dist/lite/.claude/skills/secrets-bootstrap/SKILL.md +209 -0
  89. package/dist/lite/.claude/skills/skill/SKILL.md +304 -0
  90. package/dist/lite/.claude/skills/smoke-test/SKILL.md +100 -0
  91. package/dist/lite/.claude/skills/state/SKILL.md +204 -0
  92. package/dist/lite/.claude/skills/substrate-check/SKILL.md +218 -0
  93. package/dist/lite/.claude/skills/synthetic-user/SKILL.md +381 -0
  94. package/dist/lite/.claude/skills/task-scenarios/SKILL.md +105 -0
  95. package/dist/lite/.claude/skills/tier-dependency-audit/SKILL.md +139 -0
  96. package/dist/lite/.claude/skills/ux-migration/SKILL.md +424 -0
  97. package/dist/lite/.claude/skills/value-prop-canvas/SKILL.md +297 -0
  98. package/dist/lite/README.md +5 -1
  99. package/dist/lite/gitignore +6 -0
  100. package/dist/lite/lib/bassclef-dir-resolver.sh +85 -0
  101. package/dist/lite/lib/clone-failure-classifier.sh +59 -0
  102. package/dist/lite/lib/fixture-builder.sh +190 -0
  103. package/dist/lite/lib/fresh-install-check.sh +49 -0
  104. package/dist/lite/lib/hook-heartbeat.sh +0 -1
  105. package/dist/lite/lib/mechanism-fidelity.sh +569 -0
  106. package/dist/lite/lib/npm-version-check.sh +112 -0
  107. package/dist/lite/lib/preview-state.sh +229 -0
  108. package/dist/lite/lib/session-rescue.sh +0 -1
  109. package/dist/lite/scripts/generate-lite-manifest.sh +145 -4
  110. package/dist/lite/scripts/lite-manifest-drift-check.sh +5 -1
  111. package/dist/lite/standards/architect-review-discipline.md +222 -0
  112. package/dist/lite/standards/bassclef-platform.md +249 -0
  113. package/dist/lite/standards/bassclef-test-repos.md +97 -0
  114. package/dist/lite/standards/bassclef-wiring-manifest.json +25 -1
  115. package/dist/lite/standards/cold-adopter-harness.md +205 -0
  116. package/dist/lite/standards/data-management.md +135 -0
  117. package/dist/lite/standards/frontend-stack/nextjs-prisma.md +124 -0
  118. package/dist/lite/standards/lite-manifest-schema-changes.md +27 -0
  119. package/dist/lite/standards/lite-manifest.json +1456 -14
  120. package/dist/lite/standards/mechanism-fidelity.md +205 -0
  121. package/dist/lite/standards/operator-private-discipline.md +200 -0
  122. package/dist/lite/standards/operator-to-public-flow.md +195 -0
  123. package/dist/lite/standards/prototype-gallery.md +251 -0
  124. package/dist/lite/standards/pseudonym-key-schema.md +109 -0
  125. package/dist/lite/standards/sibling-validation-checklist.md +181 -0
  126. package/dist/lite/standards/slack-notifications.md +161 -0
  127. package/dist/lite/standards/synthetic-user-testing.md +173 -0
  128. package/dist/lite/standards/vendor-bindings.md +165 -0
  129. package/dist/lite/standards/vendor-plugins/frontend-design.md +66 -0
  130. package/dist/lite/standards/vpc-fit-validation.md +167 -0
  131. package/dist/lite/templates/gallery-yml.md +113 -0
  132. package/dist/lite/templates/product-canvas.md +169 -0
  133. package/dist/lite/templates/value-prop-canvas.md +180 -0
  134. package/package.json +3 -3
  135. /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