Skip to content
This repository was archived by the owner on Oct 27, 2025. It is now read-only.

Commit 04a4d07

Browse files
committed
Add context-aware skills with additionalDirectories support
- Parse additionalDirectories from .claude/settings*.json files - Walk up from current dir to git root looking for settings files - Resolve relative paths from where settings file is located - Search current dir .claude/skills/ (no location tag) - Search additional context dirs .claude/skills/ (with location tags) - Show project name as location tag for skills from additionalDirectories - Current dir skills shadow both additional context and global skills - Filter out current dir from additionalDirectories to avoid duplicates - Update documentation in writing-skills/SKILL.md for project skills This replaces the hierarchical walk-up approach with context-aware discovery that respects Claude Code's /add-dir functionality.
1 parent cdcd624 commit 04a4d07

File tree

3 files changed

+227
-25
lines changed

3 files changed

+227
-25
lines changed

skills/meta/writing-skills/SKILL.md

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,22 @@ The entire skill creation process follows RED-GREEN-REFACTOR.
4747

4848
## When to Create a Skill
4949

50-
**Create when:**
50+
**Create global skill when:**
5151
- Technique wasn't intuitively obvious to you
5252
- You'd reference this again across projects
5353
- Pattern applies broadly (not project-specific)
54-
- Others would benefit
54+
- Others would benefit from it
5555

56-
**Don't create for:**
56+
**Create project skill (`.claude/skills/`) when:**
57+
- Team-specific workflows and conventions
58+
- Project architecture patterns
59+
- Domain-specific best practices
60+
- Onboarding patterns for this codebase
61+
62+
**Don't create skill for:**
5763
- One-off solutions
5864
- Standard practices well-documented elsewhere
59-
- Project-specific conventions (put in CLAUDE.md)
65+
- Simple reminders (use CLAUDE.md instead)
6066

6167
## Skill Types
6268

@@ -71,16 +77,33 @@ API docs, syntax guides, tool documentation (office docs)
7177

7278
## Directory Structure
7379

74-
**All skills are in the skills repository at `${SUPERPOWERS_SKILLS_ROOT}`:**
80+
**Global skills** (in your branch at `${SUPERPOWERS_SKILLS_ROOT}`):
81+
82+
```
83+
${SUPERPOWERS_SKILLS_ROOT}/
84+
skills/
85+
category/
86+
skill-name/
87+
SKILL.md # Main reference (required)
88+
supporting-file.* # Only if needed
89+
```
90+
91+
**Project skills** (version-controlled with project):
7592

7693
```
77-
${SUPERPOWERS_SKILLS_ROOT}
78-
skill-name/
79-
SKILL.md # Main reference (required)
80-
supporting-file.* # Only if needed
94+
.claude/
95+
skills/
96+
category/
97+
skill-name/
98+
SKILL.md # Same structure as global
99+
tool/ # Executable scripts if needed
81100
```
82101

83-
**Flat namespace** - all skills in one searchable location
102+
**Key differences:**
103+
- Global skills: Broadly applicable, personal working branch
104+
- Project skills: Team-shared, version-controlled
105+
- Current dir project skills shadow global skills when paths match
106+
- Additional context project skills (from additionalDirectories) shown with location tags
84107

85108
**Separate files for:**
86109
1. **Heavy reference** (100+ lines) - API docs, comprehensive syntax

skills/using-skills/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
22
name: Getting Started with Skills
3-
description: Skills wiki intro - mandatory workflows, search tool, brainstorming triggers
3+
description: Skills wiki intro - mandatory workflows, search tool, context-aware project skills
44
when_to_use: when starting any conversation
5-
version: 4.0.2
5+
version: 4.1.0
66
---
77

88
# Getting Started with Skills

skills/using-skills/find-skills

Lines changed: 192 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,114 @@
11
#!/usr/bin/env bash
22
# find-skills - Find and list skills with when_to_use guidance
33
# Shows all skills by default, filters by pattern if provided
4+
# Searches current dir + additionalDirectories from settings, then global skills
45

56
set -euo pipefail
67

78
# Determine directories
89
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
910
SKILLS_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
1011

