kicad-sch-api 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kicad-sch-api might be problematic. Click here for more details.
- kicad_sch_api/__init__.py +2 -2
- kicad_sch_api/core/components.py +6 -2
- kicad_sch_api/core/parser.py +37 -15
- kicad_sch_api/utils/validation.py +3 -3
- {kicad_sch_api-0.0.1.dist-info → kicad_sch_api-0.0.2.dist-info}/METADATA +2 -17
- kicad_sch_api-0.0.2.dist-info/RECORD +18 -0
- kicad_sch_api/mcp/__init__.py +0 -5
- kicad_sch_api/mcp/server.py +0 -500
- kicad_sch_api-0.0.1.dist-info/RECORD +0 -20
- {kicad_sch_api-0.0.1.dist-info → kicad_sch_api-0.0.2.dist-info}/WHEEL +0 -0
- {kicad_sch_api-0.0.1.dist-info → kicad_sch_api-0.0.2.dist-info}/entry_points.txt +0 -0
- {kicad_sch_api-0.0.1.dist-info → kicad_sch_api-0.0.2.dist-info}/licenses/LICENSE +0 -0
- {kicad_sch_api-0.0.1.dist-info → kicad_sch_api-0.0.2.dist-info}/top_level.txt +0 -0
kicad_sch_api/__init__.py
CHANGED
|
@@ -42,7 +42,7 @@ Advanced Usage:
|
|
|
42
42
|
print(f"Found {len(issues)} validation issues")
|
|
43
43
|
"""
|
|
44
44
|
|
|
45
|
-
__version__ = "0.0.
|
|
45
|
+
__version__ = "0.0.2"
|
|
46
46
|
__author__ = "Circuit-Synth"
|
|
47
47
|
__email__ = "info@circuit-synth.com"
|
|
48
48
|
|
|
@@ -54,7 +54,7 @@ from .library.cache import SymbolLibraryCache, get_symbol_cache
|
|
|
54
54
|
from .utils.validation import ValidationError, ValidationIssue
|
|
55
55
|
|
|
56
56
|
# Version info
|
|
57
|
-
VERSION_INFO = (0, 0,
|
|
57
|
+
VERSION_INFO = (0, 0, 2)
|
|
58
58
|
|
|
59
59
|
# Public API
|
|
60
60
|
__all__ = [
|
kicad_sch_api/core/components.py
CHANGED
|
@@ -471,9 +471,13 @@ class ComponentCollection:
|
|
|
471
471
|
matching = self.filter(**criteria)
|
|
472
472
|
|
|
473
473
|
for component in matching:
|
|
474
|
-
# Update basic properties
|
|
474
|
+
# Update basic properties and handle special cases
|
|
475
475
|
for key, value in updates.items():
|
|
476
|
-
if
|
|
476
|
+
if key == 'properties' and isinstance(value, dict):
|
|
477
|
+
# Handle properties dictionary specially
|
|
478
|
+
for prop_name, prop_value in value.items():
|
|
479
|
+
component.set_property(prop_name, str(prop_value))
|
|
480
|
+
elif hasattr(component, key) and key not in ['properties']:
|
|
477
481
|
setattr(component, key, value)
|
|
478
482
|
else:
|
|
479
483
|
# Add as custom property
|
kicad_sch_api/core/parser.py
CHANGED
|
@@ -200,7 +200,7 @@ class SExpressionParser:
|
|
|
200
200
|
element_type = str(item[0]) if isinstance(item[0], sexpdata.Symbol) else None
|
|
201
201
|
|
|
202
202
|
if element_type == "version":
|
|
203
|
-
schematic_data["version"] = item[1] if len(item) > 1 else None
|
|
203
|
+
schematic_data["version"] = str(item[1]) if len(item) > 1 else None
|
|
204
204
|
elif element_type == "generator":
|
|
205
205
|
schematic_data["generator"] = item[1] if len(item) > 1 else None
|
|
206
206
|
elif element_type == "uuid":
|
|
@@ -244,9 +244,9 @@ class SExpressionParser:
|
|
|
244
244
|
if schematic_data.get("title_block"):
|
|
245
245
|
sexp_data.append(self._title_block_to_sexp(schematic_data["title_block"]))
|
|
246
246
|
|
|
247
|
-
# Add lib_symbols
|
|
248
|
-
|
|
249
|
-
|
|
247
|
+
# Add lib_symbols (always include for KiCAD compatibility)
|
|
248
|
+
lib_symbols = schematic_data.get("lib_symbols", {})
|
|
249
|
+
sexp_data.append(self._lib_symbols_to_sexp(lib_symbols))
|
|
250
250
|
|
|
251
251
|
# Add components
|
|
252
252
|
for component in schematic_data.get("components", []):
|
|
@@ -264,6 +264,11 @@ class SExpressionParser:
|
|
|
264
264
|
for label in schematic_data.get("labels", []):
|
|
265
265
|
sexp_data.append(self._label_to_sexp(label))
|
|
266
266
|
|
|
267
|
+
# Add symbol_instances (required by KiCAD)
|
|
268
|
+
symbol_instances = schematic_data.get("symbol_instances", [])
|
|
269
|
+
if symbol_instances or schematic_data.get("components"):
|
|
270
|
+
sexp_data.append([sexpdata.Symbol("symbol_instances")])
|
|
271
|
+
|
|
267
272
|
return sexp_data
|
|
268
273
|
|
|
269
274
|
def _parse_title_block(self, item: List[Any]) -> Dict[str, Any]:
|
|
@@ -321,7 +326,11 @@ class SExpressionParser:
|
|
|
321
326
|
elif prop_name == "Footprint":
|
|
322
327
|
symbol_data["footprint"] = prop_data.get("value")
|
|
323
328
|
else:
|
|
324
|
-
|
|
329
|
+
# Unescape quotes in property values when loading
|
|
330
|
+
prop_value = prop_data.get("value")
|
|
331
|
+
if prop_value:
|
|
332
|
+
prop_value = str(prop_value).replace('\\"', '"')
|
|
333
|
+
symbol_data["properties"][prop_name] = prop_value
|
|
325
334
|
elif element_type == "in_bom":
|
|
326
335
|
symbol_data["in_bom"] = sub_item[1] == "yes" if len(sub_item) > 1 else True
|
|
327
336
|
elif element_type == "on_board":
|
|
@@ -379,13 +388,15 @@ class SExpressionParser:
|
|
|
379
388
|
if symbol_data.get("lib_id"):
|
|
380
389
|
sexp.append([sexpdata.Symbol("lib_id"), symbol_data["lib_id"]])
|
|
381
390
|
|
|
382
|
-
# Add position and rotation
|
|
391
|
+
# Add position and rotation (preserve original format)
|
|
383
392
|
pos = symbol_data.get("position", Point(0, 0))
|
|
384
393
|
rotation = symbol_data.get("rotation", 0)
|
|
385
|
-
if
|
|
386
|
-
|
|
387
|
-
else
|
|
388
|
-
|
|
394
|
+
# Format numbers as integers if they are whole numbers
|
|
395
|
+
x = int(pos.x) if pos.x == int(pos.x) else pos.x
|
|
396
|
+
y = int(pos.y) if pos.y == int(pos.y) else pos.y
|
|
397
|
+
r = int(rotation) if rotation == int(rotation) else rotation
|
|
398
|
+
# Always include rotation for format consistency with KiCAD
|
|
399
|
+
sexp.append([sexpdata.Symbol("at"), x, y, r])
|
|
389
400
|
|
|
390
401
|
if symbol_data.get("uuid"):
|
|
391
402
|
sexp.append([sexpdata.Symbol("uuid"), symbol_data["uuid"]])
|
|
@@ -395,11 +406,14 @@ class SExpressionParser:
|
|
|
395
406
|
sexp.append([sexpdata.Symbol("property"), "Reference", symbol_data["reference"]])
|
|
396
407
|
if symbol_data.get("value"):
|
|
397
408
|
sexp.append([sexpdata.Symbol("property"), "Value", symbol_data["value"]])
|
|
398
|
-
|
|
399
|
-
|
|
409
|
+
footprint = symbol_data.get("footprint")
|
|
410
|
+
if footprint is not None: # Include empty strings but not None
|
|
411
|
+
sexp.append([sexpdata.Symbol("property"), "Footprint", footprint])
|
|
400
412
|
|
|
401
413
|
for prop_name, prop_value in symbol_data.get("properties", {}).items():
|
|
402
|
-
|
|
414
|
+
# Escape quotes in property values for proper S-expression format
|
|
415
|
+
escaped_value = str(prop_value).replace('"', '\\"')
|
|
416
|
+
sexp.append([sexpdata.Symbol("property"), prop_name, escaped_value])
|
|
403
417
|
|
|
404
418
|
# Add BOM and board settings
|
|
405
419
|
sexp.append([sexpdata.Symbol("in_bom"), "yes" if symbol_data.get("in_bom", True) else "no"])
|
|
@@ -426,8 +440,16 @@ class SExpressionParser:
|
|
|
426
440
|
|
|
427
441
|
def _lib_symbols_to_sexp(self, lib_symbols: Dict[str, Any]) -> List[Any]:
|
|
428
442
|
"""Convert lib_symbols to S-expression."""
|
|
429
|
-
|
|
430
|
-
|
|
443
|
+
sexp = [sexpdata.Symbol("lib_symbols")]
|
|
444
|
+
|
|
445
|
+
# Add each symbol definition
|
|
446
|
+
for symbol_name, symbol_def in lib_symbols.items():
|
|
447
|
+
if isinstance(symbol_def, dict):
|
|
448
|
+
symbol_sexp = [sexpdata.Symbol("symbol"), symbol_name]
|
|
449
|
+
# Add symbol definition details (for now, basic structure)
|
|
450
|
+
sexp.append(symbol_sexp)
|
|
451
|
+
|
|
452
|
+
return sexp
|
|
431
453
|
|
|
432
454
|
def get_validation_issues(self) -> List[ValidationIssue]:
|
|
433
455
|
"""Get list of validation issues from last parse operation."""
|
|
@@ -87,7 +87,7 @@ class SchematicValidator:
|
|
|
87
87
|
"""
|
|
88
88
|
self.strict = strict
|
|
89
89
|
self.issues = []
|
|
90
|
-
self._valid_reference_pattern = re.compile(r"^[A-Z]+[0-9]
|
|
90
|
+
self._valid_reference_pattern = re.compile(r"^(#[A-Z]+[0-9]+|[A-Z]+[0-9]*)$")
|
|
91
91
|
self._valid_lib_id_pattern = re.compile(r"^[^:]+:[^:]+$")
|
|
92
92
|
|
|
93
93
|
def validate_schematic_data(self, schematic_data: Dict[str, Any]) -> List[ValidationIssue]:
|
|
@@ -133,7 +133,7 @@ class SchematicValidator:
|
|
|
133
133
|
Validate component reference format.
|
|
134
134
|
|
|
135
135
|
Args:
|
|
136
|
-
reference: Reference to validate (e.g., "R1", "U5")
|
|
136
|
+
reference: Reference to validate (e.g., "R1", "U5", "#PWR01")
|
|
137
137
|
|
|
138
138
|
Returns:
|
|
139
139
|
True if reference is valid
|
|
@@ -231,7 +231,7 @@ class SchematicValidator:
|
|
|
231
231
|
message=f"{context}: Invalid reference format: {reference}",
|
|
232
232
|
level=ValidationLevel.ERROR,
|
|
233
233
|
context={"reference": reference},
|
|
234
|
-
suggestion="Reference should match pattern: [A-Z]+[0-9]*",
|
|
234
|
+
suggestion="Reference should match pattern: [A-Z]+[0-9]* or #[A-Z]+[0-9]* (for power symbols)",
|
|
235
235
|
)
|
|
236
236
|
)
|
|
237
237
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kicad-sch-api
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: Professional KiCAD schematic manipulation library with exact format preservation and AI agent integration
|
|
5
5
|
Author-email: Circuit-Synth <info@circuit-synth.com>
|
|
6
6
|
Maintainer-email: Circuit-Synth <info@circuit-synth.com>
|
|
@@ -10,7 +10,7 @@ Project-URL: Documentation, https://circuit-synth.github.io/kicad-sch-api/
|
|
|
10
10
|
Project-URL: Repository, https://github.com/circuit-synth/kicad-sch-api.git
|
|
11
11
|
Project-URL: Bug Reports, https://github.com/circuit-synth/kicad-sch-api/issues
|
|
12
12
|
Project-URL: Changelog, https://github.com/circuit-synth/kicad-sch-api/blob/main/CHANGELOG.md
|
|
13
|
-
Keywords: kicad,schematic,eda,electronics,circuit-design,ai,
|
|
13
|
+
Keywords: kicad,schematic,eda,electronics,circuit-design,ai,automation,pcb
|
|
14
14
|
Classifier: Development Status :: 4 - Beta
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
16
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -28,8 +28,6 @@ Description-Content-Type: text/markdown
|
|
|
28
28
|
License-File: LICENSE
|
|
29
29
|
Requires-Dist: sexpdata>=0.0.3
|
|
30
30
|
Requires-Dist: typing-extensions>=4.0.0; python_version < "3.11"
|
|
31
|
-
Provides-Extra: mcp
|
|
32
|
-
Requires-Dist: mcp>=0.1.0; extra == "mcp"
|
|
33
31
|
Provides-Extra: dev
|
|
34
32
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
35
33
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
@@ -49,13 +47,11 @@ Dynamic: license-file
|
|
|
49
47
|
|
|
50
48
|
**Professional KiCAD Schematic Manipulation Library with AI Agent Integration**
|
|
51
49
|
|
|
52
|
-
A modern, high-performance Python library for programmatic manipulation of KiCAD schematic files (.kicad_sch) with exact format preservation, enhanced component management, and native AI agent support via Model Context Protocol (MCP).
|
|
53
50
|
|
|
54
51
|
## 🚀 Key Features
|
|
55
52
|
|
|
56
53
|
- **📋 Exact Format Preservation**: Output matches KiCAD's native formatting exactly
|
|
57
54
|
- **⚡ High Performance**: Optimized for large schematics with symbol caching
|
|
58
|
-
- **🤖 AI Agent Integration**: Native MCP server for seamless AI agent interaction
|
|
59
55
|
- **🔧 Enhanced API**: Intuitive object-oriented interface with bulk operations
|
|
60
56
|
- **📚 Advanced Library Management**: Multi-source symbol lookup and caching
|
|
61
57
|
- **✅ Professional Validation**: Comprehensive error collection and reporting
|
|
@@ -68,7 +64,6 @@ A modern, high-performance Python library for programmatic manipulation of KiCAD
|
|
|
68
64
|
| **Schematic Support** | ✅ Full | ✅ Full | ❌ PCB Only |
|
|
69
65
|
| **Format Preservation** | ✅ Exact | ❌ Basic | N/A |
|
|
70
66
|
| **Performance** | ✅ Optimized | ⚠️ Basic | N/A |
|
|
71
|
-
| **AI Integration** | ✅ Native MCP | ❌ None | ❌ None |
|
|
72
67
|
| **Library Management** | ✅ Advanced | ⚠️ Basic | N/A |
|
|
73
68
|
| **Runtime Dependencies** | ❌ None | ❌ None | ✅ KiCAD Required |
|
|
74
69
|
|
|
@@ -83,8 +78,6 @@ git clone https://github.com/circuit-synth/kicad-sch-api.git
|
|
|
83
78
|
cd kicad-sch-api/python
|
|
84
79
|
pip install -e .
|
|
85
80
|
|
|
86
|
-
# For AI agent integration (MCP server)
|
|
87
|
-
cd ../mcp-server
|
|
88
81
|
npm install
|
|
89
82
|
npm run build
|
|
90
83
|
```
|
|
@@ -141,15 +134,12 @@ stats = sch.get_performance_stats()
|
|
|
141
134
|
print(f"Cache hit rate: {stats['symbol_cache']['hit_rate_percent']}%")
|
|
142
135
|
```
|
|
143
136
|
|
|
144
|
-
### AI Agent Integration (MCP)
|
|
145
137
|
|
|
146
|
-
Configure in Claude Desktop or compatible MCP client:
|
|
147
138
|
|
|
148
139
|
```json
|
|
149
140
|
{
|
|
150
141
|
"kicad-sch": {
|
|
151
142
|
"command": "node",
|
|
152
|
-
"args": ["/path/to/kicad-sch-api/mcp-server/dist/index.js"],
|
|
153
143
|
"env": {
|
|
154
144
|
"PYTHON_PATH": "python3",
|
|
155
145
|
"KICAD_SCH_API_PATH": "/path/to/kicad-sch-api/python"
|
|
@@ -165,7 +155,6 @@ User: "Create a voltage divider circuit with two 10k resistors"
|
|
|
165
155
|
|
|
166
156
|
Claude: I'll create a voltage divider circuit for you.
|
|
167
157
|
|
|
168
|
-
[Agent automatically uses MCP tools to:]
|
|
169
158
|
1. Create new schematic
|
|
170
159
|
2. Add R1 (10k resistor) at (100, 100)
|
|
171
160
|
3. Add R2 (10k resistor) at (100, 150)
|
|
@@ -187,8 +176,6 @@ The library consists of two main components:
|
|
|
187
176
|
- **Symbol Caching**: High-performance library symbol management
|
|
188
177
|
- **Comprehensive Validation**: Error collection and professional reporting
|
|
189
178
|
|
|
190
|
-
### MCP Server (AI Integration)
|
|
191
|
-
- **TypeScript MCP Server**: Implements Anthropic's MCP specification
|
|
192
179
|
- **Python Bridge**: Reliable subprocess communication
|
|
193
180
|
- **Comprehensive Tools**: 15+ tools for complete schematic manipulation
|
|
194
181
|
- **Professional Error Handling**: Detailed error context for AI agents
|
|
@@ -200,8 +187,6 @@ The library consists of two main components:
|
|
|
200
187
|
cd python
|
|
201
188
|
python -m pytest tests/ -v --cov=kicad_sch_api
|
|
202
189
|
|
|
203
|
-
# MCP server tests
|
|
204
|
-
cd mcp-server
|
|
205
190
|
npm test
|
|
206
191
|
|
|
207
192
|
# Format preservation tests
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
kicad_sch_api/__init__.py,sha256=mogTeOic6O-WWOfpoRuBzPicVNha-gbdMrhJX4ir5PY,2821
|
|
2
|
+
kicad_sch_api/py.typed,sha256=e4ldqxwpY7pNDG1olbvj4HSKr8sZ9vxgA_2ek8xXn-Q,70
|
|
3
|
+
kicad_sch_api/core/__init__.py,sha256=ur_KeYBlGKl-e1hLpLdxAhGV2A-PCCGkcqd0r6KSeBA,566
|
|
4
|
+
kicad_sch_api/core/components.py,sha256=P_-qZPNpA8LAT0M3dLHFF6XSFyNAnSlcKjUf6aREdUo,22425
|
|
5
|
+
kicad_sch_api/core/formatter.py,sha256=MVF3Hhc5ZVPyVDYnGcxb88q0x0UTr2DQa45gppiFqNk,11953
|
|
6
|
+
kicad_sch_api/core/parser.py,sha256=Ao1j8JPWHGBkznUi9k-RO4_sLcCrkE_5ialSxZAd0cQ,17943
|
|
7
|
+
kicad_sch_api/core/schematic.py,sha256=O76nZvj4qffHkFrMJV5Z35xU95efPW-_mtAD8Nni7ao,15553
|
|
8
|
+
kicad_sch_api/core/types.py,sha256=VyzloTl4RbjMKj0TKu5rEZ-rtxtiT8nvQw8L6xawEvs,9980
|
|
9
|
+
kicad_sch_api/library/__init__.py,sha256=NG9UTdcpn25Bl9tPsYs9ED7bvpaVPVdtLMbnxkQkOnU,250
|
|
10
|
+
kicad_sch_api/library/cache.py,sha256=_JtzEGgO7ViIKF4W2zVrvmHQBIiosp9hOr9pG06Tw6I,18917
|
|
11
|
+
kicad_sch_api/utils/__init__.py,sha256=1V_yGgI7jro6MUc4Pviux_WIeJ1wmiYFID186SZwWLQ,277
|
|
12
|
+
kicad_sch_api/utils/validation.py,sha256=7QZBtPHKu24SA6Bhkj7M-rxs76AVQZWhISDfsJYfV_0,15620
|
|
13
|
+
kicad_sch_api-0.0.2.dist-info/licenses/LICENSE,sha256=Em65Nvte1G9MHc0rHqtYuGkCPcshD588itTa358J6gs,1070
|
|
14
|
+
kicad_sch_api-0.0.2.dist-info/METADATA,sha256=0wa5C_1GHCirtAdlUTh2Eqqa1AreZkeTts6WkQaNjd8,6810
|
|
15
|
+
kicad_sch_api-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
kicad_sch_api-0.0.2.dist-info/entry_points.txt,sha256=VWKsFi2Jv7G_tmio3cNVhhIBfv_OZFaKa-T_ED84lc8,57
|
|
17
|
+
kicad_sch_api-0.0.2.dist-info/top_level.txt,sha256=n0ex4gOJ1b_fARowcGqRzyOGZcHRhc5LZa6_vVgGxcI,14
|
|
18
|
+
kicad_sch_api-0.0.2.dist-info/RECORD,,
|
kicad_sch_api/mcp/__init__.py
DELETED
kicad_sch_api/mcp/server.py
DELETED
|
@@ -1,500 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Python MCP interface for kicad-sch-api.
|
|
4
|
-
|
|
5
|
-
This script provides the Python side of the MCP bridge, handling commands
|
|
6
|
-
from the TypeScript MCP server and executing them using kicad-sch-api.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import logging
|
|
11
|
-
import sys
|
|
12
|
-
import traceback
|
|
13
|
-
from typing import Any, Dict, Optional
|
|
14
|
-
|
|
15
|
-
# Configure logging
|
|
16
|
-
logging.basicConfig(
|
|
17
|
-
level=logging.INFO,
|
|
18
|
-
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
19
|
-
handlers=[logging.StreamHandler(sys.stderr)],
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger("kicad_sch_api.mcp")
|
|
23
|
-
|
|
24
|
-
# Import kicad-sch-api components
|
|
25
|
-
try:
|
|
26
|
-
from ..core.components import Component
|
|
27
|
-
from ..core.schematic import Schematic
|
|
28
|
-
from ..library.cache import get_symbol_cache
|
|
29
|
-
from ..utils.validation import ValidationError, ValidationIssue
|
|
30
|
-
except ImportError as e:
|
|
31
|
-
logger.error(f"Failed to import kicad-sch-api modules: {e}")
|
|
32
|
-
sys.exit(1)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class MCPInterface:
|
|
36
|
-
"""MCP command interface for kicad-sch-api."""
|
|
37
|
-
|
|
38
|
-
def __init__(self):
|
|
39
|
-
"""Initialize the MCP interface."""
|
|
40
|
-
self.current_schematic: Optional[Schematic] = None
|
|
41
|
-
self.symbol_cache = get_symbol_cache()
|
|
42
|
-
|
|
43
|
-
# Command handlers
|
|
44
|
-
self.handlers = {
|
|
45
|
-
"ping": self.ping,
|
|
46
|
-
"load_schematic": self.load_schematic,
|
|
47
|
-
"save_schematic": self.save_schematic,
|
|
48
|
-
"create_schematic": self.create_schematic,
|
|
49
|
-
"add_component": self.add_component,
|
|
50
|
-
"update_component": self.update_component,
|
|
51
|
-
"remove_component": self.remove_component,
|
|
52
|
-
"get_component": self.get_component,
|
|
53
|
-
"find_components": self.find_components,
|
|
54
|
-
"add_wire": self.add_wire,
|
|
55
|
-
"connect_components": self.connect_components,
|
|
56
|
-
"bulk_update_components": self.bulk_update_components,
|
|
57
|
-
"validate_schematic": self.validate_schematic,
|
|
58
|
-
"get_schematic_summary": self.get_schematic_summary,
|
|
59
|
-
"search_library_symbols": self.search_library_symbols,
|
|
60
|
-
"add_library_path": self.add_library_path,
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
logger.info("MCP interface initialized")
|
|
64
|
-
|
|
65
|
-
def ping(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
66
|
-
"""Health check command."""
|
|
67
|
-
return {
|
|
68
|
-
"success": True,
|
|
69
|
-
"message": "kicad-sch-api MCP interface is ready",
|
|
70
|
-
"version": "0.0.1",
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
def load_schematic(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
74
|
-
"""Load a schematic file."""
|
|
75
|
-
try:
|
|
76
|
-
file_path = params.get("file_path")
|
|
77
|
-
if not file_path:
|
|
78
|
-
return {"success": False, "error": "file_path parameter required"}
|
|
79
|
-
|
|
80
|
-
self.current_schematic = Schematic.load(file_path)
|
|
81
|
-
summary = self.current_schematic.get_summary()
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
"success": True,
|
|
85
|
-
"message": f"Loaded schematic: {file_path}",
|
|
86
|
-
"summary": summary,
|
|
87
|
-
}
|
|
88
|
-
except Exception as e:
|
|
89
|
-
logger.error(f"Error loading schematic: {e}")
|
|
90
|
-
return {"success": False, "error": str(e)}
|
|
91
|
-
|
|
92
|
-
def save_schematic(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
93
|
-
"""Save the current schematic."""
|
|
94
|
-
try:
|
|
95
|
-
if not self.current_schematic:
|
|
96
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
97
|
-
|
|
98
|
-
file_path = params.get("file_path")
|
|
99
|
-
preserve_format = params.get("preserve_format", True)
|
|
100
|
-
|
|
101
|
-
self.current_schematic.save(file_path, preserve_format)
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
"success": True,
|
|
105
|
-
"message": f"Saved schematic to: {self.current_schematic.file_path}",
|
|
106
|
-
}
|
|
107
|
-
except Exception as e:
|
|
108
|
-
logger.error(f"Error saving schematic: {e}")
|
|
109
|
-
return {"success": False, "error": str(e)}
|
|
110
|
-
|
|
111
|
-
def create_schematic(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
112
|
-
"""Create a new schematic."""
|
|
113
|
-
try:
|
|
114
|
-
name = params.get("name", "New Circuit")
|
|
115
|
-
self.current_schematic = Schematic.create(name)
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
"success": True,
|
|
119
|
-
"message": f"Created new schematic: {name}",
|
|
120
|
-
"summary": self.current_schematic.get_summary(),
|
|
121
|
-
}
|
|
122
|
-
except Exception as e:
|
|
123
|
-
logger.error(f"Error creating schematic: {e}")
|
|
124
|
-
return {"success": False, "error": str(e)}
|
|
125
|
-
|
|
126
|
-
def add_component(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
127
|
-
"""Add a component to the schematic."""
|
|
128
|
-
try:
|
|
129
|
-
if not self.current_schematic:
|
|
130
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
131
|
-
|
|
132
|
-
lib_id = params.get("lib_id")
|
|
133
|
-
if not lib_id:
|
|
134
|
-
return {"success": False, "error": "lib_id parameter required"}
|
|
135
|
-
|
|
136
|
-
# Extract parameters
|
|
137
|
-
reference = params.get("reference")
|
|
138
|
-
value = params.get("value", "")
|
|
139
|
-
position = params.get("position")
|
|
140
|
-
footprint = params.get("footprint")
|
|
141
|
-
properties = params.get("properties", {})
|
|
142
|
-
|
|
143
|
-
# Convert position if provided
|
|
144
|
-
pos_tuple = None
|
|
145
|
-
if position:
|
|
146
|
-
pos_tuple = (position["x"], position["y"])
|
|
147
|
-
|
|
148
|
-
# Add component
|
|
149
|
-
component = self.current_schematic.components.add(
|
|
150
|
-
lib_id=lib_id,
|
|
151
|
-
reference=reference,
|
|
152
|
-
value=value,
|
|
153
|
-
position=pos_tuple,
|
|
154
|
-
footprint=footprint,
|
|
155
|
-
**properties,
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
"success": True,
|
|
160
|
-
"message": f"Added component: {component.reference}",
|
|
161
|
-
"component": component.to_dict(),
|
|
162
|
-
}
|
|
163
|
-
except Exception as e:
|
|
164
|
-
logger.error(f"Error adding component: {e}")
|
|
165
|
-
return {"success": False, "error": str(e)}
|
|
166
|
-
|
|
167
|
-
def update_component(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
168
|
-
"""Update a component's properties."""
|
|
169
|
-
try:
|
|
170
|
-
if not self.current_schematic:
|
|
171
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
172
|
-
|
|
173
|
-
reference = params.get("reference")
|
|
174
|
-
if not reference:
|
|
175
|
-
return {"success": False, "error": "reference parameter required"}
|
|
176
|
-
|
|
177
|
-
component = self.current_schematic.components.get(reference)
|
|
178
|
-
if not component:
|
|
179
|
-
return {"success": False, "error": f"Component not found: {reference}"}
|
|
180
|
-
|
|
181
|
-
# Apply updates
|
|
182
|
-
updates = 0
|
|
183
|
-
if "value" in params:
|
|
184
|
-
component.value = params["value"]
|
|
185
|
-
updates += 1
|
|
186
|
-
|
|
187
|
-
if "position" in params:
|
|
188
|
-
pos = params["position"]
|
|
189
|
-
component.position = (pos["x"], pos["y"])
|
|
190
|
-
updates += 1
|
|
191
|
-
|
|
192
|
-
if "footprint" in params:
|
|
193
|
-
component.footprint = params["footprint"]
|
|
194
|
-
updates += 1
|
|
195
|
-
|
|
196
|
-
if "properties" in params:
|
|
197
|
-
for name, value in params["properties"].items():
|
|
198
|
-
component.set_property(name, value)
|
|
199
|
-
updates += 1
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
"success": True,
|
|
203
|
-
"message": f"Updated component {reference} ({updates} changes)",
|
|
204
|
-
"component": component.to_dict(),
|
|
205
|
-
}
|
|
206
|
-
except Exception as e:
|
|
207
|
-
logger.error(f"Error updating component: {e}")
|
|
208
|
-
return {"success": False, "error": str(e)}
|
|
209
|
-
|
|
210
|
-
def remove_component(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
211
|
-
"""Remove a component from the schematic."""
|
|
212
|
-
try:
|
|
213
|
-
if not self.current_schematic:
|
|
214
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
215
|
-
|
|
216
|
-
reference = params.get("reference")
|
|
217
|
-
if not reference:
|
|
218
|
-
return {"success": False, "error": "reference parameter required"}
|
|
219
|
-
|
|
220
|
-
success = self.current_schematic.components.remove(reference)
|
|
221
|
-
|
|
222
|
-
if success:
|
|
223
|
-
return {"success": True, "message": f"Removed component: {reference}"}
|
|
224
|
-
else:
|
|
225
|
-
return {"success": False, "error": f"Component not found: {reference}"}
|
|
226
|
-
except Exception as e:
|
|
227
|
-
logger.error(f"Error removing component: {e}")
|
|
228
|
-
return {"success": False, "error": str(e)}
|
|
229
|
-
|
|
230
|
-
def get_component(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
231
|
-
"""Get detailed information about a component."""
|
|
232
|
-
try:
|
|
233
|
-
if not self.current_schematic:
|
|
234
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
235
|
-
|
|
236
|
-
reference = params.get("reference")
|
|
237
|
-
if not reference:
|
|
238
|
-
return {"success": False, "error": "reference parameter required"}
|
|
239
|
-
|
|
240
|
-
component = self.current_schematic.components.get(reference)
|
|
241
|
-
if not component:
|
|
242
|
-
return {"success": False, "error": f"Component not found: {reference}"}
|
|
243
|
-
|
|
244
|
-
return {"success": True, "component": component.to_dict()}
|
|
245
|
-
except Exception as e:
|
|
246
|
-
logger.error(f"Error getting component: {e}")
|
|
247
|
-
return {"success": False, "error": str(e)}
|
|
248
|
-
|
|
249
|
-
def find_components(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
250
|
-
"""Find components by criteria."""
|
|
251
|
-
try:
|
|
252
|
-
if not self.current_schematic:
|
|
253
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
254
|
-
|
|
255
|
-
# Filter out None values and convert to filter criteria
|
|
256
|
-
criteria = {k: v for k, v in params.items() if v is not None}
|
|
257
|
-
|
|
258
|
-
# Special handling for in_area
|
|
259
|
-
if "in_area" in criteria:
|
|
260
|
-
area = criteria["in_area"]
|
|
261
|
-
if len(area) == 4:
|
|
262
|
-
criteria["in_area"] = tuple(area)
|
|
263
|
-
|
|
264
|
-
components = self.current_schematic.components.filter(**criteria)
|
|
265
|
-
|
|
266
|
-
return {
|
|
267
|
-
"success": True,
|
|
268
|
-
"count": len(components),
|
|
269
|
-
"components": [comp.to_dict() for comp in components],
|
|
270
|
-
}
|
|
271
|
-
except Exception as e:
|
|
272
|
-
logger.error(f"Error finding components: {e}")
|
|
273
|
-
return {"success": False, "error": str(e)}
|
|
274
|
-
|
|
275
|
-
def add_wire(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
276
|
-
"""Add a wire connection."""
|
|
277
|
-
try:
|
|
278
|
-
if not self.current_schematic:
|
|
279
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
280
|
-
|
|
281
|
-
start = params.get("start")
|
|
282
|
-
end = params.get("end")
|
|
283
|
-
|
|
284
|
-
if not start or not end:
|
|
285
|
-
return {"success": False, "error": "start and end parameters required"}
|
|
286
|
-
|
|
287
|
-
wire_uuid = self.current_schematic.add_wire(
|
|
288
|
-
(start["x"], start["y"]), (end["x"], end["y"])
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
return {
|
|
292
|
-
"success": True,
|
|
293
|
-
"message": f"Added wire from {start} to {end}",
|
|
294
|
-
"wire_uuid": wire_uuid,
|
|
295
|
-
}
|
|
296
|
-
except Exception as e:
|
|
297
|
-
logger.error(f"Error adding wire: {e}")
|
|
298
|
-
return {"success": False, "error": str(e)}
|
|
299
|
-
|
|
300
|
-
def connect_components(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
301
|
-
"""Connect two component pins with a wire."""
|
|
302
|
-
try:
|
|
303
|
-
if not self.current_schematic:
|
|
304
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
305
|
-
|
|
306
|
-
from_comp = params.get("from_component")
|
|
307
|
-
from_pin = params.get("from_pin")
|
|
308
|
-
to_comp = params.get("to_component")
|
|
309
|
-
to_pin = params.get("to_pin")
|
|
310
|
-
|
|
311
|
-
if not all([from_comp, from_pin, to_comp, to_pin]):
|
|
312
|
-
return {"success": False, "error": "All connection parameters required"}
|
|
313
|
-
|
|
314
|
-
# Get component pin positions
|
|
315
|
-
comp1 = self.current_schematic.components.get(from_comp)
|
|
316
|
-
comp2 = self.current_schematic.components.get(to_comp)
|
|
317
|
-
|
|
318
|
-
if not comp1:
|
|
319
|
-
return {"success": False, "error": f"Component not found: {from_comp}"}
|
|
320
|
-
if not comp2:
|
|
321
|
-
return {"success": False, "error": f"Component not found: {to_comp}"}
|
|
322
|
-
|
|
323
|
-
pin1_pos = comp1.get_pin_position(from_pin)
|
|
324
|
-
pin2_pos = comp2.get_pin_position(to_pin)
|
|
325
|
-
|
|
326
|
-
if not pin1_pos or not pin2_pos:
|
|
327
|
-
return {"success": False, "error": "Could not determine pin positions"}
|
|
328
|
-
|
|
329
|
-
# Add wire between pins
|
|
330
|
-
wire_uuid = self.current_schematic.add_wire(pin1_pos, pin2_pos)
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
"success": True,
|
|
334
|
-
"message": f"Connected {from_comp}.{from_pin} to {to_comp}.{to_pin}",
|
|
335
|
-
"wire_uuid": wire_uuid,
|
|
336
|
-
}
|
|
337
|
-
except Exception as e:
|
|
338
|
-
logger.error(f"Error connecting components: {e}")
|
|
339
|
-
return {"success": False, "error": str(e)}
|
|
340
|
-
|
|
341
|
-
def bulk_update_components(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
342
|
-
"""Update multiple components matching criteria."""
|
|
343
|
-
try:
|
|
344
|
-
if not self.current_schematic:
|
|
345
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
346
|
-
|
|
347
|
-
criteria = params.get("criteria", {})
|
|
348
|
-
updates = params.get("updates", {})
|
|
349
|
-
|
|
350
|
-
if not criteria or not updates:
|
|
351
|
-
return {"success": False, "error": "criteria and updates parameters required"}
|
|
352
|
-
|
|
353
|
-
count = self.current_schematic.components.bulk_update(criteria, updates)
|
|
354
|
-
|
|
355
|
-
return {"success": True, "message": f"Updated {count} components", "count": count}
|
|
356
|
-
except Exception as e:
|
|
357
|
-
logger.error(f"Error in bulk update: {e}")
|
|
358
|
-
return {"success": False, "error": str(e)}
|
|
359
|
-
|
|
360
|
-
def validate_schematic(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
361
|
-
"""Validate the current schematic."""
|
|
362
|
-
try:
|
|
363
|
-
if not self.current_schematic:
|
|
364
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
365
|
-
|
|
366
|
-
issues = self.current_schematic.validate()
|
|
367
|
-
|
|
368
|
-
# Categorize issues
|
|
369
|
-
errors = [issue for issue in issues if issue.level.value in ("error", "critical")]
|
|
370
|
-
warnings = [issue for issue in issues if issue.level.value == "warning"]
|
|
371
|
-
|
|
372
|
-
return {
|
|
373
|
-
"success": True,
|
|
374
|
-
"valid": len(errors) == 0,
|
|
375
|
-
"issue_count": len(issues),
|
|
376
|
-
"errors": [str(issue) for issue in errors],
|
|
377
|
-
"warnings": [str(issue) for issue in warnings],
|
|
378
|
-
}
|
|
379
|
-
except Exception as e:
|
|
380
|
-
logger.error(f"Error validating schematic: {e}")
|
|
381
|
-
return {"success": False, "error": str(e)}
|
|
382
|
-
|
|
383
|
-
def get_schematic_summary(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
384
|
-
"""Get summary of current schematic."""
|
|
385
|
-
try:
|
|
386
|
-
if not self.current_schematic:
|
|
387
|
-
return {"success": False, "error": "No schematic loaded"}
|
|
388
|
-
|
|
389
|
-
summary = self.current_schematic.get_summary()
|
|
390
|
-
|
|
391
|
-
return {"success": True, "summary": summary}
|
|
392
|
-
except Exception as e:
|
|
393
|
-
logger.error(f"Error getting summary: {e}")
|
|
394
|
-
return {"success": False, "error": str(e)}
|
|
395
|
-
|
|
396
|
-
def search_library_symbols(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
397
|
-
"""Search for symbols in libraries."""
|
|
398
|
-
try:
|
|
399
|
-
query = params.get("query")
|
|
400
|
-
if not query:
|
|
401
|
-
return {"success": False, "error": "query parameter required"}
|
|
402
|
-
|
|
403
|
-
library = params.get("library")
|
|
404
|
-
limit = params.get("limit", 20)
|
|
405
|
-
|
|
406
|
-
symbols = self.symbol_cache.search_symbols(query, library, limit)
|
|
407
|
-
|
|
408
|
-
symbol_results = []
|
|
409
|
-
for symbol in symbols:
|
|
410
|
-
symbol_results.append(
|
|
411
|
-
{
|
|
412
|
-
"lib_id": symbol.lib_id,
|
|
413
|
-
"name": symbol.name,
|
|
414
|
-
"library": symbol.library,
|
|
415
|
-
"description": symbol.description,
|
|
416
|
-
"reference_prefix": symbol.reference_prefix,
|
|
417
|
-
"pin_count": len(symbol.pins),
|
|
418
|
-
}
|
|
419
|
-
)
|
|
420
|
-
|
|
421
|
-
return {"success": True, "count": len(symbol_results), "symbols": symbol_results}
|
|
422
|
-
except Exception as e:
|
|
423
|
-
logger.error(f"Error searching symbols: {e}")
|
|
424
|
-
return {"success": False, "error": str(e)}
|
|
425
|
-
|
|
426
|
-
def add_library_path(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
427
|
-
"""Add a custom library path."""
|
|
428
|
-
try:
|
|
429
|
-
library_path = params.get("library_path")
|
|
430
|
-
if not library_path:
|
|
431
|
-
return {"success": False, "error": "library_path parameter required"}
|
|
432
|
-
|
|
433
|
-
success = self.symbol_cache.add_library_path(library_path)
|
|
434
|
-
|
|
435
|
-
if success:
|
|
436
|
-
return {"success": True, "message": f"Added library: {library_path}"}
|
|
437
|
-
else:
|
|
438
|
-
return {"success": False, "error": f"Failed to add library: {library_path}"}
|
|
439
|
-
except Exception as e:
|
|
440
|
-
logger.error(f"Error adding library: {e}")
|
|
441
|
-
return {"success": False, "error": str(e)}
|
|
442
|
-
|
|
443
|
-
def process_commands(self):
|
|
444
|
-
"""Main command processing loop."""
|
|
445
|
-
logger.info("Starting command processing loop")
|
|
446
|
-
|
|
447
|
-
try:
|
|
448
|
-
for line in sys.stdin:
|
|
449
|
-
try:
|
|
450
|
-
# Parse command
|
|
451
|
-
request = json.loads(line.strip())
|
|
452
|
-
command = request.get("command")
|
|
453
|
-
params = request.get("params", {})
|
|
454
|
-
request_id = request.get("id")
|
|
455
|
-
|
|
456
|
-
# Execute command
|
|
457
|
-
if command in self.handlers:
|
|
458
|
-
result = self.handlers[command](params)
|
|
459
|
-
else:
|
|
460
|
-
result = {"success": False, "error": f"Unknown command: {command}"}
|
|
461
|
-
|
|
462
|
-
# Send response
|
|
463
|
-
response = {"id": request_id, "result": result}
|
|
464
|
-
|
|
465
|
-
print(json.dumps(response))
|
|
466
|
-
sys.stdout.flush()
|
|
467
|
-
|
|
468
|
-
except json.JSONDecodeError as e:
|
|
469
|
-
logger.error(f"Invalid JSON input: {e}")
|
|
470
|
-
error_response = {"id": None, "error": f"Invalid JSON: {e}"}
|
|
471
|
-
print(json.dumps(error_response))
|
|
472
|
-
sys.stdout.flush()
|
|
473
|
-
|
|
474
|
-
except Exception as e:
|
|
475
|
-
logger.error(f"Error processing command: {e}")
|
|
476
|
-
logger.debug(traceback.format_exc())
|
|
477
|
-
error_response = {
|
|
478
|
-
"id": request.get("id") if "request" in locals() else None,
|
|
479
|
-
"error": str(e),
|
|
480
|
-
}
|
|
481
|
-
print(json.dumps(error_response))
|
|
482
|
-
sys.stdout.flush()
|
|
483
|
-
|
|
484
|
-
except KeyboardInterrupt:
|
|
485
|
-
logger.info("Received interrupt signal")
|
|
486
|
-
except Exception as e:
|
|
487
|
-
logger.error(f"Fatal error in command processing: {e}")
|
|
488
|
-
logger.debug(traceback.format_exc())
|
|
489
|
-
finally:
|
|
490
|
-
logger.info("Command processing stopped")
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
def main():
|
|
494
|
-
"""Main entry point."""
|
|
495
|
-
interface = MCPInterface()
|
|
496
|
-
interface.process_commands()
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if __name__ == "__main__":
|
|
500
|
-
main()
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
kicad_sch_api/__init__.py,sha256=kvc92hzeQ6mSe3AsXSbK-CvGgoXtljyUCrzJcmBa-ao,2821
|
|
2
|
-
kicad_sch_api/py.typed,sha256=e4ldqxwpY7pNDG1olbvj4HSKr8sZ9vxgA_2ek8xXn-Q,70
|
|
3
|
-
kicad_sch_api/core/__init__.py,sha256=ur_KeYBlGKl-e1hLpLdxAhGV2A-PCCGkcqd0r6KSeBA,566
|
|
4
|
-
kicad_sch_api/core/components.py,sha256=_sw8Hh2eemGHzYcjGoFrBeufX05G1JfO5inYMQDQD9k,22100
|
|
5
|
-
kicad_sch_api/core/formatter.py,sha256=MVF3Hhc5ZVPyVDYnGcxb88q0x0UTr2DQa45gppiFqNk,11953
|
|
6
|
-
kicad_sch_api/core/parser.py,sha256=x7aMqnsDO4Y2VJtYvgrrJJVi2r8SAFUjSCVtMIDVWDY,16652
|
|
7
|
-
kicad_sch_api/core/schematic.py,sha256=O76nZvj4qffHkFrMJV5Z35xU95efPW-_mtAD8Nni7ao,15553
|
|
8
|
-
kicad_sch_api/core/types.py,sha256=VyzloTl4RbjMKj0TKu5rEZ-rtxtiT8nvQw8L6xawEvs,9980
|
|
9
|
-
kicad_sch_api/library/__init__.py,sha256=NG9UTdcpn25Bl9tPsYs9ED7bvpaVPVdtLMbnxkQkOnU,250
|
|
10
|
-
kicad_sch_api/library/cache.py,sha256=_JtzEGgO7ViIKF4W2zVrvmHQBIiosp9hOr9pG06Tw6I,18917
|
|
11
|
-
kicad_sch_api/mcp/__init__.py,sha256=dVit9lqiieujSYkyvfycE8JTo0AEWMI4plNxCK9pSD0,128
|
|
12
|
-
kicad_sch_api/mcp/server.py,sha256=wGTdXPF6mMA8JoP-HiDUg7XJ21Dfr0ZoThB3WqKM_fQ,19079
|
|
13
|
-
kicad_sch_api/utils/__init__.py,sha256=1V_yGgI7jro6MUc4Pviux_WIeJ1wmiYFID186SZwWLQ,277
|
|
14
|
-
kicad_sch_api/utils/validation.py,sha256=i7VvhMaYxrb8wxddf0S3vvpDE7r8ldM53CDqF9-ARqI,15557
|
|
15
|
-
kicad_sch_api-0.0.1.dist-info/licenses/LICENSE,sha256=Em65Nvte1G9MHc0rHqtYuGkCPcshD588itTa358J6gs,1070
|
|
16
|
-
kicad_sch_api-0.0.1.dist-info/METADATA,sha256=jrEKEblgqF95O2aXEdzpuZhGm1gFp4RFFe3DKU86GeQ,7643
|
|
17
|
-
kicad_sch_api-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
-
kicad_sch_api-0.0.1.dist-info/entry_points.txt,sha256=VWKsFi2Jv7G_tmio3cNVhhIBfv_OZFaKa-T_ED84lc8,57
|
|
19
|
-
kicad_sch_api-0.0.1.dist-info/top_level.txt,sha256=n0ex4gOJ1b_fARowcGqRzyOGZcHRhc5LZa6_vVgGxcI,14
|
|
20
|
-
kicad_sch_api-0.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|