# Option 2: Dynamic Library Loading Plan

## Executive Summary

Replace the template-based schematic workflow with dynamic symbol loading from KiCad's installed symbol libraries. This would eliminate the 13-component limitation and provide access to ALL KiCad symbols (~10,000+ symbols from standard libraries).

**Current Status (Option 1):**

- ✅ Template-based approach working
- ✅ 13 component types supported
- ❌ Limited symbol variety
- ❌ Requires manual template updates for new types

**Proposed (Option 2):**

- 🎯 Dynamic loading from `.kicad_sym` library files
- 🎯 Access to ~10,000+ KiCad symbols
- 🎯 No template maintenance required
- 🎯 User can specify any library/symbol combination

---

## Problem Analysis

### kicad-skip Library Limitation

**Core Issue:** kicad-skip **cannot create symbols from scratch**. It can only:

1. Clone existing symbols from a loaded schematic
2. Modify properties of cloned symbols

**Current Workaround:** Pre-load template symbols in schematic file

**Proposed Solution:** Load symbols from KiCad's `.kicad_sym` library files, inject them into the schematic's `lib_symbols` section, then clone from there.

---

## KiCad Symbol Library Architecture

### Symbol Library File Format (`.kicad_sym`)

KiCad symbol libraries are S-expression files containing symbol definitions:

```lisp
(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
  (symbol "Device:R"
    (pin_numbers hide)
    (pin_names (offset 0))
    (in_bom yes)
    (on_board yes)
    (property "Reference" "R" ...)
    (property "Value" "R" ...)
    ;; Graphics definitions
    (symbol "R_0_1" ...)
    (symbol "R_1_1"
      (pin passive line ...)
    )
  )
  (symbol "Device:C" ...)
  (symbol "Device:L" ...)
  ;; ... thousands more
)
```

### Standard KiCad Library Locations

**Linux:**

- System libraries: `/usr/share/kicad/symbols/`
- User libraries: `~/.local/share/kicad/8.0/symbols/` or `~/.config/kicad/8.0/symbols/`

**Windows:**

- System libraries: `C:\Program Files\KiCad\9.0\share\kicad\symbols\`
- User libraries: `%APPDATA%\kicad\8.0\symbols\`

**macOS:**

- System libraries: `/Applications/KiCad/KiCad.app/Contents/SharedSupport/symbols/`
- User libraries: `~/Library/Preferences/kicad/8.0/symbols/`

### Standard Library Files

Common libraries (each containing 50-500 symbols):

- `Device.kicad_sym` - Passives (R, C, L, D, LED, Crystal, etc.)
- `Connector.kicad_sym` - Connectors (headers, USB, etc.)
- `Connector_Generic.kicad_sym` - Generic connectors
- `Transistor_BJT.kicad_sym` - Bipolar transistors
- `Transistor_FET.kicad_sym` - MOSFETs
- `Amplifier_Operational.kicad_sym` - Op-amps
- `Regulator_Linear.kicad_sym` - Voltage regulators
- `MCU_*.kicad_sym` - Microcontrollers
- `Interface_*.kicad_sym` - Interface ICs
- ... 100+ more libraries

---

## Implementation Strategy

### Phase 1: Library Discovery & Indexing

**Goal:** Build an index of all available symbols and their locations

**Implementation:**

```python
class SymbolLibraryManager:
    def __init__(self):
        self.library_paths = []
        self.symbol_index = {}  # {"Device:R": "/path/to/Device.kicad_sym", ...}

    def discover_libraries(self):
        """Find all KiCad symbol libraries on the system"""
        search_paths = [
            "/usr/share/kicad/symbols/",
            os.path.expanduser("~/.local/share/kicad/8.0/symbols/"),
            os.path.expanduser("~/.config/kicad/8.0/symbols/"),
        ]

        for search_path in search_paths:
            if os.path.exists(search_path):
                for lib_file in os.listdir(search_path):
                    if lib_file.endswith('.kicad_sym'):
                        self.library_paths.append(os.path.join(search_path, lib_file))

    def index_symbols(self):
        """Parse all libraries and build symbol index"""
        for lib_path in self.library_paths:
            lib_name = os.path.basename(lib_path).replace('.kicad_sym', '')
            symbols = self._parse_library(lib_path)

            for symbol_name in symbols:
                full_name = f"{lib_name}:{symbol_name}"
                self.symbol_index[full_name] = {
                    'library': lib_name,
                    'library_path': lib_path,
                    'symbol_name': symbol_name
                }

    def _parse_library(self, lib_path):
        """Parse .kicad_sym file and extract symbol names"""
        # Use sexpdata (already a dependency of kicad-skip)
        import sexpdata

        with open(lib_path, 'r') as f:
            data = sexpdata.load(f)

        symbols = []
        for item in data[2:]:  # Skip header
            if isinstance(item, list) and item[0] == Symbol('symbol'):
                symbol_name = item[1]  # e.g., "Device:R"
                # Extract just the symbol part after ':'
                if ':' in symbol_name:
                    symbol_name = symbol_name.split(':')[1]
                symbols.append(symbol_name)

        return symbols
