Claude Code Plugins

Community-maintained marketplace

Feedback

Context Compilation with cclsp

@Kaakati/rails-enterprise-dev
1
0

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

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name Context Compilation with cclsp
description 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
version 1.0.0

Context 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
mcp__cclsp__find_definition Find symbol definitions Locate where methods/classes are defined
mcp__cclsp__find_references Find all references Discover symbol usage patterns
mcp__cclsp__get_diagnostics Get errors/warnings Validate code before execution
mcp__cclsp__rename_symbol Rename symbols Safe refactoring
mcp__cclsp__rename_symbol_strict Rename at position Precise symbol renaming
mcp__cclsp__restart_server 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 # typed: sigils
  • 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 - Class definitions
  • module - Module definitions
  • method - Instance methods
  • function - Module/class methods
  • variable - Local/instance variables
  • constant - Constants

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:

  • 1 - Error (must fix)
  • 2 - Warning (should investigate)
  • 3 - Information (good to know)
  • 4 - Hint (suggestions)

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