A fresh install now activates only the safety pack; discipline hooks and agents are opt-in via an onboarding step (step 6) or `kei configure`. "People don't need Rust-only" — they pick their own stack. - _primitives/hook-packs.toml: SSoT mapping pack -> hooks, stack -> packs + agent groups. safety always on; evidence/observability/epistemic/ orchestration/git-guard/stack-rust opt-in. rust-first/no-python only under the systems stack; git-guard (no-github-push) opt-in only, pulled by no stack. - lib-profile: extract generic _toml_array (reused by lib-packs); profile_members becomes a thin wrapper (no behavior change). - lib-packs: pack/stack/agent resolvers + selection loader. - lib-hooks: filter_snippet_by_packs (install-time allowlist) + prune_kit_hooks (reconfigure removes deselected kit hooks, keeps foreign ones); activate_hooks rewired to prune + filter + merge. No custom settings.json fields (/doctor safe). - lib-agents: install_manifests filters by stack agent set (empty = install all). - onboarding: pick_stack step (reuse _onb_read_choice), persists stack_profile + enabled_packs to onboarding.toml; i18n STR_* added. - bin/kei configure -> scripts/kei-configure.sh (re-pick without reinstall); install stamps ~/.claude/.kei-kit-dir. - numeric-claims-guard: money regex no longer matches shell positionals ($1..$9); requires decimal / unit / 2+ digits / tilde. Real money + time still caught. - gate one-liner added to 8 discipline hooks (runtime toggle via hooks-control). Verified end-to-end (scratch HOME): fresh=safety only; evidence pack adds numeric+citation; systems stack wires rust-first + 14 base/systems agents (no data-science/swift); reconfigure-shrink prunes kit hooks but keeps a foreign hook; settings schema clean; assembler golden 3/3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
65 lines
3.2 KiB
Bash
Executable file
65 lines
3.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
# Runtime gate (hooks-control skill / KEI_DISABLED_HOOKS / KEI_HOOK_PROFILE).
|
|
_KEI_LIB="$(dirname "$0")/_lib/gate.sh"; if [ -r "$_KEI_LIB" ]; then . "$_KEI_LIB"; kei_hook_gate "rust-first" || exit 0; fi
|
|
# RULE 0.2 — RUST FIRST reminder hook.
|
|
#
|
|
# Fires on UserPromptSubmit. Detects keywords indicating language choice
|
|
# or new development, and injects the rust-first rule into next-turn context.
|
|
#
|
|
# Soft reminder, not a hard block. The rule itself is enforced by Claude
|
|
# consulting ~/.claude/rules/rust-first.md before writing code.
|
|
#
|
|
# Keyword sources:
|
|
# - English patterns: inline regex below
|
|
# - Non-English patterns (Russian etc): ~/.claude/hooks/ru/rust-first-keywords.txt
|
|
# One keyword or phrase per line, lowercase, substring match.
|
|
#
|
|
# Exit behavior:
|
|
# - No match: exit 0 silently
|
|
# - Match: print a JSON block with additionalContext, exit 0
|
|
|
|
PROMPT=$(jq -r '.prompt // empty' 2>/dev/null)
|
|
[ -z "$PROMPT" ] && exit 0
|
|
|
|
# Lowercase (macOS Bash 3 — use tr, not ${var,,})
|
|
PROMPT_LC=$(printf '%s' "$PROMPT" | tr '[:upper:]' '[:lower:]')
|
|
|
|
MATCH=0
|
|
|
|
# --- English language-choice patterns ------------------------------------
|
|
if printf '%s' "$PROMPT_LC" | grep -qE '(new project|start a project|choose (a )?language|choose (the )?stack|what stack|which stack|stack choice|rewrite .* in (python|go|js|javascript|typescript|swift|ruby|c\+\+)|should (we|i) use (python|go|js|javascript|typescript|swift|ruby|rust)|what language|pick a language|rust or (python|go|js)|python or rust|go or rust)'; then
|
|
MATCH=1
|
|
fi
|
|
|
|
# --- Non-English keywords via sidecar file -------------------------------
|
|
SIDECAR="$HOME/.claude/hooks/ru/rust-first-keywords.txt"
|
|
if [ "$MATCH" -eq 0 ] && [ -f "$SIDECAR" ]; then
|
|
# Substring match: each non-empty non-comment line
|
|
while IFS= read -r KW; do
|
|
# Skip empty lines and comments
|
|
case "$KW" in
|
|
''|'#'*) continue ;;
|
|
esac
|
|
if printf '%s' "$PROMPT_LC" | grep -qF -- "$KW"; then
|
|
MATCH=1
|
|
break
|
|
fi
|
|
done < "$SIDECAR"
|
|
fi
|
|
|
|
if [ "$MATCH" -eq 0 ]; then
|
|
exit 0
|
|
fi
|
|
|
|
# --- Inject additional context ------------------------------------------
|
|
cat <<'EOF'
|
|
{
|
|
"hookSpecificOutput": {
|
|
"hookEventName": "UserPromptSubmit",
|
|
"additionalContext": "<rust-first-reminder>\nRULE 0.2 RUST FIRST: language-choice keyword detected in user prompt.\n\nBefore proposing any stack, consult ~/.claude/rules/rust-first.md.\n\nDefault is Rust for all new code. A non-Rust choice requires an explicit architectural reason from the allowed list: (1) large ML training over 10M params, (2) existing language-locked project being extended, (3) platform-native UI, (4) browser/DOM runtime, (5) under-50-line throwaway script, (6) external binding only in Python/JS, (7) explicit user override with stated reason.\n\nNot acceptable reasons: 'Python is the ML language', 'I know Python better', 'faster iteration in Python', 'matplotlib easier', 'we will rewrite in Rust later', 'just a prototype', 'libraries'.\n\nDocument the chosen exception in project DECISIONS.md.\n\nIf the user has already chosen another language, respect their call but note the reason in project memory. Otherwise default to Rust and state the architectural reason the exception does not apply.\n</rust-first-reminder>"
|
|
}
|
|
}
|
|
EOF
|
|
|
|
exit 0
|