claude-setup.sh
#!/bin/bash
# Enhanced Claude Code Setup Functions
# Handles safe creation of CLAUDE.md and individual rule files
# Source the merge-rules script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/merge-rules.sh" 2>/dev/null || echo "Warning: merge-rules.sh not found"
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}✓${NC} $1"
}
log_warn() {
echo -e "${YELLOW}⚠${NC} $1"
}
log_error() {
echo -e "${RED}✗${NC} $1"
}
# Create or update main CLAUDE.md file
create_claude_main_file() {
local skills_path="${1:-skills}"
local backup="${2:-true}"
local claude_content="# Agentic Skills - Auto-Loading Rules
This file enables automatic skill loading based on your project context. Skills are loaded conditionally to minimize context overhead while ensuring the right expertise is available when needed.
## Universal Rules (Always Active)
- **ALWAYS** read the relevant \`$skills_path/<skill-name>/SKILL.md\` BEFORE writing any code or creating any file (if the skill exists)
- **NEVER** skip skill loading even for \"simple\" versions of covered tasks
- Skills contain critical patterns, constraints, and best practices for their domain
## File Type Triggers (When Skills Are Installed)
When working with these file types, the corresponding skill loads automatically if installed:
- **Word documents** (\`.docx\`) → Load \`$skills_path/docx/SKILL.md\` (if exists)
- **PDF files** (\`.pdf\`) → Load \`$skills_path/pdf/SKILL.md\` (if exists)
- **Presentations** (\`.pptx\`) → Load \`$skills_path/pptx/SKILL.md\` (if exists)
- **Spreadsheets** (\`.xlsx\`) → Load \`$skills_path/xlsx/SKILL.md\` (if exists)
- **Uploaded files** not yet in context → Load \`$skills_path/file-reading/SKILL.md\` (if exists)
## Language-Specific Rules
Language skills are stored in separate \`.claude/rules/agentic-<lang>-<version>.md\` files to avoid bloating this root file. The setup skill creates these files only for installed skills with their specific versions.
## Versioned Skill Installation
This configuration supports mixed skill and version installation:
- Install Go 1.26: Gets \`.claude/rules/agentic-go-1.26.md\`
- Install Python 3.12: Gets \`.claude/rules/agentic-python-3.12.md\`
- All skills and versions work independently without conflicts
## How It Works
1. **Conditional Loading**: Language skills only activate when you're primarily working with that language
2. **Context Optimization**: Only installed skills load, keeping context lean and focused
3. **Expertise On-Demand**: The right domain knowledge appears exactly when needed
4. **Zero Manual Invocation**: You never need to say \"use the go skill\" - it just works
5. **Additive Installation**: New skills integrate seamlessly with existing ones
## Troubleshooting
If a skill isn't loading when expected:
1. Check that you're working with files of the expected type/extension
2. Ensure the skill file exists at \`$skills_path/<skill-name>/SKILL.md\`
3. Verify the language is the primary focus of your current task
4. Check \`.claude/rules/\` for \`agentic-<lang>.md\` rule files
5. Re-run setup skill if you've installed new skills
## Customization
To modify skill loading behavior:
1. Edit the templates in \`$skills_path/project-rules/templates/\`
2. Re-run the setup skill to regenerate this file
3. The setup skill will detect newly installed skills automatically"
safe_append_to_file "CLAUDE.md" "$claude_content" "md" "$backup"
}
# Create individual Claude rule file for a skill
create_claude_skill_rule() {
local skill_name="$1"
local skill_version="$2"
local skills_path="${3:-skills}"
local backup="${4:-true}"
local rule_file=".claude/rules/agentic-${skill_name}-${skill_version}.md"
local skill_path="$skills_path/$skill_name/$skill_version/SKILL.md"
# Check if skill exists
if [[ ! -f "$skill_path" ]]; then
log_error "Skill file not found: $skill_path"
return 1
fi
# Create rule content based on skill type
local rule_content=""
case "$skill_name" in
"go")
rule_content="# Go ${skill_version} Skill Rules
## Trigger Conditions
This rule activates when working primarily with Go files:
- \`**/*.go\` (Go source files)
- \`**/go.mod\` (Go module definition)
- \`**/go.sum\` (Go dependency checksums)
## Skill Loading
When these conditions are met:
**Read \`$skills_path/$skill_name/$skill_version/SKILL.md\` before writing or editing Go code.**
This skill provides:
- Enterprise Go $skill_version development patterns
- Modern tooling integration (mockery, testcontainers, golangci-lint)
- Security practices and performance optimization
- Production-ready patterns for senior developers
## Context Notes
- Only loads when Go is the primary language focus
- Does not load for HTML templates (\`.tmpl\`) used only for templating
- Provides comprehensive guidance across all Go development phases"
;;
"python")
rule_content="# Python ${skill_version} Skill Rules
## Trigger Conditions
This rule activates when working primarily with Python files:
- \`**/*.py\` (Python source files)
## Skill Loading
When these conditions are met:
**Read \`$skills_path/$skill_name/$skill_version/SKILL.md\` before writing or editing Python code.**
This skill provides:
- Python $skill_version best practices and patterns
- Framework-specific guidance (Django, FastAPI, Flask)
- Testing strategies and performance optimization
- Modern Python tooling and deployment practices"
;;
"typescript")
rule_content="# TypeScript ${skill_version} Skill Rules
## Trigger Conditions
This rule activates when working primarily with TypeScript files:
- \`**/*.ts\` (TypeScript source files)
- \`**/*.tsx\` (TypeScript JSX files)
## Skill Loading
When these conditions are met:
**Read \`$skills_path/$skill_name/$skill_version/SKILL.md\` before writing or editing TypeScript code.**
This skill provides:
- TypeScript $skill_version patterns and best practices
- React and Node.js integration guidance
- Type safety and modern JavaScript features
- Build tooling and deployment strategies"
;;
"rust")
rule_content="# Rust ${skill_version} Skill Rules
## Trigger Conditions
This rule activates when working primarily with Rust files:
- \`**/*.rs\` (Rust source files)
## Skill Loading
When these conditions are met:
**Read \`$skills_path/$skill_name/$skill_version/SKILL.md\` before writing or editing Rust code.**
This skill provides:
- Rust $skill_version systems programming patterns
- Memory safety and performance optimization
- Cargo ecosystem and crate development
- Async programming and error handling"
;;
*)
rule_content="# ${skill_name} ${skill_version} Skill Rules
## Trigger Conditions
This rule activates when working with ${skill_name}-related files.
## Skill Loading
**Read \`$skills_path/$skill_name/$skill_version/SKILL.md\` when working with ${skill_name}.**
This skill provides expertise specific to ${skill_name} version ${skill_version}."
;;
esac
# Ensure directory exists
mkdir -p ".claude/rules"
safe_append_to_file "$rule_file" "$rule_content" "md" "$backup"
}
# Main Claude setup function
setup_claude_rules() {
local detected_skills_output="$1"
local skills_path="${2:-skills}"
local backup="${3:-true}"
log_info "Setting up Claude Code rules"
# Ensure .claude/rules directory exists
mkdir -p ".claude/rules"
# Create or update main CLAUDE.md file
create_claude_main_file "$skills_path" "$backup"
# Process detected skills and create individual rules (VERSION dirs and ALIAS symlinks)
local skills_processed=0
_claude_create_skill_rule() {
local skill="$1"
local version="$2"
local path="$3"
if create_claude_skill_rule "$skill" "$version" "$path" "$backup"; then
log_info "Created Claude rule for $skill $version"
fi
}
process_detected_skill_versions "$detected_skills_output" "$skills_path" _claude_create_skill_rule skills_processed
if [[ $skills_processed -eq 0 ]]; then
log_warn "No Claude skill-specific rules created"
else
log_info "Created $skills_processed Claude skill rules"
fi
}
# Functions are available when script is sourcedcodex-setup.sh
#!/bin/bash
# Enhanced Codex/AGENTS.md Setup Functions
# Handles safe appending to existing AGENTS.md with skill configuration
# Source the merge-rules script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/merge-rules.sh" 2>/dev/null || echo "Warning: merge-rules.sh not found"
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}✓${NC} $1"
}
log_warn() {
echo -e "${YELLOW}⚠${NC} $1"
}
log_error() {
echo -e "${RED}✗${NC} $1"
}
# Generate skill-specific rule content for Codex/AGENTS.md
generate_codex_skill_rules() {
local skill_name="$1"
local skill_version="$2"
local skills_path="$3"
case "$skill_name" in
"go")
echo "
#### Go Development Agent (Version $skill_version)
**Activation**: When working with \`.go\`, \`go.mod\`, or \`go.sum\` files
**Skill Source**: \`$skills_path/go/$skill_version/SKILL.md\`
**Capabilities**:
- Enterprise Go $skill_version development patterns and idioms
- Modern tooling integration: mockery, testcontainers, golangci-lint
- Security practices: input validation, cryptography, authentication
- Performance optimization: profiling, memory management, concurrency
- Production deployment: architecture, monitoring, containerization
**Usage**: Automatically loads when Go is the primary language focus. Provides comprehensive guidance for enterprise-grade Go development."
;;
"python")
echo "
#### Python Development Agent (Version $skill_version)
**Activation**: When working with \`.py\` files
**Skill Source**: \`$skills_path/python/$skill_version/SKILL.md\`
**Capabilities**:
- Python $skill_version best practices and design patterns
- Framework expertise: Django, FastAPI, Flask
- Testing strategies and performance optimization
- Modern tooling and deployment practices
- Package management and virtual environments
**Usage**: Automatically activates for Python-focused development tasks."
;;
"typescript")
echo "
#### TypeScript Development Agent (Version $skill_version)
**Activation**: When working with \`.ts\` or \`.tsx\` files
**Skill Source**: \`$skills_path/typescript/$skill_version/SKILL.md\`
**Capabilities**:
- TypeScript $skill_version patterns and advanced typing
- React and Node.js integration best practices
- Type safety and modern JavaScript features
- Build tooling: webpack, esbuild, vite
- Testing and deployment strategies
**Usage**: Loads automatically for TypeScript and React development."
;;
"rust")
echo "
#### Rust Development Agent (Version $skill_version)
**Activation**: When working with \`.rs\` files
**Skill Source**: \`$skills_path/rust/$skill_version/SKILL.md\`
**Capabilities**:
- Rust $skill_version systems programming patterns
- Memory safety and zero-cost abstractions
- Cargo ecosystem and crate development
- Async programming with tokio
- Performance optimization and error handling
**Usage**: Automatically engages for Rust systems programming tasks."
;;
*)
echo "
#### ${skill_name} Agent (Version $skill_version)
**Activation**: When working with ${skill_name}-related files
**Skill Source**: \`$skills_path/$skill_name/$skill_version/SKILL.md\`
**Capabilities**: ${skill_name} version $skill_version specialized expertise and patterns
**Usage**: Context-aware activation for ${skill_name} development tasks."
;;
esac
}
# Create or append Codex/AGENTS.md configuration
setup_codex_agents() {
local detected_skills_output="$1"
local skills_path="${2:-skills}"
local backup="${3:-true}"
# Check if AGENTS.md already exists
local agents_file_exists=false
if [[ -f "AGENTS.md" ]]; then
agents_file_exists=true
fi
# Build the agentic skills section
local codex_content=""
# Only add header if this is a new section
if [[ "$agents_file_exists" == "true" ]]; then
# Check if agentic skills section already exists
if ! grep -q "# Agentic Skills Auto-Loading" "AGENTS.md" 2>/dev/null; then
codex_content+="
# Agentic Skills Auto-Loading
This section configures automatic skill loading for enhanced development capabilities."
fi
else
# New AGENTS.md file
codex_content="# Development Agents Configuration
## Overview
This file configures intelligent development agents that automatically activate based on project context and file types.
# Agentic Skills Auto-Loading
This section configures automatic skill loading for enhanced development capabilities."
fi
# Add universal rules if not a duplicate
if [[ "$agents_file_exists" == "false" ]] || ! grep -q "## Universal Agent Rules" "AGENTS.md" 2>/dev/null; then
codex_content+="
## Universal Agent Rules
**Core Principle**: Always consult the relevant \`$skills_path/<skill-name>/SKILL.md\` before writing code or creating files.
**Mandatory Behavior**:
- Never skip skill loading, even for simple tasks within a skill's domain
- Skills contain critical patterns, constraints, and best practices
- Load file-reading skill first when working with uploaded files not yet in context
**File Type Triggers**:
- **Documents**: \`.docx\`, \`.pdf\`, \`.pptx\`, \`.xlsx\` → Load corresponding document processing skills
- **Code**: Language-specific triggers activate relevant development skills"
fi
# Add language-specific agents
codex_content+="
## Language-Specific Development Agents"
# Process detected skills and add their rules (VERSION dirs and ALIAS symlinks)
local skills_processed=0
_codex_append_skill_rule() {
local skill="$1"
local version="$2"
local path="$3"
if [[ "$agents_file_exists" == "false" ]] || ! grep -q "$skill Development Agent (Version $version)" "AGENTS.md" 2>/dev/null; then
codex_content+="$(generate_codex_skill_rules "$skill" "$version" "$path")"
fi
}
process_detected_skill_versions "$detected_skills_output" "$skills_path" _codex_append_skill_rule skills_processed
# Add configuration notes
if [[ "$agents_file_exists" == "false" ]] || ! grep -q "## Agent Configuration Notes" "AGENTS.md" 2>/dev/null; then
codex_content+="
## Agent Configuration Notes
### Conditional Activation
- Language agents only activate when that language is the primary focus
- File type agents load based on specific file extensions
- Multiple agents can collaborate when working across languages/technologies
### Context Optimization
- Only relevant agents load to maintain focused assistance
- Agent knowledge is sourced from versioned skill files
- No manual agent invocation required - activation is automatic
### Version Management
- Multiple skill versions can coexist without conflicts
- Agents use specific version knowledge (e.g., Go 1.26 vs Go 1.25)
- Re-run setup after installing new skills to update agent configurations
### Troubleshooting
1. Verify skill files exist at \`$skills_path/<skill-name>/<version>/SKILL.md\`
2. Check file extensions match agent trigger patterns
3. Ensure target language/technology is the primary task focus
4. Confirm AGENTS.md is being read by your development environment"
fi
# Write the content (append to existing or create new)
safe_append_to_file "AGENTS.md" "$codex_content" "md" "$backup"
return $skills_processed
}
# Main Codex setup function
setup_codex_configuration() {
local detected_skills_output="$1"
local skills_path="${2:-skills}"
local backup="${3:-true}"
log_info "Setting up Codex/AGENTS.md configuration"
# Setup the agents configuration
setup_codex_agents "$detected_skills_output" "$skills_path" "$backup"
local skills_processed=$?
if [[ $skills_processed -eq 0 ]]; then
log_warn "No new Codex agent configurations added"
else
log_info "Added/updated $skills_processed Codex agent configurations"
fi
}
# Functions are available when script is sourcedcopilot-setup.sh
#!/bin/bash
# Enhanced GitHub Copilot Setup Functions
# Handles safe creation of .github/copilot-instructions.md with all skills and versions
# Source the merge-rules script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/merge-rules.sh" 2>/dev/null || echo "Warning: merge-rules.sh not found"
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}✓${NC} $1"
}
log_warn() {
echo -e "${YELLOW}⚠${NC} $1"
}
log_error() {
echo -e "${RED}✗${NC} $1"
}
# Generate skill-specific rule content for GitHub Copilot
generate_copilot_skill_rules() {
local skill_name="$1"
local skill_version="$2"
local skills_path="$3"
case "$skill_name" in
"go")
echo "
#### Go Development (Version $skill_version)
**Trigger files**: \`.go\`, \`go.mod\`, \`go.sum\`
**Instruction**: When working primarily with Go files, load and follow \`$skills_path/go/$skill_version/SKILL.md\`
**Reference format**: \"Following $skills_path/go/$skill_version/SKILL.md guidelines for Go development...\"
**Capabilities**:
- Enterprise Go $skill_version development patterns
- Modern tooling integration (mockery, testcontainers, golangci-lint)
- Security practices and performance optimization
- Production-ready patterns for senior developers"
;;
"python")
echo "
#### Python Development (Version $skill_version)
**Trigger files**: \`.py\`
**Instruction**: When working primarily with Python files, load and follow \`$skills_path/python/$skill_version/SKILL.md\`
**Reference format**: \"Following $skills_path/python/$skill_version/SKILL.md guidelines for Python development...\"
**Capabilities**:
- Python $skill_version best practices and patterns
- Framework-specific guidance (Django, FastAPI, Flask)
- Testing strategies and performance optimization
- Modern Python tooling and deployment practices"
;;
"typescript")
echo "
#### TypeScript Development (Version $skill_version)
**Trigger files**: \`.ts\`, \`.tsx\`
**Instruction**: When working primarily with TypeScript files, load and follow \`$skills_path/typescript/$skill_version/SKILL.md\`
**Reference format**: \"Following $skills_path/typescript/$skill_version/SKILL.md guidelines for TypeScript development...\"
**Capabilities**:
- TypeScript $skill_version patterns and best practices
- React and Node.js integration guidance
- Type safety and modern JavaScript features
- Build tooling and deployment strategies"
;;
"rust")
echo "
#### Rust Development (Version $skill_version)
**Trigger files**: \`.rs\`
**Instruction**: When working primarily with Rust files, load and follow \`$skills_path/rust/$skill_version/SKILL.md\`
**Reference format**: \"Following $skills_path/rust/$skill_version/SKILL.md guidelines for Rust development...\"
**Capabilities**:
- Rust $skill_version systems programming patterns
- Memory safety and performance optimization
- Cargo ecosystem and crate development
- Async programming and error handling"
;;
*)
echo "
#### ${skill_name} (Version $skill_version)
**Instruction**: When working with ${skill_name}, load and follow \`$skills_path/$skill_name/$skill_version/SKILL.md\`
**Reference format**: \"Following $skills_path/$skill_name/$skill_version/SKILL.md guidelines for ${skill_name}...\"
**Capabilities**: ${skill_name} version $skill_version expertise and patterns"
;;
esac
}
# Create complete GitHub Copilot instructions file
create_copilot_instructions() {
local detected_skills_output="$1"
local skills_path="${2:-skills}"
local backup="${3:-true}"
local copilot_content="# GitHub Copilot - Agentic Skills Integration
Auto-loading skill configuration for GitHub Copilot.
## Core Instruction
**Before writing any code or creating any file, consult the relevant skill documentation from the \`$skills_path/\` directory.**
## Skill Loading Rules
### Universal Requirements
- Always read the appropriate \`$skills_path/<skill-name>/SKILL.md\` before coding
- Never skip skill loading, even for simple tasks within a skill's domain
- Skills contain critical patterns, constraints, and best practices
### File Type Triggers
Load these skills automatically based on file context (only if installed):
- **Word Documents (\`.docx\`)**: Load and follow \`$skills_path/docx/SKILL.md\` (if exists)
- **PDF Files (\`.pdf\`)**: Load and follow \`$skills_path/pdf/SKILL.md\` (if exists)
- **Presentations (\`.pptx\`)**: Load and follow \`$skills_path/pptx/SKILL.md\` (if exists)
- **Spreadsheets (\`.xlsx\`)**: Load and follow \`$skills_path/xlsx/SKILL.md\` (if exists)
- **Uploaded Files**: If files are uploaded but not in context, load \`$skills_path/file-reading/SKILL.md\` (if exists)
### Language-Specific Skills
Load these skills when the specified language is the primary focus (only if installed):"
# Process detected skills and add their rules (VERSION dirs and ALIAS symlinks)
local skills_processed=0
_copilot_append_skill_rule() {
local skill="$1"
local version="$2"
local path="$3"
copilot_content+="$(generate_copilot_skill_rules "$skill" "$version" "$path")"
}
process_detected_skill_versions "$detected_skills_output" "$skills_path" _copilot_append_skill_rule skills_processed
# Add implementation guidelines
copilot_content+="
## Implementation Guidelines
### Conditional Loading
- Language skills only activate when that language is the primary task focus
- Don't load multiple language skills simultaneously unless truly needed
- File type skills load whenever those file formats are involved
### Context Optimization
- Only load relevant skills to keep suggestions focused and efficient
- Skills are loaded automatically based on project context
- No manual skill invocation should be required
### Skill Reference Format
When a skill is loaded, reference it in responses like:
\`\`\`
Following $skills_path/go/SKILL.md guidelines for Go development...
\`\`\`
## Usage Notes
- Skills are located in the \`$skills_path/\` directory relative to project root
- Each skill has comprehensive documentation in its \`SKILL.md\` file
- Skills may have prerequisites or dependencies listed in their documentation
- Multiple skill versions can coexist without conflicts
## Troubleshooting
If skills aren't being applied correctly:
1. Verify the skill file exists at \`$skills_path/<skill-name>/SKILL.md\`
2. Check that file extensions match the trigger patterns
3. Ensure the language/file type is the primary focus of the current task
4. Confirm GitHub Copilot is reading instructions from \`.github/copilot-instructions.md\`
## Customization
To modify skill loading:
1. Edit the template at \`$skills_path/project-rules/templates/copilot.md\`
2. Re-run the setup skill to update \`.github/copilot-instructions.md\`
3. Changes will apply to all team members using GitHub Copilot"
# Ensure directory exists
mkdir -p ".github"
# Write the complete content
safe_append_to_file ".github/copilot-instructions.md" "$copilot_content" "md" "$backup"
return $skills_processed
}
# Main GitHub Copilot setup function
setup_copilot_instructions() {
local detected_skills_output="$1"
local skills_path="${2:-skills}"
local backup="${3:-true}"
log_info "Setting up GitHub Copilot instructions"
# Create the complete instructions file
create_copilot_instructions "$detected_skills_output" "$skills_path" "$backup"
local skills_processed=$?
if [[ $skills_processed -eq 0 ]]; then
log_warn "No GitHub Copilot skill-specific rules created"
else
log_info "Created GitHub Copilot instructions with $skills_processed skills"
fi
}
# Functions are available when script is sourceddetect-versions.sh
#!/bin/bash
# Version Detection Script for Agentic Skills Setup
# Detects installed skills and resolves version aliases properly
detect_skill_versions() {
local skills_dir="$1"
local skill_name="$2"
local skill_path="$skills_dir/$skill_name"
if [[ ! -d "$skill_path" ]]; then
return 1
fi
# Array to store detected versions
declare -a versions=()
declare -a resolved_versions=()
# Find all version directories (numeric versions only)
for version_dir in "$skill_path"/*; do
if [[ -d "$version_dir" ]]; then
version=$(basename "$version_dir")
# Skip symlinks for now, process them separately
if [[ ! -L "$version_dir" ]] && [[ -f "$version_dir/SKILL.md" ]]; then
versions+=("$version")
fi
fi
done
# Process symlinks (like 'latest')
for link in "$skill_path"/*; do
if [[ -L "$link" ]]; then
link_name=$(basename "$link")
target=$(readlink "$link")
# Resolve target to absolute path if relative
if [[ "$target" != /* ]]; then
target="$skill_path/$target"
fi
target_version=$(basename "$target")
# Check if target version exists and is valid
if [[ " ${versions[*]} " =~ " ${target_version} " ]]; then
echo "ALIAS,$link_name,$target_version"
resolved_versions+=("$target_version")
fi
fi
done
# Output real versions (excluding those that are just aliases)
for version in "${versions[@]}"; do
if [[ ! " ${resolved_versions[*]} " =~ " ${version} " ]]; then
echo "VERSION,$version,$version"
fi
done
}
# Detect all skills and their versions
detect_all_skills() {
local skills_dir="${1:-skills}"
if [[ ! -d "$skills_dir" ]]; then
echo "ERROR: Skills directory '$skills_dir' not found"
return 1
fi
for skill_dir in "$skills_dir"/*; do
if [[ -d "$skill_dir" ]]; then
skill_name=$(basename "$skill_dir")
# Skip template directories and other non-skill directories
if [[ "$skill_name" == "project-rules" ]]; then
continue
fi
echo "SKILL,$skill_name"
detect_skill_versions "$skills_dir" "$skill_name"
fi
done
}
# Main execution
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
detect_all_skills "$@"
fienhanced-setup.sh
#!/bin/bash
# Enhanced Agentic Skills Setup
# Addresses all the gaps identified in the original workflow:
# - Creates missing .cursor/rules/skills.mdc index file
# - Improved version detection for latest vs versioned skills
# - Safer symlink creation with idempotent behavior
# - Merge/append behavior for existing rule files
# - Comprehensive error handling and validation
set -euo pipefail
# Script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Source helper scripts
source "$SCRIPT_DIR/detect-versions.sh" 2>/dev/null || echo "Warning: detect-versions.sh not found"
source "$SCRIPT_DIR/manage-symlinks.sh" 2>/dev/null || echo "Warning: manage-symlinks.sh not found"
source "$SCRIPT_DIR/merge-rules.sh" 2>/dev/null || echo "Warning: merge-rules.sh not found"
source "$SCRIPT_DIR/claude-setup.sh" 2>/dev/null || echo "Warning: claude-setup.sh not found"
source "$SCRIPT_DIR/windsurf-setup.sh" 2>/dev/null || echo "Warning: windsurf-setup.sh not found"
source "$SCRIPT_DIR/copilot-setup.sh" 2>/dev/null || echo "Warning: copilot-setup.sh not found"
source "$SCRIPT_DIR/codex-setup.sh" 2>/dev/null || echo "Warning: codex-setup.sh not found"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m' # No Color
log_header() {
echo -e "\n${BOLD}${BLUE}$1${NC}"
}
log_info() {
echo -e "${GREEN}✓${NC} $1"
}
log_warn() {
echo -e "${YELLOW}⚠${NC} $1"
}
log_error() {
echo -e "${RED}✗${NC} $1"
}
log_debug() {
echo -e "${BLUE}ℹ${NC} $1"
}
# Configuration
SKILLS_SOURCE_DIR="${SKILLS_SOURCE_DIR:-.agents/skills}"
SKILLS_LINK_DIR="${SKILLS_LINK_DIR:-skills}"
FORCE_UPDATE="${FORCE_UPDATE:-false}"
BACKUP_EXISTING="${BACKUP_EXISTING:-true}"
# Step 1: Validate environment and setup skills directory access
setup_skills_access() {
log_header "Setting up skills directory access"
# Check if skills source directory exists
if [[ ! -d "$SKILLS_SOURCE_DIR" ]]; then
log_warn "Skills source directory '$SKILLS_SOURCE_DIR' not found"
log_warn "Falling back to local skills directory"
SKILLS_SOURCE_DIR="skills"
if [[ ! -d "$SKILLS_SOURCE_DIR" ]]; then
log_error "No skills directory found. Please install skills first."
exit 1
fi
fi
# Setup symlink if needed (only if skills source is not the current skills dir)
if [[ "$SKILLS_SOURCE_DIR" != "skills" ]]; then
setup_skills_symlink "$SKILLS_SOURCE_DIR" "$FORCE_UPDATE"
if [[ $? -ne 0 ]]; then
log_error "Failed to setup skills symlink"
exit 1
fi
# Validate symlink
validate_skills_symlink
if [[ $? -ne 0 ]]; then
log_error "Skills symlink validation failed"
exit 1
fi
fi
log_info "Skills directory access configured"
}
# Step 2: Detect installed skills and versions
detect_skills_and_versions() {
log_header "Detecting installed skills and versions"
# Use the enhanced detection script
local detection_output=$(detect_all_skills "$SKILLS_LINK_DIR")
if [[ -z "$detection_output" ]]; then
log_warn "No skills detected"
return 1
fi
# Parse detection output
declare -A skills=()
declare -A versions=()
declare -A aliases=()
while IFS= read -r line; do
if [[ "$line" =~ ^SKILL, ]]; then
skill_name=$(echo "$line" | cut -d',' -f2)
skills["$skill_name"]=1
log_debug "Found skill: $skill_name"
elif [[ "$line" =~ ^VERSION, ]]; then
skill_version=$(echo "$line" | cut -d',' -f2)
# Assuming current skill context
versions["$current_skill"]+="$skill_version "
log_debug "Found version: $skill_version"
elif [[ "$line" =~ ^ALIAS, ]]; then
alias_name=$(echo "$line" | cut -d',' -f2)
target_version=$(echo "$line" | cut -d',' -f3)
aliases["$current_skill,$alias_name"]="$target_version"
log_debug "Found alias: $alias_name -> $target_version"
fi
# Track current skill for version/alias context
if [[ "$line" =~ ^SKILL, ]]; then
current_skill=$(echo "$line" | cut -d',' -f2)
fi
done <<< "$detection_output"
# Export for use in other functions
export DETECTED_SKILLS_OUTPUT="$detection_output"
log_info "Skill detection completed"
}
# Step 3: Detect IDE configurations
detect_ide_configurations() {
log_header "Detecting IDE configurations"
declare -a detected_ides=()
# Check for Cursor (.cursor/ directory or Cursor runtime environment)
if [[ -d ".cursor" ]]; then
detected_ides+=("cursor")
log_info "Detected Cursor IDE (.cursor/ directory)"
elif [[ "${SETUP_CURSOR:-}" == "true" ]] \
|| [[ -n "${CURSOR_TRACE_ID:-}" ]] \
|| [[ -n "${CURSOR_AGENT:-}" ]] \
|| [[ "${TERM_PROGRAM:-}" == "cursor" ]]; then
mkdir -p ".cursor/rules"
detected_ides+=("cursor")
log_info "Detected Cursor IDE (runtime) — created .cursor/rules/"
fi
# Check for Claude Code
if [[ -d ".claude" ]] || [[ -f "CLAUDE.md" ]]; then
detected_ides+=("claude")
log_info "Detected Claude Code (.claude/ directory or CLAUDE.md)"
fi
# Check for Windsurf
if [[ -d ".windsurf" ]]; then
detected_ides+=("windsurf")
log_info "Detected Windsurf IDE (.windsurf/ directory)"
fi
# Check for GitHub Copilot
if [[ -d ".github" ]]; then
detected_ides+=("github")
log_info "Detected GitHub presence (.github/ directory)"
fi
# Check for Codex
if [[ -f "AGENTS.md" ]]; then
detected_ides+=("codex")
log_info "Detected Codex (AGENTS.md file)"
fi
if [[ ${#detected_ides[@]} -eq 0 ]]; then
log_warn "No IDE configurations detected"
log_warn "You may need to manually configure your IDE"
return 1
fi
export DETECTED_IDES=("${detected_ides[@]}")
log_info "IDE detection completed: ${detected_ides[*]}"
}
# Step 4: Create IDE-specific rules
# Create Claude Code rules
create_claude_rules() {
log_header "Creating Claude Code rules"
# Source the function and call it
source "$SCRIPT_DIR/claude-setup.sh"
setup_claude_rules "$DETECTED_SKILLS_OUTPUT" "$SKILLS_LINK_DIR" "$BACKUP_EXISTING"
}
# Create Windsurf rules
create_windsurf_rules() {
log_header "Creating Windsurf rules"
# Source the function and call it
source "$SCRIPT_DIR/windsurf-setup.sh"
setup_windsurf_rules "$DETECTED_SKILLS_OUTPUT" "$SKILLS_LINK_DIR" "$BACKUP_EXISTING"
}
# Create GitHub Copilot rules
create_github_rules() {
log_header "Creating GitHub Copilot instructions"
# Source the function and call it
source "$SCRIPT_DIR/copilot-setup.sh"
setup_copilot_instructions "$DETECTED_SKILLS_OUTPUT" "$SKILLS_LINK_DIR" "$BACKUP_EXISTING"
}
# Create Codex/AGENTS.md configuration
create_codex_rules() {
log_header "Creating Codex/AGENTS.md configuration"
# Source the function and call it
source "$SCRIPT_DIR/codex-setup.sh"
setup_codex_configuration "$DETECTED_SKILLS_OUTPUT" "$SKILLS_LINK_DIR" "$BACKUP_EXISTING"
}
# Create Cursor rules
create_cursor_rules() {
log_header "Creating Cursor rules"
# Ensure .cursor/rules directory exists
mkdir -p ".cursor/rules"
# Create or update skills.mdc index file (with merge safety)
local skills_index_content="---
description: Agentic Skills Index — Master routing file for all skills
alwaysApply: true
---
# Agentic Skills Routing Index
This file serves as the master index for all agentic skills. It references all generated skill-specific rule files in this directory.
## Universal Rules (Always Active)
Load file reading skill for uploads not in context:
- File uploads not yet in context → load skills/file-reading/SKILL.md if available
## Core Principle
**Always read the relevant SKILL.md before writing any code.** Never skip skill loading even for simple tasks.
## Auto-Generated Files
This index references the following auto-generated rule files:
- \`agentic-universal.mdc\` - Base universal rules (always active)
- \`agentic-{skill}-{version}.mdc\` - Individual skill routing rules
Re-run the setup skill after installing new skills to update this configuration."
safe_append_to_file ".cursor/rules/skills.mdc" "$skills_index_content" "mdc" "$BACKUP_EXISTING"
# Create universal rules
local universal_content="---
description: Universal agentic skill routing — always active
alwaysApply: true
---
# Universal Agentic Skills Rules
Before writing any code or creating any file, read the SKILL.md for the relevant skill if available.
Never skip skill loading even for simple tasks.
File uploads not yet in context → load skills/file-reading/SKILL.md if available."
safe_append_to_file ".cursor/rules/agentic-universal.mdc" "$universal_content" "mdc" "$BACKUP_EXISTING"
# Process detected skills and create individual rules (VERSION dirs and ALIAS symlinks)
local skills_processed=0
_cursor_create_skill_rule() {
local skill="$1"
local version="$2"
local skills_path="$3"
if update_cursor_rule "$skill" "$version" "$skills_path"; then
log_info "Created Cursor rule for $skill $version"
fi
}
process_detected_skill_versions "$DETECTED_SKILLS_OUTPUT" "$SKILLS_LINK_DIR" _cursor_create_skill_rule skills_processed
if [[ $skills_processed -eq 0 ]]; then
log_warn "No skill-specific rules created"
else
log_info "Created $skills_processed Cursor skill rules"
fi
}
# Step 5: Validation and summary
validate_setup() {
log_header "Validating setup"
local validation_passed=true
# Check skills directory
if [[ -L "$SKILLS_LINK_DIR" ]]; then
validate_skills_symlink
if [[ $? -ne 0 ]]; then
validation_passed=false
fi
elif [[ -d "$SKILLS_LINK_DIR" ]]; then
log_info "Skills directory exists as regular directory"
else
log_error "Skills directory not accessible"
validation_passed=false
fi
# Check IDE configurations if detected
if [[ " ${DETECTED_IDES[*]} " =~ " cursor " ]]; then
local cursor_files_count=$(find ".cursor/rules" -name "agentic-*.mdc" 2>/dev/null | wc -l)
if [[ $cursor_files_count -gt 0 ]]; then
log_info "Created $cursor_files_count Cursor rule files"
else
log_warn "No Cursor rule files found"
validation_passed=false
fi
fi
if [[ " ${DETECTED_IDES[*]} " =~ " claude " ]]; then
if [[ -f "CLAUDE.md" ]] && [[ -d ".claude/rules" ]]; then
local claude_files_count=$(find ".claude/rules" -name "agentic-*.md" 2>/dev/null | wc -l)
log_info "Created Claude Code configuration with $claude_files_count skill rules"
else
log_warn "Claude Code configuration incomplete"
validation_passed=false
fi
fi
if [[ " ${DETECTED_IDES[*]} " =~ " windsurf " ]]; then
if [[ -f ".windsurf/rules/skills.md" ]]; then
log_info "Created Windsurf skills configuration"
else
log_warn "Windsurf configuration missing"
validation_passed=false
fi
fi
if [[ " ${DETECTED_IDES[*]} " =~ " github " ]]; then
if [[ -f ".github/copilot-instructions.md" ]]; then
log_info "Created GitHub Copilot instructions"
else
log_warn "GitHub Copilot configuration missing"
validation_passed=false
fi
fi
if [[ " ${DETECTED_IDES[*]} " =~ " codex " ]]; then
if [[ -f "AGENTS.md" ]] && grep -q "Agentic Skills Auto-Loading" "AGENTS.md" 2>/dev/null; then
log_info "Updated AGENTS.md with agentic skills configuration"
else
log_warn "Codex/AGENTS.md configuration incomplete"
validation_passed=false
fi
fi
if [[ "$validation_passed" == "true" ]]; then
log_info "Setup validation passed"
return 0
else
log_error "Setup validation failed"
return 1
fi
}
# Print setup summary
print_summary() {
log_header "Setup Summary"
echo -e "\n${BOLD}Skills Configuration:${NC}"
if [[ -L "$SKILLS_LINK_DIR" ]]; then
local target=$(readlink "$SKILLS_LINK_DIR")
echo " Skills directory: $SKILLS_LINK_DIR -> $target"
else
echo " Skills directory: $SKILLS_LINK_DIR (direct)"
fi
echo -e "\n${BOLD}Detected Skills:${NC}"
while IFS= read -r line; do
if [[ "$line" =~ ^SKILL, ]]; then
current_skill=$(echo "$line" | cut -d',' -f2)
echo " - $current_skill"
elif [[ "$line" =~ ^VERSION, ]]; then
version=$(echo "$line" | cut -d',' -f2)
echo " version $version"
elif [[ "$line" =~ ^ALIAS, ]]; then
alias_name=$(echo "$line" | cut -d',' -f2)
target_version=$(echo "$line" | cut -d',' -f3)
echo " $alias_name -> $target_version"
fi
done <<< "$DETECTED_SKILLS_OUTPUT"
echo -e "\n${BOLD}IDE Configurations:${NC}"
for ide in "${DETECTED_IDES[@]}"; do
case "$ide" in
"cursor")
local rule_count=$(find ".cursor/rules" -name "agentic-*.mdc" 2>/dev/null | wc -l)
echo " - Cursor: $rule_count rule files created in .cursor/rules/"
;;
"claude")
local claude_rule_count=$(find ".claude/rules" -name "agentic-*.md" 2>/dev/null | wc -l)
echo " - Claude Code: CLAUDE.md + $claude_rule_count skill rules in .claude/rules/"
;;
"windsurf")
if [[ -f ".windsurf/rules/skills.md" ]]; then
echo " - Windsurf: .windsurf/rules/skills.md created"
else
echo " - Windsurf: configuration failed"
fi
;;
"github")
if [[ -f ".github/copilot-instructions.md" ]]; then
echo " - GitHub Copilot: .github/copilot-instructions.md created"
else
echo " - GitHub Copilot: configuration failed"
fi
;;
"codex")
if grep -q "Agentic Skills Auto-Loading" "AGENTS.md" 2>/dev/null; then
echo " - Codex: AGENTS.md updated with skill configuration"
else
echo " - Codex: AGENTS.md configuration failed"
fi
;;
*)
echo " - $ide: detected but not configured yet"
;;
esac
done
echo -e "\n${BOLD}Next Steps:${NC}"
echo " 1. Commit the generated files to version control"
echo " 2. Skills will auto-load based on file patterns and versions"
echo " 3. Re-run setup after installing new skills"
echo " 4. Multiple skill versions can coexist without conflicts"
}
# Main execution
main() {
echo -e "${BOLD}${GREEN}Enhanced Agentic Skills Setup${NC}"
echo "Addressing workflow gaps with improved robustness..."
# Parse command line options
while [[ $# -gt 0 ]]; do
case $1 in
--force)
FORCE_UPDATE="true"
shift
;;
--no-backup)
BACKUP_EXISTING="false"
shift
;;
--skills-dir)
SKILLS_SOURCE_DIR="$2"
shift 2
;;
--cursor)
SETUP_CURSOR="true"
shift
;;
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --force Force update existing symlinks and rules"
echo " --no-backup Don't create backup files"
echo " --skills-dir DIR Specify skills source directory"
echo " --cursor Configure Cursor even without .cursor/ directory"
echo " --help Show this help"
exit 0
;;
*)
log_error "Unknown option: $1"
exit 1
;;
esac
done
# Execute setup steps
setup_skills_access
detect_skills_and_versions
detect_ide_configurations
# Create IDE-specific rules (don't exit on errors)
for ide in "${DETECTED_IDES[@]}"; do
case "$ide" in
"cursor")
create_cursor_rules || log_warn "Cursor rules creation had issues"
;;
"claude")
create_claude_rules || log_warn "Claude rules creation had issues"
;;
"windsurf")
create_windsurf_rules || log_warn "Windsurf rules creation had issues"
;;
"github")
create_github_rules || log_warn "GitHub rules creation had issues"
;;
"codex")
create_codex_rules || log_warn "Codex rules creation had issues"
;;
*)
log_warn "IDE $ide detected but configuration not implemented yet"
;;
esac
done
validate_setup
if [[ $? -eq 0 ]]; then
print_summary
log_info "Enhanced setup completed successfully!"
else
log_error "Setup completed with validation errors"
exit 1
fi
}
# Run main if called directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fimanage-symlinks.sh
#!/bin/bash
# Safe Symlink Management for Agentic Skills Setup
# Handles symlink creation with safety checks and idempotent behavior
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}✓${NC} $1"
}
log_warn() {
echo -e "${YELLOW}⚠${NC} $1"
}
log_error() {
echo -e "${RED}✗${NC} $1"
}
# Check if a symlink is safe to create or update
check_symlink_safety() {
local link_path="$1"
local target_path="$2"
# If link doesn't exist, it's safe to create
if [[ ! -e "$link_path" && ! -L "$link_path" ]]; then
return 0
fi
# If it exists and is a symlink
if [[ -L "$link_path" ]]; then
current_target=$(readlink "$link_path")
# If pointing to the same target, no action needed
if [[ "$current_target" == "$target_path" ]]; then
log_info "Symlink $link_path already points to $target_path"
return 1 # No action needed
fi
# If pointing to different target, check if we should update
log_warn "Symlink $link_path currently points to: $current_target"
log_warn "Setup wants to point it to: $target_path"
return 2 # Needs update
fi
# If it exists but is not a symlink
if [[ -e "$link_path" ]]; then
if [[ -d "$link_path" ]]; then
log_error "Cannot create symlink: $link_path exists as a directory"
else
log_error "Cannot create symlink: $link_path exists as a regular file"
fi
return 3 # Cannot create
fi
return 0 # Safe to create
}
# Create or update symlink safely
create_safe_symlink() {
local link_path="$1"
local target_path="$2"
local force="${3:-false}"
# Check safety
check_symlink_safety "$link_path" "$target_path"
local safety_result=$?
case $safety_result in
0)
# Safe to create
ln -sf "$target_path" "$link_path"
log_info "Created symlink: $link_path -> $target_path"
return 0
;;
1)
# No action needed
return 0
;;
2)
# Needs update
if [[ "$force" == "true" ]]; then
ln -sf "$target_path" "$link_path"
log_info "Updated symlink: $link_path -> $target_path"
return 0
else
log_warn "Use --force to update existing symlink"
return 1
fi
;;
3)
# Cannot create
log_error "Cannot create symlink at $link_path"
return 1
;;
esac
}
# Setup skills symlink with safety checks
setup_skills_symlink() {
local target_skills_dir="$1"
local force="${2:-false}"
# Default target is .agents/skills if not specified
if [[ -z "$target_skills_dir" ]]; then
target_skills_dir=".agents/skills"
fi
# Check if target directory exists
if [[ ! -d "$target_skills_dir" ]]; then
log_error "Target skills directory '$target_skills_dir' does not exist"
log_error "Cannot create symlink to non-existent directory"
return 1
fi
# Check if skills directory already exists
if [[ -L "skills" ]]; then
current_target=$(readlink "skills")
# If already pointing to the right place
if [[ "$current_target" == "$target_skills_dir" ]]; then
log_info "Skills symlink already correctly configured"
return 0
fi
# If pointing somewhere else
log_warn "Skills symlink currently points to: $current_target"
log_warn "Setup wants to point it to: $target_skills_dir"
if [[ "$force" == "true" ]]; then
ln -sf "$target_skills_dir" "skills"
log_info "Updated skills symlink to point to $target_skills_dir"
return 0
else
log_warn "Use --force to update existing skills symlink"
return 1
fi
elif [[ -d "skills" ]] && [[ ! -L "skills" ]]; then
# skills exists as a regular directory
log_error "Cannot create symlink: 'skills' exists as a directory"
log_error "You may need to:"
log_error " 1. Backup existing skills/ directory"
log_error " 2. Remove or rename it"
log_error " 3. Re-run setup to create the symlink"
return 1
elif [[ -f "skills" ]]; then
# skills exists as a file
log_error "Cannot create symlink: 'skills' exists as a file"
return 1
else
# Safe to create new symlink
ln -sf "$target_skills_dir" "skills"
log_info "Created skills symlink: skills -> $target_skills_dir"
return 0
fi
}
# Validate symlink integrity
validate_skills_symlink() {
if [[ ! -L "skills" ]]; then
log_error "Skills symlink does not exist"
return 1
fi
local target=$(readlink "skills")
if [[ ! -d "$target" ]]; then
log_error "Skills symlink points to non-existent directory: $target"
return 1
fi
# Check if we can read from the target
if [[ ! -r "$target" ]]; then
log_error "Cannot read from skills target directory: $target"
return 1
fi
log_info "Skills symlink is valid and points to: $target"
return 0
}
# Main function for command line usage
main() {
case "$1" in
"setup")
setup_skills_symlink "$2" "$3"
;;
"validate")
validate_skills_symlink
;;
"create")
if [[ $# -lt 3 ]]; then
echo "Usage: $0 create <link_path> <target_path> [force]"
exit 1
fi
create_safe_symlink "$2" "$3" "$4"
;;
*)
echo "Usage: $0 {setup|validate|create} [options]"
echo ""
echo "Commands:"
echo " setup [target_dir] [force] Setup skills symlink"
echo " validate Validate existing symlink"
echo " create <link> <target> [force] Create/update symlink"
echo ""
echo "Options:"
echo " force Update existing symlinks"
exit 1
;;
esac
}
# Run main if called directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fimerge-rules.sh
#!/bin/bash
# Safe Rule File Merging for Agentic Skills Setup
# Handles appending/merging rule content without duplicating blocks
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}✓${NC} $1"
}
log_warn() {
echo -e "${YELLOW}⚠${NC} $1"
}
log_error() {
echo -e "${RED}✗${NC} $1"
}
log_debug() {
echo -e "${BLUE}ℹ${NC} $1"
}
# Check if a rule block already exists in a file
rule_block_exists() {
local file_path="$1"
local rule_identifier="$2"
if [[ ! -f "$file_path" ]]; then
return 1 # File doesn't exist, so rule doesn't exist
fi
# Look for the rule identifier in the file
if grep -q "$rule_identifier" "$file_path"; then
return 0 # Rule exists
fi
return 1 # Rule doesn't exist
}
# Extract rule identifier from content
extract_rule_identifier() {
local content="$1"
local file_type="$2"
case "$file_type" in
"mdc"|"md")
# For markdown files, look for description in YAML frontmatter
echo "$content" | grep -E "^description:" | head -1
;;
"claude")
# For Claude files, look for skill name/version patterns
echo "$content" | grep -E "skills/[^/]+(/[^/]+)?/SKILL\.md" | head -1
;;
*)
# Generic approach: use first significant line
echo "$content" | grep -v "^#" | grep -v "^$" | head -1
;;
esac
}
# Safely append content to file
safe_append_to_file() {
local file_path="$1"
local new_content="$2"
local file_type="$3"
local backup="${4:-true}"
# Create backup if requested
if [[ "$backup" == "true" && -f "$file_path" ]]; then
cp "$file_path" "${file_path}.backup.$(date +%s)"
log_debug "Created backup: ${file_path}.backup.$(date +%s)"
fi
# Extract rule identifier to check for duplicates
local rule_id=$(extract_rule_identifier "$new_content" "$file_type")
if [[ -f "$file_path" ]] && [[ -n "$rule_id" ]]; then
if rule_block_exists "$file_path" "$rule_id"; then
log_warn "Rule already exists in $file_path, skipping"
return 1
fi
fi
# If file exists, append with separator
if [[ -f "$file_path" ]]; then
echo "" >> "$file_path"
echo "# --- Added by agentic setup ---" >> "$file_path"
echo "$new_content" >> "$file_path"
log_info "Appended rule to existing file: $file_path"
else
# Create new file
mkdir -p "$(dirname "$file_path")"
echo "$new_content" > "$file_path"
log_info "Created new rule file: $file_path"
fi
return 0
}
# Merge multiple rule files safely
merge_rule_files() {
local target_file="$1"
local file_type="$2"
shift 2
local source_files=("$@")
local temp_file=$(mktemp)
local any_changes=false
# If target exists, start with its content
if [[ -f "$target_file" ]]; then
cp "$target_file" "$temp_file"
fi
# Process each source file
for source_file in "${source_files[@]}"; do
if [[ -f "$source_file" ]]; then
local content=$(cat "$source_file")
local rule_id=$(extract_rule_identifier "$content" "$file_type")
# Check if this rule already exists
if [[ -n "$rule_id" ]] && rule_block_exists "$temp_file" "$rule_id"; then
log_warn "Skipping duplicate rule from $source_file"
continue
fi
# Append with separator
echo "" >> "$temp_file"
echo "# --- Merged from $source_file ---" >> "$temp_file"
cat "$source_file" >> "$temp_file"
any_changes=true
log_info "Merged content from: $source_file"
fi
done
# Only update target if there were changes
if [[ "$any_changes" == "true" ]]; then
mkdir -p "$(dirname "$target_file")"
cp "$temp_file" "$target_file"
log_info "Updated merged file: $target_file"
fi
rm -f "$temp_file"
}
# Process VERSION and ALIAS lines from detect_all_skills output.
# Invokes callback(skill_name, version, skills_path) for each unique pair with SKILL.md present.
# Optional 4th arg: name of variable to receive invocation count.
process_detected_skill_versions() {
local detection_output="$1"
local skills_path="${2:-skills}"
local callback="$3"
local current_skill=""
local count=0
declare -A processed=()
while IFS= read -r line; do
[[ -z "$line" ]] && continue
if [[ "$line" =~ ^SKILL, ]]; then
current_skill=$(echo "$line" | cut -d',' -f2)
continue
fi
local version=""
if [[ "$line" =~ ^VERSION, ]]; then
version=$(echo "$line" | cut -d',' -f2)
elif [[ "$line" =~ ^ALIAS, ]]; then
version=$(echo "$line" | cut -d',' -f3)
else
continue
fi
[[ -z "$current_skill" || -z "$version" ]] && continue
local key="${current_skill}::${version}"
if [[ -n "${processed[$key]:-}" ]]; then
continue
fi
if [[ ! -f "$skills_path/$current_skill/$version/SKILL.md" ]]; then
continue
fi
processed[$key]=1
"$callback" "$current_skill" "$version" "$skills_path"
count=$((count + 1))
done <<< "$detection_output"
if [[ $# -ge 4 && -n "${4:-}" ]]; then
printf -v "$4" '%s' "$count"
fi
return 0
}
# Create or update Cursor rule file
update_cursor_rule() {
local skill_name="$1"
local skill_version="$2"
local skills_path="${3:-skills}"
local rule_file=".cursor/rules/agentic-${skill_name}-${skill_version}.mdc"
local skill_path="$skills_path/$skill_name/$skill_version/SKILL.md"
# Check if skill exists
if [[ ! -f "$skill_path" ]]; then
log_error "Skill file not found: $skill_path"
return 1
fi
# Create rule content based on skill type
local rule_content=""
case "$skill_name" in
"go")
rule_content="---
description: Go skill routing (version $skill_version)
globs: [\"**/*.go\", \"**/go.mod\", \"**/go.sum\"]
alwaysApply: false
---
Read $skills_path/$skill_name/$skill_version/SKILL.md before writing or editing Go code."
;;
"python")
rule_content="---
description: Python skill routing (version $skill_version)
globs: [\"**/*.py\"]
alwaysApply: false
---
Read $skills_path/$skill_name/$skill_version/SKILL.md before writing or editing Python code."
;;
"typescript")
rule_content="---
description: TypeScript skill routing (version $skill_version)
globs: [\"**/*.ts\", \"**/*.tsx\"]
alwaysApply: false
---
Read $skills_path/$skill_name/$skill_version/SKILL.md before writing or editing TypeScript code."
;;
"rust")
rule_content="---
description: Rust skill routing (version $skill_version)
globs: [\"**/*.rs\"]
alwaysApply: false
---
Read $skills_path/$skill_name/$skill_version/SKILL.md before writing or editing Rust code."
;;
*)
rule_content="---
description: $skill_name skill routing (version $skill_version)
globs: [\"**/*\"]
alwaysApply: false
---
Read $skills_path/$skill_name/$skill_version/SKILL.md when working with $skill_name."
;;
esac
safe_append_to_file "$rule_file" "$rule_content" "mdc" true
}
# Main function for command line usage
main() {
case "$1" in
"append")
if [[ $# -lt 4 ]]; then
echo "Usage: $0 append <file_path> <content> <file_type> [backup]"
exit 1
fi
safe_append_to_file "$2" "$3" "$4" "${5:-true}"
;;
"merge")
if [[ $# -lt 3 ]]; then
echo "Usage: $0 merge <target_file> <file_type> <source_file1> [source_file2] ..."
exit 1
fi
target="$2"
file_type="$3"
shift 3
merge_rule_files "$target" "$file_type" "$@"
;;
"cursor-rule")
if [[ $# -lt 3 ]]; then
echo "Usage: $0 cursor-rule <skill_name> <skill_version> [skills_path]"
exit 1
fi
update_cursor_rule "$2" "$3" "$4"
;;
"check")
if [[ $# -lt 3 ]]; then
echo "Usage: $0 check <file_path> <rule_identifier>"
exit 1
fi
if rule_block_exists "$2" "$3"; then
echo "Rule exists in file"
exit 0
else
echo "Rule does not exist in file"
exit 1
fi
;;
*)
echo "Usage: $0 {append|merge|cursor-rule|check} [options]"
echo ""
echo "Commands:"
echo " append <file> <content> <type> [backup] Safely append content to file"
echo " merge <target> <type> <source1> ... Merge multiple files"
echo " cursor-rule <skill> <version> [path] Create/update Cursor rule"
echo " check <file> <identifier> Check if rule exists"
echo ""
echo "File types: mdc, md, claude"
exit 1
;;
esac
}
# Run main if called directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fiSKILL.md
---
name: setup
description: >
Use when the user wants to configure agentic skills for their IDE, setup skill routing,
generate IDE rule files, or configure project-specific skill automation. Also use when
setting up a new project to work with agentic skills. Do NOT use for general project
setup unrelated to skill routing or IDE configuration.
---
# Setup
## When to Invoke
- User asks to setup agentic skills for their project
- User wants to configure skill routing for their IDE
- User mentions generating IDE rule files
- User asks how to configure their IDE to work with skills
- User wants to enable automatic skill loading
- NOT: General project initialization unrelated to skills
- NOT: Installing skills (use npx skills add instead)
## Prerequisites
None. This is typically the first skill to run in a new project.
## Reference
This skill sets up automatic skill routing for your IDE by detecting which IDEs are configured in your project and generating the appropriate rule files.
### Supported IDEs
The skill detects and configures these IDEs:
| IDE | Detection | Generated File |
|---|---|---|
| Claude Code | `.claude/` directory or existing `CLAUDE.md` | `CLAUDE.md` at project root |
| Cursor | `.cursor/` directory, Cursor runtime env, or `--cursor` flag | `.cursor/rules/skills.mdc` |
| Windsurf | `.windsurf/` directory | `.windsurf/rules/skills.md` |
| GitHub Copilot | `.github/` directory | `.github/copilot-instructions.md` |
| Codex | `AGENTS.md` exists | `AGENTS.md` (append to existing) |
### Setup Process
1. **Scan for IDE indicators**: Check project root for IDE-specific directories and files
2. **Copy templates**: For each detected IDE, copy the appropriate template from `skills/project-rules/templates/`
3. **Render rules**: Generate IDE-specific rule files with proper skill routing logic
4. **Handle existing files**: If target files exist, append skill routing blocks rather than overwrite
5. **Report results**: Print summary of what was created or updated
### Routing Logic Generated
The setup creates rules that automatically load the right skills based on context:
**File Type Triggers:**
- `.docx` files → load `skills/docx/SKILL.md`
- `.pdf` files → load `skills/pdf/SKILL.md`
- `.pptx` files → load `skills/pptx/SKILL.md`
- `.xlsx` files → load `skills/xlsx/SKILL.md`
- Uploaded files not in context → load `skills/file-reading/SKILL.md` first
**Language Triggers (only when primary language):**
- `.go`, `go.mod`, `go.sum` → load `skills/go/SKILL.md`
- `.py` files → load `skills/python/SKILL.md`
- `.ts`, `.tsx` files → load `skills/typescript/SKILL.md`
- `.rs` files → load `skills/rust/SKILL.md`
**Universal Rules:**
- Always read the relevant `SKILL.md` BEFORE writing any code
- Never skip skill loading even for "simple" tasks
### Implementation Steps
When this skill is invoked, it follows an enhanced workflow that addresses robustness and safety:
1. **Setup skills directory access (Enhanced):**
```
- Detect skills source directory (.agents/skills, skills/, or custom path)
- Create safe symlink if needed (with conflict detection and validation)
- Verify symlink integrity and permissions
- Support idempotent reruns without breaking existing setups
```
2. **Scan for installed skills with improved version detection:**
```
- Check for versioned skills: skills/<name>/<version>/SKILL.md (e.g., skills/go/1.26/SKILL.md)
- Check for direct skills: skills/<name>/SKILL.md (e.g., skills/setup/SKILL.md)
- Follow symlinks intelligently: skills/<name>/latest -> <version>/
- Detect whether "latest" is alias or separate version
- Extract version metadata from SKILL.md frontmatter (version, stability, features)
- Generate rules only for actual versions, not aliases
- Support any skill type: languages, frameworks, auth, infrastructure, databases
```
3. **Check project root for IDE indicators:**
```
- Look for .claude/ directory or CLAUDE.md file
- Look for .cursor/ directory (or Cursor runtime / `--cursor` flag to create it)
- Look for .windsurf/ directory
- Look for .github/ directory
- Look for existing AGENTS.md file
```
4. **For each detected IDE (with merge safety):**
- Copy the base template from `skills/project-rules/templates/`
- Create skills index file (e.g., `.cursor/rules/skills.mdc`)
- Generate universal rules file
- Create individual skill rule files with version
- **Enhanced safety:** Check for existing content before writing
- **Enhanced safety:** Append/merge rather than overwrite existing files
- **Enhanced safety:** Create backup files before modifications
- **Enhanced safety:** Validate rule uniqueness to prevent duplicates
5. **Target file paths with index files:**
- Cursor:
- `.cursor/rules/skills.mdc` (master index - NEW)
- `.cursor/rules/agentic-universal.mdc` (always active rules)
- `.cursor/rules/agentic-<skill>-<version>.mdc` per versioned skill
- Claude Code: `CLAUDE.md` (project root) + `.claude/rules/agentic-<skill>-<version>.md` per versioned skill
- Windsurf: `.windsurf/rules/skills.md` (single file with all rules and versions)
- GitHub Copilot: `.github/copilot-instructions.md` (single file with all rules and versions)
- Codex: `AGENTS.md` (project root, append with version info)
6. **Enhanced validation and safety:**
```
- Validate symlink integrity
- Check file permissions and accessibility
- Verify rule file syntax
- Test for duplicate content
- Validate skill file existence
- Create backups before modifications
```
7. **Print comprehensive summary:**
```
✓ Skills directory access configured: skills -> .agents/skills
✓ Detected skills: go (latest -> 1.26), python (3.12), react (18.2)
✓ Version resolution: latest alias resolved to actual versions
✓ Detected Cursor (.cursor/ directory)
✓ Created .cursor/rules/skills.mdc (master index)
✓ Created .cursor/rules/agentic-universal.mdc (universal rules)
✓ Created .cursor/rules/agentic-go-1.26.mdc (version-specific rules)
✓ Validation: All symlinks and rule files verified
Next steps:
- Commit the generated files to version control
- Skills auto-load based on file patterns and versions
- Multiple skill versions can coexist without conflicts
- Re-run setup safely after installing new skills
- Enhanced merge behavior prevents duplicate rules
```
### Template Rendering
Templates in `skills/project-rules/templates/` contain the IDE-specific syntax for:
- Conditional loading based on file patterns
- Skill references pointing to correct SKILL.md files
- IDE-specific configuration (globs, alwaysApply, etc.)
The setup skill copies these templates exactly - no variable substitution is needed as templates contain the final rule syntax.
## Enhanced Tools and Scripts
The setup skill includes several utility scripts for improved robustness:
### Version Detection (`detect-versions.sh`)
- Intelligently detects installed skills and their versions
- Properly handles symlinks and aliases (e.g., `latest -> 1.26`)
- Differentiates between real versions and aliases
- Prevents duplicate rule generation for aliased versions
- Outputs structured data for processing
### Symlink Management (`manage-symlinks.sh`)
- Safe symlink creation with conflict detection
- Idempotent behavior - safe to run multiple times
- Validates existing symlinks and their targets
- Provides clear warnings for manual intervention needed
- Supports force updates when needed
### Rule Merging (`merge-rules.sh`)
- Safe append/merge behavior for existing files
- Duplicate detection prevents rule conflicts
- Creates backup files before modifications
- Supports multiple IDE rule formats
- Maintains rule integrity across reruns
- `process_detected_skill_versions` — creates IDE rules from both `VERSION` (real version dirs) and `ALIAS` (symlinks like `latest -> 1.26`)
### Enhanced Setup (`enhanced-setup.sh`)
- Main orchestration script incorporating all improvements
- Comprehensive validation and error handling
- Clear progress reporting and summary
- Command-line options for customization
- Addresses all identified workflow gaps
## Constraints
- Never overwrite existing IDE rule files completely - always append/merge
- Only generate rules for IDEs that are actually detected in the project
- Always create necessary directories before writing files
- Create backup files before modifying existing configurations
- Print clear summary of actions taken with validation results
- If no IDEs are detected, inform the user and suggest manual setup options
- Never modify the skill files themselves, only generate IDE rule files
- Ensure generated rules use conditional loading (not always-on) for language skills
- Validate symlink integrity and provide clear error messages for conflicts
- Support safe reruns without breaking existing configurationswindsurf-setup.sh
#!/bin/bash
# Enhanced Windsurf Setup Functions
# Handles safe creation of .windsurf/rules/skills.md with all skills and versions
# Source the merge-rules script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/merge-rules.sh" 2>/dev/null || echo "Warning: merge-rules.sh not found"
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}✓${NC} $1"
}
log_warn() {
echo -e "${YELLOW}⚠${NC} $1"
}
log_error() {
echo -e "${RED}✗${NC} $1"
}
# Generate skill-specific rule content for Windsurf
generate_windsurf_skill_rules() {
local skill_name="$1"
local skill_version="$2"
local skills_path="$3"
case "$skill_name" in
"go")
echo "
### Go Development (Version $skill_version)
**Triggers on**: \`*.go\`, \`go.mod\`, \`go.sum\`
```
When editing Go files → load $skills_path/go/$skill_version/SKILL.md
```
- Enterprise Go $skill_version development patterns
- Modern tooling integration (mockery, testcontainers, golangci-lint)
- Security practices and performance optimization
- Production-ready patterns for senior developers"
;;
"python")
echo "
### Python Development (Version $skill_version)
**Triggers on**: \`*.py\`
```
When editing Python files → load $skills_path/python/$skill_version/SKILL.md
```
- Python $skill_version best practices and patterns
- Framework-specific guidance (Django, FastAPI, Flask)
- Testing strategies and performance optimization
- Modern Python tooling and deployment practices"
;;
"typescript")
echo "
### TypeScript Development (Version $skill_version)
**Triggers on**: \`*.ts\`, \`*.tsx\`
```
When editing TypeScript files → load $skills_path/typescript/$skill_version/SKILL.md
```
- TypeScript $skill_version patterns and best practices
- React and Node.js integration guidance
- Type safety and modern JavaScript features
- Build tooling and deployment strategies"
;;
"rust")
echo "
### Rust Development (Version $skill_version)
**Triggers on**: \`*.rs\`
```
When editing Rust files → load $skills_path/rust/$skill_version/SKILL.md
```
- Rust $skill_version systems programming patterns
- Memory safety and performance optimization
- Cargo ecosystem and crate development
- Async programming and error handling"
;;
*)
echo "
### ${skill_name} (Version $skill_version)
**Triggers on**: Files related to ${skill_name}
```
When working with ${skill_name} → load $skills_path/$skill_name/$skill_version/SKILL.md
```
- ${skill_name} version $skill_version expertise and patterns"
;;
esac
}
# Create complete Windsurf rules file
create_windsurf_rules() {
local detected_skills_output="$1"
local skills_path="${2:-skills}"
local backup="${3:-true}"
local windsurf_content="# Agentic Skills - Windsurf Rules
Auto-loading skill configuration for Windsurf IDE.
## Universal Rules
**Always apply these rules:**
- Read the relevant \`$skills_path/<skill-name>/SKILL.md\` BEFORE writing any code or creating any file
- Never skip skill loading even for \"simple\" versions of covered tasks
- When files are uploaded but not yet in context → load \`$skills_path/file-reading/SKILL.md\` FIRST
## Conditional Skill Loading
### Language Skills (Load only when working primarily with these file types)"
# Process detected skills and add their rules (VERSION dirs and ALIAS symlinks)
local skills_processed=0
_windsurf_append_skill_rule() {
local skill="$1"
local version="$2"
local path="$3"
windsurf_content+="$(generate_windsurf_skill_rules "$skill" "$version" "$path")"
}
process_detected_skill_versions "$detected_skills_output" "$skills_path" _windsurf_append_skill_rule skills_processed
# Add document processing section
windsurf_content+="
### Document Processing Skills (Load when working with these file types)
**Microsoft Word** - If \`$skills_path/docx/SKILL.md\` exists, triggers on: \`*.docx\`
\`\`\`
When working with Word documents → load $skills_path/docx/SKILL.md
\`\`\`
**PDF Documents** - If \`$skills_path/pdf/SKILL.md\` exists, triggers on: \`*.pdf\`
\`\`\`
When working with PDF files → load $skills_path/pdf/SKILL.md
\`\`\`
**PowerPoint Presentations** - If \`$skills_path/pptx/SKILL.md\` exists, triggers on: \`*.pptx\`
\`\`\`
When working with presentations → load $skills_path/pptx/SKILL.md
\`\`\`
**Excel Spreadsheets** - If \`$skills_path/xlsx/SKILL.md\` exists, triggers on: \`*.xlsx\`
\`\`\`
When working with spreadsheets → load $skills_path/xlsx/SKILL.md
\`\`\`
## Configuration Notes
- **Conditional Loading**: Language skills only activate when those file types are the primary focus
- **Context Optimization**: Only installed skills load to keep context lean
- **Zero Manual Invocation**: Skills auto-load based on project context
- **Single Source of Truth**: Skill content lives in \`$skills_path/\` directory, not in these rules
- **Version Support**: Supports multiple skill versions without conflicts
- **Additive Installation**: New skills integrate without affecting existing ones
## Troubleshooting
If skills aren't loading as expected:
1. Verify you're working with files matching the trigger patterns
2. Check that skill files exist at \`$skills_path/<skill-name>/SKILL.md\`
3. Ensure the language is the primary focus of your current task
4. Confirm Windsurf is reading from \`.windsurf/rules/skills.md\`
## Customization
To modify loading behavior:
1. Edit \`$skills_path/project-rules/templates/windsurf.md\`
2. Re-run the setup skill to regenerate \`.windsurf/rules/skills.md\`
3. Commit changes to share with your team"
# Ensure directory exists
mkdir -p ".windsurf/rules"
# Write the complete content
safe_append_to_file ".windsurf/rules/skills.md" "$windsurf_content" "md" "$backup"
return $skills_processed
}
# Main Windsurf setup function
setup_windsurf_rules() {
local detected_skills_output="$1"
local skills_path="${2:-skills}"
local backup="${3:-true}"
log_info "Setting up Windsurf rules"
# Create the complete rules file
create_windsurf_rules "$detected_skills_output" "$skills_path" "$backup"
local skills_processed=$?
if [[ $skills_processed -eq 0 ]]; then
log_warn "No Windsurf skill-specific rules created"
else
log_info "Created Windsurf rules file with $skills_processed skills"
fi
}
# Functions are available when script is sourced