@hybridlabor-api/bdb-hardware-pcb 0.1.0

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.
Files changed (192) hide show
  1. package/README.md +116 -0
  2. package/config/mcp/antigravity.json +22 -0
  3. package/config/mcp/claude.json +22 -0
  4. package/config/mcp/codex.toml +25 -0
  5. package/docs/adr/ADR-001-KICAD-OPENSCAD-MCP-STRATEGY.md +334 -0
  6. package/docs/review_documentation.md +121 -0
  7. package/installer.js +358 -0
  8. package/mcp_servers/kicad-mcp-server/.env.example +22 -0
  9. package/mcp_servers/kicad-mcp-server/.github/workflows/ci.yml +36 -0
  10. package/mcp_servers/kicad-mcp-server/CLAUDE.md +487 -0
  11. package/mcp_servers/kicad-mcp-server/README.md +316 -0
  12. package/mcp_servers/kicad-mcp-server/docs/DEVICE_TREE.md +416 -0
  13. package/mcp_servers/kicad-mcp-server/docs/INSTALLATION.md +332 -0
  14. package/mcp_servers/kicad-mcp-server/docs/PIN_ANALYSIS.md +332 -0
  15. package/mcp_servers/kicad-mcp-server/docs/README.md +240 -0
  16. package/mcp_servers/kicad-mcp-server/docs/TESTING.md +613 -0
  17. package/mcp_servers/kicad-mcp-server/docs/VALIDATION.md +268 -0
  18. package/mcp_servers/kicad-mcp-server/pyproject.toml +96 -0
  19. package/mcp_servers/kicad-mcp-server/requirements-dev.txt +16 -0
  20. package/mcp_servers/kicad-mcp-server/requirements-test.txt +24 -0
  21. package/mcp_servers/kicad-mcp-server/requirements.txt +13 -0
  22. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__init__.py +3 -0
  23. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__main__.py +17 -0
  24. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/config.py +46 -0
  25. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/__init__.py +1 -0
  26. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/types.py +87 -0
  27. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/__init__.py +1 -0
  28. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/netlist_parser.py +234 -0
  29. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser.py +375 -0
  30. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser_kicad.py +327 -0
  31. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/schematic_parser.py +902 -0
  32. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/server.py +71 -0
  33. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/__init__.py +1 -0
  34. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/arduino/connectivity_test.cpp.j2 +189 -0
  35. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/atmega.dts.j2 +77 -0
  36. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/esp32.dts.j2 +77 -0
  37. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/nrf52.dts.j2 +77 -0
  38. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/stm32f4.dts.j2 +89 -0
  39. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/esp_idf/test_suite.c.j2 +340 -0
  40. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/pytest/test_connectivity.py.j2 +147 -0
  41. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/st_hal/hal_test.c.j2 +313 -0
  42. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_gpio_test.py.j2 +99 -0
  43. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_i2c_test.py.j2 +117 -0
  44. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_pinmux_test.py.j2 +43 -0
  45. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_spi_test.py.j2 +94 -0
  46. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_gpio_test.c.j2 +113 -0
  47. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_i2c_test.c.j2 +101 -0
  48. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_spi_test.c.j2 +94 -0
  49. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/unittest/test_schematic.py.j2 +172 -0
  50. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/__init__.py +36 -0
  51. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/device_tree.py +1187 -0
  52. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/hierarchical_analysis.py +211 -0
  53. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/netlist.py +320 -0
  54. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/parts_registry.py +142 -0
  55. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb.py +955 -0
  56. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb_layout.py +308 -0
  57. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pin_analysis.py +765 -0
  58. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/project.py +196 -0
  59. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic.py +319 -0
  60. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_editor.py +674 -0
  61. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_search.py +158 -0
  62. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/validation.py +866 -0
  63. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/visualization.py +225 -0
  64. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/__init__.py +1 -0
  65. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/file_handlers.py +65 -0
  66. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_cli.py +103 -0
  67. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_version.py +103 -0
  68. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/parts_registry.py +197 -0
  69. package/mcp_servers/kicad-mcp-server/tests/__init__.py +1 -0
  70. package/mcp_servers/kicad-mcp-server/tests/examples/ESP32S3_TEST.md +219 -0
  71. package/mcp_servers/kicad-mcp-server/tests/fixtures/README.md +65 -0
  72. package/mcp_servers/kicad-mcp-server/tests/fixtures/__init__.py +1 -0
  73. package/mcp_servers/kicad-mcp-server/tests/fixtures/example_pcb.kicad_pcb +177 -0
  74. package/mcp_servers/kicad-mcp-server/tests/fixtures/example_schematic.kicad_sch +145 -0
  75. package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/child.kicad_sch +24 -0
  76. package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/root.kicad_sch +38 -0
  77. package/mcp_servers/kicad-mcp-server/tests/test_tools/__init__.py +1 -0
  78. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_hierarchical_labels.py +195 -0
  79. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_kicad_cli.py +116 -0
  80. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_netlist_cache_path.py +29 -0
  81. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic.py +189 -0
  82. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic_hierarchy.py +69 -0
  83. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_visualization.py +136 -0
  84. package/mcp_servers/kicad-mcp-server/uv.lock +2873 -0
  85. package/mcp_servers/openscad-mcp-server/.dockerignore +9 -0
  86. package/mcp_servers/openscad-mcp-server/.github/workflows/test.yml +40 -0
  87. package/mcp_servers/openscad-mcp-server/Dockerfile +29 -0
  88. package/mcp_servers/openscad-mcp-server/LICENSE +21 -0
  89. package/mcp_servers/openscad-mcp-server/README.md +154 -0
  90. package/mcp_servers/openscad-mcp-server/docs/audit.md +56 -0
  91. package/mcp_servers/openscad-mcp-server/docs/docker.md +66 -0
  92. package/mcp_servers/openscad-mcp-server/docs/issue-followup.md +19 -0
  93. package/mcp_servers/openscad-mcp-server/docs/jetson.md +17 -0
  94. package/mcp_servers/openscad-mcp-server/glama.json +4 -0
  95. package/mcp_servers/openscad-mcp-server/legacy/README.md +15 -0
  96. package/mcp_servers/openscad-mcp-server/legacy/README.original.md +294 -0
  97. package/mcp_servers/openscad-mcp-server/legacy/implementation_plan.md +100 -0
  98. package/mcp_servers/openscad-mcp-server/legacy/old/download_sam2_checkpoint.py +115 -0
  99. package/mcp_servers/openscad-mcp-server/legacy/old/src/ai/sam_segmentation.py +209 -0
  100. package/mcp_servers/openscad-mcp-server/legacy/old/src/models/threestudio_generator.py +231 -0
  101. package/mcp_servers/openscad-mcp-server/legacy/old/src/workflow/image_to_model_pipeline.py +260 -0
  102. package/mcp_servers/openscad-mcp-server/legacy/old/test_sam2_segmentation.py +96 -0
  103. package/mcp_servers/openscad-mcp-server/legacy/requirements.txt +57 -0
  104. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/README.md +39 -0
  105. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/ai-driven-code-generation.md +122 -0
  106. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/export-formats.md +76 -0
  107. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/ai/ai_service.py.md +51 -0
  108. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/main.py.md +63 -0
  109. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/models/code_generator.py.md +63 -0
  110. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/nlp/parameter_extractor.py.md +63 -0
  111. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/ai/natural-language-processing.md +78 -0
  112. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/nlp/parameter-extraction.md +173 -0
  113. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/export-formats.md +91 -0
  114. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/openscad-basics.md +66 -0
  115. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/primitive-testing.md +79 -0
  116. package/mcp_servers/openscad-mcp-server/legacy/src/__init__.py +0 -0
  117. package/mcp_servers/openscad-mcp-server/legacy/src/ai/ai_service.py +257 -0
  118. package/mcp_servers/openscad-mcp-server/legacy/src/ai/gemini_api.py +161 -0
  119. package/mcp_servers/openscad-mcp-server/legacy/src/ai/venice_api.py +203 -0
  120. package/mcp_servers/openscad-mcp-server/legacy/src/config.py +121 -0
  121. package/mcp_servers/openscad-mcp-server/legacy/src/main.py +1456 -0
  122. package/mcp_servers/openscad-mcp-server/legacy/src/main.py.new +404 -0
  123. package/mcp_servers/openscad-mcp-server/legacy/src/main_remote.py +401 -0
  124. package/mcp_servers/openscad-mcp-server/legacy/src/models/__init__.py +0 -0
  125. package/mcp_servers/openscad-mcp-server/legacy/src/models/code_generator.py +321 -0
  126. package/mcp_servers/openscad-mcp-server/legacy/src/models/cuda_mvs.py +209 -0
  127. package/mcp_servers/openscad-mcp-server/legacy/src/models/scad_templates/basic_shapes.scad +144 -0
  128. package/mcp_servers/openscad-mcp-server/legacy/src/nlp/__init__.py +0 -0
  129. package/mcp_servers/openscad-mcp-server/legacy/src/nlp/parameter_extractor.py +388 -0
  130. package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/__init__.py +0 -0
  131. package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/wrapper.py +418 -0
  132. package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/__init__.py +1 -0
  133. package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/printer_discovery.py +471 -0
  134. package/mcp_servers/openscad-mcp-server/legacy/src/remote/connection_manager.py +537 -0
  135. package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_client.py +435 -0
  136. package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_server.py +787 -0
  137. package/mcp_servers/openscad-mcp-server/legacy/src/remote/error_handling.py +415 -0
  138. package/mcp_servers/openscad-mcp-server/legacy/src/testing/__init__.py +0 -0
  139. package/mcp_servers/openscad-mcp-server/legacy/src/testing/primitive_tester.py +203 -0
  140. package/mcp_servers/openscad-mcp-server/legacy/src/testing/test_primitives.py +98 -0
  141. package/mcp_servers/openscad-mcp-server/legacy/src/utils/__init__.py +1 -0
  142. package/mcp_servers/openscad-mcp-server/legacy/src/utils/cad_exporter.py +241 -0
  143. package/mcp_servers/openscad-mcp-server/legacy/src/utils/format_validator.py +206 -0
  144. package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_exporter.py +140 -0
  145. package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_repair.py +91 -0
  146. package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_validator.py +123 -0
  147. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/__init__.py +0 -0
  148. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/headless_renderer.py +52 -0
  149. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/renderer.py +177 -0
  150. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/web_interface.py +639 -0
  151. package/mcp_servers/openscad-mcp-server/legacy/src/workflow/image_approval.py +148 -0
  152. package/mcp_servers/openscad-mcp-server/legacy/src/workflow/multi_view_to_model_pipeline.py +338 -0
  153. package/mcp_servers/openscad-mcp-server/legacy/test_complete_workflow.py +374 -0
  154. package/mcp_servers/openscad-mcp-server/legacy/test_cuda_mvs.py +191 -0
  155. package/mcp_servers/openscad-mcp-server/legacy/test_gemini_api.py +168 -0
  156. package/mcp_servers/openscad-mcp-server/legacy/test_image_approval.py +192 -0
  157. package/mcp_servers/openscad-mcp-server/legacy/test_image_approval_workflow.py +251 -0
  158. package/mcp_servers/openscad-mcp-server/legacy/test_image_to_model_pipeline.py +145 -0
  159. package/mcp_servers/openscad-mcp-server/legacy/test_model_selection.py +41 -0
  160. package/mcp_servers/openscad-mcp-server/legacy/test_multi_view_pipeline.py +290 -0
  161. package/mcp_servers/openscad-mcp-server/legacy/test_primitives.sh +13 -0
  162. package/mcp_servers/openscad-mcp-server/legacy/test_rabbit_direct.py +71 -0
  163. package/mcp_servers/openscad-mcp-server/legacy/test_remote_cuda_mvs.py +283 -0
  164. package/mcp_servers/openscad-mcp-server/legacy/test_venice_example.py +69 -0
  165. package/mcp_servers/openscad-mcp-server/pyproject.toml +33 -0
  166. package/mcp_servers/openscad-mcp-server/requirements.txt +2 -0
  167. package/mcp_servers/openscad-mcp-server/scad/simple_cube.scad +2 -0
  168. package/mcp_servers/openscad-mcp-server/scripts/test_docker.py +220 -0
  169. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__init__.py +3 -0
  170. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__main__.py +3 -0
  171. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/engine.py +94 -0
  172. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/geometry.py +242 -0
  173. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/server.py +245 -0
  174. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/service.py +190 -0
  175. package/mcp_servers/openscad-mcp-server/tests/conftest.py +17 -0
  176. package/mcp_servers/openscad-mcp-server/tests/test_engine.py +37 -0
  177. package/mcp_servers/openscad-mcp-server/tests/test_geometry.py +106 -0
  178. package/mcp_servers/openscad-mcp-server/tests/test_integration.py +172 -0
  179. package/mcp_servers/openscad-mcp-server/tests/test_transports.py +314 -0
  180. package/mcp_servers/openscad-mcp-server/uv.lock +1123 -0
  181. package/package.json +44 -0
  182. package/scripts/install_mcps.sh +86 -0
  183. package/scripts/openscad_wrapper.sh +62 -0
  184. package/scripts/run_kicad_mcp.sh +25 -0
  185. package/scripts/run_openscad_mcp.sh +41 -0
  186. package/scripts/test_mcp_connection.py +626 -0
  187. package/scripts/test_mcp_connection.sh +168 -0
  188. package/skills/code-first-hardware-design/SKILL.md +260 -0
  189. package/skills/pcb-constraint-definition/SKILL.md +236 -0
  190. package/skills/pcb-layout-routing-automation/SKILL.md +153 -0
  191. package/skills/pcb-validation-dfm-signoff/SKILL.md +194 -0
  192. package/skills/schematic-datasheet-analysis/SKILL.md +202 -0
