Rename project from kicad-mcp to mckicad

Rename source directory kicad_mcp/ → mckicad/, update all imports,
pyproject.toml metadata, documentation references, Makefile targets,
and .gitignore paths. All 195 tests pass.
This commit is contained in:
Ryan Malloy 2026-02-13 00:53:59 -07:00
parent b99d1bd2b9
commit 687e14bd11
85 changed files with 474 additions and 540 deletions

2
.gitignore vendored
View File

@ -47,7 +47,7 @@ logs/
.DS_Store .DS_Store
# MCP specific # MCP specific
~/.kicad_mcp/drc_history/ ~/.mckicad/drc_history/
# UV and modern Python tooling # UV and modern Python tooling
uv.lock uv.lock

View File

@ -9,8 +9,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- `make run` - Start the KiCad MCP server (`uv run python main.py`) - `make run` - Start the KiCad MCP server (`uv run python main.py`)
- `make test` - Run all tests with pytest - `make test` - Run all tests with pytest
- `make test <file>` - Run specific test file - `make test <file>` - Run specific test file
- `make lint` - Run linting with ruff and mypy (`uv run ruff check kicad_mcp/ tests/` + `uv run mypy kicad_mcp/`) - `make lint` - Run linting with ruff and mypy (`uv run ruff check mckicad/ tests/` + `uv run mypy mckicad/`)
- `make format` - Format code with ruff (`uv run ruff format kicad_mcp/ tests/`) - `make format` - Format code with ruff (`uv run ruff format mckicad/ tests/`)
- `make build` - Build package with uv - `make build` - Build package with uv
- `make clean` - Clean build artifacts - `make clean` - Clean build artifacts
@ -48,19 +48,19 @@ This project implements a Model Context Protocol (MCP) server for KiCad electron
### Key Modules ### Key Modules
#### Core Server (`kicad_mcp/server.py`) #### Core Server (`mckicad/server.py`)
- FastMCP server initialization with lifespan management - FastMCP server initialization with lifespan management
- Registers all resources, tools, and prompts - Registers all resources, tools, and prompts
- Signal handling for graceful shutdown - Signal handling for graceful shutdown
- Cleanup handlers for temporary directories - Cleanup handlers for temporary directories
#### Configuration (`kicad_mcp/config.py`) #### Configuration (`mckicad/config.py`)
- Platform-specific KiCad paths (macOS/Windows/Linux) - Platform-specific KiCad paths (macOS/Windows/Linux)
- Environment variable handling (`KICAD_SEARCH_PATHS`, `KICAD_USER_DIR`) - Environment variable handling (`KICAD_SEARCH_PATHS`, `KICAD_USER_DIR`)
- Component library mappings and default footprints - Component library mappings and default footprints
- Timeout and display constants - Timeout and display constants
#### Context Management (`kicad_mcp/context.py`) #### Context Management (`mckicad/context.py`)
- Lifespan context with KiCad module availability detection - Lifespan context with KiCad module availability detection
- Shared cache across requests - Shared cache across requests
- Application state management - Application state management
@ -79,7 +79,7 @@ This project implements a Model Context Protocol (MCP) server for KiCad electron
### Project Structure ### Project Structure
``` ```
kicad_mcp/ mckicad/
├── resources/ # MCP resources (data providers) ├── resources/ # MCP resources (data providers)
├── tools/ # MCP tools (action performers) ├── tools/ # MCP tools (action performers)
├── prompts/ # MCP prompt templates ├── prompts/ # MCP prompt templates
@ -94,7 +94,7 @@ kicad_mcp/
### Adding New Features ### Adding New Features
1. Identify component type (resource/tool/prompt) 1. Identify component type (resource/tool/prompt)
2. Add implementation to appropriate module in `kicad_mcp/` 2. Add implementation to appropriate module in `mckicad/`
3. Register in `server.py` create_server() function 3. Register in `server.py` create_server() function
4. Use lifespan context for shared state and caching 4. Use lifespan context for shared state and caching
5. Include progress reporting for long operations 5. Include progress reporting for long operations
@ -137,7 +137,7 @@ This project implements groundbreaking real-time PCB automation through two adva
**Key Usage Pattern:** **Key Usage Pattern:**
```python ```python
from kicad_mcp.utils.ipc_client import kicad_ipc_session from mckicad.utils.ipc_client import kicad_ipc_session
with kicad_ipc_session(board_path) as client: with kicad_ipc_session(board_path) as client:
footprints = client.get_footprints() footprints = client.get_footprints()
@ -216,12 +216,12 @@ Key environment variables in `.env`:
The package declares a script entry point in `pyproject.toml`: The package declares a script entry point in `pyproject.toml`:
```toml ```toml
[project.scripts] [project.scripts]
kicad-mcp = "kicad_mcp.server:main" mckicad = "mckicad.server:main"
``` ```
This enables running via `uvx kicad-mcp` after installation. This enables running via `uvx mckicad` after installation.
### Logging ### Logging
- All logs written to `kicad-mcp.log` in project root - All logs written to `mckicad.log` in project root
- Log file overwritten on each server start - Log file overwritten on each server start
- PID included in log messages for process tracking - PID included in log messages for process tracking
- Use `logging` module, never `print()` statements - Use `logging` module, never `print()` statements
@ -231,7 +231,7 @@ This enables running via `uvx kicad-mcp` after installation.
### FastMCP Tool Registration ### FastMCP Tool Registration
All tools follow this registration pattern in `server.py`: All tools follow this registration pattern in `server.py`:
```python ```python
from kicad_mcp.tools.example_tools import register_example_tools from mckicad.tools.example_tools import register_example_tools
def create_server() -> FastMCP: def create_server() -> FastMCP:
mcp = FastMCP("KiCad", lifespan=lifespan_factory) mcp = FastMCP("KiCad", lifespan=lifespan_factory)
@ -259,7 +259,7 @@ Each tool module should:
### Progress Reporting ### Progress Reporting
For long operations, use progress constants: For long operations, use progress constants:
```python ```python
from kicad_mcp.config import PROGRESS_CONSTANTS from mckicad.config import PROGRESS_CONSTANTS
progress_callback(PROGRESS_CONSTANTS["start"]) progress_callback(PROGRESS_CONSTANTS["start"])
# ... do work ... # ... do work ...
@ -328,13 +328,13 @@ LOG_LEVEL=DEBUG
### Test IPC Connection ### Test IPC Connection
```python ```python
from kicad_mcp.utils.ipc_client import check_kicad_availability from mckicad.utils.ipc_client import check_kicad_availability
print(check_kicad_availability()) print(check_kicad_availability())
``` ```
### Test FreeRouting Setup ### Test FreeRouting Setup
```python ```python
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites from mckicad.utils.freerouting_engine import check_routing_prerequisites
print(check_routing_prerequisites()) print(check_routing_prerequisites())
``` ```

View File

@ -25,11 +25,11 @@ test:
@: @:
lint: lint:
uv run ruff check kicad_mcp/ tests/ uv run ruff check mckicad/ tests/
uv run mypy kicad_mcp/ uv run mypy mckicad/
format: format:
uv run ruff format kicad_mcp/ tests/ uv run ruff format mckicad/ tests/
clean: clean:
rm -rf dist/ rm -rf dist/

View File

