@thebassclef/lite 0.0.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +85 -4
- package/dist/cli.cjs +1612 -0
- package/dist/cli.js +1589 -0
- package/dist/index.cjs +4 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/package.json +51 -3
- package/substrate/.bassclef/lite-manifest.json +2701 -0
- package/substrate/.claude/agents/architect.md +70 -0
- package/substrate/.claude/agents/builder.md +114 -0
- package/substrate/.claude/agents/designer.md +156 -0
- package/substrate/.claude/agents/reviewer.md +88 -0
- package/substrate/.claude/hooks/artifact-ingestion-gate.sh +357 -0
- package/substrate/.claude/hooks/assert-verify-steering.sh +77 -0
- package/substrate/.claude/hooks/bassclef-source-config-validate.sh +215 -0
- package/substrate/.claude/hooks/bassclef-sync.sh +634 -0
- package/substrate/.claude/hooks/compound-noun-scrub.sh +292 -0
- package/substrate/.claude/hooks/kiss-expansion-inject.sh +69 -0
- package/substrate/.claude/hooks/longrun-prep-compounding-axis-check.sh +492 -0
- package/substrate/.claude/hooks/longrun-prep-compounding-sequence-check.sh +492 -0
- package/substrate/.claude/hooks/plain-english-steering.sh +156 -0
- package/substrate/.claude/hooks/post-skill-friction-check.sh +177 -0
- package/substrate/.claude/hooks/post-skill-telemetry.sh +62 -0
- package/substrate/.claude/hooks/pre-build-gate.sh +511 -0
- package/substrate/.claude/hooks/pre-commit-gate.sh +451 -0
- package/substrate/.claude/hooks/session-end.sh +433 -0
- package/substrate/.claude/hooks/session-reflection.sh +303 -0
- package/substrate/.claude/hooks/skill-body-grade-gate.sh +219 -0
- package/substrate/.claude/hooks/skill-body-intent-drift.sh +107 -0
- package/substrate/.claude/hooks/skill-step-list-check.sh +171 -0
- package/substrate/.claude/hooks/state-validate.sh +271 -0
- package/substrate/.claude/hooks/substrate-clarity-gate.sh +1110 -0
- package/substrate/.claude/hooks/temperance-gate.sh +147 -0
- package/substrate/.claude/hooks/testing-tier-enforce.sh +233 -0
- package/substrate/.claude/hooks/turn-prose-grade-measure.sh +219 -0
- package/substrate/.claude/hooks/turn-prose-kiss-check.sh +463 -0
- package/substrate/.claude/hooks/vocabulary-migration-check.sh +171 -0
- package/substrate/.claude/hooks/whereami-utc-gate.sh +142 -0
- package/substrate/.claude/luminaries/alan-cooper.md +170 -0
- package/substrate/.claude/luminaries/alistair-cockburn.md +140 -0
- package/substrate/.claude/luminaries/amazon-pr-faq.md +34 -0
- package/substrate/.claude/luminaries/ash-maurya.md +121 -0
- package/substrate/.claude/luminaries/bill-buxton.md +210 -0
- package/substrate/.claude/luminaries/charles-sanders-peirce.md +150 -0
- package/substrate/.claude/luminaries/david-ogilvy.md +192 -0
- package/substrate/.claude/luminaries/don-norman.md +173 -0
- package/substrate/.claude/luminaries/edward-tufte.md +179 -0
- package/substrate/.claude/luminaries/eric-evans.md +160 -0
- package/substrate/.claude/luminaries/frederick-brooks.md +50 -0
- package/substrate/.claude/luminaries/gang-of-four.md +157 -0
- package/substrate/.claude/luminaries/glenford-myers.md +230 -0
- package/substrate/.claude/luminaries/hunt-thomas.md +115 -0
- package/substrate/.claude/luminaries/hyrum-wright.md +94 -0
- package/substrate/.claude/luminaries/jason-fried-dhh.md +46 -0
- package/substrate/.claude/luminaries/jesse-james-garrett.md +154 -0
- package/substrate/.claude/luminaries/john-ousterhout.md +94 -0
- package/substrate/.claude/luminaries/karl-popper.md +132 -0
- package/substrate/.claude/luminaries/kent-beck.md +168 -0
- package/substrate/.claude/luminaries/linus-torvalds.md +218 -0
- package/substrate/.claude/luminaries/martin-fowler.md +164 -0
- package/substrate/.claude/luminaries/michael-feathers.md +125 -0
- package/substrate/.claude/luminaries/michael-nygard.md +118 -0
- package/substrate/.claude/luminaries/robert-c-martin.md +164 -0
- package/substrate/.claude/luminaries/saltzer-schroeder.md +194 -0
- package/substrate/.claude/luminaries/sophia-prater.md +193 -0
- package/substrate/.claude/luminaries/stephen-toulmin.md +131 -0
- package/substrate/.claude/luminaries/tony-hoare.md +170 -0
- package/substrate/.claude/luminaries/vaughn-vernon.md +50 -0
- package/substrate/.claude/luminaries/w-edwards-deming.md +158 -0
- package/substrate/.claude/rules/accessor-library-discipline.md +138 -0
- package/substrate/.claude/rules/adr-discipline.md +120 -0
- package/substrate/.claude/rules/api-conventions.md +125 -0
- package/substrate/.claude/rules/artifact-ingestion.md +179 -0
- package/substrate/.claude/rules/assert-only-after-verify.md +137 -0
- package/substrate/.claude/rules/blocked-items.md +146 -0
- package/substrate/.claude/rules/bootstrap-pair-discipline.md +141 -0
- package/substrate/.claude/rules/branching.md +28 -0
- package/substrate/.claude/rules/cold-adopter-harness-discipline.md +129 -0
- package/substrate/.claude/rules/commit-conventions.md +22 -0
- package/substrate/.claude/rules/compounding-axis-fresh-analysis.md +188 -0
- package/substrate/.claude/rules/compounding-sequence-fresh-analysis.md +188 -0
- package/substrate/.claude/rules/context-engineering.md +202 -0
- package/substrate/.claude/rules/context-management.md +85 -0
- package/substrate/.claude/rules/defensive-bash.md +68 -0
- package/substrate/.claude/rules/deferred-actions.md +233 -0
- package/substrate/.claude/rules/destructive-operations.md +69 -0
- package/substrate/.claude/rules/diagnosis.md +38 -0
- package/substrate/.claude/rules/github-issue-flash-tweet.md +156 -0
- package/substrate/.claude/rules/guardrails.md +73 -0
- package/substrate/.claude/rules/hook-wire-on-author.md +103 -0
- package/substrate/.claude/rules/identifier-leak-prevention.md +104 -0
- package/substrate/.claude/rules/iteration-bet-brief-completeness.md +54 -0
- package/substrate/.claude/rules/lite-manifest-schema-change-discipline.md +98 -0
- package/substrate/.claude/rules/longrun-prep-plan-doc-compression.md +89 -0
- package/substrate/.claude/rules/loop-discipline.md +81 -0
- package/substrate/.claude/rules/manual-prod-approval.md +100 -0
- package/substrate/.claude/rules/marker-enrichment-discipline.md +99 -0
- package/substrate/.claude/rules/mobile-ephemeral-session.md +109 -0
- package/substrate/.claude/rules/new-dependency-check.md +51 -0
- package/substrate/.claude/rules/oo-ad-entry-point.md +117 -0
- package/substrate/.claude/rules/operator-facing-prose.md +196 -0
- package/substrate/.claude/rules/option-label-discipline.md +108 -0
- package/substrate/.claude/rules/pattern-annotation.md +100 -0
- package/substrate/.claude/rules/plain-english-discipline.md +156 -0
- package/substrate/.claude/rules/plan-enumeration-needs-value-props.md +211 -0
- package/substrate/.claude/rules/pr-body-shape.md +317 -0
- package/substrate/.claude/rules/pr-strategy.md +167 -0
- package/substrate/.claude/rules/pr-title-shape.md +161 -0
- package/substrate/.claude/rules/prototype-workflow.md +65 -0
- package/substrate/.claude/rules/reserved-skill-names.md +123 -0
- package/substrate/.claude/rules/schema-management.md +49 -0
- package/substrate/.claude/rules/sdlc-gates.md +149 -0
- package/substrate/.claude/rules/security.md +37 -0
- package/substrate/.claude/rules/session-artifacts.md +236 -0
- package/substrate/.claude/rules/skill-composition-declarations.md +124 -0
- package/substrate/.claude/rules/skill-description-clarity.md +247 -0
- package/substrate/.claude/rules/skill-procedure-step-list.md +137 -0
- package/substrate/.claude/rules/state-schema-validation.md +162 -0
- package/substrate/.claude/rules/stuck-signal-diagnostic.md +140 -0
- package/substrate/.claude/rules/substrate-config-schema.md +98 -0
- package/substrate/.claude/rules/test-list-discipline.md +175 -0
- package/substrate/.claude/rules/test-sufficiency.md +210 -0
- package/substrate/.claude/rules/testing-tier-config.md +145 -0
- package/substrate/.claude/rules/testing.md +38 -0
- package/substrate/.claude/rules/turn-estimate-grounding.md +134 -0
- package/substrate/.claude/rules/visual-hierarchy.md +437 -0
- package/substrate/.claude/rules/we-dont-break-adopters.md +126 -0
- package/substrate/.claude/rules/whereami-load-bearing.md +202 -0
- package/substrate/.claude/rules/writing-craft-discipline.md +92 -0
- package/substrate/.claude/rules/wu-sequencing-compounds.md +145 -0
- package/substrate/.claude/skills/build/SKILL.md +640 -0
- package/substrate/.claude/skills/chronicle/SKILL.md +55 -0
- package/substrate/.claude/skills/clean-artifacts/SKILL.md +249 -0
- package/substrate/.claude/skills/decompose/SKILL.md +280 -0
- package/substrate/.claude/skills/diagnose/SKILL.md +297 -0
- package/substrate/.claude/skills/feynman/SKILL.md +90 -0
- package/substrate/.claude/skills/howdoi/SKILL.md +105 -0
- package/substrate/.claude/skills/ia-model/SKILL.md +108 -0
- package/substrate/.claude/skills/interaction-design/SKILL.md +112 -0
- package/substrate/.claude/skills/interpret-input/SKILL.md +180 -0
- package/substrate/.claude/skills/journal/SKILL.md +209 -0
- package/substrate/.claude/skills/kiss/SKILL.md +449 -0
- package/substrate/.claude/skills/launch/SKILL.md +915 -0
- package/substrate/.claude/skills/lean-canvas/SKILL.md +332 -0
- package/substrate/.claude/skills/longrun/SKILL.md +463 -0
- package/substrate/.claude/skills/luminary/SKILL.md +481 -0
- package/substrate/.claude/skills/ogilvy-writing-audit/SKILL.md +177 -0
- package/substrate/.claude/skills/onboard-repo/SKILL.md +1624 -0
- package/substrate/.claude/skills/pattern-review/SKILL.md +99 -0
- package/substrate/.claude/skills/personas/SKILL.md +207 -0
- package/substrate/.claude/skills/promote/SKILL.md +283 -0
- package/substrate/.claude/skills/requirement/SKILL.md +98 -0
- package/substrate/.claude/skills/retro/SKILL.md +117 -0
- package/substrate/.claude/skills/riff/SKILL.md +114 -0
- package/substrate/.claude/skills/roadmap-reconcile/SKILL.md +163 -0
- package/substrate/.claude/skills/session-end/SKILL.md +309 -0
- package/substrate/.claude/skills/session-log/SKILL.md +299 -0
- package/substrate/.claude/skills/skills/SKILL.md +228 -0
- package/substrate/.claude/skills/spec/SKILL.md +105 -0
- package/substrate/.claude/skills/sprint/SKILL.md +392 -0
- package/substrate/.claude/skills/stage/SKILL.md +384 -0
- package/substrate/.claude/skills/state-a-problem/SKILL.md +185 -0
- package/substrate/.claude/skills/temperance/SKILL.md +108 -0
- package/substrate/.claude/skills/use-case/SKILL.md +417 -0
- package/substrate/.claude/skills/user-stories/SKILL.md +268 -0
- package/substrate/.claude/skills/value-prop/SKILL.md +251 -0
- package/substrate/.claude/skills/verify/SKILL.md +160 -0
- package/substrate/.claude/skills/visual-review/SKILL.md +503 -0
- package/substrate/.claude/skills/whats-the-plan/SKILL.md +202 -0
- package/substrate/.claude/skills/whereami/SKILL.md +307 -0
- package/substrate/AGENTS.md +79 -0
- package/substrate/CLAUDE-lite.md +85 -0
- package/substrate/CODE_OF_CONDUCT.md +28 -0
- package/substrate/CONTRIBUTING.md +177 -0
- package/substrate/README.md +173 -0
- package/substrate/SECURITY.md +19 -0
- package/substrate/architecture/decisions/ADR-029-release-pipeline.md +79 -0
- package/substrate/architecture/decisions/ADR-031-non-breaking-changes-adopter-discipline.md +139 -0
- package/substrate/architecture/decisions/ADR-032-adopter-sync-dispatcher-architecture.md +192 -0
- package/substrate/architecture/decisions/ADR-039-release-tagging-scheme.md +145 -0
- package/substrate/architecture/decisions/ADR-040-planning-skill-vocabulary-and-lite-profile.md +155 -0
- package/substrate/architecture/decisions/ADR-044-unified-skill-body-template.md +162 -0
- package/substrate/lib/clean-artifacts-sweep.sh +112 -0
- package/substrate/lib/code-comment-discipline.sh +144 -0
- package/substrate/lib/composer-preflight.sh +459 -0
- package/substrate/lib/hook-inject.sh +255 -0
- package/substrate/lib/luminary-pick.sh +96 -0
- package/substrate/lib/output-discipline.sh +143 -0
- package/substrate/lib/prose-scan-boundary.sh +171 -0
- package/substrate/lib/rewrite-check.sh +214 -0
- package/substrate/lib/state.sh +1372 -0
- package/substrate/lib/telemetry.sh +205 -0
- package/substrate/lib/tier-check.sh +187 -0
- package/substrate/lib/tier-dependency-audit.sh +1088 -0
- package/substrate/presence/install/bassclef-hook-connect.sh +178 -0
- package/substrate/presence/install/bassclef-sync.dispatcher.template.sh +841 -0
- package/substrate/presence/install/bassclef-sync.template.sh +2076 -0
- package/substrate/presence/install/schedule-auto-save.cron.sh +88 -0
- package/substrate/presence/install/schedule-auto-save.taskscheduler.md +122 -0
- package/substrate/scripts/aggregate-telemetry.sh +217 -0
- package/substrate/scripts/analyze-tier-dependencies.sh +239 -0
- package/substrate/scripts/generate-lite-manifest.sh +505 -0
- package/substrate/scripts/generate-tier-manifest.sh +28 -0
- package/substrate/scripts/intent-drift-check.sh +456 -0
- package/substrate/scripts/lite-manifest-drift-check.sh +146 -0
- package/substrate/scripts/render-lite-manifest-doc.sh +150 -0
- package/substrate/standards/adr-template.md +86 -0
- package/substrate/standards/api-conventions/nextjs.md +84 -0
- package/substrate/standards/artifact-composition.md +209 -0
- package/substrate/standards/bash-hook-safety.md +246 -0
- package/substrate/standards/bassclef-configs-schema.md +232 -0
- package/substrate/standards/bassclef-evolution.md +143 -0
- package/substrate/standards/bassclef-internal-jargon.md +244 -0
- package/substrate/standards/bassclef-managed-sentinel.md +96 -0
- package/substrate/standards/bassclef-source-config.md +228 -0
- package/substrate/standards/branch-stacking.md +408 -0
- package/substrate/standards/code-safety-principles.md +176 -0
- package/substrate/standards/composer-prerequisites.md +155 -0
- package/substrate/standards/deferred-actions-schema.md +204 -0
- package/substrate/standards/dependency-discipline/cargo.md +39 -0
- package/substrate/standards/dependency-discipline/gem.md +43 -0
- package/substrate/standards/dependency-discipline/go-mod.md +41 -0
- package/substrate/standards/dependency-discipline/npm.md +42 -0
- package/substrate/standards/dependency-discipline/pip.md +42 -0
- package/substrate/standards/deployment-topology/ec2-tailscale.md +225 -0
- package/substrate/standards/deployment-topology.md +69 -0
- package/substrate/standards/docs-sync-allowlist.md +76 -0
- package/substrate/standards/domain-and-dns.md +145 -0
- package/substrate/standards/frontend-stack.md +67 -0
- package/substrate/standards/frontmatter-schema.md +154 -0
- package/substrate/standards/graceful-exit.md +227 -0
- package/substrate/standards/hook-idempotency.md +102 -0
- package/substrate/standards/hook-injection-discipline.md +202 -0
- package/substrate/standards/hook-install-class.md +215 -0
- package/substrate/standards/input-handler-interface.md +152 -0
- package/substrate/standards/lite-manifest-schema-changes.md +135 -0
- package/substrate/standards/luminary-matching.md +105 -0
- package/substrate/standards/luminary-problem-patterns.md +481 -0
- package/substrate/standards/migration-discipline/active-record.md +50 -0
- package/substrate/standards/migration-discipline/alembic.md +43 -0
- package/substrate/standards/migration-discipline/gorm.md +50 -0
- package/substrate/standards/migration-discipline/prisma.md +53 -0
- package/substrate/standards/migration-discipline/sqlalchemy.md +51 -0
- package/substrate/standards/mobile-ephemeral-session.md +167 -0
- package/substrate/standards/model-routing-discipline.md +160 -0
- package/substrate/standards/ogilvy-writing-rules.md +225 -0
- package/substrate/standards/opener-discipline.md +96 -0
- package/substrate/standards/operator-facing-prose-discipline.md +201 -0
- package/substrate/standards/persona-schema.md +229 -0
- package/substrate/standards/pluggable-luminaries.md +323 -0
- package/substrate/standards/pr-body-discipline.md +115 -0
- package/substrate/standards/preview-state-schema.md +189 -0
- package/substrate/standards/project-directory-layout.md +276 -0
- package/substrate/standards/release-tagging.md +137 -0
- package/substrate/standards/reserved-skill-names.md +120 -0
- package/substrate/standards/scannable-multi-option-output.md +261 -0
- package/substrate/standards/sdlc-compliance.md +286 -0
- package/substrate/standards/sdlc-gates/typescript.md +57 -0
- package/substrate/standards/secrets-lifecycle.md +210 -0
- package/substrate/standards/security-scanner-adapter.md +145 -0
- package/substrate/standards/session-board.md +256 -0
- package/substrate/standards/skill-output-discipline.md +90 -0
- package/substrate/standards/state-spine-contract.md +255 -0
- package/substrate/standards/state-spine.md +511 -0
- package/substrate/standards/steering-hints/kiss-words.md +11 -0
- package/substrate/standards/substrate-config-schema.md +267 -0
- package/substrate/standards/tech-stack-config.md +109 -0
- package/substrate/standards/tier-dependency-analysis.md +167 -0
- package/substrate/standards/tier-runtime-deps/lite.md +57 -0
- package/substrate/standards/tier-tag-schema.md +155 -0
- package/substrate/standards/two-layer-config.md +99 -0
- package/substrate/standards/use-case-format.md +292 -0
- package/substrate/standards/user-story-invest.md +268 -0
- package/substrate/standards/velocity-and-appetite.md +229 -0
- package/substrate/standards/voice-input-pattern.md +119 -0
- package/substrate/standards/whereami-schema.md +301 -0
- package/substrate/standards/worktree-management.md +211 -0
- package/substrate/standards/writing-guide.md +213 -0
- package/substrate/templates/chronicle-template.md +75 -0
- package/substrate/templates/deferred-action-template.md +45 -0
- package/substrate/templates/memory-proposal-template.md +77 -0
- package/substrate/templates/persona-template.md +200 -0
- package/substrate/templates/pr-faq.md +45 -0
- package/substrate/templates/secret-rotation-template.md +162 -0
- package/substrate/templates/spec-template.md +131 -0
- package/substrate/templates/use-case-template.md +194 -0
- package/substrate/templates/user-story-template.md +107 -0
- package/substrate/templates/whereami-template.md +101 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# install-class: dual
|
|
4
|
+
# Skill-step-list check — Stop hook that scans the transcript for a Skill tool
|
|
5
|
+
# dispatch followed by an assistant response without a step-list block.
|
|
6
|
+
#
|
|
7
|
+
# Per sunj-labs/bassclef-upstream#1119: closes the SKILL-step-skip class caught
|
|
8
|
+
# in session 2026-08-05a. Rule at .claude/rules/skill-procedure-step-list.md.
|
|
9
|
+
# Ships as pair per bootstrap-pair-discipline.md.
|
|
10
|
+
#
|
|
11
|
+
# Sibling pattern: .claude/hooks/turn-prose-kiss-check.sh (Stop hook + transcript scan).
|
|
12
|
+
#
|
|
13
|
+
# Input (stdin): JSON with transcript_path + session_id
|
|
14
|
+
# {"transcript_path": "/path/to/transcript.jsonl", "session_id": "..."}
|
|
15
|
+
#
|
|
16
|
+
# Toggle (env or .claude/bassclef-configs.jsonc):
|
|
17
|
+
# SKILL_STEP_LIST_TOGGLE=true — advisory (exit 0, findings to stderr) [DEFAULT]
|
|
18
|
+
# SKILL_STEP_LIST_TOGGLE=strict — strict (exit 2, blocks stop, forces rewrite)
|
|
19
|
+
# SKILL_STEP_LIST_TOGGLE=false — silent (exit 0, no scan)
|
|
20
|
+
#
|
|
21
|
+
# Override per-call:
|
|
22
|
+
# SKIP_SKILL_STEP_LIST=1 — skip scan, exit 0
|
|
23
|
+
#
|
|
24
|
+
# Exit codes:
|
|
25
|
+
# 0 — pass (no findings, advisory mode, toggle off, override set, or no Skill dispatched)
|
|
26
|
+
# 2 — block stop (strict mode + Skill dispatched + no step-list in response)
|
|
27
|
+
#
|
|
28
|
+
# Reads:
|
|
29
|
+
# - $TRANSCRIPT_PATH (JSONL, one event per line)
|
|
30
|
+
# - .claude/bassclef-configs.jsonc (toggle when env not set)
|
|
31
|
+
|
|
32
|
+
set +e
|
|
33
|
+
|
|
34
|
+
# === Override path ===
|
|
35
|
+
if [ "${SKIP_SKILL_STEP_LIST:-0}" = "1" ]; then
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# === Source install-class resolver ===
|
|
40
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
41
|
+
|
|
42
|
+
# Hook liveness heartbeat (silent-fail)
|
|
43
|
+
{
|
|
44
|
+
for _hb_c in "${SCRIPT_DIR}/../../lib/hook-heartbeat.sh" "${HOME:-/}/lib/hook-heartbeat.sh"; do
|
|
45
|
+
[ -f "$_hb_c" ] && source "$_hb_c" && heartbeat_mark "skill-step-list-check" && break
|
|
46
|
+
done
|
|
47
|
+
unset _hb_c
|
|
48
|
+
} 2>/dev/null || true
|
|
49
|
+
|
|
50
|
+
for _candidate in \
|
|
51
|
+
"${SCRIPT_DIR}/../../lib/hook-inject.sh" \
|
|
52
|
+
"${HOME_OVERRIDE:-${HOME:-/}}/lib/hook-inject.sh"; do
|
|
53
|
+
if [ -f "$_candidate" ]; then
|
|
54
|
+
# shellcheck disable=SC1090
|
|
55
|
+
source "$_candidate"
|
|
56
|
+
break
|
|
57
|
+
fi
|
|
58
|
+
done
|
|
59
|
+
unset _candidate
|
|
60
|
+
|
|
61
|
+
# === Parse stdin JSON ===
|
|
62
|
+
STDIN_JSON=$(cat 2>/dev/null || echo '{}')
|
|
63
|
+
TRANSCRIPT_PATH=$(echo "$STDIN_JSON" | jq -r '.transcript_path // ""' 2>/dev/null)
|
|
64
|
+
|
|
65
|
+
# Graceful skip if no transcript path
|
|
66
|
+
if [ -z "$TRANSCRIPT_PATH" ]; then
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# Graceful skip if transcript file missing
|
|
71
|
+
if [ ! -f "$TRANSCRIPT_PATH" ]; then
|
|
72
|
+
exit 0
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# === Resolve config file ===
|
|
76
|
+
if declare -f resolve_config_path >/dev/null 2>&1; then
|
|
77
|
+
CONFIG_FILE=$(resolve_config_path 2>/dev/null) || CONFIG_FILE=""
|
|
78
|
+
else
|
|
79
|
+
CONFIG_FILE="${SCRIPT_DIR}/../bassclef-configs.jsonc"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# === Resolve toggle ===
|
|
83
|
+
TOGGLE="${SKILL_STEP_LIST_TOGGLE:-}"
|
|
84
|
+
if [ -z "$TOGGLE" ]; then
|
|
85
|
+
if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then
|
|
86
|
+
TOGGLE=$(sed 's|//.*||g' "$CONFIG_FILE" 2>/dev/null \
|
|
87
|
+
| jq -r '.skill_step_list.toggle // "true"' 2>/dev/null)
|
|
88
|
+
fi
|
|
89
|
+
TOGGLE="${TOGGLE:-true}"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
case "$TOGGLE" in
|
|
93
|
+
true|"true") MODE="advisory" ;;
|
|
94
|
+
strict|"strict") MODE="strict" ;;
|
|
95
|
+
false|"false") exit 0 ;;
|
|
96
|
+
*) MODE="advisory" ;;
|
|
97
|
+
esac
|
|
98
|
+
|
|
99
|
+
# === Scan transcript for a Skill tool_use ===
|
|
100
|
+
# We scan the last 200 lines to keep the hook fast.
|
|
101
|
+
LAST_LINES=$(tail -200 "$TRANSCRIPT_PATH" 2>/dev/null)
|
|
102
|
+
|
|
103
|
+
# Find lines with Skill tool_use
|
|
104
|
+
SKILL_LINE_NUM=$(echo "$LAST_LINES" | grep -n '"type":"tool_use"' 2>/dev/null | grep '"name":"Skill"' | tail -1 | cut -d: -f1)
|
|
105
|
+
|
|
106
|
+
if [ -z "$SKILL_LINE_NUM" ]; then
|
|
107
|
+
# No Skill dispatched in recent transcript — silent pass
|
|
108
|
+
exit 0
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# === Find the assistant text_content event AFTER the Skill line ===
|
|
112
|
+
# The response we care about is the one after the Skill tool_result comes back.
|
|
113
|
+
AFTER_SKILL=$(echo "$LAST_LINES" | tail -n +"$((SKILL_LINE_NUM + 1))" 2>/dev/null)
|
|
114
|
+
|
|
115
|
+
# Get the latest assistant text_content in that portion
|
|
116
|
+
LAST_ASSISTANT_AFTER=$(echo "$AFTER_SKILL" | grep '"type":"assistant"' | tail -1)
|
|
117
|
+
|
|
118
|
+
if [ -z "$LAST_ASSISTANT_AFTER" ]; then
|
|
119
|
+
# Skill dispatched but no response yet — silent pass
|
|
120
|
+
exit 0
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# Extract text content from the assistant message
|
|
124
|
+
MESSAGE_TEXT=$(echo "$LAST_ASSISTANT_AFTER" \
|
|
125
|
+
| jq -r '.message.content[]? |
|
|
126
|
+
if .type == "text" then
|
|
127
|
+
.text
|
|
128
|
+
else
|
|
129
|
+
empty
|
|
130
|
+
end' 2>/dev/null)
|
|
131
|
+
|
|
132
|
+
if [ -z "$MESSAGE_TEXT" ]; then
|
|
133
|
+
# Assistant message had no text content (tool_use only) — silent pass
|
|
134
|
+
exit 0
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# === Check for step-list pattern ===
|
|
138
|
+
# Pattern: two markers must both appear:
|
|
139
|
+
# 1. A "Step N" reference (Step + digit)
|
|
140
|
+
# 2. At least one "[x]" or "[~]" bracket status marker
|
|
141
|
+
HAS_STEP=$(echo "$MESSAGE_TEXT" | grep -cE 'Step[[:space:]]+[0-9]+' 2>/dev/null)
|
|
142
|
+
HAS_MARKER=$(echo "$MESSAGE_TEXT" | grep -cE '\[[x~]\]' 2>/dev/null)
|
|
143
|
+
|
|
144
|
+
if [ "$HAS_STEP" -gt 0 ] && [ "$HAS_MARKER" -gt 0 ]; then
|
|
145
|
+
# Step-list present — silent pass
|
|
146
|
+
exit 0
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# === Missing step-list — emit finding ===
|
|
150
|
+
NOTICE="SKILL step-list check: recent Skill dispatch response lacks a step-list block."
|
|
151
|
+
NOTICE="$NOTICE Per .claude/rules/skill-procedure-step-list.md,"
|
|
152
|
+
NOTICE="$NOTICE write each SKILL procedure step with [x] done+source OR [~] deferred+reason."
|
|
153
|
+
|
|
154
|
+
# Trace log if trace-helper available
|
|
155
|
+
if [ -f "${SCRIPT_DIR}/../../lib/trace-helper.sh" ]; then
|
|
156
|
+
source "${SCRIPT_DIR}/../../lib/trace-helper.sh" 2>/dev/null
|
|
157
|
+
if declare -f trace_record >/dev/null 2>&1; then
|
|
158
|
+
trace_record "skill-step-list-check" "advisory-fired" "$NOTICE" 2>/dev/null
|
|
159
|
+
fi
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
if [ "$MODE" = "strict" ]; then
|
|
163
|
+
echo "🛑 BLOCKED: $NOTICE" >&2
|
|
164
|
+
echo "" >&2
|
|
165
|
+
echo "To resolve: rewrite the response with a step list at the top." >&2
|
|
166
|
+
echo "Override: SKIP_SKILL_STEP_LIST=1 <command> (logged)" >&2
|
|
167
|
+
exit 2
|
|
168
|
+
else
|
|
169
|
+
echo "⚠ ADVISORY: $NOTICE" >&2
|
|
170
|
+
exit 0
|
|
171
|
+
fi
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# state-validate.sh — PreToolUse hook that validates state-spine entity
|
|
4
|
+
# writes against their JSON schemas.
|
|
5
|
+
#
|
|
6
|
+
# Wires per .claude/settings.json under hooks.PreToolUse with matcher
|
|
7
|
+
# "Edit|Write". Reads the tool's event JSON from stdin, detects whether
|
|
8
|
+
# the target file is a state-spine entity, extracts its data per the
|
|
9
|
+
# entity's storage shape, validates against the schema, and BLOCKs
|
|
10
|
+
# (exit non-zero) on failure.
|
|
11
|
+
#
|
|
12
|
+
# Per ADR-015: JSON Schema only for v0. Validator: ajv-cli draft-2020-12.
|
|
13
|
+
# Per state-spine.md: three storage shapes (A/B/C) handled via path-based
|
|
14
|
+
# entity-type detection.
|
|
15
|
+
# Per accessor-library-discipline.md: this is the write-side gate.
|
|
16
|
+
#
|
|
17
|
+
# Override: SKIP_STATE_VALIDATE=1 (logged to stderr).
|
|
18
|
+
#
|
|
19
|
+
# Pairs with: .claude/rules/state-schema-validation.md
|
|
20
|
+
|
|
21
|
+
set -u
|
|
22
|
+
|
|
23
|
+
# === Hook liveness heartbeat (WU-3 of bet 2026-07-31d; closes #1002) ===
|
|
24
|
+
# Silent-fail — a missing lib never crashes the hook.
|
|
25
|
+
{
|
|
26
|
+
_hb_sd="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
for _hb_c in "${_hb_sd}/../../lib/hook-heartbeat.sh" "${HOME:-/}/lib/hook-heartbeat.sh"; do
|
|
28
|
+
[ -f "$_hb_c" ] && source "$_hb_c" && heartbeat_mark "state-validate" && break
|
|
29
|
+
done
|
|
30
|
+
unset _hb_sd _hb_c
|
|
31
|
+
} 2>/dev/null || true
|
|
32
|
+
|
|
33
|
+
# === Override path ===
|
|
34
|
+
|
|
35
|
+
if [ "${SKIP_STATE_VALIDATE:-}" = "1" ]; then
|
|
36
|
+
echo "[state-validate.sh] SKIP_STATE_VALIDATE=1 — bypassing validation" >&2
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# === Read PreToolUse event ===
|
|
41
|
+
|
|
42
|
+
EVENT_JSON="$(cat)"
|
|
43
|
+
TOOL_NAME="$(echo "$EVENT_JSON" | jq -r '.tool_name // empty')"
|
|
44
|
+
TARGET_PATH="$(echo "$EVENT_JSON" | jq -r '.tool_input.file_path // empty')"
|
|
45
|
+
|
|
46
|
+
# Tool filter: only Edit and Write trigger validation
|
|
47
|
+
case "$TOOL_NAME" in
|
|
48
|
+
Edit|Write|MultiEdit) ;;
|
|
49
|
+
*) exit 0 ;;
|
|
50
|
+
esac
|
|
51
|
+
|
|
52
|
+
# Empty path → pass-through
|
|
53
|
+
if [ -z "$TARGET_PATH" ]; then
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# === Resolve project dir + schema dir ===
|
|
58
|
+
|
|
59
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
60
|
+
SCHEMA_DIR="${PROJECT_DIR}/standards/state-spine/schemas"
|
|
61
|
+
|
|
62
|
+
# If schema dir doesn't exist, this repo doesn't have spine v0 yet — pass through
|
|
63
|
+
if [ ! -d "$SCHEMA_DIR" ]; then
|
|
64
|
+
exit 0
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# === Path-based entity detection ===
|
|
68
|
+
#
|
|
69
|
+
# Returns the entity slug (matching <slug>.schema.json) if path is a
|
|
70
|
+
# state-spine entity, empty otherwise.
|
|
71
|
+
detect_entity_type() {
|
|
72
|
+
local path="$1"
|
|
73
|
+
# Strip project_dir prefix if present
|
|
74
|
+
local rel="${path#${PROJECT_DIR}/}"
|
|
75
|
+
rel="${rel#./}"
|
|
76
|
+
|
|
77
|
+
local result=""
|
|
78
|
+
case "$rel" in
|
|
79
|
+
docs/whereami.json) result="whereami" ;;
|
|
80
|
+
substrate.config.json) result="substrate-config" ;;
|
|
81
|
+
docs/preview-state/*.json) result="preview-state" ;;
|
|
82
|
+
docs/iteration-bets/*.md) result="iteration-bet" ;;
|
|
83
|
+
docs/roadmaps/*.md) result="roadmap" ;;
|
|
84
|
+
docs/stacks/*.md) result="stack" ;;
|
|
85
|
+
docs/decompositions/*.md) result="decomposition" ;;
|
|
86
|
+
docs/personas/*.md) result="persona" ;;
|
|
87
|
+
docs/deferred-actions/*.md) result="deferred-action" ;;
|
|
88
|
+
chronicle/*.md|docs/chronicle/*.md) result="chronicle" ;;
|
|
89
|
+
state/markers/*/*.json) result="marker" ;;
|
|
90
|
+
docs/sessions/*.json) result="session" ;;
|
|
91
|
+
docs/sessions/*.md) result="session" ;;
|
|
92
|
+
docs/input-artifacts/*.json) result="input-artifact" ;;
|
|
93
|
+
state/template-instances/*.json) result="template-instance" ;;
|
|
94
|
+
state/inbox/*.json) result="inbox-work-item" ;;
|
|
95
|
+
state/stack-templates/*.json) result="stack-template" ;;
|
|
96
|
+
bassclef-platform.yml|bassclef-platform.yaml) result="bassclef-platform" ;;
|
|
97
|
+
esac
|
|
98
|
+
|
|
99
|
+
# bassclef-platform.yml lives at the root of a SIBLING repo (e.g.,
|
|
100
|
+
# sunj-labs/platform-config). When edited from outside PROJECT_DIR,
|
|
101
|
+
# the absolute path doesn't strip cleanly — match by basename suffix
|
|
102
|
+
# as a fallback. The file MUST be at a repo root (not nested).
|
|
103
|
+
if [ -z "$result" ]; then
|
|
104
|
+
case "$path" in
|
|
105
|
+
*/bassclef-platform.yml|*/bassclef-platform.yaml) result="bassclef-platform" ;;
|
|
106
|
+
esac
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
echo "$result"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
ENTITY="$(detect_entity_type "$TARGET_PATH")"
|
|
113
|
+
|
|
114
|
+
# Non-state file → pass-through
|
|
115
|
+
if [ -z "$ENTITY" ]; then
|
|
116
|
+
exit 0
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# === Resolve schema file ===
|
|
120
|
+
|
|
121
|
+
SCHEMA_FILE="${SCHEMA_DIR}/${ENTITY}.schema.json"
|
|
122
|
+
if [ ! -f "$SCHEMA_FILE" ]; then
|
|
123
|
+
echo "[state-validate.sh] schema for entity '$ENTITY' not found at $SCHEMA_FILE — pass-through" >&2
|
|
124
|
+
exit 0
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# === Storage shape detection ===
|
|
128
|
+
#
|
|
129
|
+
# A = pure JSON (whole file validates)
|
|
130
|
+
# B = markdown + YAML frontmatter (extract frontmatter, validate)
|
|
131
|
+
# C = pure JSON (whole file validates) — same as A but for collection entities
|
|
132
|
+
#
|
|
133
|
+
# In practice for the validator: anything ending in .json validates the
|
|
134
|
+
# whole file; anything ending in .md extracts YAML frontmatter and
|
|
135
|
+
# validates that.
|
|
136
|
+
|
|
137
|
+
extract_data_for_validation() {
|
|
138
|
+
local path="$1"
|
|
139
|
+
if [ ! -f "$path" ]; then
|
|
140
|
+
echo "[state-validate.sh] file does not exist at write time: $path" >&2
|
|
141
|
+
return 4
|
|
142
|
+
fi
|
|
143
|
+
case "$path" in
|
|
144
|
+
*.json)
|
|
145
|
+
cat "$path"
|
|
146
|
+
;;
|
|
147
|
+
*.md)
|
|
148
|
+
# Extract YAML frontmatter, convert to JSON
|
|
149
|
+
awk '/^---$/{c++; next} c==1' "$path" | python3 -c "
|
|
150
|
+
import sys, yaml, json
|
|
151
|
+
try:
|
|
152
|
+
data = yaml.safe_load(sys.stdin) or {}
|
|
153
|
+
print(json.dumps(data, default=str))
|
|
154
|
+
except Exception as e:
|
|
155
|
+
print(f'YAML parse failed: {e}', file=sys.stderr)
|
|
156
|
+
sys.exit(3)
|
|
157
|
+
"
|
|
158
|
+
;;
|
|
159
|
+
*.yml|*.yaml)
|
|
160
|
+
# Pure YAML file (Shape D — entity is the whole file). Convert
|
|
161
|
+
# to JSON and validate. Used by bassclef-platform.yml.
|
|
162
|
+
python3 -c "
|
|
163
|
+
import sys, yaml, json
|
|
164
|
+
try:
|
|
165
|
+
with open('$path') as f:
|
|
166
|
+
data = yaml.safe_load(f) or {}
|
|
167
|
+
print(json.dumps(data, default=str))
|
|
168
|
+
except Exception as e:
|
|
169
|
+
print(f'YAML parse failed: {e}', file=sys.stderr)
|
|
170
|
+
sys.exit(3)
|
|
171
|
+
"
|
|
172
|
+
;;
|
|
173
|
+
*)
|
|
174
|
+
echo "[state-validate.sh] unsupported extension for path: $path" >&2
|
|
175
|
+
return 5
|
|
176
|
+
;;
|
|
177
|
+
esac
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# Hoare precondition cure (#1001): on new-file Write the target does not
|
|
181
|
+
# yet exist on disk. Read tool_input.content from the event JSON and
|
|
182
|
+
# materialize a temp file so the extract function can read it. Preserves
|
|
183
|
+
# the disk-read path for Edit and existing-file Write. Silent no-op when
|
|
184
|
+
# content is absent (matches prior file-missing behavior).
|
|
185
|
+
NEW_FILE_TMP=""
|
|
186
|
+
if [ ! -f "$TARGET_PATH" ] && [ "$TOOL_NAME" = "Write" ]; then
|
|
187
|
+
WRITE_CONTENT="$(echo "$EVENT_JSON" | jq -r '.tool_input.content // empty')"
|
|
188
|
+
if [ -n "$WRITE_CONTENT" ]; then
|
|
189
|
+
NEW_FILE_TMP="$(mktemp -t state-validate-newfile-XXXXXX)${TARGET_PATH##*.}"
|
|
190
|
+
# Add trailing dot if the extension detection above collapsed
|
|
191
|
+
case "$TARGET_PATH" in
|
|
192
|
+
*.*) NEW_FILE_TMP="${NEW_FILE_TMP%.*}.${TARGET_PATH##*.}" ;;
|
|
193
|
+
esac
|
|
194
|
+
printf '%s' "$WRITE_CONTENT" > "$NEW_FILE_TMP"
|
|
195
|
+
trap 'rm -f "$NEW_FILE_TMP"' EXIT
|
|
196
|
+
DATA_JSON="$(extract_data_for_validation "$NEW_FILE_TMP")"
|
|
197
|
+
EXTRACT_EXIT=$?
|
|
198
|
+
else
|
|
199
|
+
exit 0
|
|
200
|
+
fi
|
|
201
|
+
else
|
|
202
|
+
DATA_JSON="$(extract_data_for_validation "$TARGET_PATH")"
|
|
203
|
+
EXTRACT_EXIT=$?
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
if [ "$EXTRACT_EXIT" -ne 0 ]; then
|
|
207
|
+
exit "$EXTRACT_EXIT"
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
# Legacy pass-through: pre-spine artifacts have no YAML frontmatter at
|
|
211
|
+
# all (e.g., bets authored before schemas existed). Skip validation
|
|
212
|
+
# with WARN to nudge migration without BLOCKing routine work. WU-4
|
|
213
|
+
# migration brings these into conformance progressively.
|
|
214
|
+
if [ -z "$DATA_JSON" ] || [ "$DATA_JSON" = "{}" ] || [ "$DATA_JSON" = "null" ]; then
|
|
215
|
+
echo "[state-validate.sh] WARN: $TARGET_PATH ($ENTITY) has no YAML frontmatter — legacy pass-through. Migrate via WU-4 to add minimal metadata." >&2
|
|
216
|
+
exit 0
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# === Validate via ajv-cli ===
|
|
220
|
+
|
|
221
|
+
# Stage data as a temp JSON file for ajv -d.
|
|
222
|
+
# Note: ajv-cli detects format by file extension, and macOS mktemp -t
|
|
223
|
+
# appends random suffix AFTER the template, so .json must be explicitly
|
|
224
|
+
# tacked on via mv to ensure it's the trailing extension.
|
|
225
|
+
DATA_TMP_RAW="$(mktemp -t state-validate-XXXXXX)"
|
|
226
|
+
DATA_TMP="${DATA_TMP_RAW}.json"
|
|
227
|
+
mv "$DATA_TMP_RAW" "$DATA_TMP"
|
|
228
|
+
trap 'rm -f "$DATA_TMP"' EXIT
|
|
229
|
+
echo "$DATA_JSON" > "$DATA_TMP"
|
|
230
|
+
|
|
231
|
+
# Stage all schemas as -r refs so $ref resolution works (e.g., _reference, _value_prop, _appetite_actuals, step)
|
|
232
|
+
REF_ARGS=""
|
|
233
|
+
for ref_schema in "$SCHEMA_DIR"/_*.schema.json "$SCHEMA_DIR"/step.schema.json; do
|
|
234
|
+
if [ -f "$ref_schema" ] && [ "$ref_schema" != "$SCHEMA_FILE" ]; then
|
|
235
|
+
REF_ARGS="$REF_ARGS -r $ref_schema"
|
|
236
|
+
fi
|
|
237
|
+
done
|
|
238
|
+
|
|
239
|
+
# Run ajv-cli; silence its noisy npm warnings, capture only validation result
|
|
240
|
+
VALIDATION_OUTPUT="$(npx -y -p ajv-cli@5 -p ajv-formats@2 ajv validate \
|
|
241
|
+
--spec=draft2020 \
|
|
242
|
+
-s "$SCHEMA_FILE" \
|
|
243
|
+
$REF_ARGS \
|
|
244
|
+
-d "$DATA_TMP" \
|
|
245
|
+
-c ajv-formats 2>&1)"
|
|
246
|
+
VALIDATION_EXIT=$?
|
|
247
|
+
|
|
248
|
+
if [ "$VALIDATION_EXIT" -eq 0 ]; then
|
|
249
|
+
exit 0
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
# === BLOCK with structured error ===
|
|
253
|
+
|
|
254
|
+
echo "" >&2
|
|
255
|
+
echo "🛑 STATE-VALIDATE — BLOCKED 🛑" >&2
|
|
256
|
+
echo "" >&2
|
|
257
|
+
echo "Entity: $ENTITY" >&2
|
|
258
|
+
echo "Schema: $SCHEMA_FILE" >&2
|
|
259
|
+
echo "Path: $TARGET_PATH" >&2
|
|
260
|
+
echo "" >&2
|
|
261
|
+
echo "Validation error:" >&2
|
|
262
|
+
# Strip noisy npm warnings; surface only ajv's actual error lines
|
|
263
|
+
echo "$VALIDATION_OUTPUT" | grep -v "^npm warn" | grep -v "^$" >&2
|
|
264
|
+
echo "" >&2
|
|
265
|
+
echo "Per .claude/rules/state-schema-validation.md: state-spine entity" >&2
|
|
266
|
+
echo "writes must conform to their schema. Either:" >&2
|
|
267
|
+
echo " 1. Fix the data to match the schema (preferred)" >&2
|
|
268
|
+
echo " 2. Update the schema if it's wrong (requires PR review)" >&2
|
|
269
|
+
echo " 3. Override with SKIP_STATE_VALIDATE=1 (logged; rare; not for normal work)" >&2
|
|
270
|
+
echo "" >&2
|
|
271
|
+
exit 3
|