```

### Phase 2: Dynamic Symbol Injection

**Goal:** Load symbol definition from library file and inject into schematic

**Challenge:** kicad-skip works with loaded schematics, but we need to dynamically add symbols to the `lib_symbols` section.

**Solution:** Modify the schematic's S-expression data directly before loading with kicad-skip:

```python
def inject_symbol_into_schematic(schematic_path, library_path, symbol_name):
    """
    1. Read schematic S-expression
    2. Read library S-expression
    3. Extract symbol definition from library
    4. Inject into schematic's lib_symbols section
    5. Save modified schematic
    6. Reload with kicad-skip
    """
    import sexpdata

    # Load schematic
    with open(schematic_path, 'r') as f:
        sch_data = sexpdata.load(f)

    # Load library
    with open(library_path, 'r') as f:
        lib_data = sexpdata.load(f)

    # Find symbol definition in library
    symbol_def = None
    for item in lib_data[2:]:
        if isinstance(item, list) and item[0] == Symbol('symbol'):
            if symbol_name in str(item[1]):
                symbol_def = item
                break

    if not symbol_def:
        raise ValueError(f"Symbol {symbol_name} not found in {library_path}")

    # Find lib_symbols section in schematic
    lib_symbols_index = None
    for i, item in enumerate(sch_data):
        if isinstance(item, list) and item[0] == Symbol('lib_symbols'):
            lib_symbols_index = i
            break

    # Inject symbol definition
    if lib_symbols_index:
        sch_data[lib_symbols_index].append(symbol_def)

    # Save modified schematic
    with open(schematic_path, 'w') as f:
        sexpdata.dump(sch_data, f)

    # Reload with kicad-skip
    return Schematic(schematic_path)
```

### Phase 3: Template Instance Creation

**Goal:** Create offscreen template instances that can be cloned

**After injection:** Symbol definition is in `lib_symbols`, but we need an instance to clone from:

```python
def create_template_instance(schematic, library_name, symbol_name):
    """
    Create an offscreen template instance that can be cloned
    Similar to our current _TEMPLATE_R approach
    """
    # This requires directly manipulating the S-expression
    # Add a symbol instance at offscreen position with special reference

    template_ref = f"_TEMPLATE_{library_name}_{symbol_name}"

    # Create symbol instance (S-expression)
    symbol_instance = [
        Symbol('symbol'),
        [Symbol('lib_id'), f"{library_name}:{symbol_name}"],
        [Symbol('at'), -100, -100 - (len(schematic.symbol) * 10), 0],
        [Symbol('unit'), 1],
        [Symbol('in_bom'), Symbol('no')],
        [Symbol('on_board'), Symbol('no')],
        [Symbol('dnp'), Symbol('yes')],
        [Symbol('uuid'), str(uuid.uuid4())],
        [Symbol('property'), "Reference", template_ref, ...],
        # ... more properties
    ]

    # Inject into schematic and reload
    # ... (similar to inject_symbol_into_schematic)

    return template_ref