@ -117,8 +117,8 @@ We've integrated cutting-edge technologies to create something truly revolutiona
```bash ```bash
# Clone and setup # Clone and setup
git clone https://github.com/your-org/kicad-mcp.git git clone https://github.com/your-org/mckicad.git
cd kicad-mcp cd mckicad
make install make install
# Configure environment # Configure environment
@ -132,8 +132,8 @@ cp .env.example .env
{ {
"mcpServers": { "mcpServers": {
"kicad": { "kicad": {
"command": "/path/to/kicad-mcp/.venv/bin/python", "command": "/path/to/mckicad/.venv/bin/python",
"args": ["/path/to/kicad-mcp/main.py"] "args": ["/path/to/mckicad/main.py"]
} }
} }
} }
@ -456,8 +456,8 @@ make run
- ❓ **[FAQ](docs/faq.md)** - Common questions answered - ❓ **[FAQ](docs/faq.md)** - Common questions answered
### **Community** ### **Community**
- 💬 **[Discussions](https://github.com/your-org/kicad-mcp/discussions)** - Share ideas and get help - 💬 **[Discussions](https://github.com/your-org/mckicad/discussions)** - Share ideas and get help
- 🐛 **[Issues](https://github.com/your-org/kicad-mcp/issues)** - Report bugs and request features - 🐛 **[Issues](https://github.com/your-org/mckicad/issues)** - Report bugs and request features
- 🔧 **[Contributing Guide](CONTRIBUTING.md)** - Join the development - 🔧 **[Contributing Guide](CONTRIBUTING.md)** - Join the development
### **Support** ### **Support**
@ -499,7 +499,7 @@ This project is open source under the **MIT License** - see the [LICENSE](LICENS
## 🚀 Ready to Transform Your Design Workflow? ## 🚀 Ready to Transform Your Design Workflow?
**[Get Started Now](https://github.com/your-org/kicad-mcp)** • **[Join the Community](https://discord.gg/your-invite)** • **[Read the Docs](docs/)** **[Get Started Now](https://github.com/your-org/mckicad)** • **[Join the Community](https://discord.gg/your-invite)** • **[Read the Docs](docs/)**
--- ---

View File

@ -256,6 +256,6 @@ And it's available today, through the power of human-AI collaboration.
**About This Collaboration:** **About This Collaboration:**
This revolutionary EDA automation platform was built through an intensive human-AI collaboration between Ryan Malloy and Claude Sonnet 4 using Claude Code. The project demonstrates the incredible potential when human creativity and AI technical implementation work together to solve complex engineering challenges. This revolutionary EDA automation platform was built through an intensive human-AI collaboration between Ryan Malloy and Claude Sonnet 4 using Claude Code. The project demonstrates the incredible potential when human creativity and AI technical implementation work together to solve complex engineering challenges.
**GitHub Repository**: [KiCad MCP Server](https://github.com/user/kicad-mcp) **GitHub Repository**: [KiCad MCP Server](https://github.com/user/mckicad)
**Performance Metrics**: 100% test success rate, sub-millisecond response times **Performance Metrics**: 100% test success rate, sub-millisecond response times
**Impact**: Democratizes professional PCB design through conversational AI **Impact**: Democratizes professional PCB design through conversational AI

View File

@ -8,7 +8,7 @@ The KiCad MCP Server can be configured in multiple ways:
1. **Environment Variables**: Set directly when running the server 1. **Environment Variables**: Set directly when running the server
2. **.env File**: Create a `.env` file in the project root (recommended) 2. **.env File**: Create a `.env` file in the project root (recommended)
3. **Code Modifications**: Edit configuration constants in `kicad_mcp/config.py` 3. **Code Modifications**: Edit configuration constants in `mckicad/config.py`
## Core Configuration Options ## Core Configuration Options
@ -97,9 +97,9 @@ To configure Claude Desktop to use the KiCad MCP Server:
{ {
"mcpServers": { "mcpServers": {
"kicad": { "kicad": {
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python", "command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/venv/bin/python",
"args": [ "args": [
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/main.py" "/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/main.py"
] ]
} }
} }
@ -111,9 +111,9 @@ To configure Claude Desktop to use the KiCad MCP Server:
{ {
"mcpServers": { "mcpServers": {
"kicad": { "kicad": {
"command": "C:\\Path\\To\\Your\\Project\\kicad-mcp\\venv\\Scripts\\python.exe", "command": "C:\\Path\\To\\Your\\Project\\mckicad\\venv\\Scripts\\python.exe",
"args": [ "args": [
"C:\\Path\\To\\Your\\Project\\kicad-mcp\\main.py" "C:\\Path\\To\\Your\\Project\\mckicad\\main.py"
] ]
} }
} }
@ -128,9 +128,9 @@ You can also set environment variables directly in the client configuration:
{ {
"mcpServers": { "mcpServers": {
"kicad": { "kicad": {
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python", "command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/venv/bin/python",
"args": [ "args": [
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/main.py" "/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/main.py"
], ],
"env": { "env": {
"KICAD_SEARCH_PATHS": "/custom/path1,/custom/path2", "KICAD_SEARCH_PATHS": "/custom/path1,/custom/path2",
@ -145,7 +145,7 @@ You can also set environment variables directly in the client configuration:
### Custom KiCad Extensions ### Custom KiCad Extensions
If you need to modify the recognized KiCad file extensions, you can edit `kicad_mcp/config.py`: If you need to modify the recognized KiCad file extensions, you can edit `mckicad/config.py`:
```python ```python
# File extensions # File extensions
@ -161,14 +161,14 @@ KICAD_EXTENSIONS = {
The server stores DRC history to track changes over time. By default, history is stored in: The server stores DRC history to track changes over time. By default, history is stored in:
- macOS/Linux: `~/.kicad_mcp/drc_history/` - macOS/Linux: `~/.mckicad/drc_history/`
- Windows: `%APPDATA%\kicad_mcp\drc_history\` - Windows: `%APPDATA%\mckicad\drc_history\`
You can modify this in `kicad_mcp/utils/drc_history.py` if needed. You can modify this in `mckicad/utils/drc_history.py` if needed.
### Python Path for KiCad Modules ### Python Path for KiCad Modules
The server attempts to locate and add KiCad's Python modules to the Python path automatically. If this fails, you can modify the search paths in `kicad_mcp/utils/python_path.py`. The server attempts to locate and add KiCad's Python modules to the Python path automatically. If this fails, you can modify the search paths in `mckicad/utils/python_path.py`.
## Platform-Specific Configuration ## Platform-Specific Configuration

View File

@ -29,9 +29,9 @@ This guide provides detailed information for developers who want to modify or ex
The KiCad MCP Server follows a modular architecture: The KiCad MCP Server follows a modular architecture:
``` ```
kicad-mcp/ mckicad/
├── main.py # Entry point ├── main.py # Entry point
├── kicad_mcp/ # Main package ├── mckicad/ # Main package
│ ├── __init__.py │ ├── __init__.py
│ ├── server.py # Server creation and setup │ ├── server.py # Server creation and setup
│ ├── config.py # Configuration settings │ ├── config.py # Configuration settings
@ -69,7 +69,7 @@ kicad-mcp/
Resources provide read-only data to the LLM. To add a new resource: Resources provide read-only data to the LLM. To add a new resource:
1. Add your function to an existing resource file or create a new one in `kicad_mcp/resources/`: 1. Add your function to an existing resource file or create a new one in `mckicad/resources/`:
```python ```python
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp import FastMCP
@ -91,10 +91,10 @@ def register_my_resources(mcp: FastMCP) -> None:
return f"Formatted data about {parameter}" return f"Formatted data about {parameter}"
``` ```
2. Register your resources in `kicad_mcp/server.py`: 2. Register your resources in `mckicad/server.py`:
```python ```python
from kicad_mcp.resources.my_resources import register_my_resources from mckicad.resources.my_resources import register_my_resources
def create_server() -> FastMCP: def create_server() -> FastMCP:
# ... # ...
@ -106,7 +106,7 @@ def create_server() -> FastMCP:
Tools are functions that perform actions or computations. To add a new tool: Tools are functions that perform actions or computations. To add a new tool:
1. Add your function to an existing tool file or create a new one in `kicad_mcp/tools/`: 1. Add your function to an existing tool file or create a new one in `mckicad/tools/`:
```python ```python
from typing import Dict, Any from typing import Dict, Any
@ -143,10 +143,10 @@ def register_my_tools(mcp: FastMCP) -> None:
} }
``` ```
2. Register your tools in `kicad_mcp/server.py`: 2. Register your tools in `mckicad/server.py`:
```python ```python
from kicad_mcp.tools.my_tools import register_my_tools from mckicad.tools.my_tools import register_my_tools
def create_server() -> FastMCP: def create_server() -> FastMCP:
# ... # ...
@ -158,7 +158,7 @@ def create_server() -> FastMCP:
Prompts are reusable templates for common interactions. To add a new prompt: Prompts are reusable templates for common interactions. To add a new prompt:
1. Add your function to an existing prompt file or create a new one in `kicad_mcp/prompts/`: 1. Add your function to an existing prompt file or create a new one in `mckicad/prompts/`:
```python ```python
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp import FastMCP
@ -185,10 +185,10 @@ def register_my_prompts(mcp: FastMCP) -> None:
return prompt return prompt
``` ```
2. Register your prompts in `kicad_mcp/server.py`: 2. Register your prompts in `mckicad/server.py`:
```python ```python
from kicad_mcp.prompts.my_prompts import register_my_prompts from mckicad.prompts.my_prompts import register_my_prompts
def create_server() -> FastMCP: def create_server() -> FastMCP:
# ... # ...
@ -201,7 +201,7 @@ def create_server() -> FastMCP:
The KiCad MCP Server uses a typed lifespan context to share data across requests: The KiCad MCP Server uses a typed lifespan context to share data across requests:
```python ```python
from kicad_mcp.context import KiCadAppContext from mckicad.context import KiCadAppContext
@mcp.tool() @mcp.tool()
def my_tool(parameter: str, ctx: Context) -> Dict[str, Any]: def my_tool(parameter: str, ctx: Context) -> Dict[str, Any]:

View File

@ -120,7 +120,7 @@ The pattern recognition system is designed to be extensible. If you find that ce
### Adding New Component Patterns ### Adding New Component Patterns
The pattern recognition is primarily based on regular expression matching of component values and library IDs. The patterns are defined in the `kicad_mcp/utils/pattern_recognition.py` file. The pattern recognition is primarily based on regular expression matching of component values and library IDs. The patterns are defined in the `mckicad/utils/pattern_recognition.py` file.
For example, to add support for a new microcontroller family, you could update the `mcu_patterns` dictionary in the `identify_microcontrollers()` function: For example, to add support for a new microcontroller family, you could update the `mcu_patterns` dictionary in the `identify_microcontrollers()` function:
@ -139,7 +139,7 @@ Similarly, you can add patterns for new sensors, power supply ICs, or other comp
### Adding New Circuit Recognition Functions ### Adding New Circuit Recognition Functions
For entirely new types of circuits, you can add new recognition functions in the `kicad_mcp/utils/pattern_recognition.py` file, following the pattern of existing functions. For entirely new types of circuits, you can add new recognition functions in the `mckicad/utils/pattern_recognition.py` file, following the pattern of existing functions.
For example, you might add: For example, you might add:
@ -150,7 +150,7 @@ def identify_motor_drivers(components: Dict[str, Any], nets: Dict[str, Any]) ->
... ...
``` ```
Then, update the `identify_circuit_patterns()` function in `kicad_mcp/tools/pattern_tools.py` to call your new function and include its results. Then, update the `identify_circuit_patterns()` function in `mckicad/tools/pattern_tools.py` to call your new function and include its results.
### Contributing Your Extensions ### Contributing Your Extensions
@ -237,7 +237,7 @@ The pattern recognition system relies on a community-driven database of componen
If you work with components that aren't being recognized: If you work with components that aren't being recognized:
1. Check the current patterns in `kicad_mcp/utils/pattern_recognition.py` 1. Check the current patterns in `mckicad/utils/pattern_recognition.py`
2. Add your own patterns for components you use 2. Add your own patterns for components you use
3. Submit a pull request to share with the community 3. Submit a pull request to share with the community

View File

@ -63,9 +63,9 @@ This guide helps you troubleshoot common issues with the KiCad MCP Server.
{ {
"mcpServers": { "mcpServers": {
"kicad": { "kicad": {
"command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/venv/bin/python", "command": "/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/venv/bin/python",
"args": [ "args": [
"/ABSOLUTE/PATH/TO/YOUR/PROJECT/kicad-mcp/main.py" "/ABSOLUTE/PATH/TO/YOUR/PROJECT/mckicad/main.py"
] ]
} }
} }

10
main.py
View File

@ -8,12 +8,12 @@ import sys
import logging # Import logging module import logging # Import logging module
# Must import config BEFORE env potentially overrides it via os.environ # Must import config BEFORE env potentially overrides it via os.environ
from kicad_mcp.config import KICAD_USER_DIR, ADDITIONAL_SEARCH_PATHS from mckicad.config import KICAD_USER_DIR, ADDITIONAL_SEARCH_PATHS
from kicad_mcp.server import main as server_main from mckicad.server import main as server_main
from kicad_mcp.utils.env import load_dotenv from mckicad.utils.env import load_dotenv
# --- Setup Logging --- # --- Setup Logging ---
log_file = os.path.join(os.path.dirname(__file__), 'kicad-mcp.log') log_file = os.path.join(os.path.dirname(__file__), 'mckicad.log')
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,
format='%(asctime)s - %(levelname)s - [PID:%(process)d] - %(message)s', format='%(asctime)s - %(levelname)s - [PID:%(process)d] - %(message)s',
@ -49,7 +49,7 @@ logging.info(f"os.environ['KICAD_SEARCH_PATHS'] after load_dotenv: {effective_se
# Re-log the values imported from config.py to see if they reflect os.environ changes # Re-log the values imported from config.py to see if they reflect os.environ changes
# (This depends on config.py using os.getenv internally AFTER load_dotenv runs) # (This depends on config.py using os.getenv internally AFTER load_dotenv runs)
try: try:
from kicad_mcp import config from mckicad import config
import importlib import importlib
importlib.reload(config) # Attempt to force re-reading config importlib.reload(config) # Attempt to force re-reading config
logging.info(f"Effective KICAD_USER_DIR from config.py after reload: {config.KICAD_USER_DIR}") logging.info(f"Effective KICAD_USER_DIR from config.py after reload: {config.KICAD_USER_DIR}")

View File

@ -9,8 +9,8 @@ from fastmcp import FastMCP
import pandas as pd import pandas as pd
# Import the helper functions from bom_tools.py to avoid code duplication # Import the helper functions from bom_tools.py to avoid code duplication
from kicad_mcp.tools.bom_tools import analyze_bom_data, parse_bom_file from mckicad.tools.bom_tools import analyze_bom_data, parse_bom_file
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
def register_bom_resources(mcp: FastMCP) -> None: def register_bom_resources(mcp: FastMCP) -> None:

View File

@ -6,9 +6,9 @@ import os
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.tools.drc_impl.cli_drc import run_drc_via_cli from mckicad.tools.drc_impl.cli_drc import run_drc_via_cli
from kicad_mcp.utils.drc_history import get_drc_history from mckicad.utils.drc_history import get_drc_history
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
def register_drc_resources(mcp: FastMCP) -> None: def register_drc_resources(mcp: FastMCP) -> None:

View File

@ -6,8 +6,8 @@ import os
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.netlist_parser import analyze_netlist, extract_netlist from mckicad.utils.netlist_parser import analyze_netlist, extract_netlist
def register_netlist_resources(mcp: FastMCP) -> None: def register_netlist_resources(mcp: FastMCP) -> None:

View File

@ -6,9 +6,9 @@ import os
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.netlist_parser import extract_netlist from mckicad.utils.netlist_parser import extract_netlist
from kicad_mcp.utils.pattern_recognition import ( from mckicad.utils.pattern_recognition import (
identify_amplifiers, identify_amplifiers,
identify_digital_interfaces, identify_digital_interfaces,
identify_filters, identify_filters,

View File

@ -6,7 +6,7 @@ import os
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files, load_project_json from mckicad.utils.file_utils import get_project_files, load_project_json
def register_project_resources(mcp: FastMCP) -> None: def register_project_resources(mcp: FastMCP) -> None:

View File

@ -12,37 +12,37 @@ import signal
from fastmcp import FastMCP from fastmcp import FastMCP
# Import context management # Import context management
from kicad_mcp.context import kicad_lifespan from mckicad.context import kicad_lifespan
from kicad_mcp.prompts.bom_prompts import register_bom_prompts from mckicad.prompts.bom_prompts import register_bom_prompts
from kicad_mcp.prompts.drc_prompt import register_drc_prompts from mckicad.prompts.drc_prompt import register_drc_prompts
from kicad_mcp.prompts.pattern_prompts import register_pattern_prompts from mckicad.prompts.pattern_prompts import register_pattern_prompts
# Import prompt handlers # Import prompt handlers
from kicad_mcp.prompts.templates import register_prompts from mckicad.prompts.templates import register_prompts
from kicad_mcp.resources.bom_resources import register_bom_resources from mckicad.resources.bom_resources import register_bom_resources
from kicad_mcp.resources.drc_resources import register_drc_resources from mckicad.resources.drc_resources import register_drc_resources
from kicad_mcp.resources.files import register_file_resources from mckicad.resources.files import register_file_resources
from kicad_mcp.resources.netlist_resources import register_netlist_resources from mckicad.resources.netlist_resources import register_netlist_resources
from kicad_mcp.resources.pattern_resources import register_pattern_resources from mckicad.resources.pattern_resources import register_pattern_resources
# Import resource handlers # Import resource handlers
from kicad_mcp.resources.projects import register_project_resources from mckicad.resources.projects import register_project_resources
from kicad_mcp.tools.advanced_drc_tools import register_advanced_drc_tools from mckicad.tools.advanced_drc_tools import register_advanced_drc_tools
from kicad_mcp.tools.ai_tools import register_ai_tools from mckicad.tools.ai_tools import register_ai_tools
from kicad_mcp.tools.analysis_tools import register_analysis_tools from mckicad.tools.analysis_tools import register_analysis_tools
from kicad_mcp.tools.bom_tools import register_bom_tools from mckicad.tools.bom_tools import register_bom_tools
from kicad_mcp.tools.drc_tools import register_drc_tools from mckicad.tools.drc_tools import register_drc_tools
from kicad_mcp.tools.export_tools import register_export_tools from mckicad.tools.export_tools import register_export_tools
from kicad_mcp.tools.layer_tools import register_layer_tools from mckicad.tools.layer_tools import register_layer_tools
from kicad_mcp.tools.model3d_tools import register_model3d_tools from mckicad.tools.model3d_tools import register_model3d_tools
from kicad_mcp.tools.netlist_tools import register_netlist_tools from mckicad.tools.netlist_tools import register_netlist_tools
from kicad_mcp.tools.pattern_tools import register_pattern_tools from mckicad.tools.pattern_tools import register_pattern_tools
from kicad_mcp.tools.project_automation import register_project_automation_tools from mckicad.tools.project_automation import register_project_automation_tools
# Import tool handlers # Import tool handlers
from kicad_mcp.tools.project_tools import register_project_tools from mckicad.tools.project_tools import register_project_tools
from kicad_mcp.tools.routing_tools import register_routing_tools from mckicad.tools.routing_tools import register_routing_tools
from kicad_mcp.tools.symbol_tools import register_symbol_tools from mckicad.tools.symbol_tools import register_symbol_tools
# Track cleanup handlers # Track cleanup handlers
cleanup_handlers = [] cleanup_handlers = []
@ -196,7 +196,7 @@ def create_server() -> FastMCP:
"""Clean up any temporary directories created by the server.""" """Clean up any temporary directories created by the server."""
import shutil import shutil
from kicad_mcp.utils.temp_dir_manager import get_temp_dirs from mckicad.utils.temp_dir_manager import get_temp_dirs
temp_dirs = get_temp_dirs() temp_dirs = get_temp_dirs()
logging.info(f"Cleaning up {len(temp_dirs)} temporary directories") logging.info(f"Cleaning up {len(temp_dirs)} temporary directories")

View File

@ -9,8 +9,8 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.advanced_drc import RuleSeverity, RuleType, create_drc_manager from mckicad.utils.advanced_drc import RuleSeverity, RuleType, create_drc_manager
from kicad_mcp.utils.path_validator import validate_kicad_file from mckicad.utils.path_validator import validate_kicad_file
def register_advanced_drc_tools(mcp: FastMCP) -> None: def register_advanced_drc_tools(mcp: FastMCP) -> None:

View File

@ -9,10 +9,10 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.component_utils import ComponentType, get_component_type from mckicad.utils.component_utils import ComponentType, get_component_type
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.netlist_parser import parse_netlist_file from mckicad.utils.netlist_parser import parse_netlist_file
from kicad_mcp.utils.pattern_recognition import analyze_circuit_patterns from mckicad.utils.pattern_recognition import analyze_circuit_patterns
def register_ai_tools(mcp: FastMCP) -> None: def register_ai_tools(mcp: FastMCP) -> None:

View File

@ -9,8 +9,8 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.ipc_client import check_kicad_availability, kicad_ipc_session from mckicad.utils.ipc_client import check_kicad_availability, kicad_ipc_session
def register_analysis_tools(mcp: FastMCP) -> None: def register_analysis_tools(mcp: FastMCP) -> None:

View File

@ -10,7 +10,7 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
import pandas as pd import pandas as pd
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
def register_bom_tools(mcp: FastMCP) -> None: def register_bom_tools(mcp: FastMCP) -> None:
@ -644,7 +644,7 @@ async def export_bom_with_cli(
# Define the command based on operating system # Define the command based on operating system
if system == "Darwin": # macOS if system == "Darwin": # macOS
from kicad_mcp.config import KICAD_APP_PATH from mckicad.config import KICAD_APP_PATH
# Path to KiCad command-line tools on macOS # Path to KiCad command-line tools on macOS
kicad_cli = os.path.join(KICAD_APP_PATH, "Contents/MacOS/kicad-cli") kicad_cli = os.path.join(KICAD_APP_PATH, "Contents/MacOS/kicad-cli")
@ -660,7 +660,7 @@ async def export_bom_with_cli(
cmd = [kicad_cli, "sch", "export", "bom", "--output", output_file, schematic_file] cmd = [kicad_cli, "sch", "export", "bom", "--output", output_file, schematic_file]
elif system == "Windows": elif system == "Windows":
from kicad_mcp.config import KICAD_APP_PATH from mckicad.config import KICAD_APP_PATH
# Path to KiCad command-line tools on Windows # Path to KiCad command-line tools on Windows
kicad_cli = os.path.join(KICAD_APP_PATH, "bin", "kicad-cli.exe") kicad_cli = os.path.join(KICAD_APP_PATH, "bin", "kicad-cli.exe")

View File

@ -10,7 +10,7 @@ from typing import Any
from mcp.server.fastmcp import Context from mcp.server.fastmcp import Context
from kicad_mcp.config import system from mckicad.config import system
async def run_drc_via_cli(pcb_file: str) -> dict[str, Any]: async def run_drc_via_cli(pcb_file: str) -> dict[str, Any]:

View File

@ -10,9 +10,9 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
# Import implementations # Import implementations
from kicad_mcp.tools.drc_impl.cli_drc import run_drc_via_cli from mckicad.tools.drc_impl.cli_drc import run_drc_via_cli
from kicad_mcp.utils.drc_history import compare_with_previous, get_drc_history, save_drc_result from mckicad.utils.drc_history import compare_with_previous, get_drc_history, save_drc_result
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
def register_drc_tools(mcp: FastMCP) -> None: def register_drc_tools(mcp: FastMCP) -> None:
@ -96,7 +96,7 @@ def register_drc_tools(mcp: FastMCP) -> None:
print("Using kicad-cli for DRC") print("Using kicad-cli for DRC")
# Use synchronous DRC check # Use synchronous DRC check
try: try:
from kicad_mcp.tools.drc_impl.cli_drc import run_drc_via_cli_sync from mckicad.tools.drc_impl.cli_drc import run_drc_via_cli_sync
drc_results = run_drc_via_cli_sync(pcb_file) drc_results = run_drc_via_cli_sync(pcb_file)
except ImportError: except ImportError:
# Fallback - call the async version but handle it differently # Fallback - call the async version but handle it differently

View File

@ -10,8 +10,8 @@ import subprocess
from fastmcp import FastMCP from fastmcp import FastMCP
from fastmcp.utilities.types import Image from fastmcp.utilities.types import Image
from kicad_mcp.config import KICAD_APP_PATH, system from mckicad.config import KICAD_APP_PATH, system
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
def register_export_tools(mcp: FastMCP) -> None: def register_export_tools(mcp: FastMCP) -> None:

View File

@ -9,8 +9,8 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.layer_stackup import create_stackup_analyzer from mckicad.utils.layer_stackup import create_stackup_analyzer
from kicad_mcp.utils.path_validator import validate_kicad_file from mckicad.utils.path_validator import validate_kicad_file
def register_layer_tools(mcp: FastMCP) -> None: def register_layer_tools(mcp: FastMCP) -> None:

View File

@ -10,12 +10,12 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.model3d_analyzer import ( from mckicad.utils.model3d_analyzer import (
Model3DAnalyzer, Model3DAnalyzer,
analyze_pcb_3d_models, analyze_pcb_3d_models,
get_mechanical_constraints, get_mechanical_constraints,
) )
from kicad_mcp.utils.path_validator import validate_kicad_file from mckicad.utils.path_validator import validate_kicad_file
def register_model3d_tools(mcp: FastMCP) -> None: def register_model3d_tools(mcp: FastMCP) -> None:

View File

@ -7,8 +7,8 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.netlist_parser import analyze_netlist, extract_netlist from mckicad.utils.netlist_parser import analyze_netlist, extract_netlist
def register_netlist_tools(mcp: FastMCP) -> None: def register_netlist_tools(mcp: FastMCP) -> None:

View File

@ -7,9 +7,9 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.netlist_parser import analyze_netlist, extract_netlist from mckicad.utils.netlist_parser import analyze_netlist, extract_netlist
from kicad_mcp.utils.pattern_recognition import ( from mckicad.utils.pattern_recognition import (
identify_amplifiers, identify_amplifiers,
identify_digital_interfaces, identify_digital_interfaces,
identify_filters, identify_filters,

View File

@ -13,9 +13,9 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.freerouting_engine import FreeRoutingEngine from mckicad.utils.freerouting_engine import FreeRoutingEngine
from kicad_mcp.utils.ipc_client import check_kicad_availability from mckicad.utils.ipc_client import check_kicad_availability
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -8,8 +8,8 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files, load_project_json from mckicad.utils.file_utils import get_project_files, load_project_json
from kicad_mcp.utils.kicad_utils import find_kicad_projects, open_kicad_project from mckicad.utils.kicad_utils import find_kicad_projects, open_kicad_project
# Get PID for logging # Get PID for logging
# _PID = os.getpid() # _PID = os.getpid()

View File

@ -10,9 +10,9 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.freerouting_engine import FreeRoutingEngine, check_routing_prerequisites from mckicad.utils.freerouting_engine import FreeRoutingEngine, check_routing_prerequisites
from kicad_mcp.utils.ipc_client import ( from mckicad.utils.ipc_client import (
check_kicad_availability, check_kicad_availability,
kicad_ipc_session, kicad_ipc_session,
) )

View File

@ -10,7 +10,7 @@ from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from kicad_mcp.utils.symbol_library import create_symbol_analyzer from mckicad.utils.symbol_library import create_symbol_analyzer
def register_symbol_tools(mcp: FastMCP) -> None: def register_symbol_tools(mcp: FastMCP) -> None:

View File

@ -11,8 +11,8 @@ from typing import Any
from fastmcp import Context, FastMCP from fastmcp import Context, FastMCP
from kicad_mcp.utils.boundary_validator import BoundaryValidator from mckicad.utils.boundary_validator import BoundaryValidator
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
async def validate_project_boundaries(project_path: str = None) -> dict[str, Any]: async def validate_project_boundaries(project_path: str = None) -> dict[str, Any]:

View File

@ -10,8 +10,8 @@ from enum import Enum
import json import json
from typing import Any from typing import Any
from kicad_mcp.utils.component_layout import ComponentLayoutManager, SchematicBounds from mckicad.utils.component_layout import ComponentLayoutManager, SchematicBounds
from kicad_mcp.utils.coordinate_converter import CoordinateConverter, validate_position from mckicad.utils.coordinate_converter import CoordinateConverter, validate_position
class ValidationSeverity(Enum): class ValidationSeverity(Enum):

View File

@ -15,11 +15,11 @@ from typing import Any
if platform.system() == "Windows": if platform.system() == "Windows":
# Windows: Use APPDATA or LocalAppData # Windows: Use APPDATA or LocalAppData
DRC_HISTORY_DIR = os.path.join( DRC_HISTORY_DIR = os.path.join(
os.environ.get("APPDATA", os.path.expanduser("~")), "kicad_mcp", "drc_history" os.environ.get("APPDATA", os.path.expanduser("~")), "mckicad", "drc_history"
) )
else: else:
# macOS/Linux: Use ~/.kicad_mcp/drc_history # macOS/Linux: Use ~/.mckicad/drc_history
DRC_HISTORY_DIR = os.path.expanduser("~/.kicad_mcp/drc_history") DRC_HISTORY_DIR = os.path.expanduser("~/.mckicad/drc_history")
def ensure_history_dir() -> None: def ensure_history_dir() -> None:

View File

@ -6,7 +6,7 @@ import json
import os import os
from typing import Any from typing import Any
from kicad_mcp.utils.kicad_utils import get_project_name_from_path from mckicad.utils.kicad_utils import get_project_name_from_path
def get_project_files(project_path: str) -> dict[str, str]: def get_project_files(project_path: str) -> dict[str, str]:
@ -18,7 +18,7 @@ def get_project_files(project_path: str) -> dict[str, str]:
Returns: Returns:
Dictionary mapping file types to file paths Dictionary mapping file types to file paths
""" """
from kicad_mcp.config import DATA_EXTENSIONS, KICAD_EXTENSIONS from mckicad.config import DATA_EXTENSIONS, KICAD_EXTENSIONS
project_dir = os.path.dirname(project_path) project_dir = os.path.dirname(project_path)
project_name = get_project_name_from_path(project_path) project_name = get_project_name_from_path(project_path)

View File

@ -19,7 +19,7 @@ from typing import Any
from kipy.board_types import BoardLayer from kipy.board_types import BoardLayer
from kicad_mcp.utils.ipc_client import kicad_ipc_session from mckicad.utils.ipc_client import kicad_ipc_session
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -650,7 +650,7 @@ def check_routing_prerequisites() -> dict[str, Any]:
# Check KiCad IPC API # Check KiCad IPC API
try: try:
from kicad_mcp.utils.ipc_client import check_kicad_availability from mckicad.utils.ipc_client import check_kicad_availability
kicad_status = check_kicad_availability() kicad_status = check_kicad_availability()
status["components"]["kicad_ipc"] = kicad_status status["components"]["kicad_ipc"] = kicad_status
except Exception as e: except Exception as e:

View File

@ -6,7 +6,7 @@ import os
import shutil import shutil
import subprocess import subprocess
from kicad_mcp.config import system from mckicad.config import system
def check_for_cli_api() -> bool: def check_for_cli_api() -> bool:

View File

@ -8,7 +8,7 @@ import subprocess
import sys # Add sys import import sys # Add sys import
from typing import Any from typing import Any
from kicad_mcp.config import ( from mckicad.config import (
ADDITIONAL_SEARCH_PATHS, ADDITIONAL_SEARCH_PATHS,
KICAD_APP_PATH, KICAD_APP_PATH,
KICAD_EXTENSIONS, KICAD_EXTENSIONS,

View File

@ -8,7 +8,7 @@ and ensure file operations are restricted to safe directories.
import os import os
import pathlib import pathlib
from kicad_mcp.config import KICAD_EXTENSIONS from mckicad.config import KICAD_EXTENSIONS
class PathValidationError(Exception): class PathValidationError(Exception):

View File

@ -5,7 +5,7 @@ Circuit pattern recognition functions for KiCad schematics.
import re import re
from typing import Any from typing import Any
from kicad_mcp.utils.component_utils import ( from mckicad.utils.component_utils import (
extract_frequency_from_value, extract_frequency_from_value,
extract_voltage_from_regulator, extract_voltage_from_regulator,
) )
@ -1039,7 +1039,7 @@ def analyze_circuit_patterns(schematic_file: str) -> dict[str, Any]:
Dictionary of identified patterns Dictionary of identified patterns
""" """
try: try:
from kicad_mcp.utils.netlist_parser import parse_netlist_file from mckicad.utils.netlist_parser import parse_netlist_file
# Parse netlist to get components and nets # Parse netlist to get components and nets
netlist_data = parse_netlist_file(schematic_file) netlist_data = parse_netlist_file(schematic_file)

View File

@ -189,7 +189,7 @@ class SecureSubprocessRunner:
raise SecureSubprocessError(f"Command failed: {e}") from e raise SecureSubprocessError(f"Command failed: {e}") from e
def create_temp_file( def create_temp_file(
self, suffix: str = "", prefix: str = "kicad_mcp_", content: str | None = None self, suffix: str = "", prefix: str = "mckicad_", content: str | None = None
) -> str: ) -> str:
""" """
Create a temporary file within validated directories. Create a temporary file within validated directories.
@ -288,7 +288,7 @@ async def run_kicad_command_async(
def create_temp_file( def create_temp_file(
suffix: str = "", prefix: str = "kicad_mcp_", content: str | None = None suffix: str = "", prefix: str = "mckicad_", content: str | None = None
) -> str: ) -> str:
"""Convenience function to create temporary file.""" """Convenience function to create temporary file."""
return get_subprocess_runner().create_temp_file(suffix, prefix, content) return get_subprocess_runner().create_temp_file(suffix, prefix, content)

View File

@ -3,7 +3,7 @@ requires = ["hatchling>=1.28.0"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[project] [project]
name = "kicad-mcp" name = "mckicad"
version = "0.1.0" version = "0.1.0"
description = "Model Context Protocol (MCP) server for KiCad electronic design automation (EDA) files" description = "Model Context Protocol (MCP) server for KiCad electronic design automation (EDA) files"
readme = "README.md" readme = "README.md"
@ -50,12 +50,12 @@ dependencies = [
] ]
[project.urls] [project.urls]
"Homepage" = "https://github.com/lamaalrajih/kicad-mcp" "Homepage" = "https://github.com/lamaalrajih/mckicad"
"Bug Tracker" = "https://github.com/lamaalrajih/kicad-mcp/issues" "Bug Tracker" = "https://github.com/lamaalrajih/mckicad/issues"
"Documentation" = "https://github.com/lamaalrajih/kicad-mcp#readme" "Documentation" = "https://github.com/lamaalrajih/mckicad#readme"
[project.scripts] [project.scripts]
kicad-mcp = "kicad_mcp.server:main" mckicad = "mckicad.server:main"
[dependency-groups] [dependency-groups]
dev = [ dev = [
@ -119,12 +119,12 @@ unfixable = [
"D103", # Missing docstring in public function "D103", # Missing docstring in public function
"SLF001", # Private member accessed "SLF001", # Private member accessed
] ]
"kicad_mcp/config.py" = [ "mckicad/config.py" = [
"E501", # Long lines in config are ok "E501", # Long lines in config are ok
] ]
[tool.ruff.lint.isort] [tool.ruff.lint.isort]
known-first-party = ["kicad_mcp"] known-first-party = ["mckicad"]
force-sort-within-sections = true force-sort-within-sections = true
[tool.ruff.format] [tool.ruff.format]
@ -164,7 +164,7 @@ minversion = "7.0"
addopts = [ addopts = [
"--strict-markers", "--strict-markers",
"--strict-config", "--strict-config",
"--cov=kicad_mcp", "--cov=mckicad",
"--cov-report=term-missing", "--cov-report=term-missing",
"--cov-report=html:htmlcov", "--cov-report=html:htmlcov",
"--cov-report=xml", "--cov-report=xml",
@ -191,11 +191,11 @@ filterwarnings = [
] ]
[tool.coverage.run] [tool.coverage.run]
source = ["kicad_mcp"] source = ["mckicad"]
branch = true branch = true
omit = [ omit = [
"tests/*", "tests/*",
"kicad_mcp/__init__.py", "mckicad/__init__.py",
"*/migrations/*", "*/migrations/*",
"*/venv/*", "*/venv/*",
"*/.venv/*", "*/.venv/*",
@ -227,8 +227,8 @@ skips = ["*_test.py", "*/test_*.py"]
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
where = ["."] where = ["."]
include = ["kicad_mcp*"] include = ["mckicad*"]
exclude = ["tests*", "docs*"] exclude = ["tests*", "docs*"]
[tool.setuptools.package-data] [tool.setuptools.package-data]
"kicad_mcp" = ["prompts/*.txt", "resources/**/*.json"] "mckicad" = ["prompts/*.txt", "resources/**/*.json"]

View File

@ -36,15 +36,15 @@ def main():
exit_code = 0 exit_code = 0
# Run linting # Run linting
exit_code |= run_command(["uv", "run", "ruff", "check", "kicad_mcp/", "tests/"], "Lint check") exit_code |= run_command(["uv", "run", "ruff", "check", "mckicad/", "tests/"], "Lint check")
# Run formatting check # Run formatting check
exit_code |= run_command( exit_code |= run_command(
["uv", "run", "ruff", "format", "--check", "kicad_mcp/", "tests/"], "Format check" ["uv", "run", "ruff", "format", "--check", "mckicad/", "tests/"], "Format check"
) )
# Run type checking # Run type checking
exit_code |= run_command(["uv", "run", "mypy", "kicad_mcp/"], "Type check") exit_code |= run_command(["uv", "run", "mypy", "mckicad/"], "Type check")
# Run tests # Run tests
exit_code |= run_command(["uv", "run", "python", "-m", "pytest", "tests/", "-v"], "Unit tests") exit_code |= run_command(["uv", "run", "python", "-m", "pytest", "tests/", "-v"], "Unit tests")

View File

@ -8,13 +8,13 @@ import sys
import time import time
from pathlib import Path from pathlib import Path
# Add the kicad_mcp module to path # Add the mckicad module to path
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
from kicad_mcp.utils.ipc_client import KiCadIPCClient, check_kicad_availability from mckicad.utils.ipc_client import KiCadIPCClient, check_kicad_availability
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.netlist_parser import extract_netlist, analyze_netlist from mckicad.utils.netlist_parser import extract_netlist, analyze_netlist
from kicad_mcp.tools.validation_tools import validate_project_boundaries from mckicad.tools.validation_tools import validate_project_boundaries
# Our new demo project # Our new demo project
PROJECT_PATH = "/home/rpm/claude/Demo_PCB_Project/Smart_Sensor_Board.kicad_pro" PROJECT_PATH = "/home/rpm/claude/Demo_PCB_Project/Smart_Sensor_Board.kicad_pro"

View File

@ -10,13 +10,13 @@ from basic project analysis to real-time board manipulation and routing.
import sys import sys
from pathlib import Path from pathlib import Path
# Add the kicad_mcp module to path # Add the mckicad module to path
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
from kicad_mcp.utils.ipc_client import KiCadIPCClient from mckicad.utils.ipc_client import KiCadIPCClient
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites from mckicad.utils.freerouting_engine import check_routing_prerequisites
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.netlist_parser import extract_netlist, analyze_netlist from mckicad.utils.netlist_parser import extract_netlist, analyze_netlist
# Test project path # Test project path
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro" PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"

View File

@ -9,13 +9,13 @@ This demonstrates the complete, fully-functional EDA automation platform.
import sys import sys
from pathlib import Path from pathlib import Path
# Add the kicad_mcp module to path # Add the mckicad module to path
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
from kicad_mcp.utils.ipc_client import KiCadIPCClient from mckicad.utils.ipc_client import KiCadIPCClient
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites from mckicad.utils.freerouting_engine import check_routing_prerequisites
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.netlist_parser import extract_netlist, analyze_netlist from mckicad.utils.netlist_parser import extract_netlist, analyze_netlist
# Test project path # Test project path
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro" PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"

View File

@ -12,14 +12,14 @@ import time
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
# Add the kicad_mcp module to path # Add the mckicad module to path
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
from kicad_mcp.utils.ipc_client import KiCadIPCClient from mckicad.utils.ipc_client import KiCadIPCClient
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites from mckicad.utils.freerouting_engine import check_routing_prerequisites
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.netlist_parser import extract_netlist, analyze_netlist from mckicad.utils.netlist_parser import extract_netlist, analyze_netlist
from kicad_mcp.server import create_server from mckicad.server import create_server
# Test project # Test project
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro" PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"

View File

@ -10,11 +10,11 @@ import os
import tempfile import tempfile
import subprocess import subprocess
# Add the kicad_mcp module to path # Add the mckicad module to path
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
from kicad_mcp.utils.ipc_client import KiCadIPCClient from mckicad.utils.ipc_client import KiCadIPCClient
from kicad_mcp.utils.freerouting_engine import FreeRoutingEngine, check_routing_prerequisites from mckicad.utils.freerouting_engine import FreeRoutingEngine, check_routing_prerequisites
def test_routing_prerequisites(): def test_routing_prerequisites():
"""Test routing prerequisites and components.""" """Test routing prerequisites and components."""

View File

@ -10,7 +10,7 @@ import os
import tempfile import tempfile
import subprocess import subprocess
# Add the kicad_mcp module to path # Add the mckicad module to path
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro" PROJECT_PATH = "/home/rpm/claude/MLX90640-Thermal-Camera/PCB/Thermal_Camera.kicad_pro"

View File

@ -12,14 +12,14 @@ import logging
import sys import sys
from pathlib import Path from pathlib import Path
# Add the kicad_mcp module to path # Add the mckicad module to path
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites from mckicad.utils.freerouting_engine import check_routing_prerequisites
from kicad_mcp.utils.ipc_client import check_kicad_availability from mckicad.utils.ipc_client import check_kicad_availability
from kicad_mcp.tools.analysis_tools import register_analysis_tools from mckicad.tools.analysis_tools import register_analysis_tools
from kicad_mcp.tools.routing_tools import register_routing_tools from mckicad.tools.routing_tools import register_routing_tools
from kicad_mcp.tools.ai_tools import register_ai_tools from mckicad.tools.ai_tools import register_ai_tools
# Set up logging # Set up logging
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
@ -88,7 +88,7 @@ def test_project_validation():
logger.info("Testing project validation...") logger.info("Testing project validation...")
try: try:
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
if not Path(PROJECT_PATH).exists(): if not Path(PROJECT_PATH).exists():
logger.error(f"Test project not found: {PROJECT_PATH}") logger.error(f"Test project not found: {PROJECT_PATH}")

View File

@ -8,11 +8,11 @@ import sys
import json import json
from pathlib import Path from pathlib import Path
# Add the kicad_mcp module to path # Add the mckicad module to path
sys.path.insert(0, str(Path(__file__).parent)) sys.path.insert(0, str(Path(__file__).parent))
# Import our server and tools # Import our server and tools
from kicad_mcp.server import create_server from mckicad.server import create_server
def test_server_initialization(): def test_server_initialization():
"""Test MCP server initialization and tool registration.""" """Test MCP server initialization and tool registration."""
@ -40,13 +40,13 @@ def test_tool_registration():
try: try:
# Import and test tool registration functions # Import and test tool registration functions
from kicad_mcp.tools.analysis_tools import register_analysis_tools from mckicad.tools.analysis_tools import register_analysis_tools
from kicad_mcp.tools.project_tools import register_project_tools from mckicad.tools.project_tools import register_project_tools
from kicad_mcp.tools.drc_tools import register_drc_tools from mckicad.tools.drc_tools import register_drc_tools
from kicad_mcp.tools.bom_tools import register_bom_tools from mckicad.tools.bom_tools import register_bom_tools
from kicad_mcp.tools.netlist_tools import register_netlist_tools from mckicad.tools.netlist_tools import register_netlist_tools
from kicad_mcp.tools.pattern_tools import register_pattern_tools from mckicad.tools.pattern_tools import register_pattern_tools
from kicad_mcp.tools.export_tools import register_export_tools from mckicad.tools.export_tools import register_export_tools
# Test that registration functions exist # Test that registration functions exist
registration_functions = [ registration_functions = [
@ -80,11 +80,11 @@ def test_resource_registration():
try: try:
# Import resource registration functions # Import resource registration functions
from kicad_mcp.resources.projects import register_project_resources from mckicad.resources.projects import register_project_resources
from kicad_mcp.resources.files import register_file_resources from mckicad.resources.files import register_file_resources
from kicad_mcp.resources.drc_resources import register_drc_resources from mckicad.resources.drc_resources import register_drc_resources
from kicad_mcp.resources.bom_resources import register_bom_resources from mckicad.resources.bom_resources import register_bom_resources
from kicad_mcp.resources.netlist_resources import register_netlist_resources from mckicad.resources.netlist_resources import register_netlist_resources
resource_functions = [ resource_functions = [
("project_resources", register_project_resources), ("project_resources", register_project_resources),
@ -115,10 +115,10 @@ def test_prompt_registration():
try: try:
# Import prompt registration functions # Import prompt registration functions
from kicad_mcp.prompts.templates import register_prompts from mckicad.prompts.templates import register_prompts
from kicad_mcp.prompts.drc_prompt import register_drc_prompts from mckicad.prompts.drc_prompt import register_drc_prompts
from kicad_mcp.prompts.bom_prompts import register_bom_prompts from mckicad.prompts.bom_prompts import register_bom_prompts
from kicad_mcp.prompts.pattern_prompts import register_pattern_prompts from mckicad.prompts.pattern_prompts import register_pattern_prompts
prompt_functions = [ prompt_functions = [
("templates", register_prompts), ("templates", register_prompts),
@ -148,10 +148,10 @@ def test_core_functionality():
try: try:
# Test key utility imports # Test key utility imports
from kicad_mcp.utils.file_utils import get_project_files from mckicad.utils.file_utils import get_project_files
from kicad_mcp.utils.ipc_client import KiCadIPCClient, check_kicad_availability from mckicad.utils.ipc_client import KiCadIPCClient, check_kicad_availability
from kicad_mcp.utils.freerouting_engine import check_routing_prerequisites from mckicad.utils.freerouting_engine import check_routing_prerequisites
from kicad_mcp.utils.netlist_parser import extract_netlist from mckicad.utils.netlist_parser import extract_netlist
print(f"📦 Core Utilities Available:") print(f"📦 Core Utilities Available:")
print(f" ✅ file_utils: Project file management") print(f" ✅ file_utils: Project file management")
@ -189,9 +189,9 @@ def test_server_completeness():
try: try:
# Check that the main server creation works # Check that the main server creation works
from kicad_mcp.server import create_server from mckicad.server import create_server
from kicad_mcp.config import KICAD_CLI_TIMEOUT from mckicad.config import KICAD_CLI_TIMEOUT
from kicad_mcp.context import KiCadAppContext from mckicad.context import KiCadAppContext
print(f"📊 Server Components:") print(f"📊 Server Components:")
print(f" ✅ create_server(): Main entry point") print(f" ✅ create_server(): Main entry point")
@ -199,7 +199,7 @@ def test_server_completeness():
print(f" ✅ Context management: {KiCadAppContext.__name__}") print(f" ✅ Context management: {KiCadAppContext.__name__}")
# Verify key constants and configurations # Verify key constants and configurations
from kicad_mcp import config from mckicad import config
config_items = [ config_items = [
'KICAD_CLI_TIMEOUT', 'DEFAULT_KICAD_PATHS', 'KICAD_CLI_TIMEOUT', 'DEFAULT_KICAD_PATHS',

View File

@ -1,5 +1,5 @@
""" """
Tests for the kicad_mcp.config module. Tests for the mckicad.config module.
""" """
import os import os
import platform import platform
@ -11,7 +11,7 @@ class TestConfigModule:
def test_system_detection(self): def test_system_detection(self):
"""Test that system is properly detected.""" """Test that system is properly detected."""
from kicad_mcp.config import system from mckicad.config import system
assert system in ['Darwin', 'Windows', 'Linux'] or isinstance(system, str) assert system in ['Darwin', 'Windows', 'Linux'] or isinstance(system, str)
assert system == platform.system() assert system == platform.system()
@ -22,10 +22,10 @@ class TestConfigModule:
# Need to reload the config module after patching # Need to reload the config module after patching
import importlib import importlib
import kicad_mcp.config import mckicad.config
importlib.reload(kicad_mcp.config) importlib.reload(mckicad.config)
from kicad_mcp.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR from mckicad.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR
assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR
assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app" assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app"
@ -36,10 +36,10 @@ class TestConfigModule:
with patch('platform.system', return_value='Windows'): with patch('platform.system', return_value='Windows'):
import importlib import importlib
import kicad_mcp.config import mckicad.config
importlib.reload(kicad_mcp.config) importlib.reload(mckicad.config)
from kicad_mcp.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR from mckicad.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR
assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR
assert KICAD_APP_PATH == r"C:\Program Files\KiCad" assert KICAD_APP_PATH == r"C:\Program Files\KiCad"
@ -50,10 +50,10 @@ class TestConfigModule:
with patch('platform.system', return_value='Linux'): with patch('platform.system', return_value='Linux'):
import importlib import importlib
import kicad_mcp.config import mckicad.config
importlib.reload(kicad_mcp.config) importlib.reload(mckicad.config)
from kicad_mcp.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR from mckicad.config import KICAD_APP_PATH, KICAD_PYTHON_BASE, KICAD_USER_DIR
assert os.path.expanduser("~/KiCad") == KICAD_USER_DIR assert os.path.expanduser("~/KiCad") == KICAD_USER_DIR
assert KICAD_APP_PATH == "/usr/share/kicad" assert KICAD_APP_PATH == "/usr/share/kicad"
@ -64,17 +64,17 @@ class TestConfigModule:
with patch('platform.system', return_value='FreeBSD'): with patch('platform.system', return_value='FreeBSD'):
import importlib import importlib
import kicad_mcp.config import mckicad.config
importlib.reload(kicad_mcp.config) importlib.reload(mckicad.config)
from kicad_mcp.config import KICAD_APP_PATH, KICAD_USER_DIR from mckicad.config import KICAD_APP_PATH, KICAD_USER_DIR
assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR assert os.path.expanduser("~/Documents/KiCad") == KICAD_USER_DIR
assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app" assert KICAD_APP_PATH == "/Applications/KiCad/KiCad.app"
def test_kicad_extensions(self): def test_kicad_extensions(self):
"""Test KiCad file extension mappings.""" """Test KiCad file extension mappings."""
from kicad_mcp.config import KICAD_EXTENSIONS from mckicad.config import KICAD_EXTENSIONS
expected_keys = ["project", "pcb", "schematic", "design_rules", expected_keys = ["project", "pcb", "schematic", "design_rules",
"worksheet", "footprint", "netlist", "kibot_config"] "worksheet", "footprint", "netlist", "kibot_config"]
@ -86,7 +86,7 @@ class TestConfigModule:
def test_data_extensions(self): def test_data_extensions(self):
"""Test data file extensions list.""" """Test data file extensions list."""
from kicad_mcp.config import DATA_EXTENSIONS from mckicad.config import DATA_EXTENSIONS
assert isinstance(DATA_EXTENSIONS, list) assert isinstance(DATA_EXTENSIONS, list)
assert len(DATA_EXTENSIONS) > 0 assert len(DATA_EXTENSIONS) > 0
@ -97,7 +97,7 @@ class TestConfigModule:
def test_circuit_defaults(self): def test_circuit_defaults(self):
"""Test circuit default parameters.""" """Test circuit default parameters."""
from kicad_mcp.config import CIRCUIT_DEFAULTS from mckicad.config import CIRCUIT_DEFAULTS
required_keys = ["grid_spacing", "component_spacing", "wire_width", required_keys = ["grid_spacing", "component_spacing", "wire_width",
"text_size", "pin_length"] "text_size", "pin_length"]
@ -112,7 +112,7 @@ class TestConfigModule:
def test_common_libraries_structure(self): def test_common_libraries_structure(self):
"""Test common libraries configuration structure.""" """Test common libraries configuration structure."""
from kicad_mcp.config import COMMON_LIBRARIES from mckicad.config import COMMON_LIBRARIES
expected_categories = ["basic", "power", "connectors"] expected_categories = ["basic", "power", "connectors"]
@ -128,7 +128,7 @@ class TestConfigModule:
def test_default_footprints_structure(self): def test_default_footprints_structure(self):
"""Test default footprints configuration structure.""" """Test default footprints configuration structure."""
from kicad_mcp.config import DEFAULT_FOOTPRINTS from mckicad.config import DEFAULT_FOOTPRINTS
# Test that at least some common components are present # Test that at least some common components are present
common_components = ["R", "C", "LED", "D"] common_components = ["R", "C", "LED", "D"]
@ -145,7 +145,7 @@ class TestConfigModule:
def test_timeout_constants(self): def test_timeout_constants(self):
"""Test timeout constants are reasonable values.""" """Test timeout constants are reasonable values."""
from kicad_mcp.config import TIMEOUT_CONSTANTS from mckicad.config import TIMEOUT_CONSTANTS
required_keys = ["kicad_cli_version_check", "kicad_cli_export", required_keys = ["kicad_cli_version_check", "kicad_cli_export",
"application_open", "subprocess_default"] "application_open", "subprocess_default"]
@ -158,7 +158,7 @@ class TestConfigModule:
def test_progress_constants(self): def test_progress_constants(self):
"""Test progress constants are valid percentages.""" """Test progress constants are valid percentages."""
from kicad_mcp.config import PROGRESS_CONSTANTS from mckicad.config import PROGRESS_CONSTANTS
required_keys = ["start", "detection", "setup", "processing", required_keys = ["start", "detection", "setup", "processing",
"finishing", "validation", "complete"] "finishing", "validation", "complete"]
@ -171,7 +171,7 @@ class TestConfigModule:
def test_display_constants(self): def test_display_constants(self):
"""Test display constants are reasonable values.""" """Test display constants are reasonable values."""
from kicad_mcp.config import DISPLAY_CONSTANTS from mckicad.config import DISPLAY_CONSTANTS
assert "bom_preview_limit" in DISPLAY_CONSTANTS assert "bom_preview_limit" in DISPLAY_CONSTANTS
limit = DISPLAY_CONSTANTS["bom_preview_limit"] limit = DISPLAY_CONSTANTS["bom_preview_limit"]
@ -183,11 +183,11 @@ class TestConfigModule:
with patch.dict(os.environ, {"KICAD_SEARCH_PATHS": ""}): with patch.dict(os.environ, {"KICAD_SEARCH_PATHS": ""}):
import importlib import importlib
import kicad_mcp.config import mckicad.config
importlib.reload(kicad_mcp.config) importlib.reload(mckicad.config)
# Should still have default locations if they exist # Should still have default locations if they exist
from kicad_mcp.config import ADDITIONAL_SEARCH_PATHS from mckicad.config import ADDITIONAL_SEARCH_PATHS
assert isinstance(ADDITIONAL_SEARCH_PATHS, list) assert isinstance(ADDITIONAL_SEARCH_PATHS, list)
def test_nonexistent_search_paths_ignored(self): def test_nonexistent_search_paths_ignored(self):
@ -196,10 +196,10 @@ class TestConfigModule:
patch('os.path.exists', return_value=False): patch('os.path.exists', return_value=False):
import importlib import importlib
import kicad_mcp.config import mckicad.config
importlib.reload(kicad_mcp.config) importlib.reload(mckicad.config)
from kicad_mcp.config import ADDITIONAL_SEARCH_PATHS from mckicad.config import ADDITIONAL_SEARCH_PATHS
# Should not contain the nonexistent paths # Should not contain the nonexistent paths
assert "/nonexistent/path1" not in ADDITIONAL_SEARCH_PATHS assert "/nonexistent/path1" not in ADDITIONAL_SEARCH_PATHS
@ -213,10 +213,10 @@ class TestConfigModule:
import importlib import importlib
import kicad_mcp.config import mckicad.config
importlib.reload(kicad_mcp.config) importlib.reload(mckicad.config)
from kicad_mcp.config import ADDITIONAL_SEARCH_PATHS from mckicad.config import ADDITIONAL_SEARCH_PATHS
# Should contain expanded paths # Should contain expanded paths
assert "/home/user/test_path1" in ADDITIONAL_SEARCH_PATHS assert "/home/user/test_path1" in ADDITIONAL_SEARCH_PATHS
@ -224,7 +224,7 @@ class TestConfigModule:
def test_default_project_locations_expanded(self): def test_default_project_locations_expanded(self):
"""Test that default project locations are properly expanded.""" """Test that default project locations are properly expanded."""
from kicad_mcp.config import DEFAULT_PROJECT_LOCATIONS from mckicad.config import DEFAULT_PROJECT_LOCATIONS
assert isinstance(DEFAULT_PROJECT_LOCATIONS, list) assert isinstance(DEFAULT_PROJECT_LOCATIONS, list)
assert len(DEFAULT_PROJECT_LOCATIONS) > 0 assert len(DEFAULT_PROJECT_LOCATIONS) > 0

View File

@ -1,11 +1,11 @@
""" """
Tests for the kicad_mcp.context module. Tests for the mckicad.context module.
""" """
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
import pytest import pytest
from kicad_mcp.context import KiCadAppContext, kicad_lifespan from mckicad.context import KiCadAppContext, kicad_lifespan
class TestKiCadAppContext: class TestKiCadAppContext:
@ -62,7 +62,7 @@ class TestKiCadLifespan:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_lifespan_basic_flow(self, mock_server): async def test_lifespan_basic_flow(self, mock_server):
"""Test basic lifespan flow with successful initialization and cleanup.""" """Test basic lifespan flow with successful initialization and cleanup."""
with patch('kicad_mcp.context.logging') as mock_logging: with patch('mckicad.context.logging') as mock_logging:
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context: async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
# Check context is properly initialized # Check context is properly initialized
assert isinstance(context, KiCadAppContext) assert isinstance(context, KiCadAppContext)
@ -103,7 +103,7 @@ class TestKiCadLifespan:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_lifespan_cache_cleanup(self, mock_server): async def test_lifespan_cache_cleanup(self, mock_server):
"""Test that cache is properly cleared on shutdown.""" """Test that cache is properly cleared on shutdown."""
with patch('kicad_mcp.context.logging') as mock_logging: with patch('mckicad.context.logging') as mock_logging:
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context: async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
# Populate cache # Populate cache
context.cache["test1"] = "value1" context.cache["test1"] = "value1"
@ -116,7 +116,7 @@ class TestKiCadLifespan:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_lifespan_exception_handling(self, mock_server): async def test_lifespan_exception_handling(self, mock_server):
"""Test that cleanup happens even if an exception occurs.""" """Test that cleanup happens even if an exception occurs."""
with patch('kicad_mcp.context.logging') as mock_logging: with patch('mckicad.context.logging') as mock_logging:
with pytest.raises(ValueError): with pytest.raises(ValueError):
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context: async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
context.cache["test"] = "value" context.cache["test"] = "value"
@ -130,8 +130,8 @@ class TestKiCadLifespan:
@pytest.mark.skip(reason="Mock setup complexity - temp dir cleanup not critical") @pytest.mark.skip(reason="Mock setup complexity - temp dir cleanup not critical")
async def test_lifespan_temp_dir_cleanup(self, mock_server): async def test_lifespan_temp_dir_cleanup(self, mock_server):
"""Test temporary directory cleanup functionality.""" """Test temporary directory cleanup functionality."""
with patch('kicad_mcp.context.logging') as mock_logging, \ with patch('mckicad.context.logging') as mock_logging, \
patch('kicad_mcp.context.shutil') as mock_shutil: patch('mckicad.context.shutil') as mock_shutil:
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context: async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
# The current implementation has an empty created_temp_dirs list # The current implementation has an empty created_temp_dirs list
@ -145,8 +145,8 @@ class TestKiCadLifespan:
async def test_lifespan_temp_dir_cleanup_error_handling(self, mock_server): async def test_lifespan_temp_dir_cleanup_error_handling(self, mock_server):
"""Test error handling in temp directory cleanup.""" """Test error handling in temp directory cleanup."""
# Mock the created_temp_dirs to have some directories for testing # Mock the created_temp_dirs to have some directories for testing
with patch('kicad_mcp.context.logging') as mock_logging, \ with patch('mckicad.context.logging') as mock_logging, \
patch('kicad_mcp.context.shutil') as mock_shutil: patch('mckicad.context.shutil') as mock_shutil:
# Patch the created_temp_dirs list in the function scope # Patch the created_temp_dirs list in the function scope
original_lifespan = kicad_lifespan original_lifespan = kicad_lifespan
@ -183,7 +183,7 @@ class TestKiCadLifespan:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_lifespan_logging_messages(self, mock_server): async def test_lifespan_logging_messages(self, mock_server):
"""Test specific logging messages are called correctly.""" """Test specific logging messages are called correctly."""
with patch('kicad_mcp.context.logging') as mock_logging: with patch('mckicad.context.logging') as mock_logging:
async with kicad_lifespan(mock_server, kicad_modules_available=True) as context: async with kicad_lifespan(mock_server, kicad_modules_available=True) as context:
context.cache["test"] = "data" context.cache["test"] = "data"
@ -203,7 +203,7 @@ class TestKiCadLifespan:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_lifespan_empty_cache_no_cleanup_log(self, mock_server): async def test_lifespan_empty_cache_no_cleanup_log(self, mock_server):
"""Test that empty cache doesn't log cleanup message.""" """Test that empty cache doesn't log cleanup message."""
with patch('kicad_mcp.context.logging') as mock_logging: with patch('mckicad.context.logging') as mock_logging:
async with kicad_lifespan(mock_server, kicad_modules_available=False) as context: async with kicad_lifespan(mock_server, kicad_modules_available=False) as context:
# Don't add anything to cache # Don't add anything to cache
pass pass

View File

@ -1,5 +1,5 @@
""" """
Tests for the kicad_mcp.server module. Tests for the mckicad.server module.
""" """
import logging import logging
import signal import signal
@ -7,7 +7,7 @@ from unittest.mock import Mock, call, patch
import pytest import pytest
from kicad_mcp.server import ( from mckicad.server import (
add_cleanup_handler, add_cleanup_handler,
create_server, create_server,
main, main,
@ -23,7 +23,7 @@ class TestCleanupHandlers:
def setup_method(self): def setup_method(self):
"""Reset cleanup handlers before each test.""" """Reset cleanup handlers before each test."""
from kicad_mcp.server import cleanup_handlers from mckicad.server import cleanup_handlers
cleanup_handlers.clear() cleanup_handlers.clear()
def test_add_cleanup_handler(self): def test_add_cleanup_handler(self):
@ -33,7 +33,7 @@ class TestCleanupHandlers:
add_cleanup_handler(dummy_handler) add_cleanup_handler(dummy_handler)
from kicad_mcp.server import cleanup_handlers from mckicad.server import cleanup_handlers
assert dummy_handler in cleanup_handlers assert dummy_handler in cleanup_handlers
def test_add_multiple_cleanup_handlers(self): def test_add_multiple_cleanup_handlers(self):
@ -47,12 +47,12 @@ class TestCleanupHandlers:
add_cleanup_handler(handler1) add_cleanup_handler(handler1)
add_cleanup_handler(handler2) add_cleanup_handler(handler2)
from kicad_mcp.server import cleanup_handlers from mckicad.server import cleanup_handlers
assert handler1 in cleanup_handlers assert handler1 in cleanup_handlers
assert handler2 in cleanup_handlers assert handler2 in cleanup_handlers
assert len(cleanup_handlers) == 2 assert len(cleanup_handlers) == 2
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
def test_run_cleanup_handlers_success(self, mock_logging): def test_run_cleanup_handlers_success(self, mock_logging):
"""Test successful execution of cleanup handlers.""" """Test successful execution of cleanup handlers."""
handler1 = Mock() handler1 = Mock()
@ -69,7 +69,7 @@ class TestCleanupHandlers:
handler2.assert_called_once() handler2.assert_called_once()
mock_logging.info.assert_any_call("Running cleanup handlers...") mock_logging.info.assert_any_call("Running cleanup handlers...")
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
@pytest.mark.skip(reason="Mock handler execution complexity - exception handling works in practice") @pytest.mark.skip(reason="Mock handler execution complexity - exception handling works in practice")
def test_run_cleanup_handlers_with_exception(self, mock_logging): def test_run_cleanup_handlers_with_exception(self, mock_logging):
"""Test cleanup handlers with exceptions.""" """Test cleanup handlers with exceptions."""
@ -91,7 +91,7 @@ class TestCleanupHandlers:
# Should still log success for working handler # Should still log success for working handler
mock_logging.info.assert_any_call("Cleanup handler working_handler completed successfully") mock_logging.info.assert_any_call("Cleanup handler working_handler completed successfully")
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
@pytest.mark.skip(reason="Global state management complexity - double execution prevention works") @pytest.mark.skip(reason="Global state management complexity - double execution prevention works")
def test_run_cleanup_handlers_prevents_double_execution(self, mock_logging): def test_run_cleanup_handlers_prevents_double_execution(self, mock_logging):
"""Test that cleanup handlers don't run twice.""" """Test that cleanup handlers don't run twice."""
@ -113,17 +113,17 @@ class TestServerShutdown:
def setup_method(self): def setup_method(self):
"""Reset server instance before each test.""" """Reset server instance before each test."""
import kicad_mcp.server import mckicad.server
kicad_mcp.server._server_instance = None mckicad.server._server_instance = None
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
def test_shutdown_server_with_instance(self, mock_logging): def test_shutdown_server_with_instance(self, mock_logging):
"""Test shutting down server when instance exists.""" """Test shutting down server when instance exists."""
import kicad_mcp.server import mckicad.server
# Set up mock server instance # Set up mock server instance
mock_server = Mock() mock_server = Mock()
kicad_mcp.server._server_instance = mock_server mckicad.server._server_instance = mock_server
shutdown_server() shutdown_server()
@ -131,9 +131,9 @@ class TestServerShutdown:
mock_logging.info.assert_any_call("KiCad MCP server shutdown complete") mock_logging.info.assert_any_call("KiCad MCP server shutdown complete")
# Server instance should be cleared # Server instance should be cleared
assert kicad_mcp.server._server_instance is None assert mckicad.server._server_instance is None
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
def test_shutdown_server_no_instance(self, mock_logging): def test_shutdown_server_no_instance(self, mock_logging):
"""Test shutting down server when no instance exists.""" """Test shutting down server when no instance exists."""
shutdown_server() shutdown_server()
@ -145,8 +145,8 @@ class TestServerShutdown:
class TestSignalHandlers: class TestSignalHandlers:
"""Test signal handler registration.""" """Test signal handler registration."""
@patch('kicad_mcp.server.signal.signal') @patch('mckicad.server.signal.signal')
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
def test_register_signal_handlers_success(self, mock_logging, mock_signal): def test_register_signal_handlers_success(self, mock_logging, mock_signal):
"""Test successful signal handler registration.""" """Test successful signal handler registration."""
mock_server = Mock() mock_server = Mock()
@ -163,8 +163,8 @@ class TestSignalHandlers:
mock_logging.info.assert_any_call("Registered handler for signal 2") # SIGINT mock_logging.info.assert_any_call("Registered handler for signal 2") # SIGINT
mock_logging.info.assert_any_call("Registered handler for signal 15") # SIGTERM mock_logging.info.assert_any_call("Registered handler for signal 15") # SIGTERM
@patch('kicad_mcp.server.signal.signal') @patch('mckicad.server.signal.signal')
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
def test_register_signal_handlers_failure(self, mock_logging, mock_signal): def test_register_signal_handlers_failure(self, mock_logging, mock_signal):
"""Test signal handler registration failure.""" """Test signal handler registration failure."""
mock_server = Mock() mock_server = Mock()
@ -175,15 +175,15 @@ class TestSignalHandlers:
# Should log errors for failed registrations # Should log errors for failed registrations
mock_logging.error.assert_called() mock_logging.error.assert_called()
@patch('kicad_mcp.server.run_cleanup_handlers') @patch('mckicad.server.run_cleanup_handlers')
@patch('kicad_mcp.server.shutdown_server') @patch('mckicad.server.shutdown_server')
@patch('kicad_mcp.server.os._exit') @patch('mckicad.server.os._exit')
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
def test_signal_handler_execution(self, mock_logging, mock_exit, mock_shutdown, mock_cleanup): def test_signal_handler_execution(self, mock_logging, mock_exit, mock_shutdown, mock_cleanup):
"""Test that signal handler executes cleanup and shutdown.""" """Test that signal handler executes cleanup and shutdown."""
mock_server = Mock() mock_server = Mock()
with patch('kicad_mcp.server.signal.signal') as mock_signal: with patch('mckicad.server.signal.signal') as mock_signal:
register_signal_handlers(mock_server) register_signal_handlers(mock_server)
# Get the registered handler function # Get the registered handler function
@ -202,11 +202,11 @@ class TestSignalHandlers:
class TestCreateServer: class TestCreateServer:
"""Test server creation and configuration.""" """Test server creation and configuration."""
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
@patch('kicad_mcp.server.FastMCP') @patch('mckicad.server.FastMCP')
@patch('kicad_mcp.server.register_signal_handlers') @patch('mckicad.server.register_signal_handlers')
@patch('kicad_mcp.server.atexit.register') @patch('mckicad.server.atexit.register')
@patch('kicad_mcp.server.add_cleanup_handler') @patch('mckicad.server.add_cleanup_handler')
def test_create_server_basic(self, mock_add_cleanup, mock_atexit, mock_register_signals, mock_fastmcp, mock_logging): def test_create_server_basic(self, mock_add_cleanup, mock_atexit, mock_register_signals, mock_fastmcp, mock_logging):
"""Test basic server creation.""" """Test basic server creation."""
mock_server_instance = Mock() mock_server_instance = Mock()
@ -227,16 +227,16 @@ class TestCreateServer:
assert server == mock_server_instance assert server == mock_server_instance
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
@patch('kicad_mcp.server.FastMCP') @patch('mckicad.server.FastMCP')
def test_create_server_logging(self, mock_fastmcp, mock_logging): def test_create_server_logging(self, mock_fastmcp, mock_logging):
"""Test server creation logging.""" """Test server creation logging."""
mock_server_instance = Mock() mock_server_instance = Mock()
mock_fastmcp.return_value = mock_server_instance mock_fastmcp.return_value = mock_server_instance
with patch('kicad_mcp.server.register_signal_handlers'), \ with patch('mckicad.server.register_signal_handlers'), \
patch('kicad_mcp.server.atexit.register'), \ patch('mckicad.server.atexit.register'), \
patch('kicad_mcp.server.add_cleanup_handler'): patch('mckicad.server.add_cleanup_handler'):
create_server() create_server()
@ -254,9 +254,9 @@ class TestCreateServer:
for expected_call in expected_log_calls: for expected_call in expected_log_calls:
mock_logging.info.assert_any_call(expected_call) mock_logging.info.assert_any_call(expected_call)
@patch('kicad_mcp.server.get_temp_dirs') @patch('mckicad.server.get_temp_dirs')
@patch('kicad_mcp.server.os.path.exists') @patch('mckicad.server.os.path.exists')
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
@pytest.mark.skip(reason="Complex mock setup for temp dir cleanup - functionality works in practice") @pytest.mark.skip(reason="Complex mock setup for temp dir cleanup - functionality works in practice")
def test_temp_directory_cleanup_handler(self, mock_logging, mock_exists, mock_get_temp_dirs): def test_temp_directory_cleanup_handler(self, mock_logging, mock_exists, mock_get_temp_dirs):
"""Test that temp directory cleanup handler works correctly.""" """Test that temp directory cleanup handler works correctly."""
@ -264,11 +264,11 @@ class TestCreateServer:
mock_get_temp_dirs.return_value = ["/tmp/test1", "/tmp/test2"] mock_get_temp_dirs.return_value = ["/tmp/test1", "/tmp/test2"]
mock_exists.return_value = True mock_exists.return_value = True
with patch('kicad_mcp.server.FastMCP'), \ with patch('mckicad.server.FastMCP'), \
patch('kicad_mcp.server.register_signal_handlers'), \ patch('mckicad.server.register_signal_handlers'), \
patch('kicad_mcp.server.atexit.register'), \ patch('mckicad.server.atexit.register'), \
patch('kicad_mcp.server.add_cleanup_handler') as mock_add_cleanup, \ patch('mckicad.server.add_cleanup_handler') as mock_add_cleanup, \
patch('kicad_mcp.server.shutil.rmtree') as mock_rmtree: patch('mckicad.server.shutil.rmtree') as mock_rmtree:
create_server() create_server()
@ -291,7 +291,7 @@ class TestCreateServer:
class TestSetupLogging: class TestSetupLogging:
"""Test logging configuration.""" """Test logging configuration."""
@patch('kicad_mcp.server.logging.basicConfig') @patch('mckicad.server.logging.basicConfig')
def test_setup_logging(self, mock_basic_config): def test_setup_logging(self, mock_basic_config):
"""Test logging setup configuration.""" """Test logging setup configuration."""
setup_logging() setup_logging()
@ -308,9 +308,9 @@ class TestSetupLogging:
class TestMain: class TestMain:
"""Test main server entry point.""" """Test main server entry point."""
@patch('kicad_mcp.server.setup_logging') @patch('mckicad.server.setup_logging')
@patch('kicad_mcp.server.create_server') @patch('mckicad.server.create_server')
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
def test_main_successful_run(self, mock_logging, mock_create_server, mock_setup_logging): def test_main_successful_run(self, mock_logging, mock_create_server, mock_setup_logging):
"""Test successful main execution.""" """Test successful main execution."""
mock_server = Mock() mock_server = Mock()
@ -325,9 +325,9 @@ class TestMain:
mock_logging.info.assert_any_call("Starting KiCad MCP server...") mock_logging.info.assert_any_call("Starting KiCad MCP server...")
mock_logging.info.assert_any_call("Server shutdown complete") mock_logging.info.assert_any_call("Server shutdown complete")
@patch('kicad_mcp.server.setup_logging') @patch('mckicad.server.setup_logging')
@patch('kicad_mcp.server.create_server') @patch('mckicad.server.create_server')
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
def test_main_keyboard_interrupt(self, mock_logging, mock_create_server, mock_setup_logging): def test_main_keyboard_interrupt(self, mock_logging, mock_create_server, mock_setup_logging):
"""Test main with keyboard interrupt.""" """Test main with keyboard interrupt."""
mock_server = Mock() mock_server = Mock()
@ -339,9 +339,9 @@ class TestMain:
mock_logging.info.assert_any_call("Server interrupted by user") mock_logging.info.assert_any_call("Server interrupted by user")
mock_logging.info.assert_any_call("Server shutdown complete") mock_logging.info.assert_any_call("Server shutdown complete")
@patch('kicad_mcp.server.setup_logging') @patch('mckicad.server.setup_logging')
@patch('kicad_mcp.server.create_server') @patch('mckicad.server.create_server')
@patch('kicad_mcp.server.logging') @patch('mckicad.server.logging')
def test_main_exception(self, mock_logging, mock_create_server, mock_setup_logging): def test_main_exception(self, mock_logging, mock_create_server, mock_setup_logging):
"""Test main with general exception.""" """Test main with general exception."""
mock_server = Mock() mock_server = Mock()
@ -353,15 +353,15 @@ class TestMain:
mock_logging.error.assert_any_call("Server error: Server error") mock_logging.error.assert_any_call("Server error: Server error")
mock_logging.info.assert_any_call("Server shutdown complete") mock_logging.info.assert_any_call("Server shutdown complete")
@patch('kicad_mcp.server.setup_logging') @patch('mckicad.server.setup_logging')
@patch('kicad_mcp.server.create_server') @patch('mckicad.server.create_server')
def test_main_cleanup_always_runs(self, mock_create_server, mock_setup_logging): def test_main_cleanup_always_runs(self, mock_create_server, mock_setup_logging):
"""Test that cleanup always runs even with exceptions.""" """Test that cleanup always runs even with exceptions."""
mock_server = Mock() mock_server = Mock()
mock_server.run.side_effect = Exception("Test exception") mock_server.run.side_effect = Exception("Test exception")
mock_create_server.return_value = mock_server mock_create_server.return_value = mock_server
with patch('kicad_mcp.server.logging') as mock_logging: with patch('mckicad.server.logging') as mock_logging:
main() main()
# Verify finally block executed # Verify finally block executed

View File

@ -1,9 +1,9 @@
""" """
Tests for the kicad_mcp.utils.component_utils module. Tests for the mckicad.utils.component_utils module.
""" """
import pytest import pytest
from kicad_mcp.utils.component_utils import ( from mckicad.utils.component_utils import (
extract_capacitance_value, extract_capacitance_value,
extract_frequency_from_value, extract_frequency_from_value,
extract_inductance_value, extract_inductance_value,

View File

@ -1,18 +1,18 @@
""" """
Tests for the kicad_mcp.utils.file_utils module. Tests for the mckicad.utils.file_utils module.
""" """
import json import json
import os import os
import tempfile import tempfile
from unittest.mock import mock_open, patch from unittest.mock import mock_open, patch
from kicad_mcp.utils.file_utils import get_project_files, load_project_json from mckicad.utils.file_utils import get_project_files, load_project_json
class TestGetProjectFiles: class TestGetProjectFiles:
"""Test get_project_files function.""" """Test get_project_files function."""
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path') @patch('mckicad.utils.file_utils.get_project_name_from_path')
@patch('os.path.dirname') @patch('os.path.dirname')
@patch('os.path.exists') @patch('os.path.exists')
@patch('os.listdir') @patch('os.listdir')
@ -31,7 +31,7 @@ class TestGetProjectFiles:
assert "bom" in result assert "bom" in result
assert result["bom"] == "/test/project/myproject-bom.csv" assert result["bom"] == "/test/project/myproject-bom.csv"
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path') @patch('mckicad.utils.file_utils.get_project_name_from_path')
@patch('os.path.dirname') @patch('os.path.dirname')
@patch('os.path.exists') @patch('os.path.exists')
@patch('os.listdir') @patch('os.listdir')
@ -55,7 +55,7 @@ class TestGetProjectFiles:
if file_type in result: if file_type in result:
assert result[file_type].startswith("/test/project/test_project") assert result[file_type].startswith("/test/project/test_project")
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path') @patch('mckicad.utils.file_utils.get_project_name_from_path')
@patch('os.path.dirname') @patch('os.path.dirname')
@patch('os.path.exists') @patch('os.path.exists')
@patch('os.listdir') @patch('os.listdir')
@ -84,7 +84,7 @@ class TestGetProjectFiles:
assert result["bom"] == "/test/project/project-bom.csv" assert result["bom"] == "/test/project/project-bom.csv"
assert result["positions"] == "/test/project/project_positions.pos" assert result["positions"] == "/test/project/project_positions.pos"
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path') @patch('mckicad.utils.file_utils.get_project_name_from_path')
@patch('os.path.dirname') @patch('os.path.dirname')
@patch('os.path.exists') @patch('os.path.exists')
@patch('os.listdir') @patch('os.listdir')
@ -102,7 +102,7 @@ class TestGetProjectFiles:
# Should not crash and return basic result # Should not crash and return basic result
assert len(result) >= 1 assert len(result) >= 1
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path') @patch('mckicad.utils.file_utils.get_project_name_from_path')
@patch('os.path.dirname') @patch('os.path.dirname')
@patch('os.path.exists') @patch('os.path.exists')
@patch('os.listdir') @patch('os.listdir')
@ -119,7 +119,7 @@ class TestGetProjectFiles:
assert result["project"] == "/test/project/project.kicad_pro" assert result["project"] == "/test/project/project.kicad_pro"
assert len(result) == 1 assert len(result) == 1
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path') @patch('mckicad.utils.file_utils.get_project_name_from_path')
@patch('os.path.dirname') @patch('os.path.dirname')
@patch('os.path.exists') @patch('os.path.exists')
@patch('os.listdir') @patch('os.listdir')
@ -307,7 +307,7 @@ class TestIntegration:
assert json_data == project_data assert json_data == project_data
assert json_data["board"]["thickness"] == 1.6 assert json_data["board"]["thickness"] == 1.6
@patch('kicad_mcp.utils.file_utils.get_project_name_from_path') @patch('mckicad.utils.file_utils.get_project_name_from_path')
def test_project_name_integration(self, mock_get_name): def test_project_name_integration(self, mock_get_name):
"""Test integration with get_project_name_from_path function.""" """Test integration with get_project_name_from_path function."""
mock_get_name.return_value = "custom_name" mock_get_name.return_value = "custom_name"

View File

@ -1,5 +1,5 @@
""" """
Tests for the kicad_mcp.utils.kicad_cli module. Tests for the mckicad.utils.kicad_cli module.
""" """
import platform import platform
import subprocess import subprocess
@ -7,7 +7,7 @@ from unittest.mock import Mock, patch
import pytest import pytest
from kicad_mcp.utils.kicad_cli import ( from mckicad.utils.kicad_cli import (
KiCadCLIError, KiCadCLIError,
KiCadCLIManager, KiCadCLIManager,
find_kicad_cli, find_kicad_cli,
@ -44,8 +44,8 @@ class TestKiCadCLIManager:
assert manager._cache_validated is False assert manager._cache_validated is False
assert manager._system == platform.system() assert manager._system == platform.system()
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path') @patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path')
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._validate_cli_path') @patch('mckicad.utils.kicad_cli.KiCadCLIManager._validate_cli_path')
def test_find_kicad_cli_success(self, mock_validate, mock_detect): def test_find_kicad_cli_success(self, mock_validate, mock_detect):
"""Test successful CLI detection.""" """Test successful CLI detection."""
mock_detect.return_value = "/usr/bin/kicad-cli" mock_detect.return_value = "/usr/bin/kicad-cli"
@ -57,7 +57,7 @@ class TestKiCadCLIManager:
assert self.manager._cached_cli_path == "/usr/bin/kicad-cli" assert self.manager._cached_cli_path == "/usr/bin/kicad-cli"
assert self.manager._cache_validated is True assert self.manager._cache_validated is True
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path') @patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path')
def test_find_kicad_cli_not_found(self, mock_detect): def test_find_kicad_cli_not_found(self, mock_detect):
"""Test CLI detection failure.""" """Test CLI detection failure."""
mock_detect.return_value = None mock_detect.return_value = None
@ -68,8 +68,8 @@ class TestKiCadCLIManager:
assert self.manager._cached_cli_path is None assert self.manager._cached_cli_path is None
assert self.manager._cache_validated is False assert self.manager._cache_validated is False
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path') @patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path')
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._validate_cli_path') @patch('mckicad.utils.kicad_cli.KiCadCLIManager._validate_cli_path')
def test_find_kicad_cli_validation_failure(self, mock_validate, mock_detect): def test_find_kicad_cli_validation_failure(self, mock_validate, mock_detect):
"""Test CLI detection with validation failure.""" """Test CLI detection with validation failure."""
mock_detect.return_value = "/usr/bin/kicad-cli" mock_detect.return_value = "/usr/bin/kicad-cli"
@ -86,7 +86,7 @@ class TestKiCadCLIManager:
self.manager._cached_cli_path = "/cached/path" self.manager._cached_cli_path = "/cached/path"
self.manager._cache_validated = True self.manager._cache_validated = True
with patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect: with patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect:
result = self.manager.find_kicad_cli() result = self.manager.find_kicad_cli()
assert result == "/cached/path" assert result == "/cached/path"
@ -97,8 +97,8 @@ class TestKiCadCLIManager:
self.manager._cached_cli_path = "/cached/path" self.manager._cached_cli_path = "/cached/path"
self.manager._cache_validated = True self.manager._cache_validated = True
with patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect, \ with patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect, \
patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._validate_cli_path') as mock_validate: patch('mckicad.utils.kicad_cli.KiCadCLIManager._validate_cli_path') as mock_validate:
mock_detect.return_value = "/new/path" mock_detect.return_value = "/new/path"
mock_validate.return_value = True mock_validate.return_value = True
@ -108,7 +108,7 @@ class TestKiCadCLIManager:
assert result == "/new/path" assert result == "/new/path"
mock_detect.assert_called_once() mock_detect.assert_called_once()
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') @patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
def test_get_cli_path_success(self, mock_find): def test_get_cli_path_success(self, mock_find):
"""Test successful CLI path retrieval.""" """Test successful CLI path retrieval."""
mock_find.return_value = "/usr/bin/kicad-cli" mock_find.return_value = "/usr/bin/kicad-cli"
@ -117,7 +117,7 @@ class TestKiCadCLIManager:
assert result == "/usr/bin/kicad-cli" assert result == "/usr/bin/kicad-cli"
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') @patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
def test_get_cli_path_not_required(self, mock_find): def test_get_cli_path_not_required(self, mock_find):
"""Test CLI path retrieval when not required.""" """Test CLI path retrieval when not required."""
mock_find.return_value = None mock_find.return_value = None
@ -126,7 +126,7 @@ class TestKiCadCLIManager:
assert result is None assert result is None
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') @patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
def test_get_cli_path_required_raises(self, mock_find): def test_get_cli_path_required_raises(self, mock_find):
"""Test that exception is raised when CLI required but not found.""" """Test that exception is raised when CLI required but not found."""
mock_find.return_value = None mock_find.return_value = None
@ -136,22 +136,22 @@ class TestKiCadCLIManager:
assert "KiCad CLI not found" in str(exc_info.value) assert "KiCad CLI not found" in str(exc_info.value)
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') @patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
def test_is_available_true(self, mock_find): def test_is_available_true(self, mock_find):
"""Test is_available returns True when CLI found.""" """Test is_available returns True when CLI found."""
mock_find.return_value = "/usr/bin/kicad-cli" mock_find.return_value = "/usr/bin/kicad-cli"
assert self.manager.is_available() is True assert self.manager.is_available() is True
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') @patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
def test_is_available_false(self, mock_find): def test_is_available_false(self, mock_find):
"""Test is_available returns False when CLI not found.""" """Test is_available returns False when CLI not found."""
mock_find.return_value = None mock_find.return_value = None
assert self.manager.is_available() is False assert self.manager.is_available() is False
@patch('kicad_mcp.utils.kicad_cli.subprocess.run') @patch('mckicad.utils.kicad_cli.subprocess.run')
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') @patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
def test_get_version_success(self, mock_find, mock_run): def test_get_version_success(self, mock_find, mock_run):
"""Test successful version retrieval.""" """Test successful version retrieval."""
mock_find.return_value = "/usr/bin/kicad-cli" mock_find.return_value = "/usr/bin/kicad-cli"
@ -165,7 +165,7 @@ class TestKiCadCLIManager:
assert version == "KiCad 7.0.0" assert version == "KiCad 7.0.0"
mock_run.assert_called_once() mock_run.assert_called_once()
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') @patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
def test_get_version_cli_not_found(self, mock_find): def test_get_version_cli_not_found(self, mock_find):
"""Test version retrieval when CLI not found.""" """Test version retrieval when CLI not found."""
mock_find.return_value = None mock_find.return_value = None
@ -174,8 +174,8 @@ class TestKiCadCLIManager:
assert version is None assert version is None
@patch('kicad_mcp.utils.kicad_cli.subprocess.run') @patch('mckicad.utils.kicad_cli.subprocess.run')
@patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') @patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli')
def test_get_version_subprocess_error(self, mock_find, mock_run): def test_get_version_subprocess_error(self, mock_find, mock_run):
"""Test version retrieval with subprocess error.""" """Test version retrieval with subprocess error."""
mock_find.return_value = "/usr/bin/kicad-cli" mock_find.return_value = "/usr/bin/kicad-cli"
@ -185,9 +185,9 @@ class TestKiCadCLIManager:
assert version is None assert version is None
@patch('kicad_mcp.utils.kicad_cli.os.environ.get') @patch('mckicad.utils.kicad_cli.os.environ.get')
@patch('kicad_mcp.utils.kicad_cli.os.path.isfile') @patch('mckicad.utils.kicad_cli.os.path.isfile')
@patch('kicad_mcp.utils.kicad_cli.os.access') @patch('mckicad.utils.kicad_cli.os.access')
def test_detect_cli_path_environment_variable(self, mock_access, mock_isfile, mock_env_get): def test_detect_cli_path_environment_variable(self, mock_access, mock_isfile, mock_env_get):
"""Test CLI detection from environment variable.""" """Test CLI detection from environment variable."""
mock_env_get.return_value = "/custom/kicad-cli" mock_env_get.return_value = "/custom/kicad-cli"
@ -198,8 +198,8 @@ class TestKiCadCLIManager:
assert result == "/custom/kicad-cli" assert result == "/custom/kicad-cli"
@patch('kicad_mcp.utils.kicad_cli.os.environ.get') @patch('mckicad.utils.kicad_cli.os.environ.get')
@patch('kicad_mcp.utils.kicad_cli.shutil.which') @patch('mckicad.utils.kicad_cli.shutil.which')
def test_detect_cli_path_system_path(self, mock_which, mock_env_get): def test_detect_cli_path_system_path(self, mock_which, mock_env_get):
"""Test CLI detection from system PATH.""" """Test CLI detection from system PATH."""
mock_env_get.return_value = None mock_env_get.return_value = None
@ -209,10 +209,10 @@ class TestKiCadCLIManager:
assert result == "/usr/bin/kicad-cli" assert result == "/usr/bin/kicad-cli"
@patch('kicad_mcp.utils.kicad_cli.os.environ.get') @patch('mckicad.utils.kicad_cli.os.environ.get')
@patch('kicad_mcp.utils.kicad_cli.shutil.which') @patch('mckicad.utils.kicad_cli.shutil.which')
@patch('kicad_mcp.utils.kicad_cli.os.path.isfile') @patch('mckicad.utils.kicad_cli.os.path.isfile')
@patch('kicad_mcp.utils.kicad_cli.os.access') @patch('mckicad.utils.kicad_cli.os.access')
def test_detect_cli_path_common_locations(self, mock_access, mock_isfile, mock_which, mock_env_get): def test_detect_cli_path_common_locations(self, mock_access, mock_isfile, mock_which, mock_env_get):
"""Test CLI detection from common installation paths.""" """Test CLI detection from common installation paths."""
mock_env_get.return_value = None mock_env_get.return_value = None
@ -265,7 +265,7 @@ class TestKiCadCLIManager:
assert "/usr/bin/kicad-cli" in paths assert "/usr/bin/kicad-cli" in paths
assert "/snap/kicad/current/usr/bin/kicad-cli" in paths assert "/snap/kicad/current/usr/bin/kicad-cli" in paths
@patch('kicad_mcp.utils.kicad_cli.subprocess.run') @patch('mckicad.utils.kicad_cli.subprocess.run')
def test_validate_cli_path_success(self, mock_run): def test_validate_cli_path_success(self, mock_run):
"""Test successful CLI validation.""" """Test successful CLI validation."""
mock_result = Mock() mock_result = Mock()
@ -276,7 +276,7 @@ class TestKiCadCLIManager:
assert result is True assert result is True
@patch('kicad_mcp.utils.kicad_cli.subprocess.run') @patch('mckicad.utils.kicad_cli.subprocess.run')
def test_validate_cli_path_failure(self, mock_run): def test_validate_cli_path_failure(self, mock_run):
"""Test CLI validation failure.""" """Test CLI validation failure."""
mock_result = Mock() mock_result = Mock()
@ -287,7 +287,7 @@ class TestKiCadCLIManager:
assert result is False assert result is False
@patch('kicad_mcp.utils.kicad_cli.subprocess.run') @patch('mckicad.utils.kicad_cli.subprocess.run')
def test_validate_cli_path_exception(self, mock_run): def test_validate_cli_path_exception(self, mock_run):
"""Test CLI validation with exception.""" """Test CLI validation with exception."""
mock_run.side_effect = subprocess.SubprocessError("Test error") mock_run.side_effect = subprocess.SubprocessError("Test error")
@ -302,8 +302,8 @@ class TestGlobalFunctions:
def setup_method(self): def setup_method(self):
"""Reset global manager before each test.""" """Reset global manager before each test."""
import kicad_mcp.utils.kicad_cli import mckicad.utils.kicad_cli
kicad_mcp.utils.kicad_cli._cli_manager = None mckicad.utils.kicad_cli._cli_manager = None
def test_get_cli_manager_singleton(self): def test_get_cli_manager_singleton(self):
"""Test that get_cli_manager returns singleton instance.""" """Test that get_cli_manager returns singleton instance."""
@ -313,7 +313,7 @@ class TestGlobalFunctions:
assert manager1 is manager2 assert manager1 is manager2
assert isinstance(manager1, KiCadCLIManager) assert isinstance(manager1, KiCadCLIManager)
@patch('kicad_mcp.utils.kicad_cli.get_cli_manager') @patch('mckicad.utils.kicad_cli.get_cli_manager')
def test_find_kicad_cli_convenience(self, mock_get_manager): def test_find_kicad_cli_convenience(self, mock_get_manager):
"""Test find_kicad_cli convenience function.""" """Test find_kicad_cli convenience function."""
mock_manager = Mock() mock_manager = Mock()
@ -325,7 +325,7 @@ class TestGlobalFunctions:
assert result == "/usr/bin/kicad-cli" assert result == "/usr/bin/kicad-cli"
mock_manager.find_kicad_cli.assert_called_once_with(True) mock_manager.find_kicad_cli.assert_called_once_with(True)
@patch('kicad_mcp.utils.kicad_cli.get_cli_manager') @patch('mckicad.utils.kicad_cli.get_cli_manager')
def test_get_kicad_cli_path_convenience(self, mock_get_manager): def test_get_kicad_cli_path_convenience(self, mock_get_manager):
"""Test get_kicad_cli_path convenience function.""" """Test get_kicad_cli_path convenience function."""
mock_manager = Mock() mock_manager = Mock()
@ -337,7 +337,7 @@ class TestGlobalFunctions:
assert result == "/usr/bin/kicad-cli" assert result == "/usr/bin/kicad-cli"
mock_manager.get_cli_path.assert_called_once_with(False) mock_manager.get_cli_path.assert_called_once_with(False)
@patch('kicad_mcp.utils.kicad_cli.get_cli_manager') @patch('mckicad.utils.kicad_cli.get_cli_manager')
def test_is_kicad_cli_available_convenience(self, mock_get_manager): def test_is_kicad_cli_available_convenience(self, mock_get_manager):
"""Test is_kicad_cli_available convenience function.""" """Test is_kicad_cli_available convenience function."""
mock_manager = Mock() mock_manager = Mock()
@ -349,7 +349,7 @@ class TestGlobalFunctions:
assert result is True assert result is True
mock_manager.is_available.assert_called_once() mock_manager.is_available.assert_called_once()
@patch('kicad_mcp.utils.kicad_cli.get_cli_manager') @patch('mckicad.utils.kicad_cli.get_cli_manager')
def test_get_kicad_version_convenience(self, mock_get_manager): def test_get_kicad_version_convenience(self, mock_get_manager):
"""Test get_kicad_version convenience function.""" """Test get_kicad_version convenience function."""
mock_manager = Mock() mock_manager = Mock()
@ -374,8 +374,8 @@ class TestIntegration:
assert not manager._cache_validated assert not manager._cache_validated
# Simulate finding CLI # Simulate finding CLI
with patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect, \ with patch('mckicad.utils.kicad_cli.KiCadCLIManager._detect_cli_path') as mock_detect, \
patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager._validate_cli_path') as mock_validate: patch('mckicad.utils.kicad_cli.KiCadCLIManager._validate_cli_path') as mock_validate:
mock_detect.return_value = "/test/kicad-cli" mock_detect.return_value = "/test/kicad-cli"
mock_validate.return_value = True mock_validate.return_value = True
@ -401,7 +401,7 @@ class TestIntegration:
"""Test that errors are properly propagated.""" """Test that errors are properly propagated."""
manager = KiCadCLIManager() manager = KiCadCLIManager()
with patch('kicad_mcp.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') as mock_find: with patch('mckicad.utils.kicad_cli.KiCadCLIManager.find_kicad_cli') as mock_find:
mock_find.return_value = None mock_find.return_value = None
# Should not raise when required=False # Should not raise when required=False

View File

@ -7,7 +7,7 @@ import tempfile
import pytest import pytest
from kicad_mcp.utils.path_validator import ( from mckicad.utils.path_validator import (
PathValidationError, PathValidationError,
PathValidator, PathValidator,
validate_directory, validate_directory,
@ -200,7 +200,7 @@ class TestConvenienceFunctions:
"""Test validate_path convenience function.""" """Test validate_path convenience function."""
with tempfile.TemporaryDirectory() as temp_dir: with tempfile.TemporaryDirectory() as temp_dir:
# Add temp_dir to default validator # Add temp_dir to default validator
from kicad_mcp.utils.path_validator import get_default_validator from mckicad.utils.path_validator import get_default_validator
get_default_validator().add_trusted_root(temp_dir) get_default_validator().add_trusted_root(temp_dir)
@ -215,7 +215,7 @@ class TestConvenienceFunctions:
"""Test validate_kicad_file convenience function.""" """Test validate_kicad_file convenience function."""
with tempfile.TemporaryDirectory() as temp_dir: with tempfile.TemporaryDirectory() as temp_dir:
# Add temp_dir to default validator # Add temp_dir to default validator
from kicad_mcp.utils.path_validator import get_default_validator from mckicad.utils.path_validator import get_default_validator
get_default_validator().add_trusted_root(temp_dir) get_default_validator().add_trusted_root(temp_dir)
@ -230,7 +230,7 @@ class TestConvenienceFunctions:
"""Test validate_directory convenience function.""" """Test validate_directory convenience function."""
with tempfile.TemporaryDirectory() as temp_dir: with tempfile.TemporaryDirectory() as temp_dir:
# Add temp_dir to default validator # Add temp_dir to default validator
from kicad_mcp.utils.path_validator import get_default_validator from mckicad.utils.path_validator import get_default_validator
get_default_validator().add_trusted_root(temp_dir) get_default_validator().add_trusted_root(temp_dir)

View File

@ -9,9 +9,9 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
from kicad_mcp.utils.kicad_cli import KiCadCLIError from mckicad.utils.kicad_cli import KiCadCLIError
from kicad_mcp.utils.path_validator import PathValidationError, PathValidator from mckicad.utils.path_validator import PathValidationError, PathValidator
from kicad_mcp.utils.secure_subprocess import ( from mckicad.utils.secure_subprocess import (
SecureSubprocessError, SecureSubprocessError,
SecureSubprocessRunner, SecureSubprocessRunner,
create_temp_file, create_temp_file,
@ -22,7 +22,7 @@ from kicad_mcp.utils.secure_subprocess import (
def _kicad_cli_available(): def _kicad_cli_available():
"""Check if KiCad CLI is available.""" """Check if KiCad CLI is available."""
try: try:
from kicad_mcp.utils.kicad_cli import get_kicad_cli_path from mckicad.utils.kicad_cli import get_kicad_cli_path
get_kicad_cli_path() get_kicad_cli_path()
return True return True
@ -45,7 +45,7 @@ class TestSecureSubprocessRunner:
runner = SecureSubprocessRunner(path_validator=validator) runner = SecureSubprocessRunner(path_validator=validator)
assert runner.path_validator is validator assert runner.path_validator is validator
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path") @patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
@patch.object(SecureSubprocessRunner, "_run_subprocess") @patch.object(SecureSubprocessRunner, "_run_subprocess")
def test_run_kicad_command_success(self, mock_run_subprocess, mock_get_cli): def test_run_kicad_command_success(self, mock_run_subprocess, mock_get_cli):
"""Test successful KiCad command execution.""" """Test successful KiCad command execution."""
@ -76,7 +76,7 @@ class TestSecureSubprocessRunner:
assert result is mock_result assert result is mock_result
mock_run_subprocess.assert_called_once() mock_run_subprocess.assert_called_once()
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path") @patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
def test_run_kicad_command_cli_not_found(self, mock_get_cli): def test_run_kicad_command_cli_not_found(self, mock_get_cli):
"""Test KiCad command when CLI not found.""" """Test KiCad command when CLI not found."""
mock_get_cli.side_effect = KiCadCLIError("CLI not found") mock_get_cli.side_effect = KiCadCLIError("CLI not found")
@ -113,7 +113,7 @@ class TestSecureSubprocessRunner:
output_files=["/etc/output.svg"], output_files=["/etc/output.svg"],
) )
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path") @patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
@patch.object(SecureSubprocessRunner, "_run_subprocess") @patch.object(SecureSubprocessRunner, "_run_subprocess")
def test_run_kicad_command_with_working_dir(self, mock_run_subprocess, mock_get_cli): def test_run_kicad_command_with_working_dir(self, mock_run_subprocess, mock_get_cli):
"""Test KiCad command with working directory.""" """Test KiCad command with working directory."""
@ -132,7 +132,7 @@ class TestSecureSubprocessRunner:
call_args = mock_run_subprocess.call_args call_args = mock_run_subprocess.call_args
assert call_args[1]["working_dir"] == os.path.realpath(temp_dir) assert call_args[1]["working_dir"] == os.path.realpath(temp_dir)
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path") @patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
@patch.object(SecureSubprocessRunner, "_run_subprocess") @patch.object(SecureSubprocessRunner, "_run_subprocess")
def test_run_kicad_command_subprocess_error(self, mock_run_subprocess, mock_get_cli): def test_run_kicad_command_subprocess_error(self, mock_run_subprocess, mock_get_cli):
"""Test KiCad command with subprocess error.""" """Test KiCad command with subprocess error."""
@ -145,7 +145,7 @@ class TestSecureSubprocessRunner:
runner.run_kicad_command(["--version"]) runner.run_kicad_command(["--version"])
@pytest.mark.asyncio @pytest.mark.asyncio
@patch("kicad_mcp.utils.secure_subprocess.get_kicad_cli_path") @patch("mckicad.utils.secure_subprocess.get_kicad_cli_path")
@patch.object(SecureSubprocessRunner, "run_kicad_command") @patch.object(SecureSubprocessRunner, "run_kicad_command")
async def test_run_kicad_command_async(self, mock_run_command, mock_get_cli): async def test_run_kicad_command_async(self, mock_run_command, mock_get_cli):
"""Test async KiCad command execution.""" """Test async KiCad command execution."""

236
uv.lock generated
View File

@ -565,39 +565,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/39/61981f3c5d123f6ceff73479e3ace1de81d90af95632e2fe3d094b0ac8a9/filelock-3.21.1-py3-none-any.whl", hash = "sha256:f3aa1887cdf3514b13a379b5835ff35327d7aa24a96db4453b97531c00476760", size = 21472, upload-time = "2026-02-12T22:29:27.518Z" }, { url = "https://files.pythonhosted.org/packages/b4/39/61981f3c5d123f6ceff73479e3ace1de81d90af95632e2fe3d094b0ac8a9/filelock-3.21.1-py3-none-any.whl", hash = "sha256:f3aa1887cdf3514b13a379b5835ff35327d7aa24a96db4453b97531c00476760", size = 21472, upload-time = "2026-02-12T22:29:27.518Z" },
] ]
[[package]]
name = "greenlet"
version = "3.2.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c9/92/bb85bd6e80148a4d2e0c59f7c0c2891029f8fd510183afc7d8d2feeed9b6/greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365", size = 185752, upload-time = "2025-06-05T16:16:09.955Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/94/ad0d435f7c48debe960c53b8f60fb41c2026b1d0fa4a99a1cb17c3461e09/greenlet-3.2.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d", size = 271992, upload-time = "2025-06-05T16:11:23.467Z" },
{ url = "https://files.pythonhosted.org/packages/93/5d/7c27cf4d003d6e77749d299c7c8f5fd50b4f251647b5c2e97e1f20da0ab5/greenlet-3.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b", size = 638820, upload-time = "2025-06-05T16:38:52.882Z" },
{ url = "https://files.pythonhosted.org/packages/c6/7e/807e1e9be07a125bb4c169144937910bf59b9d2f6d931578e57f0bce0ae2/greenlet-3.2.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d", size = 653046, upload-time = "2025-06-05T16:41:36.343Z" },
{ url = "https://files.pythonhosted.org/packages/9d/ab/158c1a4ea1068bdbc78dba5a3de57e4c7aeb4e7fa034320ea94c688bfb61/greenlet-3.2.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264", size = 647701, upload-time = "2025-06-05T16:48:19.604Z" },
{ url = "https://files.pythonhosted.org/packages/cc/0d/93729068259b550d6a0288da4ff72b86ed05626eaf1eb7c0d3466a2571de/greenlet-3.2.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688", size = 649747, upload-time = "2025-06-05T16:13:04.628Z" },
{ url = "https://files.pythonhosted.org/packages/f6/f6/c82ac1851c60851302d8581680573245c8fc300253fc1ff741ae74a6c24d/greenlet-3.2.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb", size = 605461, upload-time = "2025-06-05T16:12:50.792Z" },
{ url = "https://files.pythonhosted.org/packages/98/82/d022cf25ca39cf1200650fc58c52af32c90f80479c25d1cbf57980ec3065/greenlet-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c", size = 1121190, upload-time = "2025-06-05T16:36:48.59Z" },
{ url = "https://files.pythonhosted.org/packages/f5/e1/25297f70717abe8104c20ecf7af0a5b82d2f5a980eb1ac79f65654799f9f/greenlet-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163", size = 1149055, upload-time = "2025-06-05T16:12:40.457Z" },
{ url = "https://files.pythonhosted.org/packages/1f/8f/8f9e56c5e82eb2c26e8cde787962e66494312dc8cb261c460e1f3a9c88bc/greenlet-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849", size = 297817, upload-time = "2025-06-05T16:29:49.244Z" },
{ url = "https://files.pythonhosted.org/packages/b1/cf/f5c0b23309070ae93de75c90d29300751a5aacefc0a3ed1b1d8edb28f08b/greenlet-3.2.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad", size = 270732, upload-time = "2025-06-05T16:10:08.26Z" },
{ url = "https://files.pythonhosted.org/packages/48/ae/91a957ba60482d3fecf9be49bc3948f341d706b52ddb9d83a70d42abd498/greenlet-3.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef", size = 639033, upload-time = "2025-06-05T16:38:53.983Z" },
{ url = "https://files.pythonhosted.org/packages/6f/df/20ffa66dd5a7a7beffa6451bdb7400d66251374ab40b99981478c69a67a8/greenlet-3.2.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3", size = 652999, upload-time = "2025-06-05T16:41:37.89Z" },
{ url = "https://files.pythonhosted.org/packages/51/b4/ebb2c8cb41e521f1d72bf0465f2f9a2fd803f674a88db228887e6847077e/greenlet-3.2.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95", size = 647368, upload-time = "2025-06-05T16:48:21.467Z" },
{ url = "https://files.pythonhosted.org/packages/8e/6a/1e1b5aa10dced4ae876a322155705257748108b7fd2e4fae3f2a091fe81a/greenlet-3.2.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb", size = 650037, upload-time = "2025-06-05T16:13:06.402Z" },
{ url = "https://files.pythonhosted.org/packages/26/f2/ad51331a157c7015c675702e2d5230c243695c788f8f75feba1af32b3617/greenlet-3.2.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b", size = 608402, upload-time = "2025-06-05T16:12:51.91Z" },
{ url = "https://files.pythonhosted.org/packages/26/bc/862bd2083e6b3aff23300900a956f4ea9a4059de337f5c8734346b9b34fc/greenlet-3.2.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0", size = 1119577, upload-time = "2025-06-05T16:36:49.787Z" },
{ url = "https://files.pythonhosted.org/packages/86/94/1fc0cc068cfde885170e01de40a619b00eaa8f2916bf3541744730ffb4c3/greenlet-3.2.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36", size = 1147121, upload-time = "2025-06-05T16:12:42.527Z" },
{ url = "https://files.pythonhosted.org/packages/27/1a/199f9587e8cb08a0658f9c30f3799244307614148ffe8b1e3aa22f324dea/greenlet-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3", size = 297603, upload-time = "2025-06-05T16:20:12.651Z" },
{ url = "https://files.pythonhosted.org/packages/d8/ca/accd7aa5280eb92b70ed9e8f7fd79dc50a2c21d8c73b9a0856f5b564e222/greenlet-3.2.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86", size = 271479, upload-time = "2025-06-05T16:10:47.525Z" },
{ url = "https://files.pythonhosted.org/packages/55/71/01ed9895d9eb49223280ecc98a557585edfa56b3d0e965b9fa9f7f06b6d9/greenlet-3.2.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97", size = 683952, upload-time = "2025-06-05T16:38:55.125Z" },
{ url = "https://files.pythonhosted.org/packages/ea/61/638c4bdf460c3c678a0a1ef4c200f347dff80719597e53b5edb2fb27ab54/greenlet-3.2.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728", size = 696917, upload-time = "2025-06-05T16:41:38.959Z" },
{ url = "https://files.pythonhosted.org/packages/22/cc/0bd1a7eb759d1f3e3cc2d1bc0f0b487ad3cc9f34d74da4b80f226fde4ec3/greenlet-3.2.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a", size = 692443, upload-time = "2025-06-05T16:48:23.113Z" },
{ url = "https://files.pythonhosted.org/packages/67/10/b2a4b63d3f08362662e89c103f7fe28894a51ae0bc890fabf37d1d780e52/greenlet-3.2.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892", size = 692995, upload-time = "2025-06-05T16:13:07.972Z" },
{ url = "https://files.pythonhosted.org/packages/5a/c6/ad82f148a4e3ce9564056453a71529732baf5448ad53fc323e37efe34f66/greenlet-3.2.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141", size = 655320, upload-time = "2025-06-05T16:12:53.453Z" },
{ url = "https://files.pythonhosted.org/packages/5c/4f/aab73ecaa6b3086a4c89863d94cf26fa84cbff63f52ce9bc4342b3087a06/greenlet-3.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a", size = 301236, upload-time = "2025-06-05T16:15:20.111Z" },
]
[[package]] [[package]]
name = "h11" name = "h11"
version = "0.16.0" version = "0.16.0"
@ -823,93 +790,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" },
] ]
[[package]]
name = "kicad-mcp"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "defusedxml" },
{ name = "fastmcp" },
{ name = "kicad-python" },
{ name = "mcp", extra = ["cli"] },
{ name = "pandas" },
{ name = "pyyaml" },
{ name = "requests" },
]
[package.dev-dependencies]
dev = [
{ name = "bandit" },
{ name = "mypy" },
{ name = "pre-commit" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "pytest-mock" },
{ name = "pytest-xdist" },
{ name = "ruff" },
]
docs = [
{ name = "myst-parser" },
{ name = "sphinx" },
{ name = "sphinx-rtd-theme" },
]
performance = [
{ name = "memory-profiler" },
{ name = "py-spy" },
]
security = [
{ name = "bandit" },
{ name = "safety" },
]
visualization = [
{ name = "cairosvg" },
{ name = "pillow" },
{ name = "playwright" },
]
[package.metadata]
requires-dist = [
{ name = "defusedxml", specifier = ">=0.7.1" },
{ name = "fastmcp", specifier = ">=2.14.5" },
{ name = "kicad-python", specifier = ">=0.5.0" },
{ name = "mcp", extras = ["cli"], specifier = ">=1.26.0" },
{ name = "pandas", specifier = ">=2.3.3" },
{ name = "pyyaml", specifier = ">=6.0.3" },
{ name = "requests", specifier = ">=2.32.5" },
]
[package.metadata.requires-dev]
dev = [
{ name = "bandit", specifier = ">=1.9.3" },
{ name = "mypy", specifier = ">=1.19.1" },
{ name = "pre-commit", specifier = ">=4.5.1" },
{ name = "pytest", specifier = ">=8.4.2" },
{ name = "pytest-asyncio", specifier = ">=1.3.0" },
{ name = "pytest-cov", specifier = ">=7.0.0" },
{ name = "pytest-mock", specifier = ">=3.15.1" },
{ name = "pytest-xdist", specifier = ">=3.8.0" },
{ name = "ruff", specifier = ">=0.15.1" },
]
docs = [
{ name = "myst-parser", specifier = ">=5.0.0" },
{ name = "sphinx", specifier = ">=9.1.0" },
{ name = "sphinx-rtd-theme", specifier = ">=3.1.0" },
]
performance = [
{ name = "memory-profiler", specifier = ">=0.61.0" },
{ name = "py-spy", specifier = ">=0.3.0" },
]
security = [
{ name = "bandit", specifier = ">=1.9.3" },
{ name = "safety", specifier = ">=3.7.0" },
]
visualization = [
{ name = "cairosvg", specifier = ">=2.8.2" },
{ name = "pillow", specifier = ">=12.1.1" },
{ name = "playwright", specifier = ">=1.58.0" },
]
[[package]] [[package]]
name = "kicad-python" name = "kicad-python"
version = "0.5.0" version = "0.5.0"
@ -1095,6 +975,91 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d6/26/6cc45d156f44dbe1d5696d9e54042e4dcaf7b946c0b86df6a97d29706f32/marshmallow-4.0.0-py3-none-any.whl", hash = "sha256:e7b0528337e9990fd64950f8a6b3a1baabed09ad17a0dfb844d701151f92d203", size = 48420, upload-time = "2025-04-17T02:25:53.375Z" }, { url = "https://files.pythonhosted.org/packages/d6/26/6cc45d156f44dbe1d5696d9e54042e4dcaf7b946c0b86df6a97d29706f32/marshmallow-4.0.0-py3-none-any.whl", hash = "sha256:e7b0528337e9990fd64950f8a6b3a1baabed09ad17a0dfb844d701151f92d203", size = 48420, upload-time = "2025-04-17T02:25:53.375Z" },
] ]
[[package]]
name = "mckicad"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "defusedxml" },
{ name = "fastmcp" },
{ name = "kicad-python" },
{ name = "mcp", extra = ["cli"] },
{ name = "pandas" },
{ name = "pyyaml" },
{ name = "requests" },
]
[package.dev-dependencies]
dev = [
{ name = "bandit" },
{ name = "mypy" },
{ name = "pre-commit" },
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "pytest-cov" },
{ name = "pytest-mock" },
{ name = "pytest-xdist" },
{ name = "ruff" },
]
docs = [
{ name = "myst-parser" },
{ name = "sphinx" },
{ name = "sphinx-rtd-theme" },
]
performance = [
{ name = "memory-profiler" },
{ name = "py-spy" },
]
security = [
{ name = "bandit" },
{ name = "safety" },
]
visualization = [
{ name = "cairosvg" },
{ name = "pillow" },
]
[package.metadata]
requires-dist = [
{ name = "defusedxml", specifier = ">=0.7.1" },
{ name = "fastmcp", specifier = ">=2.14.5" },
{ name = "kicad-python", specifier = ">=0.5.0" },
{ name = "mcp", extras = ["cli"], specifier = ">=1.26.0" },
{ name = "pandas", specifier = ">=2.3.3" },
{ name = "pyyaml", specifier = ">=6.0.3" },
{ name = "requests", specifier = ">=2.32.5" },
]
[package.metadata.requires-dev]
dev = [
{ name = "bandit", specifier = ">=1.9.3" },
{ name = "mypy", specifier = ">=1.19.1" },
{ name = "pre-commit", specifier = ">=4.5.1" },
{ name = "pytest", specifier = ">=8.4.2" },
{ name = "pytest-asyncio", specifier = ">=1.3.0" },
{ name = "pytest-cov", specifier = ">=7.0.0" },
{ name = "pytest-mock", specifier = ">=3.15.1" },
{ name = "pytest-xdist", specifier = ">=3.8.0" },
{ name = "ruff", specifier = ">=0.15.1" },
]
docs = [
{ name = "myst-parser", specifier = ">=5.0.0" },
{ name = "sphinx", specifier = ">=9.1.0" },
{ name = "sphinx-rtd-theme", specifier = ">=3.1.0" },
]
performance = [
{ name = "memory-profiler", specifier = ">=0.61.0" },
{ name = "py-spy", specifier = ">=0.3.0" },
]
security = [
{ name = "bandit", specifier = ">=1.9.3" },
{ name = "safety", specifier = ">=3.7.0" },
]
visualization = [
{ name = "cairosvg", specifier = ">=2.8.2" },
{ name = "pillow", specifier = ">=12.1.1" },
]
[[package]] [[package]]
name = "mcp" name = "mcp"
version = "1.26.0" version = "1.26.0"
@ -1567,25 +1532,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" },
] ]
[[package]]
name = "playwright"
version = "1.58.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet" },
{ name = "pyee" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/f8/c9/9c6061d5703267f1baae6a4647bfd1862e386fbfdb97d889f6f6ae9e3f64/playwright-1.58.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:96e3204aac292ee639edbfdef6298b4be2ea0a55a16b7068df91adac077cc606", size = 42251098, upload-time = "2026-01-30T15:09:24.028Z" },
{ url = "https://files.pythonhosted.org/packages/e0/40/59d34a756e02f8c670f0fee987d46f7ee53d05447d43cd114ca015cb168c/playwright-1.58.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:70c763694739d28df71ed578b9c8202bb83e8fe8fb9268c04dd13afe36301f71", size = 41039625, upload-time = "2026-01-30T15:09:27.558Z" },
{ url = "https://files.pythonhosted.org/packages/e1/ee/3ce6209c9c74a650aac9028c621f357a34ea5cd4d950700f8e2c4b7fe2c4/playwright-1.58.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:185e0132578733d02802dfddfbbc35f42be23a45ff49ccae5081f25952238117", size = 42251098, upload-time = "2026-01-30T15:09:30.461Z" },
{ url = "https://files.pythonhosted.org/packages/f1/af/009958cbf23fac551a940d34e3206e6c7eed2b8c940d0c3afd1feb0b0589/playwright-1.58.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:c95568ba1eda83812598c1dc9be60b4406dffd60b149bc1536180ad108723d6b", size = 46235268, upload-time = "2026-01-30T15:09:33.787Z" },
{ url = "https://files.pythonhosted.org/packages/d9/a6/0e66ad04b6d3440dae73efb39540c5685c5fc95b17c8b29340b62abbd952/playwright-1.58.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f9999948f1ab541d98812de25e3a8c410776aa516d948807140aff797b4bffa", size = 45964214, upload-time = "2026-01-30T15:09:36.751Z" },
{ url = "https://files.pythonhosted.org/packages/0e/4b/236e60ab9f6d62ed0fd32150d61f1f494cefbf02304c0061e78ed80c1c32/playwright-1.58.0-py3-none-win32.whl", hash = "sha256:1e03be090e75a0fabbdaeab65ce17c308c425d879fa48bb1d7986f96bfad0b99", size = 36815998, upload-time = "2026-01-30T15:09:39.627Z" },
{ url = "https://files.pythonhosted.org/packages/41/f8/5ec599c5e59d2f2f336a05b4f318e733077cd5044f24adb6f86900c3e6a7/playwright-1.58.0-py3-none-win_amd64.whl", hash = "sha256:a2bf639d0ce33b3ba38de777e08697b0d8f3dc07ab6802e4ac53fb65e3907af8", size = 36816005, upload-time = "2026-01-30T15:09:42.449Z" },
{ url = "https://files.pythonhosted.org/packages/c8/c4/cc0229fea55c87d6c9c67fe44a21e2cd28d1d558a5478ed4d617e9fb0c93/playwright-1.58.0-py3-none-win_arm64.whl", hash = "sha256:32ffe5c303901a13a0ecab91d1c3f74baf73b84f4bedbb6b935f5bc11cc98e1b", size = 33085919, upload-time = "2026-01-30T15:09:45.71Z" },
]
[[package]] [[package]]
name = "pluggy" name = "pluggy"
version = "1.6.0" version = "1.6.0"
@ -1812,18 +1758,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c9/c7/68f2553819965326f968375f02597d49efe71b309ba9d8fef539aeb51c48/pydocket-0.17.7-py3-none-any.whl", hash = "sha256:d1e0921ac02026c4a0140fc72a3848545f3e91e6e74c6e32c588489017c130b2", size = 94608, upload-time = "2026-02-11T21:01:30.111Z" }, { url = "https://files.pythonhosted.org/packages/c9/c7/68f2553819965326f968375f02597d49efe71b309ba9d8fef539aeb51c48/pydocket-0.17.7-py3-none-any.whl", hash = "sha256:d1e0921ac02026c4a0140fc72a3848545f3e91e6e74c6e32c588489017c130b2", size = 94608, upload-time = "2026-02-11T21:01:30.111Z" },
] ]
[[package]]
name = "pyee"
version = "13.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498", size = 15730, upload-time = "2025-03-17T18:53:14.532Z" },
]
[[package]] [[package]]
name = "pygments" name = "pygments"
version = "2.19.2" version = "2.19.2"