@@ -0,0 +1,121 @@
1
+ # AOS Electrical & PCB Design Integration — Project Review Dossier
2
+
3
+ > **Subsystem**: `AOS-ELETRICAL&PCB`
4
+ > **Status**: **100% COMPLETE & VERIFIED** (Gate: PASS, Forensic Audit: CLEAN)
5
+ > **Target Harnesses**: Google Antigravity, Anthropic Claude Code, OpenAI Codex
6
+ > **Working Directory**: `/Users/timrennings/dev/AOS-ELETRICAL&PCB`
7
+
8
+ ---
9
+
10
+ ## 1. Project Maturity & Current State
11
+
12
+ The integration of Electrical Engineering, PCB Design, and 3D Parametric Enclosure capabilities into the **BDB Agent OS (AOS)** ecosystem is **fully implemented, tested, and ready for review**.
13
+
14
+ ```mermaid
15
+ flowchart TD
16
+ subgraph Upstream Research
17
+ M0[M0: Deep Survey & Research] -->|Evaluated 7 Tools| ADR[M1: ADR-001 Architecture Strategy]
18
+ end
19
+
20
+ subgraph Implementation
21
+ ADR --> SKILLS[M2: 5 AI-Era Hardware Skills]
22
+ ADR --> MCPS[M3: Multi-Harness MCP Setup]
23
+ end
24
+
25
+ subgraph Verification
26
+ SKILLS --> REVIEW[Reviewer 1 & 2: Content & Parity]
27
+ MCPS --> TESTS[M4: Automated stdio Live Tests]
28
+ TESTS --> CHALLENGE[Challenger 1 & 2: AST & Failure Modes]
29
+ CHALLENGE --> AUDIT[Auditor 1: Forensic Anti-Cheat]
30
+ REVIEW --> AUDIT
31
+ end
32
+
33
+ AUDIT -->|Unanimous APPROVE / CLEAN| GATE[M5: GATE STATUS - PASS]
34
+ ```
35
+
36
+ ### Milestone Delivery Matrix
37
+
38
+ | Milestone | Deliverable | Status | Independent Verdict |
39
+ |---|---|---|---|
40
+ | **M0: Survey & Research** | 3 parallel explorer reports on KiCad 9/10, OpenSCAD, and EE skill architectures | **DONE** | Validated by Orchestrator |
41
+ | **M1: Architecture ADR** | [`docs/adr/ADR-001-KICAD-OPENSCAD-MCP-STRATEGY.md`](file:///Users/timrennings/dev/AOS-ELETRICAL&PCB/docs/adr/ADR-001-KICAD-OPENSCAD-MCP-STRATEGY.md) (334 lines, 29 KB) | **DONE** | `APPROVE` (Reviewer 1) |
42
+ | **M2: AOS EE Skills** | 5 comprehensive skills (153–260 lines each, strictly non-slop, mirrored across 4 systems) | **DONE** | `APPROVE` (Reviewer 1 & Challenger 2) |
43
+ | **M3: MCP Servers** | KiCad (47 tools) & OpenSCAD (8 tools) configs + 55 lazy schemas generated | **DONE** | `APPROVE` (Reviewer 2 & Auditor 1) |
44
+ | **M4: Automated Tests** | [`scripts/test_mcp_connection.py`](file:///Users/timrennings/dev/AOS-ELETRICAL&PCB/scripts/test_mcp_connection.py) & [`.sh`](file:///Users/timrennings/dev/AOS-ELETRICAL&PCB/scripts/test_mcp_connection.sh) (55 tools tested) | **DONE** | `APPROVE` (Exit code 0, 0 failures) |
45
+ | **M5: Gate & Audit** | Forensic anti-cheat audit and stress testing (`GATE_STATUS.md`) | **DONE** | `PASS / CLEAN` (Auditor 1) |
46
+
47
+ ---
48
+
49
+ ## 2. Key Architecture Decisions (ADR-001)
50
+
51
+ 1. **Avoid In-Process SWIG Bindings (`pcbnew`)**: Legacy SWIG bindings suffer from C++ destructor/garbage collection race conditions causing segmentation faults. We adopted out-of-process stdio JSON-RPC and CLI subprocessing.
52
+ 2. **Context Window Protection**: Rather than monolithic 230+ tool servers that consume >20k tokens per turn, we deployed focused, atomic toolsets (47 KiCad tools, 8 OpenSCAD tools) with lazy-schema loading in Antigravity.
53
+ 3. **Bridge Electrical & Mechanical CAD**: OpenSCAD was integrated directly into the workflow to enable algorithmic, parametric enclosure generation based on PCB board outlines and standoff positions.
54
+ 4. **Multi-Harness Native Mirroring**: Zero-config operation across Antigravity, Claude Code, and Codex.
55
+
56
+ ---
57
+
58
+ ## 3. The 5 AI-Era Hardware Engineering Skills
59
+
60
+ All 5 skills strictly exceed the anti-slop standard (>150 lines) with actionable formulas, DRC rules, and verified code examples:
61
+
62
+ 1. **[`code-first-hardware-design`](file:///Users/timrennings/dev/AOS-ELETRICAL&PCB/skills/code-first-hardware-design/SKILL.md)** (260 lines)
63
+ - Programmatic schematic capture via Python SKiDL
64
+ - Parametric 3D mechanical enclosure generation in OpenSCAD
65
+ - Version-controlled text netlists
66
+ 2. **[`pcb-constraint-definition`](file:///Users/timrennings/dev/AOS-ELETRICAL&PCB/skills/pcb-constraint-definition/SKILL.md)** (236 lines)
67
+ - Microstrip/stripline differential impedance calculations (USB 90Ω, PCIe 85Ω, Ethernet 100Ω)
68
+ - High-voltage creepage and clearance (IEC 60950 / IEC 62368-1)
69
+ - Thermal dissipation and current-carrying capacity (IPC-2152)
70
+ 3. **[`schematic-datasheet-analysis`](file:///Users/timrennings/dev/AOS-ELETRICAL&PCB/skills/schematic-datasheet-analysis/SKILL.md)** (202 lines)
71
+ - Automated PDF datasheet parameter extraction using PyMuPDF and LLM parsing
72
+ - Pin multiplexing conflict matrix and power-domain sanity checks
73
+ - Decoupling capacitor placement hierarchies (0.1µF + 10µF bulk)
74
+ 4. **[`pcb-layout-routing-automation`](file:///Users/timrennings/dev/AOS-ELETRICAL&PCB/skills/pcb-layout-routing-automation/SKILL.md)** (153 lines)
75
+ - Layer stackup planning (4-layer: SIG-GND-PWR-SIG, 6-layer: SIG-GND-SIG-PWR-GND-SIG)
76
+ - Return-current path integrity (avoiding plane split crossings)
77
+ - Headless auto-routing via Freerouting CLI bridge
78
+ 5. **[`pcb-validation-dfm-signoff`](file:///Users/timrennings/dev/AOS-ELETRICAL&PCB/skills/pcb-validation-dfm-signoff/SKILL.md)** (194 lines)
79
+ - Unforgiving Electrical Rules Check (ERC) and Design Rules Check (DRC) sign-off
80
+ - Signal Integrity (SI) and Power Integrity (PI) impedance checks
81
+ - Design for Manufacturing (DFM) verification for standard fabrication houses (JLCPCB, PCBWay)
82
+
83
+ ---
84
+
85
+ ## 4. MCP Servers & Tool Inventory (55 Tools)
86
+
87
+ ### `kicad` (47 Tools)
88
+ - **Schematic & Connectivity**: `create_kicad_project`, `list_schematic_components`, `get_symbol_details`, `search_symbols`, `list_schematic_nets`, `get_schematic_info`, `add_component_from_library`, `add_wire`, `add_label`, `add_global_label`, `add_hierarchical_label`, `search_components_by_type`.
89
+ - **Netlist & Pin Analysis**: `generate_netlist`, `get_netlist_nets`, `get_netlist_components`, `trace_netlist_connection`, `extract_gpio_config`, `extract_i2c_devices`, `extract_spi_devices`, `extract_power_domains`, `validate_pin_configuration`, `extract_pinmux_config`, `detect_pin_conflicts`, `analyze_pin_functions`, `trace_hierarchical_connection`, `analyze_hierarchical_nets`, `generate_device_tree`.
90
+ - **PCB Layout & Parts**: `setup_pcb_layout`, `list_pcb_footprints`, `get_pcb_statistics`, `analyze_pcb_nets`, `find_tracks_by_net`, `search_parts_registry`, `get_registry_part`, `download_registry_part`.
91
+ - **Integrity Checks (SI/PI)**: `analyze_pcb_signal_integrity`, `analyze_pcb_power_integrity`.
92
+ - **ERC/DRC & Outputs**: `run_erc`, `run_drc`, `get_erc_violations`, `get_drc_violations`, `export_erc_report`, `export_drc_report`, `export_gerber`, `render_pcb`, `export_pcb_3d`, `export_schematic_svg`.
93
+
94
+ ### `openscad` (8 Tools)
95
+ - `create_3d_model`, `create_model_from_scad`, `modify_3d_model`, `export_model`, `get_model`, `get_model_source`, `get_capabilities`, `get_model_preview`.
96
+
97
+ ---
98
+
99
+ ## 5. Reviewer Verification Guide
100
+
101
+ Any reviewing agent or human evaluator can verify the subsystem by executing:
102
+
103
+ ```bash
104
+ cd /Users/timrennings/dev/AOS-ELETRICAL&PCB
105
+
106
+ # 1. Execute live multi-harness stdio test suite:
107
+ ./scripts/test_mcp_connection.sh --all
108
+
109
+ # 2. Verify skill line counts (threshold >= 50 lines):
110
+ wc -l skills/*/SKILL.md docs/adr/ADR-001-KICAD-OPENSCAD-MCP-STRATEGY.md
111
+
112
+ # 3. Verify zero drift across global skill mirrors:
113
+ for s in pcb-constraint-definition code-first-hardware-design schematic-datasheet-analysis pcb-layout-routing-automation pcb-validation-dfm-signoff; do
114
+ diff -q skills/$s/SKILL.md ~/.agents/skills/$s/SKILL.md
115
+ diff -q skills/$s/SKILL.md ~/.claude/skills/$s/SKILL.md
116
+ diff -q skills/$s/SKILL.md ~/.gemini/config/skills/$s/SKILL.md
117
+ diff -q skills/$s/SKILL.md ~/.codex/skills/$s/SKILL.md
118
+ done
119
+ ```
120
+
121
+ All commands exit with code `0` and 0 errors.
package/installer.js ADDED
@@ -0,0 +1,358 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // ==============================================================================
4
+ // BDB Hardware & PCB — cross-platform installer
5
+ // ==============================================================================
6
+ // Runs standalone (`npx @hybridlabor-api/bdb-hardware-pcb`) or as an optional
7
+ // module invoked by the main AOS installer (`node installer.js --auto`, cwd
8
+ // set to this package's own directory — same convention as bdb-synapse /
9
+ // bdb-dev-creator-extension). Every path is computed from __dirname / os.homedir()
10
+ // at runtime: nothing here may hardcode a machine-specific path.
11
+ //
12
+ // What it does, every run, non-interactively:
13
+ // 1. Sets up the kicad-mcp-server and openscad-mcp-server Python venvs
14
+ // (uv sync if uv is on PATH, else python3/python -m venv + pip install -e .)
15
+ // 2. Detects which harnesses are present on this machine (Claude, Codex,
16
+ // Antigravity/Gemini, and BDB's own ~/.agents) and, for each present one:
17
+ // - copies the 5 skill directories into its skills folder
18
+ // - merges (never clobbers) the two MCP server entries into its real
19
+ // live MCP config file
20
+ // Every step prints one honest PASS/SKIP/WARN line. A missing prerequisite
21
+ // (git, python, permission denied) warns and the rest of the install
22
+ // continues; only an unexpected exception is fatal.
23
+ // ==============================================================================
24
+
25
+ const os = require('os');
26
+ const path = require('path');
27
+ const fs = require('fs');
28
+ const { spawnSync } = require('child_process');
29
+
30
+ const AUTO = process.argv.includes('--auto');
31
+ const INSTALL_DIR = __dirname;
32
+ const HOME = os.homedir();
33
+ const IS_WIN = process.platform === 'win32';
34
+
35
+ const log = {
36
+ step: (msg) => console.log(` -> ${msg}`),
37
+ ok: (msg) => console.log(` [OK] ${msg}`),
38
+ skip: (msg) => console.log(` [SKIP] ${msg}`),
39
+ warn: (msg) => console.warn(` [WARN] ${msg}`),
40
+ err: (msg) => console.error(` [ERROR] ${msg}`),
41
+ section: (msg) => console.log(`\n${msg}`),
42
+ };
43
+
44
+ function commandExists(cmd, versionArgs = ['--version']) {
45
+ try {
46
+ const res = spawnSync(cmd, versionArgs, { stdio: 'ignore' });
47
+ return res.error === undefined || res.error === null;
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ function copyDirRecursive(src, dest) {
54
+ fs.mkdirSync(dest, { recursive: true });
55
+ fs.cpSync(src, dest, { recursive: true, force: true });
56
+ }
57
+
58
+ // --- 1. Python venv setup -----------------------------------------------
59
+
60
+ const MCP_SERVERS = [
61
+ { name: 'kicad', dir: path.join(INSTALL_DIR, 'mcp_servers', 'kicad-mcp-server') },
62
+ { name: 'openscad', dir: path.join(INSTALL_DIR, 'mcp_servers', 'openscad-mcp-server') },
63
+ ];
64
+
65
+ function findPython() {
66
+ // ponytail: first python that answers --version; not a full version-gate,
67
+ // upgrade to a strict >=3.10 check if a server's setup starts failing on it.
68
+ for (const cmd of IS_WIN ? ['python', 'py', 'python3'] : ['python3', 'python']) {
69
+ if (commandExists(cmd)) return cmd;
70
+ }
71
+ return null;
72
+ }
73
+
74
+ function setupServerEnv(server, uvBin, pythonBin) {
75
+ const venvDir = path.join(server.dir, '.venv');
76
+ if (uvBin) {
77
+ const res = spawnSync(uvBin, ['sync'], { cwd: server.dir, stdio: 'inherit' });
78
+ if (res.status === 0) {
79
+ log.ok(`${server.name}-mcp-server: dependencies synced via uv`);
80
+ return true;
81
+ }
82
+ log.warn(`${server.name}-mcp-server: 'uv sync' failed (exit ${res.status}); falling back to venv+pip`);
83
+ }
84
+ if (!pythonBin) {
85
+ log.warn(`${server.name}-mcp-server: no python3/python found on PATH — skipping venv setup. Install Python 3.10+ and re-run.`);
86
+ return false;
87
+ }
88
+ if (!fs.existsSync(venvDir)) {
89
+ const mk = spawnSync(pythonBin, ['-m', 'venv', '.venv'], { cwd: server.dir, stdio: 'inherit' });
90
+ if (mk.status !== 0) {
91
+ log.warn(`${server.name}-mcp-server: 'python -m venv' failed (exit ${mk.status})`);
92
+ return false;
93
+ }
94
+ }
95
+ const pipBin = path.join(venvDir, IS_WIN ? 'Scripts' : 'bin', IS_WIN ? 'pip.exe' : 'pip');
96
+ const install = spawnSync(pipBin, ['install', '-e', '.'], { cwd: server.dir, stdio: 'inherit' });
97
+ if (install.status !== 0) {
98
+ log.warn(`${server.name}-mcp-server: 'pip install -e .' failed (exit ${install.status})`);
99
+ return false;
100
+ }
101
+ log.ok(`${server.name}-mcp-server: dependencies installed via venv+pip`);
102
+ return true;
103
+ }
104
+
105
+ function setupPythonEnvironments() {
106
+ log.section('Python MCP server environments');
107
+ const uvBin = commandExists('uv') ? 'uv' : null;
108
+ const pythonBin = findPython();
109
+ log.step(uvBin ? 'uv detected — using uv sync' : pythonBin ? `uv not found — using ${pythonBin} -m venv` : 'neither uv nor python3/python found');
110
+ for (const server of MCP_SERVERS) {
111
+ try {
112
+ setupServerEnv(server, uvBin, pythonBin);
113
+ } catch (e) {
114
+ log.warn(`${server.name}-mcp-server: setup threw ${e.message}`);
115
+ }
116
+ }
117
+ }
118
+
119
+ // --- 2. Windows run-script wrappers (only if no shell can run the .sh) ----
120
+
121
+ function ensureWindowsRunner(server, scriptBase, moduleInvocation) {
122
+ // Prefer bash (Git for Windows ships one) so the existing, already-tested
123
+ // .sh runner stays the single source of truth. Only generate a native
124
+ // .cmd wrapper — and only for this one server — when bash is absent.
125
+ if (commandExists('bash', ['--version'])) return { command: 'bash', prefixArgs: [path.join(INSTALL_DIR, 'scripts', `${scriptBase}.sh`)] };
126
+
127
+ const cmdPath = path.join(INSTALL_DIR, 'scripts', `${scriptBase}.cmd`);
128
+ const venvPy = path.join(server.dir, '.venv', 'Scripts', 'python.exe');
129
+ const body = [
130
+ '@echo off',
131
+ `set PYTHONPATH=${path.join(server.dir, 'src')};%PYTHONPATH%`,
132
+ `if exist "${venvPy}" (`,
133
+ ` "${venvPy}" ${moduleInvocation} %*`,
134
+ ') else (',
135
+ ` python -m ${moduleInvocation.replace(/^-m /, '')} %*`,
136
+ ')',
137
+ '',
138
+ ].join('\r\n');
139
+ try {
140
+ fs.writeFileSync(cmdPath, body, 'utf-8');
141
+ log.ok(`generated Windows wrapper: ${cmdPath} (bash not found on PATH)`);
142
+ return { command: cmdPath, prefixArgs: [] };
143
+ } catch (e) {
144
+ log.warn(`could not write Windows wrapper ${cmdPath}: ${e.message}`);
145
+ return null;
146
+ }
147
+ }
148
+
149
+ function resolveServerCommand(name) {
150
+ if (name === 'kicad') {
151
+ const server = MCP_SERVERS[0];
152
+ if (!IS_WIN) return { command: path.join(INSTALL_DIR, 'scripts', 'run_kicad_mcp.sh'), args: [] };
153
+ const r = ensureWindowsRunner(server, 'run_kicad_mcp', '-m kicad_mcp_server');
154
+ return r ? { command: r.command, args: r.prefixArgs } : { command: path.join(INSTALL_DIR, 'scripts', 'run_kicad_mcp.sh'), args: [] };
155
+ }
156
+ const server = MCP_SERVERS[1];
157
+ if (!IS_WIN) return { command: path.join(INSTALL_DIR, 'scripts', 'run_openscad_mcp.sh'), args: ['--transport', 'stdio'] };
158
+ const r = ensureWindowsRunner(server, 'run_openscad_mcp', '-m openscad_mcp.server --transport stdio');
159
+ return r ? { command: r.command, args: r.prefixArgs } : { command: path.join(INSTALL_DIR, 'scripts', 'run_openscad_mcp.sh'), args: ['--transport', 'stdio'] };
160
+ }
161
+
162
+ function mcpServerEntries() {
163
+ const kicad = resolveServerCommand('kicad');
164
+ const openscad = resolveServerCommand('openscad');
165
+ return {
166
+ kicad: {
167
+ command: kicad.command,
168
+ args: kicad.args,
169
+ env: { KICAD_CLI_PATH: process.env.KICAD_CLI_PATH || '/Applications/KiCad/KiCad.app/Contents/MacOS/kicad-cli' },
170
+ },
171
+ openscad: {
172
+ command: openscad.command,
173
+ args: openscad.args,
174
+ env: { OPENSCAD_EXECUTABLE: path.join(INSTALL_DIR, 'scripts', IS_WIN ? 'openscad_wrapper.cmd' : 'openscad_wrapper.sh') },
175
+ },
176
+ };
177
+ }
178
+
179
+ // --- 3. Harness detection, skill copy, MCP config merge -------------------
180
+
181
+ const SKILLS = [
182
+ 'code-first-hardware-design',
183
+ 'pcb-constraint-definition',
184
+ 'pcb-layout-routing-automation',
185
+ 'pcb-validation-dfm-signoff',
186
+ 'schematic-datasheet-analysis',
187
+ ];
188
+
189
+ function mergeJsonMcpConfig(configPath, servers) {
190
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
191
+ let data = {};
192
+ if (fs.existsSync(configPath)) {
193
+ try {
194
+ data = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
195
+ } catch (e) {
196
+ const backup = `${configPath}.bak-${Date.now()}`;
197
+ fs.copyFileSync(configPath, backup);
198
+ log.warn(`${configPath} had invalid JSON — backed up to ${backup} and starting fresh`);
199
+ data = {};
200
+ }
201
+ }
202
+ if (typeof data !== 'object' || data === null) data = {};
203
+ if (typeof data.mcpServers !== 'object' || data.mcpServers === null) data.mcpServers = {};
204
+ Object.assign(data.mcpServers, servers);
205
+ fs.writeFileSync(configPath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
206
+ return true;
207
+ }
208
+
209
+ // Removes a `[section]` header line and every line after it up to (not
210
+ // including) the next line that starts a new header, or EOF. Line-based, not
211
+ // a real TOML parser -- deliberately, so this never depends on an extra
212
+ // dependency to merge two known-shape blocks we generated ourselves.
213
+ // ponytail: good enough for our own sections; a third party hand-editing
214
+ // inside our block could confuse it, upgrade to a real TOML lib (e.g.
215
+ // @iarna/toml) if that ever happens in practice.
216
+ function removeTomlSection(text, sectionName) {
217
+ const header = `[${sectionName}]`;
218
+ const lines = text.split('\n');
219
+ const out = [];
220
+ let skipping = false;
221
+ for (const line of lines) {
222
+ if (line.trim() === header) {
223
+ skipping = true;
224
+ continue;
225
+ }
226
+ if (skipping && /^\[/.test(line.trim())) {
227
+ skipping = false;
228
+ }
229
+ if (!skipping) out.push(line);
230
+ }
231
+ return out.join('\n');
232
+ }
233
+
234
+ function mergeTomlMcpConfig(configPath, servers) {
235
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
236
+ let existing = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf-8') : '';
237
+ let text = existing;
238
+ for (const name of Object.keys(servers)) {
239
+ text = removeTomlSection(text, `mcp_servers.${name}.env`);
240
+ text = removeTomlSection(text, `mcp_servers.${name}`);
241
+ }
242
+ text = text.replace(/\n{3,}/g, '\n\n').replace(/\s+$/, '\n');
243
+
244
+ const blocks = [];
245
+ for (const [name, cfg] of Object.entries(servers)) {
246
+ const argsToml = JSON.stringify(cfg.args || []);
247
+ const envLines = Object.entries(cfg.env || {}).map(([k, v]) => `${k} = ${JSON.stringify(v)}`).join('\n');
248
+ blocks.push(
249
+ `\n[mcp_servers.${name}]\ncommand = ${JSON.stringify(cfg.command)}\nargs = ${argsToml}\nenabled = true\n` +
250
+ (envLines ? `\n[mcp_servers.${name}.env]\n${envLines}\n` : '')
251
+ );
252
+ }
253
+ const newText = text + blocks.join('');
254
+ if (newText === existing) {
255
+ log.skip(`${configPath}: kicad/openscad already registered with current paths — no change needed`);
256
+ return false;
257
+ }
258
+ fs.writeFileSync(configPath, newText, 'utf-8');
259
+ return true;
260
+ }
261
+
262
+ function harnesses() {
263
+ const geminiDir = path.join(HOME, '.gemini');
264
+ return [
265
+ {
266
+ name: 'Claude Code',
267
+ homeMarker: path.join(HOME, '.claude'),
268
+ skillsDir: path.join(HOME, '.claude', 'skills'),
269
+ mcpConfigPath: path.join(HOME, '.claude.json'),
270
+ mcpFormat: 'json',
271
+ },
272
+ {
273
+ name: 'OpenAI Codex',
274
+ homeMarker: path.join(HOME, '.codex'),
275
+ skillsDir: path.join(HOME, '.codex', 'skills'),
276
+ mcpConfigPath: path.join(HOME, '.codex', 'config.toml'),
277
+ mcpFormat: 'toml',
278
+ },
279
+ {
280
+ name: 'Antigravity (Gemini)',
281
+ homeMarker: geminiDir,
282
+ skillsDir: path.join(geminiDir, 'config', 'skills'),
283
+ mcpConfigPath: path.join(geminiDir, 'config', 'mcp_config.json'),
284
+ mcpFormat: 'json',
285
+ },
286
+ {
287
+ name: 'BDB universal (.agents)',
288
+ homeMarker: path.join(HOME, '.agents'),
289
+ skillsDir: path.join(HOME, '.agents', 'skills'),
290
+ mcpConfigPath: null, // no separate universal MCP store; skills only
291
+ mcpFormat: null,
292
+ },
293
+ ];
294
+ }
295
+
296
+ function installIntoHarness(h, servers) {
297
+ if (!fs.existsSync(h.homeMarker)) {
298
+ log.skip(`${h.name}: ${h.homeMarker} not found — harness not detected, skipping`);
299
+ return;
300
+ }
301
+
302
+ let copied = 0;
303
+ for (const skill of SKILLS) {
304
+ const src = path.join(INSTALL_DIR, 'skills', skill);
305
+ const dest = path.join(h.skillsDir, skill);
306
+ try {
307
+ copyDirRecursive(src, dest);
308
+ copied++;
309
+ } catch (e) {
310
+ log.warn(`${h.name}: failed to copy skill '${skill}': ${e.message}`);
311
+ }
312
+ }
313
+ if (copied) log.ok(`${h.name}: ${copied}/${SKILLS.length} skills copied to ${h.skillsDir}`);
314
+
315
+ if (!h.mcpConfigPath) return;
316
+ try {
317
+ const changed = h.mcpFormat === 'toml'
318
+ ? mergeTomlMcpConfig(h.mcpConfigPath, servers)
319
+ : mergeJsonMcpConfig(h.mcpConfigPath, servers);
320
+ if (changed) log.ok(`${h.name}: kicad + openscad MCP servers registered in ${h.mcpConfigPath}`);
321
+ } catch (e) {
322
+ log.warn(`${h.name}: failed to update MCP config ${h.mcpConfigPath}: ${e.message}`);
323
+ }
324
+ }
325
+
326
+ // --- main ------------------------------------------------------------------
327
+
328
+ function main() {
329
+ console.log('================================================================================');
330
+ console.log(`BDB Hardware & PCB installer${AUTO ? ' (--auto)' : ''} — ${INSTALL_DIR}`);
331
+ console.log('================================================================================');
332
+
333
+ setupPythonEnvironments();
334
+
335
+ log.section('Harness detection & skill/MCP registration');
336
+ const servers = mcpServerEntries();
337
+ let anyHarness = false;
338
+ for (const h of harnesses()) {
339
+ if (fs.existsSync(h.homeMarker)) anyHarness = true;
340
+ installIntoHarness(h, servers);
341
+ }
342
+ if (!anyHarness) {
343
+ log.warn('No known harness (~/.claude, ~/.codex, ~/.gemini, ~/.agents) was found on this machine.');
344
+ log.warn('Python MCP servers are still set up above; install a harness and re-run to register skills/MCP.');
345
+ }
346
+
347
+ console.log('\n================================================================================');
348
+ console.log('BDB Hardware & PCB install finished.');
349
+ console.log('================================================================================');
350
+ process.exit(0);
351
+ }
352
+
353
+ try {
354
+ main();
355
+ } catch (e) {
356
+ log.err(`Fatal: ${e.stack || e.message}`);
357
+ process.exit(1);
358
+ }
@@ -0,0 +1,22 @@
1
+ # KiCad MCP Server Configuration
2
+
3
+ # Comma-separated paths to KiCad project directories
4
+ # KICAD_PROJECT_PATHS=/path/to/projects1,/path/to/projects2
5
+
6
+ # Default detail level for schematic summaries
7
+ # Options: brief, standard, detailed
8
+ DEFAULT_SUMMARY_DETAIL_LEVEL=standard
9
+
10
+ # Include nets in summaries by default
11
+ INCLUDE_NETS_IN_SUMMARY=true
12
+
13
+ # Include power analysis in summaries by default
14
+ INCLUDE_POWER_IN_SUMMARY=true
15
+
16
+ # Default test framework for code generation
17
+ # Options: pytest, unittest, arduino, zephyr, st_hal, esp_idf
18
+ DEFAULT_TEST_FRAMEWORK=pytest
19
+
20
+ # Default test type
21
+ # Options: connectivity, functional, docs, production
22
+ DEFAULT_TEST_TYPE=connectivity
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ lint:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.12"
16
+ - run: python -m pip install ruff
17
+ - run: ruff check src/ tests/
18
+
19
+ test:
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ os: [ubuntu-latest, windows-latest]
24
+ python-version: ["3.10", "3.13"]
25
+ runs-on: ${{ matrix.os }}
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - uses: actions/setup-python@v5
29
+ with:
30
+ python-version: ${{ matrix.python-version }}
31
+ - name: Install package with test extras
32
+ run: python -m pip install -e ".[test]"
33
+ - name: Run tests
34
+ run: python -m pytest tests/ -q
35
+ # Parser/hierarchy tests need no KiCad install; the kicad-cli
36
+ # integration test self-skips when KiCad is absent.