```

### Phase 4: User-Facing API

**Goal:** Simple interface for users to add any KiCad symbol

**New MCP Tool: `add_schematic_component_dynamic`**

```python
def add_schematic_component_dynamic(params):
    """
    Add component by library:symbol notation

    Example:
    {
        "library": "Device",
        "symbol": "R",
        "reference": "R1",
        "value": "10k",
        "x": 100,
        "y": 100
    }

    OR using full notation:
    {
        "lib_symbol": "Device:R",  # Full notation
        "reference": "R1",
        ...
    }
    """
    lib_symbol = params.get('lib_symbol') or f"{params['library']}:{params['symbol']}"

    # 1. Check if symbol is already in schematic's lib_symbols
    # 2. If not, inject it from library file
    # 3. Create template instance if needed
    # 4. Clone template and set properties

    return {"success": True, "reference": params['reference']}
```

---

## Advantages Over Template Approach

### ✅ Unlimited Symbol Access

- Access to ~10,000+ standard KiCad symbols
- Support for custom user libraries
- Support for 3rd-party libraries (JLCPCB, Espressif, etc.)

### ✅ No Maintenance Required

- Template doesn't need updates for new component types
- Automatically supports new KiCad library additions
- Works with custom symbol libraries

### ✅ Better User Experience

```
User: "Add an STM32F103C8T6 microcontroller at position 100,100"
AI: *Searches symbol index*
    *Finds MCU_ST_STM32F1:STM32F103C8Tx*
    *Loads from library*
    *Injects into schematic*
    *Places component*
    ✓ Done!
```

### ✅ Flexible Symbol Search

```python
# Find all resistors
symbols = lib_manager.search_symbols(query="resistor")
# Returns: ["Device:R", "Device:R_Small", "Device:R_Network", ...]

# Find all STM32 MCUs
symbols = lib_manager.search_symbols(query="STM32", library="MCU_ST_STM32F1")
```

---

## Challenges & Mitigations

### Challenge 1: S-expression Manipulation Complexity

**Problem:** Directly manipulating S-expression data is error-prone

**Mitigation:**

- Use `sexpdata` library (already a dependency)
- Create helper functions for common operations
- Add comprehensive validation and error handling
- Extensive testing with various symbol types

### Challenge 2: Performance

**Problem:** Loading/reloading schematics after injection could be slow

**Mitigation:**

- **Cache loaded symbols**: Once injected, symbol stays in schematic
- **Batch injection**: Inject multiple symbols at once
- **Lazy loading**: Only inject symbols when first used

### Challenge 3: Symbol Compatibility

**Problem:** Some symbols may have complex pin configurations or multiple units

**Mitigation:**

- Start with simple 2-pin passives (R, C, L)
- Gradually add support for multi-pin ICs
- Handle multi-unit symbols (gates, OpAmp sections) explicitly
- Document supported symbol types

### Challenge 4: Library Version Compatibility

**Problem:** KiCad symbol format may change between versions

**Mitigation:**

- Parse KiCad version from library files
- Version-specific handling if needed
- Fallback to template approach for unsupported formats

---

## Implementation Phases

### Phase A: Proof of Concept (1-2 weeks)

- [ ] Create `SymbolLibraryManager` class
- [ ] Implement library discovery (Linux paths only)
- [ ] Implement symbol indexing
- [ ] Test with Device.kicad_sym (R, C, L)
- [ ] Implement basic S-expression injection
- [ ] Test end-to-end with simple components

### Phase B: Core Functionality (2-3 weeks)

- [ ] Cross-platform library discovery (Windows, macOS)
- [ ] Symbol search functionality
- [ ] Template instance creation automation
- [ ] Multi-pin component support
- [ ] Error handling and validation
- [ ] Unit tests for all operations

### Phase C: MCP Integration (1 week)

- [ ] Create `add_schematic_component_dynamic` tool
- [ ] Update `search_symbols` to use library index
- [ ] Add `list_available_symbols` tool
- [ ] Add `list_symbol_libraries` tool
- [ ] Documentation and examples

### Phase D: Advanced Features (2-3 weeks)

- [ ] Multi-unit symbol support (e.g., quad OpAmps)
- [ ] Custom library registration
- [ ] Symbol caching and optimization
- [ ] 3rd-party library support (JLCPCB, etc.)
- [ ] Symbol preview generation

---

## Migration Strategy

### Backward Compatibility

Keep template-based approach as fallback:

```python
def add_schematic_component(params):
    """Smart component addition with fallback"""
    # Try dynamic loading first
    try:
        if 'library' in params or 'lib_symbol' in params:
            return add_schematic_component_dynamic(params)
    except Exception as e:
        logger.warning(f"Dynamic loading failed: {e}, falling back to template")

    # Fallback to template-based
    return add_schematic_component_template(params)