12+
# Function to get additional directories from Claude Code settings
13+
# Outputs: "path|settings_dir" for each additionalDirectory found
14+
get_additional_dirs() {
15+
local git_root
16+
git_root=$(git rev-parse --show-toplevel 2>/dev/null) || return
17+
18+
local current_dir
19+
current_dir=$(pwd)
20+
21+
# Walk up from current dir to git root, checking for settings files
22+
local check_dir="$current_dir"
23+
while true; do
24+
local settings_files=(
25+
"$check_dir/.claude/settings.json"
26+
"$check_dir/.claude/settings.local.json"
27+
)
28+
29+
for settings_file in "${settings_files[@]}"; do
30+
if [[ -f "$settings_file" ]]; then
31+
# Extract paths from additionalDirectories array only
32+
# Output format: "path|settings_dir" so we can resolve relative paths correctly
33+
awk -v settings_dir="$check_dir" '
34+
/"additionalDirectories"/ { in_section=1; next }
35+
in_section && /\]/ { in_section=0; next }
36+
in_section && /^[[:space:]]*"/ {
37+
gsub(/^[[:space:]]*"|"[[:space:]]*,?[[:space:]]*$/, "")
38+
print $0 "|" settings_dir
39+
}
40+
' "$settings_file" 2>/dev/null
41+
fi
42+
done
43+
44+
# Stop if we've reached git root
45+
if [[ "$(cd "$check_dir" && pwd)" == "$(cd "$git_root" && pwd)" ]]; then
46+
break
47+
fi
48+
49+
# Move up one directory
50+
check_dir=$(dirname "$check_dir")
51+
52+
# Safety check
53+
if [[ "$check_dir" == "/" ]]; then
54+
break
55+
fi
56+
done
57+
}
58+
59+
# Collect additional directories from settings (excluding current dir)
60+
CURRENT_DIR=$(pwd)
61+
ADDITIONAL_DIRS=()
62+
63+
if git rev-parse --show-toplevel >/dev/null 2>&1; then
64+
while IFS='|' read -r dir settings_dir; do
65+
[[ -z "$dir" ]] && continue
66+
67+
# Resolve relative paths from settings_dir, absolute paths as-is
68+
if [[ "$dir" = /* ]]; then
69+
# Absolute path - use as is
70+
dir_normalized=$(cd "$dir" 2>/dev/null && pwd) || continue
71+
else
72+
# Relative path - resolve from where settings file is
73+
dir_normalized=$(cd "$settings_dir" && cd "$dir" 2>/dev/null && pwd) || continue
74+
fi
75+
76+
current_normalized=$(cd "$CURRENT_DIR" 2>/dev/null && pwd)
77+
78+
# Skip if it's the current directory
79+
if [[ "$dir_normalized" == "$current_normalized" ]]; then
80+
continue
81+
fi
82+
83+
# Deduplicate: only add if not already in array
84+
already_added=false
85+
for existing_dir in "${ADDITIONAL_DIRS[@]}"; do
86+
if [[ "$existing_dir" == "$dir_normalized" ]]; then
87+
already_added=true
88+
break
89+
fi
90+
done
91+
92+
if ! $already_added; then
93+
ADDITIONAL_DIRS+=("$dir_normalized")
94+
fi
95+
done < <(get_additional_dirs)
96+
fi
97+
98+
# Project skills: current dir .claude/skills
99+
PROJECT_SKILLS_DIR=""
100+
if [[ -d "$CURRENT_DIR/.claude/skills" ]]; then
101+
PROJECT_SKILLS_DIR="$CURRENT_DIR/.claude/skills"
102+
fi
103+
104+
# Additional context skills: .claude/skills in each additionalDirectory
105+
ADDITIONAL_SKILLS_DIRS=()
106+
for dir in "${ADDITIONAL_DIRS[@]}"; do
107+
if [[ -d "$dir/.claude/skills" ]]; then
108+
ADDITIONAL_SKILLS_DIRS+=("$dir/.claude/skills")
109+
fi
110+
done
111+
11112
SUPERPOWERS_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/superpowers"
12113
LOG_FILE="${SUPERPOWERS_DIR}/search-log.jsonl"
13114

@@ -28,11 +129,15 @@ EXAMPLES:
28129
29130
OUTPUT:
30131
Each line shows: Use skill-path/SKILL.md when [trigger]
132+
Skills from additionalDirectories show location: Use skill-path (dirname) when [trigger]
31133
Paths include /SKILL.md for direct use with Read tool
134+
Current dir project skills shadow additional directory skills when paths match
32135
33136
SEARCH:
34137
Searches both skill content AND path names.
35-
Skills location: ~/.config/superpowers/skills/
138+
Current dir: .claude/skills/ in current working directory
139+
Additional context: .claude/skills/ in each additionalDirectories from .claude/settings*.json
140+
Global skills: ~/.config/superpowers/skills/
36141
EOF
37142
exit 0
38143
fi
@@ -54,7 +159,8 @@ get_skill_path() {
54159
echo "$rel_path"
55160
}
56161

57-
# Collect all matching skills
162+
# Collect all matching skills (track seen skills to handle shadowing)
163+
seen_skills_list=""
58164
results=()
59165

60166
# If pattern provided, log the search
@@ -63,23 +169,86 @@ if [[ -n "$PATTERN" ]]; then
63169
echo "{\"timestamp\":\"$timestamp\",\"query\":\"$PATTERN\"}" >> "$LOG_FILE" 2>/dev/null || true
64170
fi
65171

66-
# Search skills directory
172+
# Search current directory project skills (no location tag)
173+
if [[ -n "$PROJECT_SKILLS_DIR" ]]; then
174+
while IFS= read -r file; do
175+
[[ -z "$file" ]] && continue
176+
177+
# Get path relative to .claude/skills/, then prepend "skills/" for consistency
178+
rel_path="${file#$PROJECT_SKILLS_DIR/}"
179+
skill_path="skills/${rel_path}"
180+
181+
when_to_use=$(get_when_to_use "$file")
182+
seen_skills_list="${seen_skills_list}${skill_path}"$'\n'
183+
results+=("$skill_path||$when_to_use") # Empty location tag
184+
done < <(
185+
if [[ -n "$PATTERN" ]]; then
186+
# Pattern mode: search content and paths
187+
{
188+
grep -E -r "$PATTERN" "$PROJECT_SKILLS_DIR/" --include="SKILL.md" -l 2>/dev/null || true
189+
find "$PROJECT_SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null | grep -E "$PATTERN" 2>/dev/null || true
190+
} | sort -u
191+
else
192+
# Show all
193+
find "$PROJECT_SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null || true
194+
fi
195+
)
196+
fi
197+
198+
# Search additional context directories (with location tags)
199+
for ADDITIONAL_SKILLS_DIR in "${ADDITIONAL_SKILLS_DIRS[@]}"; do
200+
# Get project directory name for location tag (parent of .claude/skills)
201+
# /path/to/project/.claude/skills -> /path/to/project
202+
project_dir=$(dirname "$(dirname "$ADDITIONAL_SKILLS_DIR")")
203+
location_tag=$(basename "$project_dir")
204+
205+
while IFS= read -r file; do
206+
[[ -z "$file" ]] && continue
207+
208+
rel_path="${file#$ADDITIONAL_SKILLS_DIR/}"
209+
skill_path="skills/${rel_path}"
210+
211+
# Skip if already seen (shadowed by current dir)
212+
if echo "$seen_skills_list" | grep -q "^${skill_path}$"; then
213+
continue
214+
fi
215+
216+
when_to_use=$(get_when_to_use "$file")
217+
seen_skills_list="${seen_skills_list}${skill_path}"$'\n'
218+
results+=("$skill_path|$location_tag|$when_to_use")
219+
done < <(
220+
if [[ -n "$PATTERN" ]]; then
221+
{
222+
grep -E -r "$PATTERN" "$ADDITIONAL_SKILLS_DIR/" --include="SKILL.md" -l 2>/dev/null || true
223+
find "$ADDITIONAL_SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null | grep -E "$PATTERN" 2>/dev/null || true
224+
} | sort -u
225+
else
226+
find "$ADDITIONAL_SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null || true
227+
fi
228+
)
229+
done
230+
231+
# Search global skills directory (skip if shadowed by project skills)
67232
while IFS= read -r file; do
68233
[[ -z "$file" ]] && continue
69234

70235
skill_path=$(get_skill_path "$file" "$SKILLS_DIR")
236+
237+
# Skip if shadowed by project skill
238+
echo "$seen_skills_list" | grep -q "^${skill_path}$" && continue
239+
71240
when_to_use=$(get_when_to_use "$file")
72-
results+=("$skill_path|$when_to_use")
241+
results+=("$skill_path||$when_to_use") # Empty location tag
73242
done < <(
74243
if [[ -n "$PATTERN" ]]; then
75-
# Pattern mode: search content and paths
244+
# Pattern mode: search content and paths (exclude .claude directories)
76245
{
77-
grep -E -r -- "$PATTERN" "$SKILLS_DIR/" --include="SKILL.md" -l 2>/dev/null || true
78-
find "$SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null | grep -E -- "$PATTERN" 2>/dev/null || true
246+
grep -E -r -- "$PATTERN" "$SKILLS_DIR/" --include="SKILL.md" --exclude-dir=".claude" -l 2>/dev/null || true
247+
find "$SKILLS_DIR/" -name "SKILL.md" -type f -not -path "*/.claude/*" 2>/dev/null | grep -E -- "$PATTERN" 2>/dev/null || true
79248
} | sort -u
80249
else
81-
# Show all
82-
find "$SKILLS_DIR/" -name "SKILL.md" -type f 2>/dev/null || true
250+
# Show all (exclude .claude directories)
251+
find "$SKILLS_DIR/" -name "SKILL.md" -type f -not -path "*/.claude/*" 2>/dev/null || true
83252
fi
84253
)
85254

@@ -96,11 +265,21 @@ if [[ ${#results[@]} -eq 0 ]]; then
96265
fi
97266

98267
# Sort and display results
99-
printf "%s\n" "${results[@]}" | sort | while IFS='|' read -r skill_path when_to_use; do
100-
if [[ -n "$when_to_use" ]]; then
101-
echo "Use $skill_path $when_to_use"
268+
printf "%s\n" "${results[@]}" | sort | while IFS='|' read -r skill_path location_tag when_to_use; do
269+
if [[ -n "$location_tag" ]]; then
270+
# Additional directory skill - show with location tag
271+
if [[ -n "$when_to_use" ]]; then
272+
echo "Use $skill_path ($location_tag) $when_to_use"
273+
else
274+
echo "$skill_path ($location_tag)"
275+
fi
102276
else
103-
echo "$skill_path"
277+
# Current dir or global skill - no location tag
278+
if [[ -n "$when_to_use" ]]; then
279+
echo "Use $skill_path $when_to_use"
280+
else
281+
echo "$skill_path"
282+
fi
104283
fi
105284
done
106285

0 commit comments

Comments
 (0)