Initial commit
This commit is contained in:
131
hooks/skill-activation-prompt.py
Executable file
131
hooks/skill-activation-prompt.py
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Literal, Optional, TypedDict
|
||||
|
||||
|
||||
class PromptTriggers(TypedDict, total=False):
|
||||
keywords: List[str]
|
||||
intentPatterns: List[str]
|
||||
|
||||
|
||||
class SkillRule(TypedDict):
|
||||
type: Literal["guardrail", "domain"]
|
||||
enforcement: Literal["block", "suggest", "warn"]
|
||||
priority: Literal["critical", "high", "medium", "low"]
|
||||
promptTriggers: Optional[PromptTriggers]
|
||||
|
||||
|
||||
class SkillRules(TypedDict):
|
||||
version: str
|
||||
skills: Dict[str, SkillRule]
|
||||
|
||||
|
||||
class HookInput(TypedDict):
|
||||
session_id: str
|
||||
transcript_path: str
|
||||
cwd: str
|
||||
permission_mode: str
|
||||
prompt: str
|
||||
|
||||
|
||||
class MatchedSkill(TypedDict):
|
||||
name: str
|
||||
matchType: Literal["keyword", "intent"]
|
||||
config: SkillRule
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
input_data = sys.stdin.read()
|
||||
data: HookInput = json.loads(input_data)
|
||||
prompt = data["prompt"].lower()
|
||||
project_dir = (
|
||||
data.get("cwd") or os.environ.get("CLAUDE_PROJECT_DIR") or os.getcwd()
|
||||
)
|
||||
rules_path = Path(project_dir) / ".claude" / "skills" / "skill-rules.json"
|
||||
with open(rules_path, "r", encoding="utf-8") as f:
|
||||
rules: SkillRules = json.load(f)
|
||||
matched_skills: List[MatchedSkill] = []
|
||||
for skill_name, config in rules["skills"].items():
|
||||
triggers = config.get("promptTriggers")
|
||||
if not triggers:
|
||||
continue
|
||||
keywords = triggers.get("keywords", [])
|
||||
if keywords:
|
||||
keyword_match = any(kw.lower() in prompt for kw in keywords)
|
||||
if keyword_match:
|
||||
matched_skills.append(
|
||||
{"name": skill_name, "matchType": "keyword", "config": config}
|
||||
)
|
||||
continue
|
||||
intent_patterns = triggers.get("intentPatterns", [])
|
||||
if intent_patterns:
|
||||
intent_match = any(
|
||||
re.search(pattern, prompt, re.IGNORECASE)
|
||||
for pattern in intent_patterns
|
||||
)
|
||||
if intent_match:
|
||||
matched_skills.append(
|
||||
{"name": skill_name, "matchType": "intent", "config": config}
|
||||
)
|
||||
additional_context = ""
|
||||
if matched_skills:
|
||||
output = "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
|
||||
output += "🎯 SKILL ACTIVATION CHECK\n"
|
||||
output += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
|
||||
critical = [
|
||||
s for s in matched_skills if s["config"]["priority"] == "critical"
|
||||
]
|
||||
high = [s for s in matched_skills if s["config"]["priority"] == "high"]
|
||||
medium = [s for s in matched_skills if s["config"]["priority"] == "medium"]
|
||||
low = [s for s in matched_skills if s["config"]["priority"] == "low"]
|
||||
if critical:
|
||||
output += "⚠️ CRITICAL SKILLS (REQUIRED):\n"
|
||||
for s in critical:
|
||||
output += f" → {s['name']}\n"
|
||||
output += "\n"
|
||||
if high:
|
||||
output += "📚 RECOMMENDED SKILLS:\n"
|
||||
for s in high:
|
||||
output += f" → {s['name']}\n"
|
||||
output += "\n"
|
||||
if medium:
|
||||
output += "💡 SUGGESTED SKILLS:\n"
|
||||
for s in medium:
|
||||
output += f" → {s['name']}\n"
|
||||
output += "\n"
|
||||
if low:
|
||||
output += "📌 OPTIONAL SKILLS:\n"
|
||||
for s in low:
|
||||
output += f" → {s['name']}\n"
|
||||
output += "\n"
|
||||
output += "ACTION: Use Skill tool BEFORE responding\n"
|
||||
output += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
|
||||
additional_context = output
|
||||
response = {
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "UserPromptSubmit",
|
||||
"additionalContext": additional_context,
|
||||
}
|
||||
}
|
||||
print(json.dumps(response))
|
||||
sys.exit(0)
|
||||
except Exception as err:
|
||||
print(f"Error in skill-activation-prompt hook: {err}", file=sys.stderr)
|
||||
response = {
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "UserPromptSubmit",
|
||||
"additionalContext": "",
|
||||
}
|
||||
}
|
||||
print(json.dumps(response))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user