```

### Gradual Rollout

1. **Week 1-2:** Implement basic dynamic loading
2. **Week 3-4:** Test with power users, gather feedback
3. **Week 5-6:** Make dynamic loading the default
4. **Week 7+:** Deprecate template-only approach (keep as fallback)

---

## Success Criteria

### Must Have

- [ ] Load symbols from Device.kicad_sym (passives)
- [ ] Support R, C, L, D, LED (5 core types)
- [ ] Cross-platform library discovery
- [ ] Proper error handling

### Should Have

- [ ] Support for all Device.kicad_sym symbols (~50 symbols)
- [ ] Support for Connector.kicad_sym symbols
- [ ] Symbol search by name/keyword
- [ ] Performance: < 1 second per symbol injection

### Nice to Have

- [ ] Support for all standard libraries (~10,000 symbols)
- [ ] Multi-unit symbol support
- [ ] Custom library registration
- [ ] Symbol preview/documentation

---

## Risk Assessment

| Risk                            | Probability | Impact | Mitigation                                       |
| ------------------------------- | ----------- | ------ | ------------------------------------------------ |
| S-expression parsing complexity | High        | High   | Use proven `sexpdata` library, extensive testing |
| Performance degradation         | Medium      | Medium | Implement caching, lazy loading                  |
| KiCad version incompatibility   | Low         | High   | Version detection, format validation             |
| Template fallback breaks        | Low         | Medium | Maintain template approach in parallel           |
| User confusion                  | Medium      | Low    | Clear documentation, gradual rollout             |

---

## Conclusion

Dynamic library loading is **feasible and highly beneficial** for the schematic workflow. While the template-based approach (Option 1) provides immediate value with 13 component types, Option 2 would:

1. **Eliminate the 13-component limitation**
2. **Provide access to 10,000+ KiCad symbols**
3. **Remove manual template maintenance**
4. **Enable true "natural language PCB design"**

**Recommendation:**

- ✅ **Keep Option 1 (expanded template) for immediate use**
- ✅ **Implement Option 2 (dynamic loading) over 6-8 weeks**
- ✅ **Maintain template fallback for compatibility**

This gives users immediate value while we build the robust long-term solution.

---

## References

- [KiCad File Formats Documentation](https://dev-docs.kicad.org/en/file-formats/)
- [kicad-skip GitHub](https://github.com/mvnmgrx/kicad-skip)
- [sexpdata Python Library](https://github.com/jd-boyd/sexpdata)
- [KiCad Symbol Library Format Spec](https://dev-docs.kicad.org/en/file-formats/sexpr-intro/)
