Claude-skill-registry Context Compilation with cclsp
LSP-powered context extraction using cclsp MCP tools, Solargraph, and Sorbet for type-safe code generation. Trigger keywords: cclsp, LSP, context compilation, interface extraction, vocabulary, Guardian, Sorbet, type checking, find_definition, get_diagnostics, Solargraph, type safety
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/context-compilation" ~/.claude/skills/majiayu000-claude-skill-registry-context-compilation-with-cclsp && rm -rf "$T"
skills/data/context-compilation/SKILL.mdContext Compilation with cclsp + Sorbet
This skill provides patterns for LSP-powered context extraction and type-safe code generation using cclsp MCP tools, Solargraph, and Sorbet.
1. Tool Stack Overview
1.1 cclsp MCP Bridge
The cclsp MCP server provides Claude Code access to Language Server Protocol functionality:
| Tool | Purpose | Use Case |
|---|---|---|
| Find symbol definitions | Locate where methods/classes are defined |
| Find all references | Discover symbol usage patterns |
| Get errors/warnings | Validate code before execution |
| Rename symbols | Safe refactoring |
| Rename at position | Precise symbol renaming |
| Restart LSP | Recover from errors |
1.2 Solargraph (Ruby LSP)
Solargraph provides Ruby-specific language intelligence:
- Method completion: Know what methods exist on objects
- Go to definition: Jump to class/method definitions
- Hover documentation: Inline YARD documentation
- Diagnostics: Syntax errors, undefined methods
- Workspace symbols: Search all symbols in project
Configuration (
.solargraph.yml):
include: - "**/*.rb" exclude: - spec/**/* - test/**/* - vendor/**/* reporters: - rubocop - require_not_found plugins: [] require_paths: [] domains: [] max_files: 5000
1.3 Sorbet (Type Checker)
Sorbet provides static type checking for Ruby:
- Static analysis: Catch type errors at compile time
- Runtime checking: Verify types during execution
- Gradual typing: Adopt incrementally with
sigils# typed: - IDE integration: LSP support via Sorbet Language Server
Type Sigils:
# typed: ignore # Skip this file # typed: false # Only syntax errors # typed: true # Check types # typed: strict # Require signatures # typed: strong # No T.untyped
Common Commands:
# Type check entire project bundle exec srb tc # Type check specific file bundle exec srb tc app/models/user.rb # Initialize Sorbet bundle exec srb init # Generate RBI files bundle exec tapioca init bundle exec tapioca dsl bundle exec tapioca gems
1.4 Supporting Tools
| Tool | Purpose | Integration |
|---|---|---|
| parser gem | AST analysis | Deep code structure analysis |
| ripper | Built-in Ruby parser | Fallback parsing, always available |
| YARD | Documentation | Solargraph uses YARD for docs |
| Tapioca | RBI generation | Generate type signatures for gems |
2. cclsp Tool Reference
2.1 find_definition
Find where a symbol is defined:
# Usage Pattern mcp__cclsp__find_definition( file_path: "app/services/payment_service.rb", symbol_name: "PaymentGateway", symbol_kind: "class" # optional: class, method, module ) # Returns { "definitions": [ { "file": "app/gateways/payment_gateway.rb", "line": 5, "column": 1, "symbol": "PaymentGateway" } ] }
Common symbol_kind values:
- Class definitionsclass
- Module definitionsmodule
- Instance methodsmethod
- Module/class methodsfunction
- Local/instance variablesvariable
- Constantsconstant
2.2 find_references
Find all usages of a symbol:
# Usage Pattern mcp__cclsp__find_references( file_path: "app/models/user.rb", symbol_name: "authenticate", include_declaration: true ) # Returns { "references": [ { "file": "app/models/user.rb", "line": 45, "column": 3, "context": "def authenticate(password)" }, { "file": "app/controllers/sessions_controller.rb", "line": 12, "column": 8, "context": "if user.authenticate(params[:password])" } ] }
2.3 get_diagnostics
Get errors and warnings for a file:
# Usage Pattern mcp__cclsp__get_diagnostics( file_path: "app/services/order_service.rb" ) # Returns { "diagnostics": [ { "severity": 1, # 1=Error, 2=Warning, 3=Info, 4=Hint "message": "Undefined method `foo' for OrderService", "range": { "start": {"line": 25, "character": 4}, "end": {"line": 25, "character": 7} }, "source": "solargraph" } ] }
Severity Levels:
- Error (must fix)1
- Warning (should investigate)2
- Information (good to know)3
- Hint (suggestions)4
2.4 rename_symbol
Rename a symbol across the codebase:
# Preview changes (dry_run) mcp__cclsp__rename_symbol( file_path: "app/models/user.rb", symbol_name: "full_name", new_name: "display_name", dry_run: true ) # Apply changes mcp__cclsp__rename_symbol( file_path: "app/models/user.rb", symbol_name: "full_name", new_name: "display_name" )
3. Interface Extraction Patterns
3.1 Extract Class Interface
Extract public interface from a class:
# Pattern: Interface Extraction def extract_interface(class_name, file_path) # 1. Find class definition definition = mcp__cclsp__find_definition( file_path: file_path, symbol_name: class_name, symbol_kind: "class" ) # 2. Read the class file class_content = Read(definition.file) # 3. Extract public methods public_methods = class_content.scan(/^\s*def\s+(\w+)/).flatten # 4. For each method, find references to understand usage method_interfaces = public_methods.map do |method| refs = mcp__cclsp__find_references( file_path: definition.file, symbol_name: method ) { name: method, defined_at: "#{definition.file}:#{method_line}", usage_count: refs.count, callers: refs.map { |r| r.file }.uniq } end method_interfaces end
3.2 Extract Dependency Graph
Build a dependency graph for a class:
# Pattern: Dependency Graph def build_dependency_graph(entry_class, entry_file) graph = { nodes: [], edges: [] } visited = Set.new queue = [[entry_class, entry_file]] while queue.any? class_name, file_path = queue.shift next if visited.include?(class_name) visited.add(class_name) # Add node graph[:nodes] << { name: class_name, file: file_path } # Find references to other classes class_content = Read(file_path) constants = class_content.scan(/([A-Z][A-Za-z0-9]+)/).flatten.uniq constants.each do |const| definition = mcp__cclsp__find_definition( file_path: file_path, symbol_name: const, symbol_kind: "class" ) if definition && !visited.include?(const) graph[:edges] << { from: class_name, to: const } queue << [const, definition.file] end end end graph end
3.3 Per-Task Interface Extraction
Extract interfaces relevant to a specific task:
# Pattern: Task-Specific Interface Extraction def compile_task_context(task) context = { interfaces: [], vocabulary: [], cclsp_enhanced: true } # 1. Identify files mentioned in task target_files = task[:files] || [] # 2. For each file, extract interfaces target_files.each do |file| # Get diagnostics first (validates file exists) diagnostics = mcp__cclsp__get_diagnostics(file_path: file) # Find all symbols in file symbols = extract_file_symbols(file) # For each symbol, get definition and references symbols.each do |symbol| definition = mcp__cclsp__find_definition( file_path: file, symbol_name: symbol[:name], symbol_kind: symbol[:kind] ) references = mcp__cclsp__find_references( file_path: file, symbol_name: symbol[:name] ) context[:interfaces] << { symbol: symbol[:name], kind: symbol[:kind], file: file, definition: definition, references: references.count, signature: extract_signature(definition) } end end context end
4. Vocabulary Building Patterns
4.1 Project Vocabulary Extraction
Build a vocabulary of project-specific terms:
# Pattern: Project Vocabulary def build_project_vocabulary vocabulary = { models: [], services: [], controllers: [], patterns: [], domain_terms: [] } # 1. Scan models Dir["app/models/**/*.rb"].each do |file| content = Read(file) # Extract class name if content =~ /class\s+(\w+)/ model_name = $1 vocabulary[:models] << { name: model_name, file: file, associations: content.scan(/(?:has_many|belongs_to|has_one)\s+:(\w+)/).flatten, scopes: content.scan(/scope\s+:(\w+)/).flatten, validations: content.scan(/validates\s+:(\w+)/).flatten } end end # 2. Scan services Dir["app/services/**/*.rb"].each do |file| content = Read(file) if content =~ /class\s+(\w+)/ service_name = $1 vocabulary[:services] << { name: service_name, file: file, public_methods: content.scan(/^\s*def\s+(\w+)/).flatten, dependencies: extract_dependencies(content) } end end # 3. Extract domain terms from comments and names all_files = Dir["app/**/*.rb"] all_files.each do |file| content = Read(file) # Extract from comments comments = content.scan(/#\s*(.+)$/).flatten # Extract from class/method names identifiers = content.scan(/(?:class|def|module)\s+(\w+)/).flatten # Add unique terms terms = (comments + identifiers).map(&:downcase).uniq vocabulary[:domain_terms].concat(terms) end vocabulary[:domain_terms].uniq! vocabulary end
4.2 Symbol Vocabulary for Generation
Build vocabulary to guide code generation:
# Pattern: Generation Vocabulary def build_generation_vocabulary(target_file) vocab = { available_classes: [], available_methods: [], common_patterns: [], naming_conventions: [] } # 1. Find all classes in the project Dir["app/**/*.rb"].each do |file| content = Read(file) classes = content.scan(/class\s+(\w+)/).flatten vocab[:available_classes].concat(classes) end # 2. For the target file's directory, find common patterns dir = File.dirname(target_file) sibling_files = Dir["#{dir}/*.rb"] sibling_files.each do |file| content = Read(file) # Extract method patterns methods = content.scan(/def\s+(\w+)/).flatten vocab[:available_methods].concat(methods) # Extract common patterns if content.include?("Result.success") vocab[:common_patterns] << "Result monad" end if content.include?("ApplicationService") vocab[:common_patterns] << "ApplicationService inheritance" end end vocab[:available_classes].uniq! vocab[:available_methods].uniq! vocab[:common_patterns].uniq! vocab end
5. Guardian Validation Patterns
5.1 Pre-Generation Validation
Validate before generating code:
# Pattern: Pre-Generation Check def pre_generation_validate(target_file) validation = { passed: true, issues: [] } # 1. Check if cclsp is available begin mcp__cclsp__get_diagnostics(file_path: "Gemfile") rescue validation[:issues] << "cclsp not available - skipping LSP validation" return validation end # 2. Check existing file for errors if File.exist?(target_file) diagnostics = mcp__cclsp__get_diagnostics(file_path: target_file) errors = diagnostics.select { |d| d[:severity] == 1 } if errors.any? validation[:passed] = false validation[:issues] << "Existing file has #{errors.count} errors - fix first" end end # 3. Check parent class exists # (would need to parse generation template) validation end
5.2 Post-Generation Validation (Guardian)
Validate after generating code:
# Pattern: Guardian Validation def guardian_validate(file_path) result = { passed: true, errors: [], warnings: [], suggestions: [] } # 1. cclsp diagnostics (Solargraph) diagnostics = mcp__cclsp__get_diagnostics(file_path: file_path) diagnostics.each do |d| case d[:severity] when 1 # Error result[:passed] = false result[:errors] << { line: d[:range][:start][:line], message: d[:message], source: d[:source] } when 2 # Warning result[:warnings] << { line: d[:range][:start][:line], message: d[:message] } when 3, 4 # Info/Hint result[:suggestions] << { line: d[:range][:start][:line], message: d[:message] } end end # 2. Sorbet type checking (if available) sorbet_output = `bundle exec srb tc #{file_path} 2>&1` sorbet_errors = sorbet_output.lines.select { |l| l.start_with?(file_path) } sorbet_errors.each do |error| if error =~ /#{file_path}:(\d+):\s*(.+)/ result[:passed] = false result[:errors] << { line: $1.to_i, message: $2, source: "sorbet" } end end result end
5.3 Generate-Validate-Execute-Verify Cycle
Full implementation cycle with Guardian:
# Pattern: Full Implementation Cycle def implement_with_guardian(file_path, specification, max_attempts: 3) attempt = 0 loop do attempt += 1 puts "Attempt #{attempt}/#{max_attempts}: #{file_path}" # 1. GENERATE puts " 1/4 GENERATE: Writing code..." generate_code(file_path, specification) # 2. VALIDATE (Guardian) puts " 2/4 VALIDATE: Running Guardian..." validation = guardian_validate(file_path) unless validation[:passed] puts " Guardian found #{validation[:errors].count} errors" if attempt >= max_attempts return { success: false, reason: "Max attempts reached" } end # Apply fixes and retry apply_guardian_fixes(file_path, validation[:errors]) next end # 3. EXECUTE puts " 3/4 EXECUTE: Running tests..." test_result = run_tests_for_file(file_path) unless test_result[:passed] puts " Tests failed: #{test_result[:failures].count} failures" if attempt >= max_attempts return { success: false, reason: "Tests failed" } end # Analyze failures and retry analyze_and_fix_tests(file_path, test_result[:failures]) next end # 4. VERIFY puts " 4/4 VERIFY: Final check..." final_check = final_verification(file_path) return { success: true, attempts: attempt, verification: final_check } end end def apply_guardian_fixes(file_path, errors) # Group errors by type undefined_methods = errors.select { |e| e[:message].include?("Undefined method") } type_errors = errors.select { |e| e[:source] == "sorbet" } syntax_errors = errors.select { |e| e[:message].include?("syntax") } # Apply targeted fixes if undefined_methods.any? # Find correct method names using find_references fix_undefined_methods(file_path, undefined_methods) end if type_errors.any? # Add type signatures or fix type mismatches fix_type_errors(file_path, type_errors) end if syntax_errors.any? # Fix syntax issues fix_syntax_errors(file_path, syntax_errors) end end
6. Sorbet Integration Patterns
6.1 Type Signature Extraction
Extract Sorbet type signatures from a file:
# Pattern: Sorbet Signature Extraction def extract_sorbet_signatures(file_path) content = Read(file_path) signatures = [] # Find sig blocks content.scan(/sig\s*\{([^}]+)\}/) do |sig_content| sig = sig_content[0] # Parse params params = {} sig.scan(/params\(([^)]+)\)/) do |params_str| params_str[0].split(",").each do |param| name, type = param.strip.split(":").map(&:strip) params[name] = type end end # Parse returns returns = nil if sig =~ /returns\(([^)]+)\)/ returns = $1.strip end signatures << { params: params, returns: returns } end signatures end
6.2 Type-Guided Generation
Use type information to guide code generation:
# Pattern: Type-Guided Generation def generate_with_types(file_path, method_spec) # 1. Look up existing type signatures in project similar_methods = find_similar_methods(method_spec[:name]) # 2. Infer expected types from callers references = mcp__cclsp__find_references( file_path: file_path, symbol_name: method_spec[:name] ) inferred_types = infer_types_from_usage(references) # 3. Generate with explicit types signature = <<~RUBY sig { params(#{format_params(inferred_types[:params])}).returns(#{inferred_types[:returns]}) } def #{method_spec[:name]}(#{format_args(method_spec[:args])}) # Implementation end RUBY signature end
6.3 Sorbet Strictness Levels
Apply appropriate Sorbet strictness:
# Pattern: Sorbet Strictness Selection def select_sorbet_strictness(file_path) # New files: start with # typed: true # Critical business logic: use # typed: strict # Generated code: use # typed: false initially case file_path when /app\/services\// "# typed: strict" # Services should have strong types when /app\/models\// "# typed: true" # Models can start with basic types when /app\/controllers\// "# typed: false" # Controllers often have complex types when /lib\// "# typed: strict" # Library code should be well-typed else "# typed: true" # Default to basic type checking end end
7. Graceful Degradation
7.1 Tool Availability Check
Check which tools are available:
# Pattern: Availability Check check_tool_availability() { local availability="{}" # Check cclsp if mcp__cclsp__get_diagnostics --file_path "Gemfile" 2>/dev/null; then availability=$(echo "$availability" | jq '.cclsp = true') else availability=$(echo "$availability" | jq '.cclsp = false') fi # Check Solargraph if gem list solargraph -i &>/dev/null; then availability=$(echo "$availability" | jq '.solargraph = true') else availability=$(echo "$availability" | jq '.solargraph = false') fi # Check Sorbet if bundle exec srb --version &>/dev/null || gem list sorbet -i &>/dev/null; then availability=$(echo "$availability" | jq '.sorbet = true') else availability=$(echo "$availability" | jq '.sorbet = false') fi # Check parser gem if gem list parser -i &>/dev/null; then availability=$(echo "$availability" | jq '.parser = true') else availability=$(echo "$availability" | jq '.parser = false') fi echo "$availability" }
7.2 Fallback Strategies
Fallback when tools are unavailable:
# Pattern: Graceful Degradation def compile_context_with_fallback(task) availability = check_tool_availability context = { cclsp_enhanced: false, interfaces: [], vocabulary: [], fallback_used: [] } # Primary: Use cclsp if availability[:cclsp] context[:cclsp_enhanced] = true context[:interfaces] = extract_interfaces_with_cclsp(task) else # Fallback: Use grep and AST parsing context[:fallback_used] << "grep for interface extraction" context[:interfaces] = extract_interfaces_with_grep(task) end # Primary: Use Sorbet for type info if availability[:sorbet] context[:type_info] = extract_type_info_with_sorbet(task) else # Fallback: Use YARD comments context[:fallback_used] << "YARD for type hints" context[:type_info] = extract_type_info_from_yard(task) end # Primary: Use parser gem if availability[:parser] context[:ast_analysis] = analyze_with_parser(task) else # Fallback: Use ripper (always available) context[:fallback_used] << "ripper for AST" context[:ast_analysis] = analyze_with_ripper(task) end context end def extract_interfaces_with_grep(task) interfaces = [] task[:files].each do |file| # Use grep to find method definitions methods = `grep -n "def " #{file} | head -50`.lines methods.each do |line| if line =~ /^(\d+):\s*def\s+(\w+)/ interfaces << { symbol: $2, kind: "method", file: file, line: $1.to_i } end end end interfaces end
7.3 Partial Feature Mode
Enable features based on available tools:
# Pattern: Feature Flags def determine_enabled_features features = { lsp_diagnostics: false, type_checking: false, smart_refactoring: false, vocabulary_building: true, # Always available interface_extraction: true # Always available (grep fallback) } availability = check_tool_availability if availability[:cclsp] features[:lsp_diagnostics] = true features[:smart_refactoring] = true end if availability[:sorbet] features[:type_checking] = true end features end
8. Working Memory Integration
8.1 Store Compiled Context
Store context for implementation phase:
# Pattern: Store Compiled Context def store_compiled_context(task_id, context) memory_entry = { timestamp: Time.now.utc.iso8601, agent: "context-compiler", knowledge_type: "compiled_context", key: "task.#{task_id}.context", value: context, confidence: "verified" } # Write to working memory file File.open(".claude/reactree-memory.jsonl", "a") do |f| f.puts(memory_entry.to_json) end end
8.2 Read Compiled Context
Read context during implementation:
# Pattern: Read Compiled Context def read_compiled_context(task_id) memory_file = ".claude/reactree-memory.jsonl" return nil unless File.exist?(memory_file) # Read most recent context for task context = nil File.readlines(memory_file).reverse_each do |line| entry = JSON.parse(line) if entry["key"] == "task.#{task_id}.context" context = entry["value"] break end end context end
9. Quick Reference
Command Cheatsheet
# Solargraph gem install solargraph solargraph config # Generate .solargraph.yml solargraph socket --port 7658 # Start language server # Sorbet gem install sorbet sorbet-runtime bundle exec srb init # Initialize Sorbet bundle exec srb tc # Type check project bundle exec srb tc app/models/ # Type check directory bundle exec srb tc --ignore=sorbet/ # Ignore directory # Tapioca (RBI generation) gem install tapioca bundle exec tapioca init # Initialize bundle exec tapioca gems # Generate gem RBIs bundle exec tapioca dsl # Generate DSL RBIs bundle exec tapioca annotations # Sync annotations # parser gem gem install parser ruby -rparser/current -e 'p Parser::CurrentRuby.parse("def foo; end")'
cclsp MCP Quick Reference
# Find where a method is defined mcp__cclsp__find_definition( file_path: "app/models/user.rb", symbol_name: "authenticate" ) # Find all usages of a class mcp__cclsp__find_references( file_path: "app/services/payment_service.rb", symbol_name: "PaymentService" ) # Check file for errors mcp__cclsp__get_diagnostics( file_path: "app/services/order_service.rb" ) # Rename symbol (preview) mcp__cclsp__rename_symbol( file_path: "app/models/user.rb", symbol_name: "old_method", new_name: "new_method", dry_run: true )
Guardian Validation Quick Reference
# Full validation cycle def validate_file(file_path) # 1. cclsp diagnostics diagnostics = mcp__cclsp__get_diagnostics(file_path: file_path) errors = diagnostics.select { |d| d[:severity] == 1 } # 2. Sorbet check sorbet_output = `bundle exec srb tc #{file_path} 2>&1` sorbet_errors = sorbet_output.lines.count { |l| l.start_with?(file_path) } # 3. Combined result { passed: errors.empty? && sorbet_errors == 0, lsp_errors: errors.count, sorbet_errors: sorbet_errors } end