Skip to content

Commit c409d62

Browse files
committed
IOS-5420 Added auto learning feature to skills
1 parent e3307b7 commit c409d62

File tree

6 files changed

+287
-2
lines changed

6 files changed

+287
-2
lines changed

.claude/hooks/skill-activation-prompt.sh

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,52 @@ for skill in $(jq -r '.skills | keys[]' "$SKILL_RULES"); do
7676
fi
7777
done
7878

79-
# If no skills matched, exit silently
79+
# If no skills matched, check if prompt is substantial
8080
if [ ${#MATCHED_SKILLS[@]} -eq 0 ]; then
8181
echo " No skills matched" >> "$LOG_FILE"
82+
83+
# Calculate prompt size metrics
84+
CHAR_COUNT=${#USER_PROMPT}
85+
LINE_COUNT=$(echo "$USER_PROMPT" | wc -l | tr -d ' ')
86+
87+
# Threshold: 100+ characters OR 3+ lines
88+
if [ $CHAR_COUNT -ge 100 ] || [ $LINE_COUNT -ge 3 ]; then
89+
echo " Substantial prompt (${CHAR_COUNT} chars, ${LINE_COUNT} lines) - prompting user" >> "$LOG_FILE"
90+
91+
# Log to missed activations file
92+
MISSED_LOG="$LOG_DIR/skill-activations-missed.log"
93+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Missed activation" >> "$MISSED_LOG"
94+
echo " Prompt: ${USER_PROMPT:0:200}..." >> "$MISSED_LOG"
95+
echo " Size: ${CHAR_COUNT} chars, ${LINE_COUNT} lines" >> "$MISSED_LOG"
96+
echo "" >> "$MISSED_LOG"
97+
98+
# Build skill list for user
99+
echo ""
100+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
101+
echo "💡 NO SKILLS ACTIVATED"
102+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
103+
echo ""
104+
echo "Your prompt seems substantial (${CHAR_COUNT} chars, ${LINE_COUNT} lines) but no skills matched."
105+
echo ""
106+
echo "📚 Available skills:"
107+
echo ""
108+
109+
# List all skills
110+
for skill in $(jq -r '.skills | keys[]' "$SKILL_RULES"); do
111+
description=$(jq -r ".skills[\"$skill\"].description" "$SKILL_RULES")
112+
echo "$skill"
113+
echo " $description"
114+
echo ""
115+
done
116+
117+
echo "❓ Should any of these skills be activated for this task?"
118+
echo " If yes, tell me which one and I'll extract keywords from your prompt"
119+
echo " to improve future auto-activation."
120+
echo ""
121+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
122+
echo ""
123+
fi
124+
82125
exit 0
83126
fi
84127

.claude/hooks/skill-rules.json.tmp

Whitespace-only changes.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#!/bin/bash
2+
3+
# Auto-Learning Utility
4+
# Adds keywords to a skill's configuration for improved auto-activation
5+
6+
set -euo pipefail
7+
8+
# Usage check
9+
if [ $# -lt 2 ]; then
10+
echo "Usage: $0 <skill-name> <keyword1> [keyword2] [keyword3] ..."
11+
echo ""
12+
echo "Example:"
13+
echo " $0 localization-developer \"membership\" \"tiers\" \"settings\""
14+
exit 1
15+
fi
16+
17+
SKILL_NAME=$1
18+
shift
19+
NEW_KEYWORDS=("$@")
20+
21+
# Get script directory
22+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23+
SKILL_RULES="$SCRIPT_DIR/../skill-rules.json"
24+
LOG_DIR="$SCRIPT_DIR/../../logs"
25+
LOG_FILE="$LOG_DIR/skill-learning.log"
26+
27+
# Ensure log directory exists
28+
mkdir -p "$LOG_DIR"
29+
30+
# Validate skill exists
31+
if ! jq -e ".skills[\"$SKILL_NAME\"]" "$SKILL_RULES" > /dev/null 2>&1; then
32+
echo "❌ Error: Skill '$SKILL_NAME' not found in skill-rules.json"
33+
echo ""
34+
echo "Available skills:"
35+
jq -r '.skills | keys[]' "$SKILL_RULES" | sed 's/^/ - /'
36+
exit 1
37+
fi
38+
39+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
40+
echo "📚 AUTO-LEARNING: Adding keywords to $SKILL_NAME"
41+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
42+
echo ""
43+
44+
# Get existing keywords
45+
EXISTING_KEYWORDS=$(jq -r ".skills[\"$SKILL_NAME\"].promptTriggers.keywords[]" "$SKILL_RULES" 2>/dev/null || echo "")
46+
47+
# Check which keywords are new
48+
TRULY_NEW_KEYWORDS=()
49+
for keyword in "${NEW_KEYWORDS[@]}"; do
50+
keyword_lower=$(echo "$keyword" | tr '[:upper:]' '[:lower:]')
51+
52+
IS_DUPLICATE=false
53+
while IFS= read -r existing; do
54+
existing_lower=$(echo "$existing" | tr '[:upper:]' '[:lower:]')
55+
if [ "$keyword_lower" = "$existing_lower" ]; then
56+
IS_DUPLICATE=true
57+
echo "⏭️ Skipping '$keyword' (already exists)"
58+
break
59+
fi
60+
done <<< "$EXISTING_KEYWORDS"
61+
62+
if [ "$IS_DUPLICATE" = false ]; then
63+
TRULY_NEW_KEYWORDS+=("$keyword")
64+
echo "✅ Adding '$keyword'"
65+
fi
66+
done
67+
68+
# Exit if no new keywords
69+
if [ ${#TRULY_NEW_KEYWORDS[@]} -eq 0 ]; then
70+
echo ""
71+
echo "ℹ️ No new keywords to add - all provided keywords already exist"
72+
exit 0
73+
fi
74+
75+
echo ""
76+
echo "Updating skill-rules.json..."
77+
78+
# Create backup
79+
cp "$SKILL_RULES" "$SKILL_RULES.backup"
80+
81+
# Build jq update command
82+
JQ_FILTER=".skills[\"$SKILL_NAME\"].promptTriggers.keywords += ["
83+
for i in "${!TRULY_NEW_KEYWORDS[@]}"; do
84+
if [ $i -gt 0 ]; then
85+
JQ_FILTER+=", "
86+
fi
87+
JQ_FILTER+="\"${TRULY_NEW_KEYWORDS[$i]}\""
88+
done
89+
JQ_FILTER+="] | .skills[\"$SKILL_NAME\"].promptTriggers.keywords |= unique"
90+
91+
# Update skill-rules.json
92+
jq "$JQ_FILTER" "$SKILL_RULES" > "$SKILL_RULES.tmp"
93+
94+
# Validate JSON
95+
if jq empty "$SKILL_RULES.tmp" 2>/dev/null; then
96+
mv "$SKILL_RULES.tmp" "$SKILL_RULES"
97+
echo "✅ Updated skill-rules.json"
98+
99+
# Remove backup
100+
rm "$SKILL_RULES.backup"
101+
102+
# Log the update
103+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Added keywords to $SKILL_NAME" >> "$LOG_FILE"
104+
for keyword in "${TRULY_NEW_KEYWORDS[@]}"; do
105+
echo " + $keyword" >> "$LOG_FILE"
106+
done
107+
echo "" >> "$LOG_FILE"
108+
109+
echo ""
110+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
111+
echo "✨ Success! Added ${#TRULY_NEW_KEYWORDS[@]} new keyword(s)"
112+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
113+
echo ""
114+
echo "💡 The system will now auto-activate '$SKILL_NAME' for prompts"
115+
echo " containing these keywords."
116+
echo ""
117+
else
118+
echo "❌ Error: Generated invalid JSON, restoring backup"
119+
mv "$SKILL_RULES.backup" "$SKILL_RULES"
120+
rm -f "$SKILL_RULES.tmp"
121+
exit 1
122+
fi
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/bin/bash
2+
3+
# Keyword Extraction Utility
4+
# Extracts relevant technical keywords from a user prompt for skill auto-learning
5+
6+
set -euo pipefail
7+
8+
# Read prompt from argument or stdin
9+
if [ $# -eq 0 ]; then
10+
PROMPT=$(cat)
11+
else
12+
PROMPT="$1"
13+
fi
14+
15+
# Convert to lowercase
16+
PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]')
17+
18+
# Common stopwords to exclude
19+
STOPWORDS="the a an and or but in on at to for of with by from as is was are were be been being have has had do does did will would should could may might must can this that these those i you he she it we they my your his her its our their me him them what which who when where why how all each every both few more most other some such no nor not only own same so than too very just now need want like get make go see"
20+
21+
# Extract words (alphanumeric + hyphens + dots)
22+
WORDS=$(echo "$PROMPT_LOWER" | grep -oE '[a-z0-9][a-z0-9._-]*' | sort -u)
23+
24+
# Filter and score words
25+
SCORED_WORDS=""
26+
27+
for word in $WORDS; do
28+
# Skip short words
29+
if [ ${#word} -lt 3 ]; then
30+
continue
31+
fi
32+
33+
# Skip if stopword
34+
if echo " $STOPWORDS " | grep -q " $word "; then
35+
continue
36+
fi
37+
38+
# Calculate score
39+
score=1
40+
41+
# Boost technical terms
42+
if echo "$word" | grep -qE '^(view|model|controller|coordinator|service|repository|manager|handler)'; then
43+
score=$((score + 3))
44+
fi
45+
46+
# Boost Swift/iOS terms
47+
if echo "$word" | grep -qE '^(swift|swiftui|combine|async|await|observable|published)'; then
48+
score=$((score + 3))
49+
fi
50+
51+
# Boost file extensions
52+
if echo "$word" | grep -qE '\.(swift|xcstrings|yml|md)$'; then
53+
score=$((score + 2))
54+
fi
55+
56+
# Boost compound technical words
57+
if echo "$word" | grep -qE '[_.]'; then
58+
score=$((score + 2))
59+
fi
60+
61+
# Boost longer words
62+
if [ ${#word} -gt 8 ]; then
63+
score=$((score + 1))
64+
fi
65+
66+
# Store as "score word"
67+
SCORED_WORDS="$SCORED_WORDS
68+
$score $word"
69+
done
70+
71+
# Sort by score (descending) and take top 5
72+
echo "$SCORED_WORDS" | grep -v '^$' | sort -rn | head -5 | awk '{print $2}'

.claude/skills/skills-manager/SKILL.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,34 @@ echo '{"prompt":"add feature flag"}' | .claude/hooks/skill-activation-prompt.sh
9797

9898
Should output skill suggestion if match found.
9999

100+
### Auto-Learning Feature
101+
102+
**What it does**: When a substantial prompt (100+ chars OR 3+ lines) doesn't activate any skills, the system prompts you with available skills and auto-updates keywords based on your feedback.
103+
104+
**Workflow**:
105+
1. You submit a substantial prompt
106+
2. No skills activate
107+
3. System shows: "Should any of these skills be activated?"
108+
4. You respond: "Yes, localization-developer should activate"
109+
5. Claude extracts keywords from your prompt
110+
6. Claude runs: `.claude/hooks/utils/add-keywords-to-skill.sh localization-developer <keywords>`
111+
7. skill-rules.json updated
112+
8. Future similar prompts auto-activate
113+
114+
**Manual keyword extraction**:
115+
```bash
116+
# Test keyword extraction
117+
echo "Update space settings localization for membership tiers" | .claude/hooks/utils/extract-keywords.sh
118+
# Output: membership, settings, localization, tiers, update
119+
120+
# Add keywords manually
121+
.claude/hooks/utils/add-keywords-to-skill.sh localization-developer "membership" "tiers"
122+
```
123+
124+
**Logs**:
125+
- Missed activations: `.claude/logs/skill-activations-missed.log`
126+
- Learning updates: `.claude/logs/skill-learning.log`
127+
100128
## 🔧 The System Components
101129

102130
### Hooks (Automation)
@@ -112,12 +140,13 @@ Should output skill suggestion if match found.
112140

113141
**Location**: `.claude/skills/*/SKILL.md`
114142

115-
**The 5 skills**:
143+
**The 6 skills**:
116144
1. `ios-dev-guidelines` - Swift/iOS patterns
117145
2. `localization-developer` - Localization
118146
3. `code-generation-developer` - Feature flags, make generate
119147
4. `design-system-developer` - Icons, typography, colors
120148
5. `skills-manager` - This skill (meta!)
149+
6. `code-review-developer` - Code review standards
121150

122151
### Configuration
123152

CLAUDE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,25 @@ The skills system provides context-aware guidance that auto-activates based on y
5656

5757
**How it works**: When you start a task, the system analyzes your prompt and file context, then automatically suggests relevant skills. No manual loading needed.
5858

59+
**Auto-learning**: When the system fails to activate a skill for a substantial prompt (100+ chars or 3+ lines):
60+
1. You'll be prompted with available skills
61+
2. If you identify which skill should have activated, tell Claude
62+
3. Claude extracts relevant keywords from your prompt
63+
4. Keywords are automatically added to skill-rules.json
64+
5. Future similar prompts will auto-activate the skill
65+
66+
**Manual keyword management**:
67+
```bash
68+
# Extract keywords from a prompt
69+
.claude/hooks/utils/extract-keywords.sh "your prompt text"
70+
71+
# Add keywords to a skill
72+
.claude/hooks/utils/add-keywords-to-skill.sh <skill-name> <keyword1> [keyword2] ...
73+
74+
# Example
75+
.claude/hooks/utils/add-keywords-to-skill.sh localization-developer "membership" "tiers"
76+
```
77+
5978
**Learn more**: See `.claude/skills/README.md` for system overview and `.claude/hooks/README.md` for automation details.
6079

6180
#### Specialized Documentation

0 commit comments

Comments
 (0)