@thebassclef/lite 0.1.3 → 1.0.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 (293) hide show
  1. package/dist/cli.cjs +216 -137
  2. package/dist/cli.js +218 -139
  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/.bassclef-source.json +10 -0
  7. package/dist/lite/.claude/settings.json +212 -0
  8. package/dist/lite/CLAUDE.md +41 -0
  9. package/dist/lite/gitignore +58 -0
  10. package/dist/lite/standards/bassclef-wiring-manifest.json +497 -0
  11. package/dist/lite/whereami.md +24 -0
  12. package/package.json +8 -2
  13. package/substrate/.bassclef/lite-manifest.json +0 -2701
  14. package/substrate/.claude/agents/architect.md +0 -70
  15. package/substrate/.claude/agents/builder.md +0 -114
  16. package/substrate/.claude/agents/designer.md +0 -156
  17. package/substrate/.claude/agents/reviewer.md +0 -88
  18. package/substrate/.claude/hooks/artifact-ingestion-gate.sh +0 -357
  19. package/substrate/.claude/hooks/assert-verify-steering.sh +0 -77
  20. package/substrate/.claude/hooks/bassclef-source-config-validate.sh +0 -215
  21. package/substrate/.claude/hooks/bassclef-sync.sh +0 -634
  22. package/substrate/.claude/hooks/compound-noun-scrub.sh +0 -292
  23. package/substrate/.claude/hooks/kiss-expansion-inject.sh +0 -69
  24. package/substrate/.claude/hooks/longrun-prep-compounding-axis-check.sh +0 -492
  25. package/substrate/.claude/hooks/longrun-prep-compounding-sequence-check.sh +0 -492
  26. package/substrate/.claude/hooks/plain-english-steering.sh +0 -156
  27. package/substrate/.claude/hooks/post-skill-friction-check.sh +0 -177
  28. package/substrate/.claude/hooks/post-skill-telemetry.sh +0 -62
  29. package/substrate/.claude/hooks/pre-build-gate.sh +0 -511
  30. package/substrate/.claude/hooks/pre-commit-gate.sh +0 -451
  31. package/substrate/.claude/hooks/session-end.sh +0 -433
  32. package/substrate/.claude/hooks/session-reflection.sh +0 -303
  33. package/substrate/.claude/hooks/skill-body-grade-gate.sh +0 -219
  34. package/substrate/.claude/hooks/skill-body-intent-drift.sh +0 -107
  35. package/substrate/.claude/hooks/skill-step-list-check.sh +0 -171
  36. package/substrate/.claude/hooks/state-validate.sh +0 -271
  37. package/substrate/.claude/hooks/substrate-clarity-gate.sh +0 -1110
  38. package/substrate/.claude/hooks/temperance-gate.sh +0 -147
  39. package/substrate/.claude/hooks/testing-tier-enforce.sh +0 -233
  40. package/substrate/.claude/hooks/turn-prose-grade-measure.sh +0 -219
  41. package/substrate/.claude/hooks/turn-prose-kiss-check.sh +0 -463
  42. package/substrate/.claude/hooks/vocabulary-migration-check.sh +0 -171
  43. package/substrate/.claude/hooks/whereami-utc-gate.sh +0 -142
  44. package/substrate/.claude/luminaries/alan-cooper.md +0 -170
  45. package/substrate/.claude/luminaries/alistair-cockburn.md +0 -140
  46. package/substrate/.claude/luminaries/amazon-pr-faq.md +0 -34
  47. package/substrate/.claude/luminaries/ash-maurya.md +0 -121
  48. package/substrate/.claude/luminaries/bill-buxton.md +0 -210
  49. package/substrate/.claude/luminaries/charles-sanders-peirce.md +0 -150
  50. package/substrate/.claude/luminaries/david-ogilvy.md +0 -192
  51. package/substrate/.claude/luminaries/don-norman.md +0 -173
  52. package/substrate/.claude/luminaries/edward-tufte.md +0 -179
  53. package/substrate/.claude/luminaries/eric-evans.md +0 -160
  54. package/substrate/.claude/luminaries/frederick-brooks.md +0 -50
  55. package/substrate/.claude/luminaries/gang-of-four.md +0 -157
  56. package/substrate/.claude/luminaries/glenford-myers.md +0 -230
  57. package/substrate/.claude/luminaries/hunt-thomas.md +0 -115
  58. package/substrate/.claude/luminaries/hyrum-wright.md +0 -94
  59. package/substrate/.claude/luminaries/jason-fried-dhh.md +0 -46
  60. package/substrate/.claude/luminaries/jesse-james-garrett.md +0 -154
  61. package/substrate/.claude/luminaries/john-ousterhout.md +0 -94
  62. package/substrate/.claude/luminaries/karl-popper.md +0 -132
  63. package/substrate/.claude/luminaries/kent-beck.md +0 -168
  64. package/substrate/.claude/luminaries/linus-torvalds.md +0 -218
  65. package/substrate/.claude/luminaries/martin-fowler.md +0 -164
  66. package/substrate/.claude/luminaries/michael-feathers.md +0 -125
  67. package/substrate/.claude/luminaries/michael-nygard.md +0 -118
  68. package/substrate/.claude/luminaries/robert-c-martin.md +0 -164
  69. package/substrate/.claude/luminaries/saltzer-schroeder.md +0 -194
  70. package/substrate/.claude/luminaries/sophia-prater.md +0 -193
  71. package/substrate/.claude/luminaries/stephen-toulmin.md +0 -131
  72. package/substrate/.claude/luminaries/tony-hoare.md +0 -170
  73. package/substrate/.claude/luminaries/vaughn-vernon.md +0 -50
  74. package/substrate/.claude/luminaries/w-edwards-deming.md +0 -158
  75. package/substrate/.claude/rules/accessor-library-discipline.md +0 -138
  76. package/substrate/.claude/rules/adr-discipline.md +0 -120
  77. package/substrate/.claude/rules/api-conventions.md +0 -125
  78. package/substrate/.claude/rules/artifact-ingestion.md +0 -179
  79. package/substrate/.claude/rules/assert-only-after-verify.md +0 -137
  80. package/substrate/.claude/rules/blocked-items.md +0 -146
  81. package/substrate/.claude/rules/bootstrap-pair-discipline.md +0 -141
  82. package/substrate/.claude/rules/branching.md +0 -28
  83. package/substrate/.claude/rules/cold-adopter-harness-discipline.md +0 -129
  84. package/substrate/.claude/rules/commit-conventions.md +0 -22
  85. package/substrate/.claude/rules/compounding-axis-fresh-analysis.md +0 -188
  86. package/substrate/.claude/rules/compounding-sequence-fresh-analysis.md +0 -188
  87. package/substrate/.claude/rules/context-engineering.md +0 -202
  88. package/substrate/.claude/rules/context-management.md +0 -85
  89. package/substrate/.claude/rules/defensive-bash.md +0 -68
  90. package/substrate/.claude/rules/deferred-actions.md +0 -233
  91. package/substrate/.claude/rules/destructive-operations.md +0 -69
  92. package/substrate/.claude/rules/diagnosis.md +0 -38
  93. package/substrate/.claude/rules/github-issue-flash-tweet.md +0 -156
  94. package/substrate/.claude/rules/guardrails.md +0 -73
  95. package/substrate/.claude/rules/hook-wire-on-author.md +0 -103
  96. package/substrate/.claude/rules/identifier-leak-prevention.md +0 -104
  97. package/substrate/.claude/rules/iteration-bet-brief-completeness.md +0 -54
  98. package/substrate/.claude/rules/lite-manifest-schema-change-discipline.md +0 -98
  99. package/substrate/.claude/rules/longrun-prep-plan-doc-compression.md +0 -89
  100. package/substrate/.claude/rules/loop-discipline.md +0 -81
  101. package/substrate/.claude/rules/manual-prod-approval.md +0 -100
  102. package/substrate/.claude/rules/marker-enrichment-discipline.md +0 -99
  103. package/substrate/.claude/rules/mobile-ephemeral-session.md +0 -109
  104. package/substrate/.claude/rules/new-dependency-check.md +0 -51
  105. package/substrate/.claude/rules/oo-ad-entry-point.md +0 -117
  106. package/substrate/.claude/rules/operator-facing-prose.md +0 -196
  107. package/substrate/.claude/rules/option-label-discipline.md +0 -108
  108. package/substrate/.claude/rules/pattern-annotation.md +0 -100
  109. package/substrate/.claude/rules/plain-english-discipline.md +0 -156
  110. package/substrate/.claude/rules/plan-enumeration-needs-value-props.md +0 -211
  111. package/substrate/.claude/rules/pr-body-shape.md +0 -317
  112. package/substrate/.claude/rules/pr-strategy.md +0 -167
  113. package/substrate/.claude/rules/pr-title-shape.md +0 -161
  114. package/substrate/.claude/rules/prototype-workflow.md +0 -65
  115. package/substrate/.claude/rules/reserved-skill-names.md +0 -123
  116. package/substrate/.claude/rules/schema-management.md +0 -49
  117. package/substrate/.claude/rules/sdlc-gates.md +0 -149
  118. package/substrate/.claude/rules/security.md +0 -37
  119. package/substrate/.claude/rules/session-artifacts.md +0 -236
  120. package/substrate/.claude/rules/skill-composition-declarations.md +0 -124
  121. package/substrate/.claude/rules/skill-description-clarity.md +0 -247
  122. package/substrate/.claude/rules/skill-procedure-step-list.md +0 -137
  123. package/substrate/.claude/rules/state-schema-validation.md +0 -162
  124. package/substrate/.claude/rules/stuck-signal-diagnostic.md +0 -140
  125. package/substrate/.claude/rules/substrate-config-schema.md +0 -98
  126. package/substrate/.claude/rules/test-list-discipline.md +0 -175
  127. package/substrate/.claude/rules/test-sufficiency.md +0 -210
  128. package/substrate/.claude/rules/testing-tier-config.md +0 -145
  129. package/substrate/.claude/rules/testing.md +0 -38
  130. package/substrate/.claude/rules/turn-estimate-grounding.md +0 -134
  131. package/substrate/.claude/rules/visual-hierarchy.md +0 -437
  132. package/substrate/.claude/rules/we-dont-break-adopters.md +0 -126
  133. package/substrate/.claude/rules/whereami-load-bearing.md +0 -202
  134. package/substrate/.claude/rules/writing-craft-discipline.md +0 -92
  135. package/substrate/.claude/rules/wu-sequencing-compounds.md +0 -145
  136. package/substrate/.claude/skills/build/SKILL.md +0 -640
  137. package/substrate/.claude/skills/chronicle/SKILL.md +0 -55
  138. package/substrate/.claude/skills/clean-artifacts/SKILL.md +0 -249
  139. package/substrate/.claude/skills/decompose/SKILL.md +0 -280
  140. package/substrate/.claude/skills/diagnose/SKILL.md +0 -297
  141. package/substrate/.claude/skills/feynman/SKILL.md +0 -90
  142. package/substrate/.claude/skills/howdoi/SKILL.md +0 -105
  143. package/substrate/.claude/skills/ia-model/SKILL.md +0 -108
  144. package/substrate/.claude/skills/interaction-design/SKILL.md +0 -112
  145. package/substrate/.claude/skills/interpret-input/SKILL.md +0 -180
  146. package/substrate/.claude/skills/journal/SKILL.md +0 -209
  147. package/substrate/.claude/skills/kiss/SKILL.md +0 -449
  148. package/substrate/.claude/skills/launch/SKILL.md +0 -915
  149. package/substrate/.claude/skills/lean-canvas/SKILL.md +0 -332
  150. package/substrate/.claude/skills/longrun/SKILL.md +0 -463
  151. package/substrate/.claude/skills/luminary/SKILL.md +0 -481
  152. package/substrate/.claude/skills/ogilvy-writing-audit/SKILL.md +0 -177
  153. package/substrate/.claude/skills/onboard-repo/SKILL.md +0 -1624
  154. package/substrate/.claude/skills/pattern-review/SKILL.md +0 -99
  155. package/substrate/.claude/skills/personas/SKILL.md +0 -207
  156. package/substrate/.claude/skills/promote/SKILL.md +0 -283
  157. package/substrate/.claude/skills/requirement/SKILL.md +0 -98
  158. package/substrate/.claude/skills/retro/SKILL.md +0 -117
  159. package/substrate/.claude/skills/riff/SKILL.md +0 -114
  160. package/substrate/.claude/skills/roadmap-reconcile/SKILL.md +0 -163
  161. package/substrate/.claude/skills/session-end/SKILL.md +0 -309
  162. package/substrate/.claude/skills/session-log/SKILL.md +0 -299
  163. package/substrate/.claude/skills/skills/SKILL.md +0 -228
  164. package/substrate/.claude/skills/spec/SKILL.md +0 -105
  165. package/substrate/.claude/skills/sprint/SKILL.md +0 -392
  166. package/substrate/.claude/skills/stage/SKILL.md +0 -384
  167. package/substrate/.claude/skills/state-a-problem/SKILL.md +0 -185
  168. package/substrate/.claude/skills/temperance/SKILL.md +0 -108
  169. package/substrate/.claude/skills/use-case/SKILL.md +0 -417
  170. package/substrate/.claude/skills/user-stories/SKILL.md +0 -268
  171. package/substrate/.claude/skills/value-prop/SKILL.md +0 -251
  172. package/substrate/.claude/skills/verify/SKILL.md +0 -160
  173. package/substrate/.claude/skills/visual-review/SKILL.md +0 -503
  174. package/substrate/.claude/skills/whats-the-plan/SKILL.md +0 -202
  175. package/substrate/.claude/skills/whereami/SKILL.md +0 -307
  176. package/substrate/AGENTS.md +0 -79
  177. package/substrate/CLAUDE-lite.md +0 -85
  178. package/substrate/CODE_OF_CONDUCT.md +0 -28
  179. package/substrate/CONTRIBUTING.md +0 -177
  180. package/substrate/README.md +0 -173
  181. package/substrate/SECURITY.md +0 -19
  182. package/substrate/architecture/decisions/ADR-029-release-pipeline.md +0 -79
  183. package/substrate/architecture/decisions/ADR-031-non-breaking-changes-adopter-discipline.md +0 -139
  184. package/substrate/architecture/decisions/ADR-032-adopter-sync-dispatcher-architecture.md +0 -192
  185. package/substrate/architecture/decisions/ADR-039-release-tagging-scheme.md +0 -145
  186. package/substrate/architecture/decisions/ADR-040-planning-skill-vocabulary-and-lite-profile.md +0 -155
  187. package/substrate/architecture/decisions/ADR-044-unified-skill-body-template.md +0 -162
  188. package/substrate/lib/clean-artifacts-sweep.sh +0 -112
  189. package/substrate/lib/code-comment-discipline.sh +0 -144
  190. package/substrate/lib/composer-preflight.sh +0 -459
  191. package/substrate/lib/hook-inject.sh +0 -255
  192. package/substrate/lib/luminary-pick.sh +0 -96
  193. package/substrate/lib/output-discipline.sh +0 -143
  194. package/substrate/lib/prose-scan-boundary.sh +0 -171
  195. package/substrate/lib/rewrite-check.sh +0 -214
  196. package/substrate/lib/state.sh +0 -1372
  197. package/substrate/lib/telemetry.sh +0 -205
  198. package/substrate/lib/tier-check.sh +0 -187
  199. package/substrate/lib/tier-dependency-audit.sh +0 -1088
  200. package/substrate/presence/install/bassclef-hook-connect.sh +0 -178
  201. package/substrate/presence/install/bassclef-sync.dispatcher.template.sh +0 -841
  202. package/substrate/presence/install/bassclef-sync.template.sh +0 -2076
  203. package/substrate/presence/install/schedule-auto-save.cron.sh +0 -88
  204. package/substrate/presence/install/schedule-auto-save.taskscheduler.md +0 -122
  205. package/substrate/scripts/aggregate-telemetry.sh +0 -217
  206. package/substrate/scripts/analyze-tier-dependencies.sh +0 -239
  207. package/substrate/scripts/generate-lite-manifest.sh +0 -505
  208. package/substrate/scripts/generate-tier-manifest.sh +0 -28
  209. package/substrate/scripts/intent-drift-check.sh +0 -456
  210. package/substrate/scripts/lite-manifest-drift-check.sh +0 -146
  211. package/substrate/scripts/render-lite-manifest-doc.sh +0 -150
  212. package/substrate/standards/adr-template.md +0 -86
  213. package/substrate/standards/api-conventions/nextjs.md +0 -84
  214. package/substrate/standards/artifact-composition.md +0 -209
  215. package/substrate/standards/bash-hook-safety.md +0 -246
  216. package/substrate/standards/bassclef-configs-schema.md +0 -232
  217. package/substrate/standards/bassclef-evolution.md +0 -143
  218. package/substrate/standards/bassclef-internal-jargon.md +0 -244
  219. package/substrate/standards/bassclef-managed-sentinel.md +0 -96
  220. package/substrate/standards/bassclef-source-config.md +0 -228
  221. package/substrate/standards/branch-stacking.md +0 -408
  222. package/substrate/standards/code-safety-principles.md +0 -176
  223. package/substrate/standards/composer-prerequisites.md +0 -155
  224. package/substrate/standards/deferred-actions-schema.md +0 -204
  225. package/substrate/standards/dependency-discipline/cargo.md +0 -39
  226. package/substrate/standards/dependency-discipline/gem.md +0 -43
  227. package/substrate/standards/dependency-discipline/go-mod.md +0 -41
  228. package/substrate/standards/dependency-discipline/npm.md +0 -42
  229. package/substrate/standards/dependency-discipline/pip.md +0 -42
  230. package/substrate/standards/deployment-topology/ec2-tailscale.md +0 -225
  231. package/substrate/standards/deployment-topology.md +0 -69
  232. package/substrate/standards/docs-sync-allowlist.md +0 -76
  233. package/substrate/standards/domain-and-dns.md +0 -145
  234. package/substrate/standards/frontend-stack.md +0 -67
  235. package/substrate/standards/frontmatter-schema.md +0 -154
  236. package/substrate/standards/graceful-exit.md +0 -227
  237. package/substrate/standards/hook-idempotency.md +0 -102
  238. package/substrate/standards/hook-injection-discipline.md +0 -202
  239. package/substrate/standards/hook-install-class.md +0 -215
  240. package/substrate/standards/input-handler-interface.md +0 -152
  241. package/substrate/standards/lite-manifest-schema-changes.md +0 -135
  242. package/substrate/standards/luminary-matching.md +0 -105
  243. package/substrate/standards/luminary-problem-patterns.md +0 -481
  244. package/substrate/standards/migration-discipline/active-record.md +0 -50
  245. package/substrate/standards/migration-discipline/alembic.md +0 -43
  246. package/substrate/standards/migration-discipline/gorm.md +0 -50
  247. package/substrate/standards/migration-discipline/prisma.md +0 -53
  248. package/substrate/standards/migration-discipline/sqlalchemy.md +0 -51
  249. package/substrate/standards/mobile-ephemeral-session.md +0 -167
  250. package/substrate/standards/model-routing-discipline.md +0 -160
  251. package/substrate/standards/ogilvy-writing-rules.md +0 -225
  252. package/substrate/standards/opener-discipline.md +0 -96
  253. package/substrate/standards/operator-facing-prose-discipline.md +0 -201
  254. package/substrate/standards/persona-schema.md +0 -229
  255. package/substrate/standards/pluggable-luminaries.md +0 -323
  256. package/substrate/standards/pr-body-discipline.md +0 -115
  257. package/substrate/standards/preview-state-schema.md +0 -189
  258. package/substrate/standards/project-directory-layout.md +0 -276
  259. package/substrate/standards/release-tagging.md +0 -137
  260. package/substrate/standards/reserved-skill-names.md +0 -120
  261. package/substrate/standards/scannable-multi-option-output.md +0 -261
  262. package/substrate/standards/sdlc-compliance.md +0 -286
  263. package/substrate/standards/sdlc-gates/typescript.md +0 -57
  264. package/substrate/standards/secrets-lifecycle.md +0 -210
  265. package/substrate/standards/security-scanner-adapter.md +0 -145
  266. package/substrate/standards/session-board.md +0 -256
  267. package/substrate/standards/skill-output-discipline.md +0 -90
  268. package/substrate/standards/state-spine-contract.md +0 -255
  269. package/substrate/standards/state-spine.md +0 -511
  270. package/substrate/standards/steering-hints/kiss-words.md +0 -11
  271. package/substrate/standards/substrate-config-schema.md +0 -267
  272. package/substrate/standards/tech-stack-config.md +0 -109
  273. package/substrate/standards/tier-dependency-analysis.md +0 -167
  274. package/substrate/standards/tier-runtime-deps/lite.md +0 -57
  275. package/substrate/standards/tier-tag-schema.md +0 -155
  276. package/substrate/standards/two-layer-config.md +0 -99
  277. package/substrate/standards/use-case-format.md +0 -292
  278. package/substrate/standards/user-story-invest.md +0 -268
  279. package/substrate/standards/velocity-and-appetite.md +0 -229
  280. package/substrate/standards/voice-input-pattern.md +0 -119
  281. package/substrate/standards/whereami-schema.md +0 -301
  282. package/substrate/standards/worktree-management.md +0 -211
  283. package/substrate/standards/writing-guide.md +0 -213
  284. package/substrate/templates/chronicle-template.md +0 -75
  285. package/substrate/templates/deferred-action-template.md +0 -45
  286. package/substrate/templates/memory-proposal-template.md +0 -77
  287. package/substrate/templates/persona-template.md +0 -200
  288. package/substrate/templates/pr-faq.md +0 -45
  289. package/substrate/templates/secret-rotation-template.md +0 -162
  290. package/substrate/templates/spec-template.md +0 -131
  291. package/substrate/templates/use-case-template.md +0 -194
  292. package/substrate/templates/user-story-template.md +0 -107
  293. package/substrate/templates/whereami-template.md +0 -101
