@thebassclef/lite 0.1.2 → 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 +238 -127
  2. package/dist/cli.js +240 -129
  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,255 +0,0 @@
1
- #!/usr/bin/env bash
2
- # tier: lite
3
- # install-class: dual
4
- # lib/hook-inject.sh — shared helper for hooks that inject additionalContext
5
- # per standards/hook-injection-discipline.md and resolve project resources
6
- # per standards/hook-install-class.md.
7
- #
8
- # This library is SOURCED by hooks, not executed. It does not set -e/-u
9
- # globally (would affect callers); functions enforce their own contracts.
10
- #
11
- # Authority: ADR-035 (substrate-as-system + hooks-as-steering-surfaces)
12
- # Tier 0 strict TDD: lib/tests/hook-inject.test.sh
13
- # Anchors: @luminary john-ousterhout (deep modules) + @luminary michael-nygard
14
- # (fail-soft fallback chain) + @luminary tony-hoare (pre/post contracts)
15
- #
16
- # Exposed functions:
17
- # emit_additional_context <event> <content> # JSON envelope to stdout
18
- # emit_base_pair_reminders # plain-English + assert-verify reminder text
19
- # resolve_standards_path <filename> # CLAUDE_PROJECT_DIR → env → HOME → fail
20
- # resolve_config_path # same chain for bassclef-configs.jsonc
21
- # extract_block_terms <jargon-file> # BLOCK-tier rows from wordlist
22
- #
23
- # Env vars consumed:
24
- # CLAUDE_PROJECT_DIR — project root (Claude Code sets this)
25
- # KISS_STANDARDS_FILE — override for standards path resolution
26
- # BASSCLEF_CONFIG_FILE — override for config path resolution
27
- # HOME_OVERRIDE — test-only; replaces $HOME for fixture isolation
28
- #
29
- # Per standards/hook-injection-discipline.md:
30
- # - additionalContext capped at 10,000 chars (Anthropic limit)
31
- # - JSON envelope shape: {"hookSpecificOutput": {"hookEventName": "...", "additionalContext": "..."}}
32
- # - Fail-soft: all resolution helpers return non-zero with structured stderr; never silently pass
33
-
34
- # === Constants ===
35
- HOOK_INJECT_MAX_CONTENT=10000
36
-
37
- # === Resolve $HOME with test-override support ===
38
- __hook_inject_home() {
39
- echo "${HOME_OVERRIDE:-$HOME}"
40
- }
41
-
42
- # === emit_additional_context ===
43
- # Args:
44
- # $1 — event name (e.g., UserPromptSubmit, Stop)
45
- # $2 — content (the additionalContext string)
46
- # Stdout: JSON envelope per Anthropic's hookSpecificOutput shape
47
- # Exit: 0 success; 2 missing/invalid args
48
- emit_additional_context() {
49
- local event="${1:-}"
50
- local content="${2-}" # NOT :- so explicit empty string is allowed (test case)
51
-
52
- # Validate event arg
53
- if [ -z "$event" ]; then
54
- echo "emit_additional_context: missing event arg" >&2
55
- echo " Usage: emit_additional_context <event-name> <content>" >&2
56
- return 2
57
- fi
58
-
59
- # Reject event names with spaces (Anthropic events are single tokens)
60
- if echo "$event" | grep -q ' '; then
61
- echo "emit_additional_context: invalid event name '$event' (spaces not allowed)" >&2
62
- echo " Anthropic hook events are single tokens like UserPromptSubmit, Stop, PostToolUse" >&2
63
- return 2
64
- fi
65
-
66
- # Validate content arg presence (allow empty string but require the arg)
67
- if [ $# -lt 2 ]; then
68
- echo "emit_additional_context: missing content arg" >&2
69
- echo " Usage: emit_additional_context <event-name> <content>" >&2
70
- return 2
71
- fi
72
-
73
- # Truncate if exceeds Anthropic 10K cap
74
- local truncated=0
75
- if [ ${#content} -gt $HOOK_INJECT_MAX_CONTENT ]; then
76
- content="${content:0:$HOOK_INJECT_MAX_CONTENT}"
77
- truncated=1
78
- fi
79
-
80
- # Emit JSON envelope via jq for proper escaping
81
- if command -v jq >/dev/null 2>&1; then
82
- jq -n \
83
- --arg event "$event" \
84
- --arg content "$content" \
85
- '{hookSpecificOutput: {hookEventName: $event, additionalContext: $content}}'
86
- else
87
- # jq-missing fallback: emit minimally-escaped JSON
88
- # (lib design assumes jq present per standards/bash-hook-safety.md;
89
- # this fallback exists so a jq-missing system still produces parseable output)
90
- local esc_event="${event//\"/\\\"}"
91
- local esc_content="${content//\"/\\\"}"
92
- esc_content="${esc_content//$'\n'/\\n}"
93
- printf '{"hookSpecificOutput":{"hookEventName":"%s","additionalContext":"%s"}}\n' \
94
- "$esc_event" "$esc_content"
95
- fi
96
-
97
- if [ $truncated -eq 1 ]; then
98
- echo "emit_additional_context: content truncated to $HOOK_INJECT_MAX_CONTENT chars (Anthropic limit)" >&2
99
- fi
100
-
101
- return 0
102
- }
103
-
104
- # === emit_base_pair_reminders ===
105
- # Emits the shared base-pair reminder text for the 5 UserPromptExpansion
106
- # hooks per bet 2026-07-01e. Content covers both R8 cures:
107
- # 1. Plain-English discipline (per .claude/rules/operator-facing-prose.md)
108
- # 2. Assert-verify discipline (per .claude/rules/assert-only-after-verify.md)
109
- #
110
- # Callers prepend this to their skill-specific content, then pass the
111
- # combined string to emit_additional_context.
112
- #
113
- # Stdout: reminder text (no envelope; caller wraps)
114
- # Exit: 0 always
115
- emit_base_pair_reminders() {
116
- cat <<'EOF'
117
- **Plain check:** Before I make a claim in this turn, I read the source first and cite it. If I cannot cite the source, I do not ship the claim.
118
-
119
- **Base-pair reminders — plain-English + assert-verify:**
120
-
121
- Plain-English (per .claude/rules/operator-facing-prose.md):
122
- - Grade-10 reading level. Subject-verb-object. Short sentences.
123
- - Drop bassclef-internal jargon (composer, primitive, load-bearing, blast radius, tier-preset, operationalize, wirings). See standards/bassclef-internal-jargon.md for the substitution table.
124
- - Keep standard tech vocab: API, hook, PR, schema, ORM.
125
-
126
- Assert-verify (per .claude/rules/assert-only-after-verify.md):
127
- - Every operator-facing assertion carries three parts: My claim + Evidence + Why the evidence supports the claim.
128
- - Evidence means the source read inline (file path, ticket #, git SHA).
129
- - Why the evidence supports the claim means one sentence tying the source to the claim.
130
- - Optional sharpeners: Qualifier (Toulmin), Rebuttal (Popper), Alternatives (Peirce).
131
- - If you cannot fill all three, do not ship the assertion. Read the source first.
132
- - Toulmin's original vocabulary (claim / data / warrant) stays as the research citation in .claude/luminaries/stephen-toulmin.md.
133
- EOF
134
- return 0
135
- }
136
-
137
- # === resolve_standards_path ===
138
- # Resolves a standards/ file via project → env → user → fail fallback.
139
- # Args:
140
- # $1 — relative filename under standards/ (e.g., "bassclef-internal-jargon.md")
141
- # Stdout: resolved absolute path
142
- # Exit: 0 success; 1 all layers fail; 2 missing arg
143
- resolve_standards_path() {
144
- local filename="${1:-}"
145
- if [ -z "$filename" ]; then
146
- echo "resolve_standards_path: missing filename arg" >&2
147
- return 2
148
- fi
149
-
150
- local project_dir="${CLAUDE_PROJECT_DIR:-}"
151
- local env_override="${KISS_STANDARDS_FILE:-}"
152
- local home_dir
153
- home_dir="$(__hook_inject_home)"
154
-
155
- # Layer 1: project
156
- if [ -n "$project_dir" ] && [ -f "$project_dir/standards/$filename" ]; then
157
- echo "$project_dir/standards/$filename"
158
- return 0
159
- fi
160
-
161
- # Layer 2: env override (only if env file actually exists; otherwise fall through)
162
- if [ -n "$env_override" ] && [ -f "$env_override" ]; then
163
- echo "$env_override"
164
- return 0
165
- fi
166
-
167
- # Layer 3: user-level
168
- if [ -f "$home_dir/standards/$filename" ]; then
169
- echo "$home_dir/standards/$filename"
170
- return 0
171
- fi
172
-
173
- # All layers failed — structured stderr with the fallback chain attempted
174
- cat >&2 <<EOF
175
- resolve_standards_path: '$filename' not found in any fallback layer
176
- Layer 1 (project): ${project_dir:-<CLAUDE_PROJECT_DIR unset>}/standards/$filename
177
- Layer 2 (env KISS_STANDARDS_FILE): ${env_override:-<unset>}
178
- Layer 3 (user): $home_dir/standards/$filename
179
- Per standards/hook-injection-discipline.md fail-soft contract.
180
- EOF
181
- return 1
182
- }
183
-
184
- # === resolve_config_path ===
185
- # Resolves bassclef-configs.jsonc via project → env → user → fail.
186
- # Stdout: resolved absolute path
187
- # Exit: 0 success; 1 all layers fail
188
- resolve_config_path() {
189
- local project_dir="${CLAUDE_PROJECT_DIR:-}"
190
- local env_override="${BASSCLEF_CONFIG_FILE:-}"
191
- local home_dir
192
- home_dir="$(__hook_inject_home)"
193
-
194
- # Layer 1: project
195
- if [ -n "$project_dir" ] && [ -f "$project_dir/.claude/bassclef-configs.jsonc" ]; then
196
- echo "$project_dir/.claude/bassclef-configs.jsonc"
197
- return 0
198
- fi
199
-
200
- # Layer 2: env override
201
- if [ -n "$env_override" ] && [ -f "$env_override" ]; then
202
- echo "$env_override"
203
- return 0
204
- fi
205
-
206
- # Layer 3: user-level
207
- if [ -f "$home_dir/.claude/bassclef-configs.jsonc" ]; then
208
- echo "$home_dir/.claude/bassclef-configs.jsonc"
209
- return 0
210
- fi
211
-
212
- cat >&2 <<EOF
213
- resolve_config_path: bassclef-configs.jsonc not found in any fallback layer
214
- Layer 1 (project): ${project_dir:-<CLAUDE_PROJECT_DIR unset>}/.claude/bassclef-configs.jsonc
215
- Layer 2 (env BASSCLEF_CONFIG_FILE): ${env_override:-<unset>}
216
- Layer 3 (user): $home_dir/.claude/bassclef-configs.jsonc
217
- Per standards/hook-injection-discipline.md fail-soft contract.
218
- EOF
219
- return 1
220
- }
221
-
222
- # === extract_block_terms ===
223
- # Parses a bassclef-internal-jargon.md style file + returns BLOCK-tier terms.
224
- # Args:
225
- # $1 — path to the jargon file
226
- # Stdout: one term per line (BLOCK rows only)
227
- # Exit: 0 success (even if zero terms found); 1 file missing; 2 missing arg
228
- extract_block_terms() {
229
- local file="${1:-}"
230
- if [ -z "$file" ]; then
231
- echo "extract_block_terms: missing file arg" >&2
232
- return 2
233
- fi
234
- if [ ! -f "$file" ]; then
235
- echo "extract_block_terms: file not found: $file" >&2
236
- return 1
237
- fi
238
-
239
- # Parse markdown table rows: extract column 2 (term) where column 3 (tier) == BLOCK
240
- # Format: | term | TIER | replacement |
241
- awk -F'|' '
242
- /BLOCK *\|/ {
243
- # Get tier column (3rd field after split — col 1 is leading empty)
244
- tier = $3
245
- gsub(/^ +| +$/, "", tier)
246
- if (tier == "BLOCK") {
247
- term = $2
248
- gsub(/^ +| +$/, "", term)
249
- # Skip header rows (term column says "term" literally)
250
- if (term != "term" && term != "") print term
251
- }
252
- }
253
- ' "$file"
254
- return 0
255
- }
@@ -1,96 +0,0 @@
1
- #!/usr/bin/env bash
2
- # tier: lite
3
- # andon-allow: docs/operator-private/[a-z-]+/20[0-9][0-9]-[0-9]{2}
4
- #
5
- # lib/luminary-pick.sh — bassclef catalog helper for the in-turn luminary picker.
6
- #
7
- # Shape A per docs/decompositions/2026-09-03-picker-harness-llm.md.
8
- # Consult at docs/operator-private/consultations/2026-09-03-picker-shape.md
9
- # picked Shape A 5-0 (Cooper, Linus, Ousterhout, Nygard, Feathers).
10
- #
11
- # The pick itself lives in each caller SKILL body:
12
- # .claude/skills/pick-luminaries/SKILL.md
13
- # .claude/skills/launch/SKILL.md
14
- # .claude/skills/stage/SKILL.md
15
- #
16
- # Claude reads the catalog list from luminary_pick_catalog() below,
17
- # reasons over the intent inside the current turn, writes the JSON.
18
- # No shell-out. No separate API key required.
19
- #
20
- # This lib provides:
21
- # - luminary_pick_catalog() — read catalog dir; emit sorted slug list
22
- # - luminary_pick() — grace-window stub preserving signature through
23
- # 2026-10-31 per ADR-031 we-dont-break-adopters
24
- # - luminary_pick_status() — mode report for adopter debugging
25
- #
26
- # Usage from a caller SKILL body:
27
- #
28
- # source "${BASSCLEF_DIR:-$HOME/src/sunj-labs/bassclef}/lib/luminary-pick.sh"
29
- # CATALOG=$(luminary_pick_catalog)
30
- # # then Claude reasons over $CATALOG + intent, writes JSON in-turn
31
- #
32
- # Output JSON shape (produced by SKILL body prose, not by this lib):
33
- # {
34
- # "matched_luminaries": ["slug1", "slug2", ...],
35
- # "rationale": "One short sentence per pick",
36
- # "picker_version": "bassclef-harness-pick-2.0",
37
- # "provider": "claude-code-harness"
38
- # }
39
-
40
- set -euo pipefail
41
-
42
- # Read catalog dir and emit one line per luminary.
43
- # Format: "<slug> (<domain>) — <name>"
44
- # Sort: alphabetical by slug.
45
- # Return: 0 on success (including empty catalog); non-zero if dir missing.
46
- luminary_pick_catalog() {
47
- local basedir="${BASSCLEF_DIR:-$HOME/src/sunj-labs/bassclef}"
48
- local catalog_dir="${basedir}/state/luminary-implementations"
49
-
50
- if [ ! -d "$catalog_dir" ]; then
51
- echo "[luminary-pick-catalog] catalog dir missing: $catalog_dir" >&2
52
- return 2
53
- fi
54
-
55
- (
56
- shopt -s nullglob
57
- set +e
58
- for f in "$catalog_dir"/*.json; do
59
- jq -r '"\(.luminary_slug) (\(.primary_domain // "unknown")) — \(.name // .luminary_slug)"' "$f" 2>/dev/null || true
60
- done | sort -u
61
- )
62
- return 0
63
- }
64
-
65
- # Grace-window stub preserving the old function signature.
66
- # Adopter scripts that source this lib and call luminary_pick continue
67
- # to run. They see a stderr note pointing at the new home. Exit 0 so
68
- # scripts checking exit code do not error.
69
- # Retires 2026-10-31 per ADR-031.
70
- luminary_pick() {
71
- echo "[luminary-pick] moved to caller SKILL body prose per web#193." >&2
72
- echo "[luminary-pick] adopters: see .claude/skills/pick-luminaries/SKILL.md" >&2
73
- echo "[luminary-pick] grace-window stub retires 2026-10-31 per ADR-031." >&2
74
- return 0
75
- }
76
-
77
- # Report picker mode. Kept for adopter debugging.
78
- luminary_pick_status() {
79
- echo "LIVE — in-turn picker per SKILL body prose (Shape A, web#193)"
80
- return 0
81
- }
82
-
83
- if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
84
- case "${1:-}" in
85
- catalog) luminary_pick_catalog ;;
86
- status) luminary_pick_status ;;
87
- *)
88
- echo "Usage: $0 catalog # emit luminary catalog list"
89
- echo " $0 status # report picker mode"
90
- echo ""
91
- echo "The pick itself moved to caller SKILL body prose per web#193."
92
- echo "See .claude/skills/pick-luminaries/SKILL.md."
93
- exit 0
94
- ;;
95
- esac
96
- fi
@@ -1,143 +0,0 @@
1
- #!/bin/bash
2
- # tier: lite
3
- # output-discipline.sh — classifier library for substrate output discipline audit.
4
- #
5
- # Answers per artifact: does this substrate file dispatch /kiss words on its
6
- # operator-facing output? Returns one of four labels:
7
- #
8
- # PRESENT — body has /kiss dispatch pattern (dispatches /kiss words on output)
9
- # ABSENT — body emits operator-facing prose but no /kiss dispatch found
10
- # OPT_OUT — SKILL frontmatter declares no_operator_output: true
11
- # NOT_APPLICABLE — artifact type does not emit operator-facing prose (rare;
12
- # reserved for future opt-outs at type layer)
13
- #
14
- # The classifier reads the artifact body and checks for one of these patterns:
15
- # - /kiss words --grade10 (canonical form per bassclef-upstream#858)
16
- # - Dispatch .*kiss words (per audit-substrate-output-discipline SKILL Test 33)
17
- # - /kiss dispatch (loose form)
18
- #
19
- # Per canvas docs/canvases/2026-07-25-substrate-output-discipline.md Stage 2.
20
- # Ships as bet 2026-07-25a WU-A3 (bassclef-upstream#859 Iteration A).
21
- #
22
- # Usage — sourced by /audit-substrate-output-discipline skill + scripts/sweep-kiss-dispatch.sh.
23
- # source lib/output-discipline.sh
24
- # classify_artifact <artifact-type> <path> # prints label to stdout; exit 0 always
25
- # has_kiss_dispatch <path> # exit 0 if present; exit 1 if absent
26
- # has_opt_out <path> # exit 0 if opt-out declared; exit 1 if not
27
- #
28
- # Sister modules:
29
- # - lib/mechanism-fidelity.sh (bet 27c WU-3) — sister audit classifier for rule-mechanism claims
30
- # - scripts/intent-drift-check.sh — used by sweep script for cosine gate per edit
31
-
32
- set -o pipefail
33
-
34
- # === has_kiss_dispatch — bool ===
35
- # Exit 0 if body contains a /kiss dispatch pattern; exit 1 otherwise.
36
- #
37
- # Per bassclef-upstream#1246 — the first alternative `/kiss words( --[a-z0-9]+)+`
38
- # recognizes both single-flag (`/kiss words --grade10`) AND two-flag
39
- # (`/kiss words --rewrite`, `/kiss words --grade8`, `/kiss words --rewrite --grade8`)
40
- # forms explicitly. The Dispatch/dispatch alternatives remain as loose
41
- # fallbacks for prose that mentions the dispatch verb without a bare invocation.
42
- # Grace window per ADR-031 — the old single-flag form keeps passing while
43
- # 91 skills sweep to the two-flag pointer.
44
- has_kiss_dispatch() {
45
- local file="$1"
46
- [ -f "$file" ] || return 1
47
- # Match any of the four canonical /kiss dispatch patterns
48
- grep -qE '/kiss words( --[a-z0-9]+)+|Dispatch .{0,30}kiss words|dispatches /kiss on|dispatch .{0,30}/kiss' "$file" 2>/dev/null
49
- }
50
-
51
- # === has_opt_out — bool ===
52
- # Exit 0 if SKILL frontmatter declares no_operator_output: true; exit 1 otherwise.
53
- has_opt_out() {
54
- local file="$1"
55
- [ -f "$file" ] || return 1
56
- # Extract frontmatter (between leading --- ... ---) and check for opt-out field
57
- awk '/^---$/ { c++; if(c==2) exit; next } c==1' "$file" 2>/dev/null | grep -qE '^no_operator_output:[[:space:]]*true'
58
- }
59
-
60
- # === classify_artifact — return label ===
61
- # Prints one of: PRESENT | ABSENT | OPT_OUT | NOT_APPLICABLE
62
- # Exit 0 always (informational; not a gate).
63
- classify_artifact() {
64
- local artifact_type="$1"
65
- local file="$2"
66
-
67
- if [ ! -f "$file" ]; then
68
- echo "NOT_APPLICABLE"
69
- return 0
70
- fi
71
-
72
- # Opt-out declared in frontmatter (skills only today; extensible)
73
- if has_opt_out "$file"; then
74
- echo "OPT_OUT"
75
- return 0
76
- fi
77
-
78
- # Check for /kiss dispatch pattern in body
79
- if has_kiss_dispatch "$file"; then
80
- echo "PRESENT"
81
- else
82
- echo "ABSENT"
83
- fi
84
- return 0
85
- }
86
-
87
- # === walk_type — enumerate all files of a given artifact type ===
88
- # Prints one path per line. Used by audit skill to iterate.
89
- # Usage: walk_type <artifact-type> [repo-root]
90
- walk_type() {
91
- local artifact_type="$1"
92
- local repo_root="${2:-$(pwd)}"
93
-
94
- case "$artifact_type" in
95
- skill|skills)
96
- find "$repo_root/.claude/skills" -maxdepth 2 -name "SKILL.md" -type f 2>/dev/null | sort
97
- ;;
98
- rule|rules)
99
- find "$repo_root/.claude/rules" -maxdepth 1 -name "*.md" -type f 2>/dev/null | sort
100
- ;;
101
- standard|standards)
102
- find "$repo_root/standards" -maxdepth 2 -name "*.md" -type f 2>/dev/null | sort
103
- ;;
104
- hook|hooks)
105
- find "$repo_root/.claude/hooks" -maxdepth 1 -name "*.sh" -type f 2>/dev/null | sort
106
- ;;
107
- luminary|luminaries)
108
- find "$repo_root/.claude/luminaries" -maxdepth 1 -name "*.md" -type f 2>/dev/null | sort
109
- ;;
110
- adr|adrs)
111
- find "$repo_root/architecture/decisions" -maxdepth 1 -name "ADR-*.md" -type f 2>/dev/null | sort
112
- ;;
113
- persona|personas)
114
- find "$repo_root/docs/personas" -maxdepth 1 -name "*.md" -type f 2>/dev/null | sort
115
- ;;
116
- template|templates)
117
- find "$repo_root/templates" -maxdepth 1 -name "*.md" -type f 2>/dev/null | sort
118
- ;;
119
- *)
120
- return 1
121
- ;;
122
- esac
123
- return 0
124
- }
125
-
126
- # === slug_from_path — extract slug from a file path ===
127
- # Usage: slug_from_path <artifact-type> <path>
128
- slug_from_path() {
129
- local artifact_type="$1"
130
- local file="$2"
131
-
132
- case "$artifact_type" in
133
- skill|skills)
134
- basename "$(dirname "$file")"
135
- ;;
136
- hook|hooks)
137
- basename "$file" .sh
138
- ;;
139
- *)
140
- basename "$file" .md
141
- ;;
142
- esac
143
- }
@@ -1,171 +0,0 @@
1
- #!/usr/bin/env bash
2
- # tier: lite
3
- # install-class: dual
4
- # lib/prose-scan-boundary.sh
5
- #
6
- # Deep module per @luminary john-ousterhout — narrow interface hides
7
- # the boundary detection and counter file work behind 3 functions.
8
- #
9
- # Purpose — every prose scan hook (kiss, appositive, citation)
10
- # strips fenced or echoed content BEFORE it scans, and writes counter
11
- # state that plain-english-steering.sh injects into the next prompt.
12
- #
13
- # This closes #1443 canonical case — Stop side prose hooks that fire
14
- # on verbatim BLOCKED banner echoes at session start plus operator
15
- # picked shorthand labels. 5 rewrite cycles in 20 turns documented
16
- # the cost during 2026-09-09b prep.
17
- #
18
- # Anchor luminaries:
19
- # @luminary john-ousterhout — deep module hiding the boundary work
20
- # @luminary donald-norman — feedback loop through the counter markers
21
- # @luminary tony-hoare — pre and postcondition on each function
22
- # @luminary michael-feathers — characterization tests pin behavior
23
- #
24
- # Consumers source this lib:
25
- # source lib/prose-scan-boundary.sh
26
- #
27
- # Tests: lib/tests/prose-scan-boundary.test.sh (Tier 0 strict TDD)
28
-
29
- # =========================================================================
30
- # strip_scan_boundaries — remove content the scan should not read
31
- # =========================================================================
32
- # Interface: IProseScanBoundary (Larman)
33
- # Inputs: $1 = message text
34
- # Outputs: stdout — message with fenced or echoed content removed
35
- # Exit: 0 always
36
- #
37
- # Removes:
38
- # 1. BLOCKED banner blocks (session-start hook echoes; agent cannot rewrite)
39
- # 2. <!--SCAN_SKIP-->...<!--/SCAN_SKIP--> explicit fence markers
40
- # 3. Fenced code blocks (``` ... ```)
41
- # 4. Inline code (`...`)
42
- strip_scan_boundaries() {
43
- local message_text="$1"
44
- [ -z "$message_text" ] && return 0
45
- # Python does the heavy lifting for regex-safe multiline strip.
46
- # Fall back to raw text when python3 missing (fail-soft per Nygard).
47
- if command -v python3 >/dev/null 2>&1; then
48
- printf '%s' "$message_text" | python3 -c '
49
- import sys
50
- import re
51
-
52
- text = sys.stdin.read()
53
-
54
- # 1. Strip BLOCKED banner blocks. Banner shape:
55
- # 🛑🛑🛑 BLOCKED 🛑🛑🛑
56
- # ────────────
57
- # content
58
- # ────────────
59
- # ACTION: resolve OR ...
60
- # Silence is not deferral. ...
61
- #
62
- # Match the banner line and content until one of:
63
- # - "Silence is not deferral" line followed by newline (canonical end)
64
- # - Two consecutive newlines (paragraph break)
65
- # - Next banner line
66
- # - End of string
67
- text = re.sub(
68
- r"\U0001F6D1\U0001F6D1\U0001F6D1 BLOCKED \U0001F6D1\U0001F6D1\U0001F6D1"
69
- r".*?"
70
- r"(?:Silence is not deferral\.[^\n]*\n"
71
- r"|\n\n(?=\S|\n|$)"
72
- r"|(?=\U0001F6D1\U0001F6D1\U0001F6D1)"
73
- r"|$)",
74
- "",
75
- text,
76
- flags=re.DOTALL,
77
- )
78
-
79
- # 2. Strip explicit SCAN_SKIP fences
80
- text = re.sub(
81
- r"<!--SCAN_SKIP-->.*?<!--/SCAN_SKIP-->",
82
- "",
83
- text,
84
- flags=re.DOTALL,
85
- )
86
-
87
- # 3. Strip fenced code blocks
88
- text = re.sub(r"```.*?```", "", text, flags=re.DOTALL)
89
-
90
- # 4. Strip inline code
91
- text = re.sub(r"`[^`]+`", "", text)
92
-
93
- sys.stdout.write(text)
94
- '
95
- else
96
- # No python — return raw text so consumers still work
97
- printf '%s' "$message_text"
98
- fi
99
- return 0
100
- }
101
-
102
- # =========================================================================
103
- # write_prose_counter — persist a per-turn counter for the steering hook
104
- # =========================================================================
105
- # Interface: IProseCounterWriter (Larman)
106
- # Inputs: $1 = counter name (e.g. "kiss_hits", "appositive_hits")
107
- # $2 = numeric count
108
- # $3 = optional JSON payload (findings, offenders, etc.)
109
- # Outputs: writes state/markers/turn-prose-counters/<name>.json atomically
110
- # Exit: 0 always (fail-soft on write error per Nygard)
111
- write_prose_counter() {
112
- local name="$1"
113
- local count="$2"
114
- local payload="${3:-{\}}"
115
- [ -z "$name" ] && return 0
116
- [ -z "$count" ] && count=0
117
- local repo_root
118
- repo_root="$(_prose_scan_repo_root)"
119
- local dir="${repo_root}/state/markers/turn-prose-counters"
120
- local out_file="${dir}/${name}.json"
121
- mkdir -p "$dir" 2>/dev/null || return 0
122
- local ts
123
- ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "unknown")"
124
- # Atomic write via tmp + mv
125
- local tmp
126
- tmp="$(mktemp "${dir}/.${name}.XXXXXX")" || return 0
127
- cat > "$tmp" <<JSON
128
- {
129
- "counter": "${name}",
130
- "count": ${count},
131
- "updated_at": "${ts}",
132
- "payload": ${payload}
133
- }
134
- JSON
135
- mv "$tmp" "$out_file" 2>/dev/null || rm -f "$tmp"
136
- return 0
137
- }
138
-
139
- # =========================================================================
140
- # read_prose_counter — read a per-turn counter for prompt injection
141
- # =========================================================================
142
- # Interface: IProseCounterReader (Larman)
143
- # Inputs: $1 = counter name
144
- # Outputs: stdout — JSON content, or empty when marker missing
145
- # Exit: 0 always (fail-soft per Nygard)
146
- read_prose_counter() {
147
- local name="$1"
148
- [ -z "$name" ] && return 0
149
- local repo_root
150
- repo_root="$(_prose_scan_repo_root)"
151
- local marker="${repo_root}/state/markers/turn-prose-counters/${name}.json"
152
- [ -f "$marker" ] || return 0
153
- cat "$marker" 2>/dev/null || true
154
- return 0
155
- }
156
-
157
- # =========================================================================
158
- # _prose_scan_repo_root — resolve repo root under both install classes
159
- # =========================================================================
160
- # Internal helper. Prefer $CLAUDE_PROJECT_DIR (project install), fall
161
- # back to lib file's parent (operator install). Per install-class: dual
162
- # discipline from ADR-035 substrate-as-system tenet.
163
- _prose_scan_repo_root() {
164
- if [ -n "${CLAUDE_PROJECT_DIR:-}" ] && [ -d "${CLAUDE_PROJECT_DIR}" ]; then
165
- echo "${CLAUDE_PROJECT_DIR}"
166
- return 0
167
- fi
168
- local script_dir
169
- script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
170
- cd "${script_dir}/.." && pwd
171
- }