@@ -1,303 +0,0 @@
1
- #!/bin/bash
2
- # tier: lite
3
- set -e
4
-
5
- # Session Start hook coordinator. Injects reflection prompt and iterates
6
- # session-reflection.d/*.sh modules in numbered order. Modules inherit
7
- # the blocked_banner helper and $BASSCLEF_DIR defined here.
8
-
9
- # === Hook liveness heartbeat (WU-3 of bet 2026-07-31d; closes #1002) ===
10
- # Silent-fail — a missing lib never crashes the hook.
11
- {
12
- _hb_sd="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
- for _hb_c in "${_hb_sd}/../../lib/hook-heartbeat.sh" "${HOME:-/}/lib/hook-heartbeat.sh"; do
14
- [ -f "$_hb_c" ] && source "$_hb_c" && heartbeat_mark "session-reflection-coordinator" && break
15
- done
16
- unset _hb_sd _hb_c
17
- } 2>/dev/null || true
18
-
19
- INPUT=$(cat)
20
- # Fail-soft on malformed JSON — same pattern as bassclef#1101 + #1119 + #1123.
21
- # Prevents crash when harness sends malformed stdin at SessionStart.
22
- # Audit ticket: bassclef#1125 (jq+set -e pattern across all hooks).
23
- SOURCE=$(echo "$INPUT" | jq -r '.source // "unknown"' 2>/dev/null || echo "unknown")
24
- CWD=$(echo "$INPUT" | jq -r '.cwd // "."' 2>/dev/null || echo ".")
25
-
26
- cd "$CWD"
27
-
28
- CWD_HASH=$(echo -n "$CWD" | md5 -q 2>/dev/null || echo -n "$CWD" | md5sum 2>/dev/null | cut -c1-8 || echo "default")
29
- SESSION_TIMING_FILE="/tmp/claude-session-timing-${CWD_HASH}"
30
- TIMING_NOW=$(date +"%Y-%m-%dT%H:%M:%S%z")
31
- echo "$TIMING_NOW" > "$SESSION_TIMING_FILE"
32
-
33
- # Durable dual-write (mobile-ephemeral-session.md §2).
34
- DURABLE_TIMING_DIR="$CWD/state/markers/session-timing"
35
- mkdir -p "$DURABLE_TIMING_DIR" 2>/dev/null
36
- echo "$TIMING_NOW" > "$DURABLE_TIMING_DIR/${CWD_HASH}.timing" 2>/dev/null
37
-
38
- source "$(dirname "$0")/trace-helper.sh"
39
- trace_log "session-start" "$SOURCE"
40
-
41
- # Resolve bassclef dir through symlinks — used by modules to find scripts.
42
- HOOK_REAL_PATH="$(readlink -f "$0" 2>/dev/null || readlink "$0" 2>/dev/null || echo "$0")"
43
- BASSCLEF_DIR="$(cd "$(dirname "$HOOK_REAL_PATH")/../.." && pwd)"
44
-
45
- # High-contrast BLOCKED banner; used by every module that raises a gap.
46
- blocked_banner() {
47
- local msg="$1"
48
- echo ""
49
- echo "🛑🛑🛑 BLOCKED 🛑🛑🛑"
50
- echo "────────────────────────────────────────────"
51
- echo "$msg"
52
- echo "────────────────────────────────────────────"
53
- echo "ACTION: resolve OR explicitly defer (per .claude/rules/blocked-items.md)."
54
- echo "Silence is not deferral. Propose this as item #1 in your session plan."
55
- echo ""
56
- }
57
-
58
- echo "============================================"
59
- echo "SESSION START — MANDATORY REFLECT → PLAN"
60
- echo "============================================"
61
- echo ""
62
- echo "You MUST run the Session Start protocol before doing ANY other work."
63
- echo "Do NOT skip this. Do NOT jump to answering the user's question first."
64
- echo ""
65
-
66
- # === Last save state ===
67
- LAST_SAVE_FILE=".claude/LAST_SAVE"
68
- if [ -f "$LAST_SAVE_FILE" ]; then
69
- SAVE_TIMESTAMP=$(grep '^timestamp:' "$LAST_SAVE_FILE" | cut -d' ' -f2-)
70
- SAVE_MACHINE=$(grep '^machine:' "$LAST_SAVE_FILE" | cut -d' ' -f2-)
71
- SAVE_BRANCH=$(grep '^branch:' "$LAST_SAVE_FILE" | cut -d' ' -f2-)
72
- SAVE_COMMIT=$(grep '^last_commit:' "$LAST_SAVE_FILE" | cut -d' ' -f2-)
73
- echo "### LAST SAVE STATE"
74
- echo ""
75
- echo "Saved: $SAVE_TIMESTAMP"
76
- echo "Machine: $SAVE_MACHINE"
77
- echo "Branch: $SAVE_BRANCH"
78
- echo "Last commit: $SAVE_COMMIT"
79
- echo ""
80
- else
81
- echo "### NO PREVIOUS SAVE STATE FOUND"
82
- echo ""
83
- echo "This may be the first session, or save-state has never run."
84
- echo "Proceed with caution — check git log for context."
85
- echo ""
86
- fi
87
-
88
- # === Session lock check (BEFORE writing — idempotency rule) ===
89
- LOCK_FILE=".claude/SESSION_LOCK"
90
- if [ -f "$LOCK_FILE" ]; then
91
- # Platform-portable mtime: GNU `stat -c %Y` first, BSD `stat -f %m` fallback.
92
- # Order matters — GNU `stat -f` exits 0 with WRONG output (mount-point info
93
- # not mtime), so the `||` chain only triggers on BSD when -c fails. Caught
94
- # by WU-8 test of /longrun-c on Linux CI (7th TDD-find of the day; first
95
- # attempt at the fix had the order reversed and still failed).
96
- LOCK_MTIME=$(stat -c %Y "$LOCK_FILE" 2>/dev/null || stat -f %m "$LOCK_FILE" 2>/dev/null || echo "0")
97
- LOCK_AGE=$(( $(date +%s) - LOCK_MTIME ))
98
- if [ "$LOCK_AGE" -lt 7200 ]; then
99
- echo "### ANOTHER SESSION MAY BE ACTIVE"
100
- echo ""
101
- echo "SESSION_LOCK exists and is $(( LOCK_AGE / 60 )) minutes old."
102
- echo "Another agent or session may be working in this repo."
103
- echo ""
104
- echo "→ Ask the user before proceeding. Do NOT overwrite another session's work."
105
- echo ""
106
- else
107
- echo "### STALE SESSION LOCK ($(( LOCK_AGE / 3600 )) hours old)"
108
- echo ""
109
- echo "Previous session likely crashed. Proceeding with caution."
110
- echo ""
111
- fi
112
- fi
113
-
114
- # === Write session lock (AFTER checking — per standards/hook-idempotency.md) ===
115
- MACHINE=$(hostname -s 2>/dev/null || echo "unknown")
116
- echo "machine: $MACHINE" > "$LOCK_FILE"
117
- echo "timestamp: $(date +%Y-%m-%dT%H:%M:%S%z)" >> "$LOCK_FILE"
118
- echo "pid: $$" >> "$LOCK_FILE"
119
-
120
- # === Fetch origin before staleness checks (bassclef-upstream#403) ===
121
- # Several session-reflection.d/ fragments read git log / file listings
122
- # to compute staleness banners (chronicle, danger-mode summary,
123
- # architect-review, release-notes, recent-strategic-artifacts,
124
- # workflow-staleness, metrics-staleness). Without fetching origin
125
- # first, those reads see this machine's last-pull state — not the
126
- # shared truth — and may show wrong-now banners when another session
127
- # has pushed since (e.g., 2026-06-26c saw stale local while origin
128
- # had today's session-end commit from #401).
129
- #
130
- # Best-effort: || true keeps the dispatcher working when offline / no
131
- # remote / detached HEAD. Each fragment continues to use local git
132
- # state; we just ensure local has had a chance to sync first.
133
- CURRENT_BRANCH_FOR_FETCH=$(git symbolic-ref --short HEAD 2>/dev/null)
134
- if [ -n "$CURRENT_BRANCH_FOR_FETCH" ]; then
135
- git fetch origin "$CURRENT_BRANCH_FOR_FETCH" --quiet 2>/dev/null || true
136
- fi
137
-
138
- # === Iterate modules in numbered order ===
139
- # Use HOOK_REAL_PATH (line 15, symlinks resolved) so fragments load when
140
- # the dispatcher is invoked via a symlink from an adopter checkout.
141
- # Per bassclef-upstream#591 — link-relative path silently skipped all
142
- # fragments on adopter machines, breaking every session-start gate.
143
- MODULES_DIR="$(dirname "$HOOK_REAL_PATH")/session-reflection.d"
144
- if [ -d "$MODULES_DIR" ]; then
145
- # Modules use the pattern `return 0 2>/dev/null || exit 0` (safe when
146
- # sourced OR run standalone). A module returning 2 signals a fatal
147
- # condition the coordinator MUST honor (e.g., invalid substrate config
148
- # per 15-substrate-config-validate.sh L86). Per Feathers R3.2 semantic
149
- # preservation — cure b (2026-07-30) restored return semantics while
150
- # keeping the abort contract via return code 2 + this catch.
151
- #
152
- # Disable `set -e` briefly so the coordinator sees each module's
153
- # return code cleanly. Without this, `return 1` from a sourced module
154
- # would abort the coordinator via set -e regardless of code.
155
- set +e
156
- for module in "$MODULES_DIR"/*.sh; do
157
- [ -f "$module" ] || continue
158
- source "$module"
159
- MODULE_RC=$?
160
- # Modules may set their own shell options (e.g., 90-release-backlog
161
- # uses `set -euo pipefail`). Those persist to the coordinator via
162
- # sourcing. Reset to a permissive default after each module so one
163
- # module's discipline does not abort the coordinator's remaining
164
- # body. Cure b (2026-07-30) — Nygard bulkhead + Feathers R3.5.
165
- set +eu
166
- if [ "$MODULE_RC" -eq 2 ]; then
167
- # Module signaled fatal abort. Preserve pre-cure behavior — exit
168
- # with non-zero code the harness treats as coordinator failure.
169
- echo "" >&2
170
- echo "!! session-reflection: aborting after $(basename "$module") returned 2" >&2
171
- exit 1
172
- fi
173
- done
174
- # Deliberately DO NOT re-enable set -e. Cure b (2026-07-30) discovered
175
- # the coordinator's ACTIVE CONTEXT block at L162+ uses grep patterns
176
- # that return 1 when whereami has no in_flight_bet field. Under set -e
177
- # those aborted the coordinator BEFORE reaching Step 1: Read memory
178
- # (L200+). Pre-cure the abort was masked because 05-active-bet.sh
179
- # exited earlier. Post-cure the abort surfaces. Fix: coordinator runs
180
- # without set -e for the rest of its body — matches modules' tolerance
181
- # for non-zero returns from optional grep/find calls.
182
- fi
183
-
184
- # === Context loading — proactively surface active work ===
185
- echo "### ACTIVE CONTEXT (read these before planning)"
186
- echo ""
187
-
188
- if [ -f "docs/whereami.md" ]; then
189
- ACTIVE_BET=$(grep 'iteration_bet:' docs/whereami.md 2>/dev/null | head -1 | sed 's/iteration_bet: *//')
190
- if [ -n "$ACTIVE_BET" ] && [ -f "$ACTIVE_BET" ]; then
191
- echo "**Active iteration goal**: $ACTIVE_BET"
192
- head -20 "$ACTIVE_BET" 2>/dev/null
193
- echo ""
194
- fi
195
- ACTIVE_PHASE=$(grep 'iteration_phase:' docs/whereami.md 2>/dev/null | head -1 | sed 's/iteration_phase: *//')
196
- echo "**Phase**: ${ACTIVE_PHASE:-unknown}"
197
- echo ""
198
- fi
199
-
200
- for CDIR in "chronicle" "docs/chronicle"; do
201
- if [ -d "$CDIR" ]; then
202
- LATEST_CHRONICLE=$(ls -1 "$CDIR"/*.md 2>/dev/null | sort | tail -1)
203
- if [ -n "$LATEST_CHRONICLE" ]; then
204
- echo "**Last chronicle**: $(basename "$LATEST_CHRONICLE")"
205
- OPEN_THREADS=$(sed -n '/## Open threads/,/## /p' "$LATEST_CHRONICLE" 2>/dev/null | head -15)
206
- if [ -n "$OPEN_THREADS" ]; then
207
- echo "$OPEN_THREADS"
208
- fi
209
- echo ""
210
- fi
211
- break
212
- fi
213
- done
214
-
215
- if [ -f "substrate.config.md" ]; then
216
- LEVEL=$(grep '^level:' substrate.config.md 2>/dev/null | head -1 | sed 's/level: *//')
217
- echo "**Process level**: ${LEVEL:-core}"
218
- echo ""
219
- fi
220
-
221
- echo ""
222
-
223
- # === Standard reflection steps ===
224
- echo "### Step 1: Read memory"
225
- echo "Read project memory from .claude/ directory"
226
- echo ""
227
- echo "### Step 2: Recent commits"
228
- git log --oneline -5 2>/dev/null || echo "(no git history)"
229
- echo ""
230
- echo "### Step 3: Open issues"
231
- REPO_NAME=$(gh repo view --json nameWithOwner -q '.nameWithOwner' 2>/dev/null || echo "")
232
- if [ -n "$REPO_NAME" ]; then
233
- gh issue list -R "$REPO_NAME" --limit 10 2>/dev/null || echo "(issues unavailable)"
234
- else
235
- echo "(gh repo not detected — check open issues manually)"
236
- fi
237
- echo ""
238
- echo "### Step 4: Summarize + propose 1-3 items, then WAIT for human confirmation."
239
- echo ""
240
- echo "### MANDATORY RESPONSE FORMAT (user-visible)"
241
- echo ""
242
- echo "Claude Code hook stdout lands in MODEL context only — the operator"
243
- echo "never sees the 🛑 banners above. The only user-visible surface is"
244
- echo "what YOU write in your first message. So:"
245
- echo ""
246
- echo "Your FIRST user-facing message this session MUST begin with a"
247
- echo "verbatim echo of every 🛑 BLOCKED block surfaced above — same"
248
- echo "emoji, same horizontal rule, same message text — inside a fenced"
249
- echo "code block so the operator sees exactly what the hook emitted,"
250
- echo "not your paraphrase."
251
- echo ""
252
- echo "THEN, immediately below the echoed blocks, add your disposition:"
253
- echo ""
254
- echo " BLOCKED ITEMS — my disposition:"
255
- echo " - [name]: RESOLVE NOW / RESOLVE THIS SESSION / DEFER (reason)"
256
- echo ""
257
- echo "If there are zero blocked items, state: 'No blocked items surfaced"
258
- echo "by session-start hook.'"
259
- echo ""
260
- echo "Only AFTER the verbatim echo + disposition may you address the"
261
- echo "user's question. This overrides the pull of any specific user"
262
- echo "question — the user's prior instructions live in these rules."
263
- echo ""
264
-
265
- # === Manifest-aware session planning ===
266
- if [ -f "substrate.config.md" ]; then
267
- LEVEL=$(grep '^level:' substrate.config.md 2>/dev/null | head -1 | sed 's/level: *//')
268
- CEILING=$(grep 'iteration_ceiling:' substrate.config.md 2>/dev/null | head -1 | sed 's/.*iteration_ceiling: *//')
269
-
270
- ACTIVE_BET=""
271
- if [ -f "docs/whereami.md" ]; then
272
- ACTIVE_BET=$(grep 'iteration_bet:' docs/whereami.md 2>/dev/null | head -1 | sed 's/iteration_bet: *//')
273
- fi
274
-
275
- if [ -n "$ACTIVE_BET" ] && [ -f "$ACTIVE_BET" ]; then
276
- echo "### Active iteration — continue or start new?"
277
- echo ""
278
- echo "An iteration goal exists: $ACTIVE_BET"
279
- echo "Check if it's complete or still has open scope."
280
- echo "If complete → propose a new iteration from open issues."
281
- echo "If in progress → resume where it left off."
282
- else
283
- echo "### Propose iteration (manifest-aware)"
284
- echo ""
285
- echo "No active iteration goal. Based on open issues above,"
286
- echo "propose a light iteration:"
287
- echo ""
288
- echo '```'
289
- echo "## This session (light iteration)"
290
- echo "Retiring: [highest-risk open issue]"
291
- echo "Proving: [most valuable open issue]"
292
- echo "Scope: [2-4 issues that fit together]"
293
- echo "Cost ceiling: ${CEILING:-\$0}"
294
- echo '```'
295
- echo ""
296
- echo "Read the open issues and propose this. User confirms or adjusts."
297
- fi
298
- echo ""
299
- fi
300
-
301
- echo "============================================"
302
-
303
- exit 0
@@ -1,219 +0,0 @@
1
- #!/bin/bash
2
- # tier: lite
3
- # install-class: dual
4
- # skill-body-grade-gate.sh — PreToolUse Edit|Write gate on
5
- # .claude/skills/*/SKILL.md. Measures per-sentence Flesch-Kincaid grade.
6
- # Honors FKGL-EXEMPT annotations. BLOCKs when the 80%-at-grade-8-9 rule
7
- # fails per ADR-040 Decision 4.
8
- #
9
- # Sister to turn-prose-grade-measure.sh (Stop event; turn prose;
10
- # measurement only, no gate). This hook fires on file writes and blocks.
11
- #
12
- # Input (stdin): JSON with tool_name + tool_input.file_path + content/new_string
13
- # Exit: 0 pass, 2 block
14
- #
15
- # Override per-call: SKIP_SKILL_BODY_GRADE=1
16
- #
17
- # Per @luminary kent-beck (Tier 0 strict TDD)
18
- # + @luminary donald-norman (plain-English target per ADR-040 D4)
19
- # + @luminary michael-feathers (characterization tests pin real inputs)
20
-
21
- set +e
22
-
23
- # === Override path ===
24
- if [ "${SKIP_SKILL_BODY_GRADE:-0}" = "1" ]; then
25
- exit 0
26
- fi
27
-
28
- # === Parse stdin JSON ===
29
- STDIN_JSON=$(cat 2>/dev/null || echo '{}')
30
-
31
- # Bail on unparseable JSON
32
- if ! echo "$STDIN_JSON" | jq -e . >/dev/null 2>&1; then
33
- exit 0
34
- fi
35
-
36
- TOOL_NAME=$(echo "$STDIN_JSON" | jq -r '.tool_name // ""' 2>/dev/null)
37
- FILE_PATH=$(echo "$STDIN_JSON" | jq -r '.tool_input.file_path // ""' 2>/dev/null)
38
-
39
- # Only fire on Write or Edit
40
- if [ "$TOOL_NAME" != "Write" ] && [ "$TOOL_NAME" != "Edit" ]; then
41
- exit 0
42
- fi
43
-
44
- # Only fire on .claude/skills/*/SKILL.md paths
45
- case "$FILE_PATH" in
46
- *".claude/skills/"*"/SKILL.md")
47
- ;;
48
- *)
49
- exit 0
50
- ;;
51
- esac
52
-
53
- # === Extract text to grade ===
54
- if [ "$TOOL_NAME" = "Write" ]; then
55
- CONTENT=$(echo "$STDIN_JSON" | jq -r '.tool_input.content // ""' 2>/dev/null)
56
- else
57
- # Edit: grade the new_string (text being added)
58
- CONTENT=$(echo "$STDIN_JSON" | jq -r '.tool_input.new_string // ""' 2>/dev/null)
59
- fi
60
-
61
- [ -z "$CONTENT" ] && exit 0
62
-
63
- # === Run the gate via python ===
64
- # Python does the scrubbing + per-sentence FKGL + threshold check.
65
- # Same textstat-preferred / fallback pattern as turn-prose-grade-measure.sh.
66
- GATE_OUTPUT=$(printf '%s' "$CONTENT" | python3 -c '
67
- import sys, re
68
-
69
- text = sys.stdin.read()
70
-
71
- # === Strip YAML frontmatter ===
72
- # Matches leading "---\n...\n---\n" block
73
- text = re.sub(r"^---\n.*?\n---\n", "", text, count=1, flags=re.DOTALL)
74
-
75
- # === Strip FKGL-EXEMPT blocks ===
76
- # Matches <!-- FKGL-EXEMPT: any-text --> ... <!-- /FKGL-EXEMPT -->
77
- text = re.sub(r"<!--\s*FKGL-EXEMPT:.*?-->.*?<!--\s*/FKGL-EXEMPT\s*-->",
78
- "", text, flags=re.DOTALL)
79
-
80
- # === Strip code blocks ===
81
- text = re.sub(r"```.*?```", "", text, flags=re.DOTALL)
82
-
83
- # === Strip inline backticks ===
84
- text = re.sub(r"`[^`]*`", "", text)
85
-
86
- # === Strip URLs ===
87
- text = re.sub(r"https?://[^\s)]+", "", text)
88
-
89
- # === Strip markdown table pipes and headers ===
90
- text = re.sub(r"^\s*\|.*\|\s*$", "", text, flags=re.MULTILINE)
91
- text = re.sub(r"^\s*#+\s+.*$", "", text, flags=re.MULTILINE)
92
-
93
- # === Strip HTML comments ===
94
- text = re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL)
95
-
96
- # === Split into sentences ===
97
- # Also split on bullet markers + numbered lists so a bullet block is not
98
- # treated as one long "sentence" (which inflates FKGL unfairly).
99
- sentences = [s.strip() for s in re.split(r"[.!?\n]+\s*(?:[-*]\s|\d+\.\s)?", text) if s.strip()]
100
- # Second pass — plain sentence-terminator split (some markdown lines lack terminators)
101
- sentences = [ss.strip() for s in sentences for ss in re.split(r"[.!?]+", s) if ss.strip()]
102
-
103
- # === Total word count check ===
104
- all_words = re.findall(r"\b[a-zA-Z][a-zA-Z\x27]*\b", text)
105
- if len(all_words) < 20:
106
- print("SKIP short")
107
- sys.exit(0)
108
-
109
- # === Preferred: textstat per-sentence FKGL ===
110
- try:
111
- from textstat import flesch_kincaid_grade
112
- grade_fn = flesch_kincaid_grade
113
- except ImportError:
114
- # Fallback formula — same as turn-prose-grade-measure.sh
115
- def count_syllables(word):
116
- word = re.sub(r"[^a-z]", "", word.lower())
117
- if not word:
118
- return 0
119
- vowels = "aeiouy"
120
- count = 0
121
- prev_was_vowel = False
122
- for c in word:
123
- is_vowel = c in vowels
124
- if is_vowel and not prev_was_vowel:
125
- count += 1
126
- prev_was_vowel = is_vowel
127
- if word.endswith("e") and count > 1:
128
- count -= 1
129
- return max(1, count)
130
-
131
- def grade_fn(s):
132
- words = re.findall(r"\b[a-zA-Z][a-zA-Z\x27]*\b", s)
133
- if not words:
134
- return 0
135
- syllables = sum(count_syllables(w) for w in words)
136
- return 0.39 * len(words) + 11.8 * (syllables / len(words)) - 15.59
137
-
138
- # === Grade each sentence ===
139
- above_ceiling = 0
140
- graded_count = 0
141
- failing_examples = []
142
-
143
- for sent in sentences:
144
- # Skip short sentences (< 5 words — likely list items, fragments)
145
- words = re.findall(r"\b[a-zA-Z][a-zA-Z\x27]*\b", sent)
146
- if len(words) < 5:
147
- continue
148
- graded_count += 1
149
- grade = grade_fn(sent)
150
- if grade > 10:
151
- above_ceiling += 1
152
- if len(failing_examples) < 3:
153
- failing_examples.append((round(grade, 1), sent[:80]))
154
-
155
- if graded_count == 0:
156
- print("SKIP no-graded")
157
- sys.exit(0)
158
-
159
- fail_pct = (above_ceiling / graded_count) * 100
160
-
161
- # === 80% at grade 8-9 rule: at most 20% above grade 10 ===
162
- if fail_pct > 20:
163
- print(f"BLOCK {above_ceiling} {graded_count} {fail_pct:.0f}")
164
- for grade, snippet in failing_examples:
165
- print(f" EXAMPLE {grade} {snippet}")
166
- sys.exit(0)
167
-
168
- print(f"PASS {above_ceiling} {graded_count} {fail_pct:.0f}")
169
- ' 2>/dev/null)
170
-
171
- # === Parse gate result ===
172
- FIRST_LINE=$(echo "$GATE_OUTPUT" | head -1)
173
- VERDICT=$(echo "$FIRST_LINE" | awk '{print $1}')
174
-
175
- case "$VERDICT" in
176
- PASS|SKIP)
177
- exit 0
178
- ;;
179
- BLOCK)
180
- ABOVE=$(echo "$FIRST_LINE" | awk '{print $2}')
181
- TOTAL=$(echo "$FIRST_LINE" | awk '{print $3}')
182
- PCT=$(echo "$FIRST_LINE" | awk '{print $4}')
183
- ;;
184
- *)
185
- # Empty or unexpected — pass through safely
186
- exit 0
187
- ;;
188
- esac
189
-
190
- # === Emit BLOCK message ===
191
- echo "" >&2
192
- echo "🛑 SKILL-BODY-GRADE-GATE — BLOCKED" >&2
193
- echo "" >&2
194
- echo " File: $FILE_PATH" >&2
195
- echo " Result: $ABOVE of $TOTAL graded sentences above grade 10 (${PCT}%)" >&2
196
- echo " Target: 80% at grade 8-9. At most 20% above grade 10." >&2
197
- echo " Per: ADR-040 Decision 4 (grade 8-9 for 80% of prose)" >&2
198
- echo "" >&2
199
- echo " Failing sentence samples (grade | first 80 chars):" >&2
200
- echo "$GATE_OUTPUT" | grep '^ EXAMPLE' | while read -r _ grade snippet; do
201
- echo " [$grade] $snippet" >&2
202
- done
203
- echo "" >&2
204
- echo " Cure paths:" >&2
205
- echo " 1. Rewrite failing sentences in short SVO English" >&2
206
- echo " 2. Mark sentence exempt: <!-- FKGL-EXEMPT: reason --> ... <!-- /FKGL-EXEMPT -->" >&2
207
- echo "" >&2
208
- echo " Override (logged via trace-helper): SKIP_SKILL_BODY_GRADE=1" >&2
209
- echo "" >&2
210
-
211
- # === Trace log ===
212
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
213
- if [ -f "$SCRIPT_DIR/trace-helper.sh" ]; then
214
- # shellcheck disable=SC1091
215
- source "$SCRIPT_DIR/trace-helper.sh"
216
- trace_log "skill-body-grade-gate" "BLOCKED above=$ABOVE total=$TOTAL pct=$PCT file=$FILE_PATH"
217
- fi
218
-
219
- exit 2
@@ -1,107 +0,0 @@
1
- #!/usr/bin/env bash
2
- # tier: lite
3
- # install-class: dual
4
- # skill-body-intent-drift.sh — PostToolUse Edit|Write hook on
5
- # .claude/skills/*/SKILL.md. Runs scripts/intent-drift-check.sh in
6
- # advisory mode. Reports cosine + verdict to stderr. Never blocks.
7
- #
8
- # Closes bassclef-upstream#788 (narrow slice of #785). Makes /loop
9
- # step 5 automatic — no more honor-system per cure.
10
- #
11
- # Input (stdin): JSON — tool_name, tool_input.file_path
12
- # Exit: always 0 (advisory only at V1)
13
- #
14
- # Env:
15
- # VOYAGE_API_KEY — required to run the check; missing = skip silently
16
- # SKIP_SKILL_BODY_INTENT_DRIFT=1 — per-call override (logged)
17
- #
18
- # Per @luminary michael-feathers (characterization measurable not subjective)
19
- # + @luminary kent-beck (Tier 0 strict TDD)
20
-
21
- set +e
22
-
23
- # === Override path ===
24
- if [ "${SKIP_SKILL_BODY_INTENT_DRIFT:-0}" = "1" ]; then
25
- exit 0
26
- fi
27
-
28
- # === Parse stdin JSON ===
29
- STDIN_JSON=$(cat 2>/dev/null || echo '{}')
30
-
31
- if ! echo "$STDIN_JSON" | jq -e . >/dev/null 2>&1; then
32
- exit 0
33
- fi
34
-
35
- TOOL_NAME=$(echo "$STDIN_JSON" | jq -r '.tool_name // ""' 2>/dev/null)
36
- FILE_PATH=$(echo "$STDIN_JSON" | jq -r '.tool_input.file_path // ""' 2>/dev/null)
37
-
38
- # Only fire on Write or Edit
39
- if [ "$TOOL_NAME" != "Write" ] && [ "$TOOL_NAME" != "Edit" ]; then
40
- exit 0
41
- fi
42
-
43
- # Only fire on .claude/skills/*/SKILL.md
44
- case "$FILE_PATH" in
45
- *".claude/skills/"*"/SKILL.md")
46
- ;;
47
- *)
48
- exit 0
49
- ;;
50
- esac
51
-
52
- # Extract slug from path
53
- SLUG=$(echo "$FILE_PATH" | sed -E 's|.*\.claude/skills/([^/]+)/SKILL\.md|\1|')
54
- if [ -z "$SLUG" ] || [ "$SLUG" = "$FILE_PATH" ]; then
55
- exit 0
56
- fi
57
-
58
- # Locate repo root
59
- if [ -n "${CLAUDE_PROJECT_DIR:-}" ] && [ -d "$CLAUDE_PROJECT_DIR/.claude" ]; then
60
- REPO_ROOT="$CLAUDE_PROJECT_DIR"
61
- else
62
- REPO_ROOT="$(pwd)"
63
- fi
64
-
65
- VECTOR_PATH="$REPO_ROOT/.claude/embeddings/skills/${SLUG}.json"
66
- DRIFT_SCRIPT="$REPO_ROOT/scripts/intent-drift-check.sh"
67
-
68
- # Skip when baseline vector absent
69
- if [ ! -f "$VECTOR_PATH" ]; then
70
- exit 0
71
- fi
72
-
73
- # Skip when drift script missing
74
- if [ ! -x "$DRIFT_SCRIPT" ]; then
75
- exit 0
76
- fi
77
-
78
- # Skip when VOYAGE_API_KEY missing
79
- if [ -z "${VOYAGE_API_KEY:-}" ]; then
80
- exit 0
81
- fi
82
-
83
- # Run the check + surface result to stderr
84
- RESULT=$(bash "$DRIFT_SCRIPT" --skill "$SLUG" --json 2>&1)
85
- CHECK_EXIT=$?
86
-
87
- # Parse cosine + verdict from JSON result
88
- COSINE=$(echo "$RESULT" | jq -r '.cosine // "n/a"' 2>/dev/null)
89
- VERDICT=$(echo "$RESULT" | jq -r '.verdict // "n/a"' 2>/dev/null)
90
- THRESHOLD=$(echo "$RESULT" | jq -r '.threshold // "n/a"' 2>/dev/null)
91
-
92
- # Advisory surface — never blocks
93
- if [ "$VERDICT" = "PASS" ]; then
94
- echo "" >&2
95
- echo "[intent-drift-check] /$SLUG cosine $COSINE (threshold $THRESHOLD) — PASS. Intent preserved by measurable cosine." >&2
96
- elif [ "$VERDICT" = "FAIL" ]; then
97
- echo "" >&2
98
- echo "⚠ [intent-drift-check] /$SLUG cosine $COSINE (threshold $THRESHOLD) — FAIL." >&2
99
- echo " Advisory only at V1 — commit not blocked. Consider re-checking intent preservation." >&2
100
- echo " Run \`bash scripts/intent-drift-check.sh --skill $SLUG\` for detail." >&2
101
- else
102
- # Drift script failed or returned non-JSON — silent skip
103
- :
104
- fi
105
-
106
- # Always exit 0 — advisory only at V1
107
- exit 0