ecadlib 0.3.0__tar.gz → 0.3.1__tar.gz

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 (448) hide show
  1. {ecadlib-0.3.0 → ecadlib-0.3.1}/CHANGELOG.md +35 -1
  2. {ecadlib-0.3.0 → ecadlib-0.3.1}/PKG-INFO +66 -23
  3. {ecadlib-0.3.0 → ecadlib-0.3.1}/README.md +65 -22
  4. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/__init__.py +1 -1
  5. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/altium365.py +5 -4
  6. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/altium_dblib_sync.py +152 -16
  7. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/altium_export.py +45 -5
  8. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/altium_forms.py +5 -2
  9. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/altium_lib.py +83 -9
  10. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/altium_menu.py +151 -37
  11. ecadlib-0.3.1/ecadlib/altium_native.py +1293 -0
  12. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/altium_pkg.py +119 -20
  13. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/altium_project.py +18 -10
  14. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/app.py +32 -6
  15. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/assets/kicad_action_plugin.py +14 -1
  16. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/assets/kicad_reread.py +20 -2
  17. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/bom_triage.py +47 -18
  18. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/cli.py +12 -4
  19. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/clihelp.py +4 -0
  20. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/datasheet.py +53 -9
  21. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/doctor.py +22 -5
  22. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eagle_export.py +20 -9
  23. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/easy_setup.py +3 -1
  24. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/easyeda_convert.py +3 -2
  25. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/easyeda_pro_export.py +1 -4
  26. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/baselines/kicad-v1.json +6 -5
  27. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/golden/demo.txt +6 -0
  28. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/golden/specs.txt +96 -0
  29. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/golden/testpack.txt +6 -0
  30. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/firstrun.py +31 -5
  31. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/footprint_adv.py +4 -1
  32. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/footprint_rules.py +8 -0
  33. ecadlib-0.3.1/ecadlib/fp_edit.py +194 -0
  34. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/house_style.py +22 -9
  35. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/httpcache.py +3 -1
  36. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/hub.py +8 -2
  37. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/hub_copy.py +24 -2
  38. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/kicad_dblib.py +19 -4
  39. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/kicad_export.py +112 -22
  40. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/kicad_import.py +4 -3
  41. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/kicad_menu.py +2 -1
  42. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/launcher.py +3 -1
  43. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/librarian.py +38 -6
  44. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/lifecycle.py +7 -40
  45. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/llm/openai_compat.py +3 -3
  46. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/llm/rails.py +18 -4
  47. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/llm/run.py +16 -5
  48. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/mcp_server.py +3 -1
  49. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/messages.py +23 -0
  50. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/parity.py +17 -1
  51. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/plugins.py +5 -0
  52. ecadlib-0.3.1/ecadlib/providers.py +374 -0
  53. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/release_check.py +16 -4
  54. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/rules.py +4 -2
  55. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/rules_infer.py +114 -28
  56. ecadlib-0.3.1/ecadlib/sources_check.py +162 -0
  57. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/sourcing.py +58 -14
  58. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/stdparts.py +21 -2
  59. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/step_gen.py +167 -5
  60. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/symbol_layout.py +5 -0
  61. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/team.py +2 -0
  62. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/term.py +11 -1
  63. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/ui/server.py +22 -4
  64. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/ui/static/index.html +5 -5
  65. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/upkeep.py +8 -6
  66. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/visual_check.py +3 -1
  67. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib.egg-info/PKG-INFO +66 -23
  68. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib.egg-info/SOURCES.txt +29 -0
  69. ecadlib-0.3.1/tests/fixtures/easyeda_live/C5423.json +379 -0
  70. ecadlib-0.3.1/tests/fixtures/easyeda_live/C7593.json +262 -0
  71. ecadlib-0.3.1/tests/fixtures_lite.py +47 -0
  72. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/rules_fixtures.py +20 -0
  73. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_agent.py +8 -1
  74. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_agent_soak.py +1 -1
  75. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_altium_ad26_fixes.py +4 -3
  76. ecadlib-0.3.1/tests/test_altium_ad26_overnight.py +130 -0
  77. ecadlib-0.3.1/tests/test_altium_edit_guard.py +141 -0
  78. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_altium_menu.py +77 -12
  79. ecadlib-0.3.1/tests/test_altium_native.py +194 -0
  80. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_altium_polish.py +9 -6
  81. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_cli_smoke.py +4 -1
  82. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_dblib_sync.py +45 -0
  83. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_dedupe.py +1 -1
  84. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_e2e.py +3 -3
  85. ecadlib-0.3.1/tests/test_easyeda_live.py +60 -0
  86. ecadlib-0.3.1/tests/test_fab_designator.py +82 -0
  87. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_footprint_rulebook.py +9 -0
  88. ecadlib-0.3.1/tests/test_fp_edit.py +93 -0
  89. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_house_style.py +43 -0
  90. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_kicad_import.py +1 -1
  91. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_messages.py +5 -0
  92. ecadlib-0.3.1/tests/test_official_sources.py +173 -0
  93. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_qol.py +6 -1
  94. ecadlib-0.3.1/tests/test_review_1017_altium_core.py +84 -0
  95. ecadlib-0.3.1/tests/test_review_1017_cli.py +130 -0
  96. ecadlib-0.3.1/tests/test_review_1017_hub.py +72 -0
  97. ecadlib-0.3.1/tests/test_review_1017_official_first.py +39 -0
  98. ecadlib-0.3.1/tests/test_review_1017_sources.py +89 -0
  99. ecadlib-0.3.1/tests/test_review_1017_style.py +70 -0
  100. ecadlib-0.3.1/tests/test_review_1018_a365_librarian.py +80 -0
  101. ecadlib-0.3.1/tests/test_review_1018_ad26_recheck.py +185 -0
  102. ecadlib-0.3.1/tests/test_review_1018_altium_edges.py +217 -0
  103. ecadlib-0.3.1/tests/test_review_1018_bom_intake.py +104 -0
  104. ecadlib-0.3.1/tests/test_review_1018_cloud.py +30 -0
  105. ecadlib-0.3.1/tests/test_review_1018_footprints.py +84 -0
  106. ecadlib-0.3.1/tests/test_review_1018_kicad.py +196 -0
  107. ecadlib-0.3.1/tests/test_review_1018_models.py +153 -0
  108. ecadlib-0.3.1/tests/test_review_1018_team.py +57 -0
  109. ecadlib-0.3.1/tests/test_review_1018_ui_mcp.py +79 -0
  110. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_agent.py +1 -1
  111. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_altiumint.py +2 -1
  112. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_sourcing.py +35 -0
  113. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_step_gen.py +145 -1
  114. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_team.py +1 -0
  115. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_tool_packages.py +6 -2
  116. ecadlib-0.3.0/ecadlib/providers.py +0 -226
  117. {ecadlib-0.3.0 → ecadlib-0.3.1}/LICENSE +0 -0
  118. {ecadlib-0.3.0 → ecadlib-0.3.1}/MANIFEST.in +0 -0
  119. {ecadlib-0.3.0 → ecadlib-0.3.1}/NOTICE +0 -0
  120. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/__main__.py +0 -0
  121. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/a365_sync.py +0 -0
  122. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/agent.py +0 -0
  123. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/agent_queue.py +0 -0
  124. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/agent_watch.py +0 -0
  125. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/approvals.py +0 -0
  126. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/assets/demo_bom.csv +0 -0
  127. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/assets/pcblib_template.json.gz +0 -0
  128. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/bom_inputs.py +0 -0
  129. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/cad_model.py +0 -0
  130. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/cfb.py +0 -0
  131. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/cliutil.py +0 -0
  132. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/completion.py +0 -0
  133. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/costs.py +0 -0
  134. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/crosscheck.py +0 -0
  135. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/dedupe.py +0 -0
  136. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/detect.py +0 -0
  137. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/ds_route.py +0 -0
  138. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/__init__.py +0 -0
  139. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/build_set.py +0 -0
  140. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/cli.py +0 -0
  141. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/gate.py +0 -0
  142. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/golden.py +0 -0
  143. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/replay/kicad-v1.json.gz +0 -0
  144. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/runner.py +0 -0
  145. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/score.py +0 -0
  146. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/eval/sets/kicad-v1.json +0 -0
  147. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/footprint_bench.py +0 -0
  148. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/footprint_check.py +0 -0
  149. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/footprint_gen.py +0 -0
  150. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/forge.py +0 -0
  151. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/fp_share.py +0 -0
  152. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/fp_tidy.py +0 -0
  153. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/fsutil.py +0 -0
  154. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/hub_tools.py +0 -0
  155. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/kicad_index.py +0 -0
  156. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/kicad_pcm.py +0 -0
  157. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/llm/__init__.py +0 -0
  158. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/llm/cli.py +0 -0
  159. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/llm/profiles.py +0 -0
  160. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/llm/sampling.py +0 -0
  161. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/migrate.py +0 -0
  162. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/notify.py +0 -0
  163. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_schema.json +0 -0
  164. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/CP_EIA-3216-18_Kemet-A.json +0 -0
  165. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/C_0402_1005Metric.json +0 -0
  166. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/C_0805_2012Metric.json +0 -0
  167. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/DFN-8-1EP_3x3mm_P0.5mm_EP1.66x2.38mm.json +0 -0
  168. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/D_SMA.json +0 -0
  169. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/D_SMB.json +0 -0
  170. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/D_SOD-123.json +0 -0
  171. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/D_SOD-323.json +0 -0
  172. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/LED_0603_1608Metric.json +0 -0
  173. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/LED_0805_2012Metric.json +0 -0
  174. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/LQFP-48_7x7mm_P0.5mm.json +0 -0
  175. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/L_0805_2012Metric.json +0 -0
  176. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/MSOP-8_3x3mm_P0.65mm.json +0 -0
  177. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/QFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm.json +0 -0
  178. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/QFN-32-1EP_5x5mm_P0.5mm_EP3.45x3.45mm.json +0 -0
  179. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/R_0402_1005Metric.json +0 -0
  180. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/R_0603_1608Metric.json +0 -0
  181. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/R_0805_2012Metric.json +0 -0
  182. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/R_1206_3216Metric.json +0 -0
  183. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/SOIC-16_3.9x9.9mm_P1.27mm.json +0 -0
  184. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/SOIC-8_3.9x4.9mm_P1.27mm.json +0 -0
  185. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/SOT-223-3_TabPin2.json +0 -0
  186. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/SOT-23-5.json +0 -0
  187. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/SOT-23-6.json +0 -0
  188. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/SOT-23.json +0 -0
  189. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/TSSOP-16_4.4x5mm_P0.65mm.json +0 -0
  190. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/VQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm.json +0 -0
  191. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-100_11.0x11.0mm_Layout10x10_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD.json +0 -0
  192. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-121_9.0x9.0mm_Layout11x11_P0.8mm_Ball0.4mm_Pad0.35mm_NSMD.json +0 -0
  193. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-144_7.0x7.0mm_Layout13x13_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD.json +0 -0
  194. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-169_11.0x11.0mm_Layout13x13_P0.8mm_Ball0.5mm_Pad0.4mm_NSMD.json +0 -0
  195. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-256_11.0x11.0mm_Layout20x20_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD.json +0 -0
  196. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-256_17.0x17.0mm_Layout16x16_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD.json +0 -0
  197. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-324_15.0x15.0mm_Layout18x18_P0.8mm_Ball0.5mm_Pad0.4mm_NSMD.json +0 -0
  198. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-36_3.396x3.466mm_Layout6x6_P0.4mm_Ball0.25mm_Pad0.2mm_NSMD.json +0 -0
  199. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-672_27.0x27.0mm_Layout26x26_P1.0mm_Ball0.6mm_Pad0.5mm_NSMD.json +0 -0
  200. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-68_5.0x5.0mm_Layout9x9_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD.json +0 -0
  201. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/BGA-90_8.0x13.0mm_Layout2x3x15_P0.8mm.json +0 -0
  202. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/DFN-8-1EP_3x3mm_P0.5mm_EP1.65x2.38mm_ThermalVias.json +0 -0
  203. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/Example_Custom_LPad_Slot.json +0 -0
  204. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/JST_GH_SM04B-GHS-TB_1x04-1MP_P1.25mm_Horizontal.json +0 -0
  205. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/JST_PH_B4B-PH-K_1x04_P2.00mm_Vertical.json +0 -0
  206. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/JST_PH_S4B-PH-SM4-TB_1x04-1MP_P2.00mm_Horizontal.json +0 -0
  207. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal.json +0 -0
  208. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/JST_XH_B2B-XH-A_1x02_P2.50mm_Vertical.json +0 -0
  209. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/JST_XH_B4B-XH-A_1x04_P2.50mm_Vertical.json +0 -0
  210. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/JST_XH_B6B-XH-A_1x06_P2.50mm_Vertical.json +0 -0
  211. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/LFBGA-144_10x10mm_Layout12x12_P0.8mm.json +0 -0
  212. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/Molex_KK-254_AE-6410-04A_1x04_P2.54mm_Vertical.json +0 -0
  213. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/Molex_PicoBlade_53398-0471_1x04-1MP_P1.25mm_Vertical.json +0 -0
  214. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_1x05_P1.00mm_Vertical_SMD_Pin1Left.json +0 -0
  215. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_1x05_P1.27mm_Vertical_SMD_Pin1Left.json +0 -0
  216. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_1x05_P2.00mm_Vertical.json +0 -0
  217. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_1x05_P2.00mm_Vertical_SMD_Pin1Left.json +0 -0
  218. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_1x05_P2.54mm_Vertical.json +0 -0
  219. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_1x05_P2.54mm_Vertical_SMD_Pin1Left.json +0 -0
  220. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_1x05_P2.54mm_Vertical_SMD_Pin1Right.json +0 -0
  221. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_1x10_P1.00mm_Vertical.json +0 -0
  222. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_2x05_P1.00mm_Vertical_SMD.json +0 -0
  223. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_2x05_P1.27mm_Vertical.json +0 -0
  224. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_2x05_P1.27mm_Vertical_SMD.json +0 -0
  225. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_2x05_P2.00mm_Vertical_SMD.json +0 -0
  226. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_2x05_P2.54mm_Vertical.json +0 -0
  227. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_2x05_P2.54mm_Vertical_SMD.json +0 -0
  228. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/PinHeader_2x20_P2.54mm_Vertical.json +0 -0
  229. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/QFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm_ThermalVias.json +0 -0
  230. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/UFBGA-132_7x7mm_Layout12x12_P0.5mm.json +0 -0
  231. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/advanced/USB_C_Receptacle_HRO_TYPE-C-31-M-12.json +0 -0
  232. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/package_specs/standard.json.gz +0 -0
  233. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/parts_export.py +0 -0
  234. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/partview.py +0 -0
  235. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/rebuild.py +0 -0
  236. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/reports.py +0 -0
  237. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/resolve.py +0 -0
  238. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/review_cli.py +0 -0
  239. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/sexpr.py +0 -0
  240. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/sheetsafe.py +0 -0
  241. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/sim_models.py +0 -0
  242. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/team_setup.py +0 -0
  243. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/term_views.py +0 -0
  244. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/testpack.py +0 -0
  245. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/ui/__init__.py +0 -0
  246. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/ui/__main__.py +0 -0
  247. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/ui/static/hairline.js +0 -0
  248. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/ui/static/setup.html +0 -0
  249. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/upstream.py +0 -0
  250. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/userconfig.py +0 -0
  251. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib/ws_status.py +0 -0
  252. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib.egg-info/dependency_links.txt +0 -0
  253. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib.egg-info/entry_points.txt +0 -0
  254. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib.egg-info/requires.txt +0 -0
  255. {ecadlib-0.3.0 → ecadlib-0.3.1}/ecadlib.egg-info/top_level.txt +0 -0
  256. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/ecadlib_example_plugin/ecadlib_example_plugin/__init__.py +0 -0
  257. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/ecadlib_example_plugin/pyproject.toml +0 -0
  258. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/kicad_import_parts.json +0 -0
  259. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/kicad_lib/ecadlib_demo.kicad_sym +0 -0
  260. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/lifecycle-watch.yml +0 -0
  261. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/sample_bom.csv +0 -0
  262. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/symbols/CH340C.json +0 -0
  263. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/symbols/Capacitor.json +0 -0
  264. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/symbols/Resistor.json +0 -0
  265. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/symbols/TPS62133RGTR.json +0 -0
  266. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/upkeep.yml +0 -0
  267. {ecadlib-0.3.0 → ecadlib-0.3.1}/examples/upstream-watch.yml +0 -0
  268. {ecadlib-0.3.0 → ecadlib-0.3.1}/pyproject.toml +0 -0
  269. {ecadlib-0.3.0 → ecadlib-0.3.1}/setup.cfg +0 -0
  270. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/__init__.py +0 -0
  271. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/e2e_support.py +0 -0
  272. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/bom_inputs/house_columns.csv +0 -0
  273. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/bom_inputs/house_columns.toml +0 -0
  274. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/bom_inputs/semicolon_utf8bom.csv +0 -0
  275. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/bom_inputs/tab_cp1252.txt +0 -0
  276. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/bom_inputs/workbook_title_rows.xlsx +0 -0
  277. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/anthropic/datasheet_DSPART16.json +0 -0
  278. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/anthropic/proof_default.json +0 -0
  279. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-footprints/Capacitor_SMD.pretty/C_0402_1005Metric.kicad_mod +0 -0
  280. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-footprints/Capacitor_SMD.pretty/C_0603_1608Metric.kicad_mod +0 -0
  281. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-footprints/Package_QFP.pretty/LQFP-48_7x7mm_P0.5mm.kicad_mod +0 -0
  282. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-footprints/Package_SO.pretty/SOIC-8_3.9x4.9mm_P1.27mm.kicad_mod +0 -0
  283. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-footprints/Package_TO_SOT_SMD.pretty/SOT-23-5.kicad_mod +0 -0
  284. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-footprints/Resistor_SMD.pretty/R_0402_1005Metric.kicad_mod +0 -0
  285. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-footprints/Resistor_SMD.pretty/R_0603_1608Metric.kicad_mod +0 -0
  286. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-symbols/Amplifier_Operational.kicad_symdir/LM2904.kicad_sym +0 -0
  287. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-symbols/Amplifier_Operational.kicad_symdir/LM358.kicad_sym +0 -0
  288. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-symbols/Device.kicad_symdir/C.kicad_sym +0 -0
  289. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-symbols/Device.kicad_symdir/R.kicad_sym +0 -0
  290. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-symbols/MCU_ST_STM32F1.kicad_symdir/STM32F103C8Tx.kicad_sym +0 -0
  291. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/e2e/kicad/kicad-symbols/MCU_ST_STM32F1.kicad_symdir/STM32F103C_8-B_Tx.kicad_sym +0 -0
  292. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/easyeda/C2002.json +0 -0
  293. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/easyeda/C7593.json +0 -0
  294. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/Regulator_Linear_AMS1117.kicad_sym +0 -0
  295. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/USB_C_Receptacle_HRO_TYPE-C-31-M-12.kicad_mod +0 -0
  296. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/ecadlib_demorgan.kicad_sym +0 -0
  297. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/AMASS_XT60-M_1x02_P7.20mm_Vertical.kicad_mod +0 -0
  298. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/Banana_Cliff_FCR7350R_S16N-PC_Horizontal.kicad_mod +0 -0
  299. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/D_SMC-RM10_Universal_Handsoldering.kicad_mod +0 -0
  300. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/Infineon_PG-HSOF-8-2_ThermalVias2.kicad_mod +0 -0
  301. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/LFCSP-40-1EP_6x6mm_P0.5mm_EP4.6x4.6mm_ThermalVias.kicad_mod +0 -0
  302. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/MMCX_Molex_73415-0961_Horizontal_1.6mm-PCB.kicad_mod +0 -0
  303. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/Mounting_Wuerth_WA-SMSE-ExternalM3_H14mm_9771140360.kicad_mod +0 -0
  304. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/Nexperia_DSN0603-2_0.6x0.3mm_P0.4mm.kicad_mod +0 -0
  305. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/QFN-24-1EP_3x3mm_P0.4mm_EP1.75x1.6mm.kicad_mod +0 -0
  306. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/README.md +0 -0
  307. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad/tool_export/SOT-89-3.kicad_mod +0 -0
  308. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_bench.json +0 -0
  309. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/BGA-256_17.0x17.0mm_Layout16x16_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD.kicad_mod +0 -0
  310. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/CP_EIA-3216-18_Kemet-A.kicad_mod +0 -0
  311. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/CP_Radial_D5.0mm_P2.00mm.kicad_mod +0 -0
  312. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/C_0805_2012Metric.kicad_mod +0 -0
  313. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/DIP-8_W7.62mm.kicad_mod +0 -0
  314. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/D_DO-41_SOD81_P10.16mm_Horizontal.kicad_mod +0 -0
  315. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/D_SMA.kicad_mod +0 -0
  316. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/D_SOD-123.kicad_mod +0 -0
  317. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal.kicad_mod +0 -0
  318. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/LED_0603_1608Metric.kicad_mod +0 -0
  319. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/LQFP-48_7x7mm_P0.5mm.kicad_mod +0 -0
  320. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/L_0805_2012Metric.kicad_mod +0 -0
  321. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/PinHeader_1x04_P2.54mm_Vertical.kicad_mod +0 -0
  322. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/PinHeader_2x05_P2.54mm_Vertical.kicad_mod +0 -0
  323. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/QFN-24-1EP_4x4mm_P0.5mm_EP2.6x2.6mm_ThermalVias.kicad_mod +0 -0
  324. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/QFN-32-1EP_5x5mm_P0.5mm_EP3.45x3.45mm.kicad_mod +0 -0
  325. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/R_0402_1005Metric.kicad_mod +0 -0
  326. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/R_0603_1608Metric.kicad_mod +0 -0
  327. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/SOIC-8_3.9x4.9mm_P1.27mm.kicad_mod +0 -0
  328. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/SOT-223-3_TabPin2.kicad_mod +0 -0
  329. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/SOT-23-5.kicad_mod +0 -0
  330. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/SOT-23.kicad_mod +0 -0
  331. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/TO-220-3_Vertical.kicad_mod +0 -0
  332. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_golden/TSSOP-20_4.4x6.5mm_P0.65mm.kicad_mod +0 -0
  333. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_sch/board.kicad_pro +0 -0
  334. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_sch/board.kicad_sch +0 -0
  335. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_sch/board_netlist_kicad7.net +0 -0
  336. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_sch/board_netlist_kicad7.xml +0 -0
  337. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_sch/board_pybom_kicad7.xml +0 -0
  338. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_sch/sub.kicad_sch +0 -0
  339. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/kicad_sch/sub2/leaf.kicad_sch +0 -0
  340. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/sim/bridge.ibs +0 -0
  341. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/sim/broken.lib +0 -0
  342. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/sim/comment_char.ibs +0 -0
  343. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/sim/vendor/EEPART1.lib +0 -0
  344. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/sim/vendor/led_red.mod +0 -0
  345. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/sim/vendor/tps1234.cir +0 -0
  346. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/stdparts_kicad_numbers.json.gz +0 -0
  347. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/symbols/esp32_wroom_32.csv +0 -0
  348. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/symbols/ft232rl_ssop28.csv +0 -0
  349. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/symbols/stm32f103vet6_lqfp100.csv +0 -0
  350. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/dblib_sync_plain.txt +0 -0
  351. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/kicad_export_plain.txt +0 -0
  352. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/migrate_plain.txt +0 -0
  353. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/parity_plain.txt +0 -0
  354. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/release_check_60cols.txt +0 -0
  355. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/release_check_plain.txt +0 -0
  356. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/release_check_quiet.txt +0 -0
  357. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/release_check_tty.txt +0 -0
  358. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/release_check_verbose.txt +0 -0
  359. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/status_plain.txt +0 -0
  360. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/term/status_tty.txt +0 -0
  361. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/upgrade/r5-agent.tar.gz +0 -0
  362. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/upgrade/r7-stdparts.tar.gz +0 -0
  363. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/upgrade/v0.2.0.tar.gz +0 -0
  364. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/upgrade/v0.3.0.tar.gz +0 -0
  365. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/voice/PR.md +0 -0
  366. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/voice/parity.md +0 -0
  367. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/voice/release_check.md +0 -0
  368. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/fixtures/voice/review.md +0 -0
  369. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/kicad_pcbnew_job.py +0 -0
  370. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/nexar/schema.graphql +0 -0
  371. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/run_all.py +0 -0
  372. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_a365_sync.py +0 -0
  373. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_agent_e2e.py +0 -0
  374. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_agent_queue.py +0 -0
  375. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_agent_watch.py +0 -0
  376. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_altium_interop.py +0 -0
  377. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_altium_project.py +0 -0
  378. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_altium_tht.py +0 -0
  379. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_approvals.py +0 -0
  380. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_bom_formats.py +0 -0
  381. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_costs.py +0 -0
  382. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_crosscheck.py +0 -0
  383. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_datasheet.py +0 -0
  384. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_datasheet_batch.py +0 -0
  385. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_ds_route.py +0 -0
  386. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_easy_setup.py +0 -0
  387. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_eval.py +0 -0
  388. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_evalgate.py +0 -0
  389. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_firstrun.py +0 -0
  390. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_footprint_review.py +0 -0
  391. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_footprints.py +0 -0
  392. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_footprints_advanced.py +0 -0
  393. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_fp_share.py +0 -0
  394. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_fp_tidy.py +0 -0
  395. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_fuzz.py +0 -0
  396. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_hooks.py +0 -0
  397. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_hub.py +0 -0
  398. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_independent_review.py +0 -0
  399. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_integration_io.py +0 -0
  400. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_kicad_export.py +0 -0
  401. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_kicad_menu.py +0 -0
  402. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_kicad_real.py +0 -0
  403. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_librarian.py +0 -0
  404. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_librarian_ui.py +0 -0
  405. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_lifecycle.py +0 -0
  406. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_llm.py +0 -0
  407. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_offline.py +0 -0
  408. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_packaging.py +0 -0
  409. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_parity.py +0 -0
  410. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_realworld_boms.py +0 -0
  411. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_release_check.py +0 -0
  412. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_resolve.py +0 -0
  413. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_resolve_ui.py +0 -0
  414. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_altium.py +0 -0
  415. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_core.py +0 -0
  416. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_altiumfiles.py +0 -0
  417. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_followup.py +0 -0
  418. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_footprints.py +0 -0
  419. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_frontends.py +0 -0
  420. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_kicad.py +0 -0
  421. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_llm.py +0 -0
  422. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_pipeline.py +0 -0
  423. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_full_queue_race.py +0 -0
  424. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_rvh.py +0 -0
  425. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_rvj.py +0 -0
  426. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_tool_exports.py +0 -0
  427. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_review_ui.py +0 -0
  428. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_rules.py +0 -0
  429. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_rules_infer.py +0 -0
  430. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_sim_models.py +0 -0
  431. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_site.py +0 -0
  432. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_stdparts.py +0 -0
  433. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_symbol_layout.py +0 -0
  434. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_team_history.py +0 -0
  435. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_team_setup.py +0 -0
  436. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_term.py +0 -0
  437. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_testpack.py +0 -0
  438. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_tool_exports.py +0 -0
  439. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_ui.py +0 -0
  440. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_ui_browser.py +0 -0
  441. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_ui_voice.py +0 -0
  442. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_upgrade.py +0 -0
  443. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_upkeep.py +0 -0
  444. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_upstream.py +0 -0
  445. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/test_visual_check.py +0 -0
  446. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/ui_browser.js +0 -0
  447. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/ui_fixture.py +0 -0
  448. {ecadlib-0.3.0 → ecadlib-0.3.1}/tests/upgrade_fixtures.py +0 -0
@@ -7,6 +7,39 @@ a minor release may change command-line options and file formats.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.1] - 2026-10-17
11
+
12
+ ### Added
13
+
14
+ - Altium builds the libraries itself (`ecadlib/altium_native.py`, docs/altium-native.md): every `.PcbLib` / `.SchLib` ecadlib writes gets a `.ecadbuild` recipe, and a DelphiScript (`ecadlib_build.pas`) rebuilds them inside Altium with its own object API; `ecadlib altium-native write|compare` and a *Rebuild with Altium* step in `altium-pkg` (`--no-native-build` to skip). `compare` reads Altium's result back pad by pad and pin by pin.
15
+ - The ecadlib menu in Altium lets Altium build the library files itself before it reinstalls the library: each job copies the parts' build recipes into the library folder and writes `ecadlib_build.txt`, and the menu calls `BuildLibraries` (`ecadlib_build.pas`, now part of the menu's script project). The dialog says when a library failed to build. `native_build=0` in `ecadlib_menu.ini` turns it off.
16
+ - Altium footprints carry the designator on the assembly drawing too: KiCad's `${REFERENCE}` on F.Fab becomes a `.Designator` on Mechanical 13 (the `[layers] assembly` layer), and footprints without one get it centred on the body. `ecadlib parity` and the footprint check compare it between KiCad and Altium (#37).
17
+ - Edits made in Altium are kept: each library ecadlib copies into a library folder gets a `.ecadstate` (the hash of ecadlib's file and of the file Altium built from it). A library someone changed in Altium afterwards is no longer rebuilt by the native build or replaced by `dblib-sync` / `altium-pkg`; they report it instead (`dblib-sync --overwrite-edits`, `altium-native write --rebuild-all` replace it). Libraries Altium already built and ecadlib has not changed are not rebuilt on every run.
18
+ - `ecadlib fp-edit PLAN --part P show|rename OLD=NEW|same-net A,B|undo`: fix a held part's datasheet pad table by hand, e.g. a connector whose pads are named 1+ / 1- or T / R / S (a footprint still needs a pad 1 or A1, so a person picks it). The edit is kept in `footprint_edits.json` next to the plan, applied whenever the librarian builds the part, and noted on the part with who made it. A part held because its datasheet pad table failed the check now says which check failed and what to do (no pad 1: rename a pad with `fp-edit`; pads of two nets overlap: `same-net` if they are one net, else pick a footprint), instead of "there is no footprint yet".
19
+ - `ecadlib sources-check` and a weekly `sources-canary` workflow: checks the unofficial EasyEDA / JLCPCB endpoints against known parts.
20
+
21
+ ### Changed
22
+
23
+ - Pads on one net may overlap in a datasheet pad table: pads whose pins share a name in the pin table (VIN on pins 1, 2 and the paddle) are put in `same_net` automatically (NC / DNC pins never are). An exposed pad listed twice at the same place with another size (for its thermal vias) is one pad, not a misread; an exact copy is still an error. The accuracy gate's baseline moves to 328/331 (qfn-DAC60504).
24
+ - Official part APIs first: the default provider order is Nexar, DigiKey, Mouser, then the unofficial JLCPCB search and EasyEDA. Nexar / DigiKey / Mouser answers now give the LCSC number and datasheet link. `[providers] unofficial = false` turns the unofficial endpoints off completely.
25
+
26
+ - Altium (Altium 365 first) and KiCad are the supported targets. The Fusion Electronics / EAGLE and EasyEDA Pro exports are marked experimental in the README and `ecadlib -h`: they keep working but get no new features and may be removed later.
27
+
28
+ ### Fixed
29
+
30
+ - BOM intake: `C1~C4` designator ranges are counted; European decimal quantities are not multiplied by ten; one cp1252 byte no longer garbles a UTF-8 CSV; `DNP` / `DNF` in the Value or MPN cell marks the line; a quantity-only footer row is not a part; an MPN that Excel turned into scientific notation is flagged.
31
+ - DbLib table: parts go to the right table (LCSC long descriptions, `easyeda:C7593` is an LCSC number, an IC package outranks the description words, menu parts follow their source).
32
+ - Altium: variants per channel and part, variant alternates by Design Item ID, the encoding of a DbLib Altium saved is kept, prune removes the STEP files ecadlib wrote, sheet name case, menu jobs and pin numbers; the native build keeps recipe footprint names as stored and keeps a library when a STEP file is missing.
33
+ - KiCad export: re-export touches nothing unchanged and never loses hand edits; Altium keep-outs and non-copper regions are not written as copper; Bottom Paste / Bottom Solder and fill rotation are kept; KiCad name clashes are reported; a database row never points at another part's symbol.
34
+ - 3D models: the STEP model is turned to the footprint's pads, and more EasyEDA / LCSC package names get the right body.
35
+ - House style: `style learn` needs coverage before a setting is high and does not write low settings; `power_side = "left"` puts a lone ground at the bottom.
36
+ - Standard parts refuse land variants, exposed-pad SOTs and contradicting size codes.
37
+ - The approval gate works under house parameter names, and placeholder MPNs are handled.
38
+ - Models: the feedback turn is capped, API keys are redacted, unit repair is strict, configured key names are used.
39
+ - Altium: DbLib table names Altium's database driver can open, install scripts written in cp1252, and a warning for Unicode pin names.
40
+ - Team server: no self-approval through the plan approval route, and an odd `sign_as` is a plain 400.
41
+ - Security: no API key in network error messages, framing refused in local mode, a deeply nested JSON body is a plain 400.
42
+
10
43
  ## [0.3.0] - 2026-10-17
11
44
 
12
45
  ### Added
@@ -183,7 +216,8 @@ a minor release may change command-line options and file formats.
183
216
  - Initial prototypes: IPC-7351B footprint generator driven by `package_schema.json`, BOM triage
184
217
  against local KiCad libraries and public part sources, and Altium Database Library (DbLib) export.
185
218
 
186
- [Unreleased]: https://github.com/oliBrowne/PCBLIVINGLIBRARY/compare/v0.3.0...HEAD
219
+ [Unreleased]: https://github.com/oliBrowne/PCBLIVINGLIBRARY/compare/v0.3.1...HEAD
220
+ [0.3.1]: https://github.com/oliBrowne/PCBLIVINGLIBRARY/compare/v0.3.0...v0.3.1
187
221
  [0.3.0]: https://github.com/oliBrowne/PCBLIVINGLIBRARY/compare/v0.2.0...v0.3.0
188
222
  [0.2.0]: https://github.com/oliBrowne/PCBLIVINGLIBRARY/compare/v0.1.0...v0.2.0
189
223
  [0.1.0]: https://github.com/oliBrowne/PCBLIVINGLIBRARY/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ecadlib
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: AI-assisted living ECAD component library: BOM triage, IPC-7351B footprints, Altium and KiCad export
5
5
  Author: oliver browne
6
6
  License-Expression: MIT
@@ -52,13 +52,38 @@ Requires-Dist: pillow; extra == "dev"
52
52
  Requires-Dist: tomli; extra == "dev"
53
53
  Dynamic: license-file
54
54
 
55
- # ecad-librarian: "AI living library" prototypes
56
-
57
- These are small Python prototypes for an open-source tool that manages ECAD components for KiCad and Altium. The design has three parts:
58
-
59
- 1. An LLM reads a datasheet mechanical drawing and fills `ecadlib/package_schema.json`.
60
- 2. A deterministic generator turns that JSON into an IPC-7351B land pattern.
61
- 3. A triage step works out which BOM lines already exist locally, which can be pulled from a public source, and which need a generated footprint.
55
+ # ecadlib: a librarian for your parts
56
+
57
+ ecadlib keeps your component library alive. Give it a BOM and it finds every symbol, footprint and 3D model you are
58
+ missing, draws the ones nobody has from the datasheet, makes them match the rest of your library, holds anything in
59
+ doubt for a person to sign off, and releases the rest to **Altium 365** or **KiCad**.
60
+
61
+ * **BOM in.** Altium BOMs and ActiveBOMs, KiCad BOMs and schematics, or a hand-made CSV/XLSX. Each line comes back as
62
+ already in your library, available from a source, needs a footprint, or DNP.
63
+ * **Official sources first.** Nexar, DigiKey and Mouser identify the part; symbols and footprints come from your own
64
+ libraries and the official KiCad libraries before anything else. EasyEDA/LCSC is an unofficial source, used after
65
+ those, and one setting (`[providers] unofficial = false`) turns it off.
66
+ * **Drawn from the datasheet when nobody has it.** A datasheet reader fills a package description and a deterministic
67
+ IPC-7351B generator draws the land pattern. It works with Claude by default or the model you choose; you bring the
68
+ API key.
69
+ * **Your house style.** `ecadlib style learn` reads your existing libraries and new parts follow the same naming,
70
+ descriptions, value formats and parameters.
71
+ * **Review hold.** A release check runs on every part. Anything that fails it, or still needs a person, is held back
72
+ from Altium 365 until someone signs it off.
73
+ * **Altium 365 release.** Parts go to your Altium 365 workspace, or out as `.SchLib`/`.PcbLib`, a database library
74
+ and KiCad libraries, from one plan.
75
+
76
+ Install it with `pip install ecadlib` (v0.3.1, Python 3.10+), or on Windows run the installer from the
77
+ [Releases page](https://github.com/oliBrowne/PCBLIVINGLIBRARY/releases). It is free and open source under the MIT
78
+ licence. Website: [pcblivinglibrary.vercel.app](https://pcblivinglibrary.vercel.app).
79
+
80
+ **Supported tools:** Altium (Altium 365 first) and KiCad are the supported targets: they get the testing, the
81
+ benchmarks and the fixes. The Fusion Electronics / EAGLE and EasyEDA Pro exports (section 11b) still work but are
82
+ **experimental**: kept as they are, not a focus, and they may be removed.
83
+
84
+ **Where it's going:** [docs/PRODUCT_SPEC.md](docs/PRODUCT_SPEC.md) says who ecadlib is for, what it must do and how
85
+ success is measured; [ROADMAP.md](ROADMAP.md) lists what has shipped and the next milestones (v0.3.1, v0.4.0, v0.5.0)
86
+ with their dates.
62
87
 
63
88
  ## Start here (no terminal needed)
64
89
 
@@ -71,7 +96,7 @@ who use the command line or work on ecadlib itself (section 38 covers how the in
71
96
  ## Quick start
72
97
 
73
98
  ```
74
- pip install -e . # from this checkout (Python 3.10+); `pip install ecadlib` once a release is on PyPI
99
+ pip install ecadlib # Python 3.10+ (or `pip install -e .` from this checkout)
75
100
  ecadlib demo # about a second, offline, no API keys: a small library in ./ecadlib-demo
76
101
  ecadlib init # a few questions -> ecadlib.toml, then a setup check and the next commands
77
102
  ecadlib librarian my_bom.csv -o ecadlib_workspace/librarian/my_bom # fill every missing part; `ecadlib status` shows it
@@ -100,7 +125,7 @@ See section 32 for the first-run commands in detail.
100
125
  Install it with pip (Python 3.10 or later):
101
126
 
102
127
  ```
103
- pip install ecadlib # once a release is on PyPI (see Releases below)
128
+ pip install ecadlib # from PyPI
104
129
  pip install "ecadlib[pdf]" # pypdf trims datasheets to --pages
105
130
  pip install -e ".[dev]" # from a checkout: editable, plus everything the test suite uses
106
131
  ```
@@ -149,7 +174,7 @@ That gives you a single `ecadlib` command (`python -m ecadlib` works too). There
149
174
  ```
150
175
 
151
176
  Its tools are `workspace_info`, `triage_bom` (with `summary_only` for large BOMs), `preview_footprint`, `approve_footprint`, `librarian_run` (fill every missing part, then the release check), `release_check` (with `allow` for parts a person has signed off), `held_parts`, `part_candidates` and `choose_part` (fix a held part), `approval_status` and `approve_part` (sign-off; `user` is required, the agent must pass the person's own name), `find_duplicates` (with `merge`), `build_altium_models`, `export_altium_dblib`, `altium365_plan`, `altium365_push` (dry run unless `dry_run` is false; held parts are never pushed), `sim_models` (SPICE/IBIS models; `attach` writes them) and `list_example_specs`.
152
- 3. **CLI**, for scripts and CI: `ecadlib triage`, `ecadlib footprint`, `ecadlib check`, `ecadlib footprint-bench`, `ecadlib dblib`, `ecadlib dblib-sync`, `ecadlib altium-lib`, `ecadlib altium365`, `ecadlib kicad-export` (the library as KiCad files, section 11), `ecadlib eagle-export` and `ecadlib easyeda-export` (Fusion Electronics / EAGLE and EasyEDA Pro, section 11b), `ecadlib resolve`, `ecadlib approve`, `ecadlib dedupe`, `ecadlib sourcing`, `ecadlib step-gen`, `ecadlib upkeep` (section 15), `ecadlib a365`, `ecadlib project`, `ecadlib where-used` and `ecadlib sim-models` (section 17), `ecadlib init` and `ecadlib rules` (section 18), `ecadlib demo` (section 32), `ecadlib kicad-pcm` and `ecadlib altium-pkg` (section 31), `ecadlib ai` (section 33), `ecadlib symbol` (section 19), `ecadlib datasheet`, `ecadlib team`, `ecadlib agent` (the library agent, section 20), `ecadlib agent-watch` (21), `ecadlib queue` (22), `ecadlib visual-check` (23), `ecadlib migrate` and `ecadlib rebuild` (35). Run `ecadlib <command> -h` for options. Each is described below; the older `python3 -m ecadlib.<module>` form still works.
177
+ 3. **CLI**, for scripts and CI: `ecadlib triage`, `ecadlib footprint`, `ecadlib check`, `ecadlib footprint-bench`, `ecadlib dblib`, `ecadlib dblib-sync`, `ecadlib altium-lib`, `ecadlib altium365`, `ecadlib kicad-export` (the library as KiCad files, section 11), `ecadlib eagle-export` and `ecadlib easyeda-export` (Fusion Electronics / EAGLE and EasyEDA Pro, experimental, section 11b), `ecadlib resolve`, `ecadlib approve`, `ecadlib dedupe`, `ecadlib sourcing`, `ecadlib step-gen`, `ecadlib upkeep` (section 15), `ecadlib a365`, `ecadlib project`, `ecadlib where-used` and `ecadlib sim-models` (section 17), `ecadlib init` and `ecadlib rules` (section 18), `ecadlib demo` (section 32), `ecadlib kicad-pcm` and `ecadlib altium-pkg` (section 31), `ecadlib ai` (section 33), `ecadlib symbol` (section 19), `ecadlib datasheet`, `ecadlib team`, `ecadlib agent` (the library agent, section 20), `ecadlib agent-watch` (21), `ecadlib queue` (22), `ecadlib visual-check` (23), `ecadlib migrate` and `ecadlib rebuild` (35). Run `ecadlib <command> -h` for options. Each is described below; the older `python3 -m ecadlib.<module>` form still works.
153
178
 
154
179
  The UI and MCP server write everything to one workspace folder (default `./ecadlib_workspace`). It has these subfolders:
155
180
  * `uploads/`
@@ -480,16 +505,27 @@ python3 -m tests.test_offline
480
505
  * `unverified (source unreachable)`, which is kept separate on purpose so a network failure is never reported as "needs generation"
481
506
  * **Providers** (`providers.py`, interface `lookup(line) -> SourceResult`, raising `NotConfigured`):
482
507
 
483
- | provider | auth needed | state in prototype |
484
- |---|---|---|
485
- | `jlcpcb_search` | none: anonymous JSON POST to `jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList` (taken from easyeda2kicad 1.0.1 `search_jlcpcb_components`; its docstring says "Works anonymously") | implemented; MPN → LCSC by exact normalised match |
486
- | `easyeda` | none: `GET https://easyeda.com/api/products/{LCSC}/components` with `Referer: https://easyeda.com/` | implemented; parses `result.dataStr.head.c_para`, `packageDetail.dataStr.shape` and `SVGNODE~{…c_etype: outline3D}` for 3D, following easyeda2kicad's importer |
487
- | `digikey` | OAuth2 client credentials (`DIGIKEY_CLIENT_ID/SECRET`), Product Information API v4 | stub |
488
- | `mouser` | `MOUSER_API_KEY` (Search API) | stub |
489
- | `nexar` | OAuth2 client credentials (`NEXAR_CLIENT_ID/SECRET`), GraphQL, quota-limited | stub |
490
- | `snapmagic` | partner token only, with no self-serve public API | stub |
491
- | `samacsys` | account plus Library Loader or partner access, with no documented public REST API | stub |
492
-
508
+ Default order: `nexar,digikey,mouser,jlcpcb_search,easyeda`. The official APIs come first; each is skipped with a
509
+ "not configured" note until its key is set. None of them hands out symbol or footprint geometry, so they identify
510
+ the part (maker, description, datasheet link, package, and the LCSC number when LCSC sells it) and the EasyEDA
511
+ route or the datasheet reader supplies the CAD data.
512
+
513
+ | provider | official? | auth needed | what it gives |
514
+ |---|---|---|---|
515
+ | `nexar` | yes (Octopart / Altium supply API) | OAuth2 client credentials (`NEXAR_CLIENT_ID/SECRET` or `NEXAR_SUPPLY_TOKEN`), GraphQL, quota-limited | MPN match, maker, description, best datasheet, LCSC number from LCSC's offer |
516
+ | `digikey` | yes (Product Information API v4) | OAuth2 client credentials (`DIGIKEY_CLIENT_ID/SECRET`) | MPN match, maker, description, datasheet, package |
517
+ | `mouser` | yes (Search API v1) | `MOUSER_API_KEY` | MPN match, maker, description, datasheet |
518
+ | `jlcpcb_search` | **no**: anonymous JSON POST to `jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList` (from easyeda2kicad 1.0.1) | none | MPN → LCSC, only when no official source found the LCSC number |
519
+ | `easyeda` | **no**: `GET https://easyeda.com/api/products/{LCSC}/components` with `Referer: https://easyeda.com/` | none | the symbol, footprint and 3D data (the only free source of ready CAD data) |
520
+ | `snapmagic` | partner token only, no self-serve public API | | stub |
521
+ | `samacsys` | account plus Library Loader or partner access, no documented public REST API | | stub |
522
+
523
+ * `[providers] unofficial = false` in `ecadlib.toml` keeps the two undocumented endpoints out entirely (triage, the
524
+ librarian's EasyEDA source, the UI). Parts then come from your libraries, the KiCad libraries, the standard
525
+ packages and the datasheet reader.
526
+ * `ecadlib sources-check` asks both unofficial endpoints about known parts (NE555DR, LM358DR, RP2040) and checks the
527
+ converter still gives the right pad and pin counts. `.github/workflows/sources-canary.yml` runs it every Monday and
528
+ keeps the raw answers as an artifact, so a change on their side shows up as a red run, not as broken user parts.
493
529
  * easyeda2kicad 1.0.1 calls `/components` with no `?version=`. The `?version=6.4.19.5` suffix comes from older releases; I kept it, and it is untested.
494
530
  * easyeda2kicad 1.0.1 also knows `pro.easyeda.com/api/components/searchByNumbers` (LCSC → v2 UUID, unauthenticated). Its other v2 endpoints are marked "requires auth" in its source.
495
531
  * I found no public, unauthenticated LCSC search endpoint. The `wmsc.lcsc.com` endpoints are undocumented and were also blocked here.
@@ -641,6 +677,10 @@ python3 -m ecadlib.altium365 plan examples/sample_bom.csv --triage out/triage_sa
641
677
  # add --models out/altium_models_kicad (section 6) -> 14 ready
642
678
  ```
643
679
 
680
+ **Altium can now build these files itself.** Every library written here also gets a plain-text recipe beside it
681
+ (`<file>.ecadbuild`), and the install script lets Altium rebuild each library from it with its own scripting API
682
+ before installing it. See [docs/altium-native.md](docs/altium-native.md) and `ecadlib altium-native`.
683
+
644
684
  No dependencies beyond the standard library. `cfb.py` writes the OLE2 container (v3, 512-byte sectors, mini stream, DIFAT for files over ~7 MB). `altium_lib.py` builds the Altium streams inside it. The record layouts are ported from **AltiumSharp** (C#, Apache-2.0; attribution in the file header and `NOTICE`). `altium-designer-mcp` (GPL-3.0) was read for notes only, and none of its code is used.
645
685
 
646
686
  What gets written:
@@ -844,7 +884,10 @@ Writes what the librarian built for Altium as a KiCad library: `mylib.kicad_sym`
844
884
  * **Checks:** every file parses with `ecadlib.sexpr`, each footprint passes release_check's footprint rule at the level the release check recorded, and symbol pins are checked against footprint pads. **In KiCad itself** (7.0.11, see section 28): the `fp-lib-table` resolves in a KiCad project and its footprints match the library; KiCad 7 refuses the KiCad 8 symbol library as such, and loads it with the two KiCad 8 tokens (`exclude_from_sim`, `generator_version`) removed, every pin, graphic and field unchanged. KiCad 8, 9 and 10 were not run here. A KiCad 7 symbol's `ki_description` becomes the `Description` field.
845
885
  * **License:** KiCad library content is CC-BY-SA 4.0. An export that includes some gets a `NOTICE.txt`, and the export stays out of git (`out/kicad/` is in `.gitignore`).
846
886
 
847
- ## 11b. `eagle_export.py` and `easyeda_pro_export.py`: Fusion Electronics / EAGLE and EasyEDA Pro
887
+ ## 11b. `eagle_export.py` and `easyeda_pro_export.py`: Fusion Electronics / EAGLE and EasyEDA Pro (experimental)
888
+
889
+ > **Experimental.** ecadlib focuses on Altium and KiCad. These two exports keep working and keep their read-back
890
+ > checks, but they get no new features and may be removed in a later release.
848
891
 
849
892
  ```
850
893
  ecadlib eagle-export out/librarian/plan.json -o out/fusion --name mylib [--kicad out/kicad] [--include-held] [--offline]
@@ -1028,7 +1071,7 @@ python3 -m tests.test_sim_models
1028
1071
  * an Altium 365 part list (an `altium365` state file, or JSON in the `a365_sync` shape `{name, folder, parameters, symbol, footprint}`): folders and parameters;
1029
1072
  * KiCad `.kicad_sym`/`.pretty`, and BOM/parts spreadsheets (column names).
1030
1073
 
1031
- Names are pattern-mined per category (`RES_{package}_{value}_{tolerance}`, `R{package}_{value}`, `{mpn}` ...) with value style (`4K7`, `4.7k`, `4.7kΩ`) detected. The written file says, for every setting, where it came from and how confident the guess is. `init` asks a question only where the evidence is split (the best pattern under 60% with a runner-up at 20% or more); `--yes` takes the best guess. If `ecadlib.toml` already exists, `init` shows a diff, writes the proposal to `ecadlib.toml.proposed` and changes nothing unless you confirm or pass `--force` (the old file is kept as `.bak`).
1074
+ Names are pattern-mined per category (`RES_{package}_{value}_{tolerance}`, `R{package}_{value}`, `{mpn}` ...) with value style (`4K7`, `4.7k`, `4.7kΩ`) detected. The written file says, for every setting, where it came from and how confident the guess is. Confidence counts how much of the library shows the convention, not only how much of the evidence agrees: a setting backed by fewer than 3 parts, footprints or symbols, or a footprint layer seen in fewer than half of the footprints (one `.Designator` in 48 footprints), is low. Low settings are not written; they are listed as comments at the top of the file, and ecadlib's default stays. The courtyard is never learned onto a layer that holds the library's 3D bodies, and a `.Designator` on a mechanical layer is not proposed when the library has silkscreen on the overlay. `init` asks a question only where the evidence is split (the best pattern under 60% with a runner-up at 20% or more); `--yes` takes the best guess. If `ecadlib.toml` already exists, `init` shows a diff, writes the proposal to `ecadlib.toml.proposed` and changes nothing unless you confirm or pass `--force` (the old file is kept as `.bak`).
1032
1075
 
1033
1076
  The UI has a **Library rules** card (type the folders to scan, review the proposals with their evidence, edit the TOML, save; replacing an existing file asks first and shows the diff). The MCP server has `propose_rules` and `save_rules`.
1034
1077
 
@@ -1,10 +1,35 @@
1
- # ecad-librarian: "AI living library" prototypes
2
-
3
- These are small Python prototypes for an open-source tool that manages ECAD components for KiCad and Altium. The design has three parts:
4
-
5
- 1. An LLM reads a datasheet mechanical drawing and fills `ecadlib/package_schema.json`.
6
- 2. A deterministic generator turns that JSON into an IPC-7351B land pattern.
7
- 3. A triage step works out which BOM lines already exist locally, which can be pulled from a public source, and which need a generated footprint.
1
+ # ecadlib: a librarian for your parts
2
+
3
+ ecadlib keeps your component library alive. Give it a BOM and it finds every symbol, footprint and 3D model you are
4
+ missing, draws the ones nobody has from the datasheet, makes them match the rest of your library, holds anything in
5
+ doubt for a person to sign off, and releases the rest to **Altium 365** or **KiCad**.
6
+
7
+ * **BOM in.** Altium BOMs and ActiveBOMs, KiCad BOMs and schematics, or a hand-made CSV/XLSX. Each line comes back as
8
+ already in your library, available from a source, needs a footprint, or DNP.
9
+ * **Official sources first.** Nexar, DigiKey and Mouser identify the part; symbols and footprints come from your own
10
+ libraries and the official KiCad libraries before anything else. EasyEDA/LCSC is an unofficial source, used after
11
+ those, and one setting (`[providers] unofficial = false`) turns it off.
12
+ * **Drawn from the datasheet when nobody has it.** A datasheet reader fills a package description and a deterministic
13
+ IPC-7351B generator draws the land pattern. It works with Claude by default or the model you choose; you bring the
14
+ API key.
15
+ * **Your house style.** `ecadlib style learn` reads your existing libraries and new parts follow the same naming,
16
+ descriptions, value formats and parameters.
17
+ * **Review hold.** A release check runs on every part. Anything that fails it, or still needs a person, is held back
18
+ from Altium 365 until someone signs it off.
19
+ * **Altium 365 release.** Parts go to your Altium 365 workspace, or out as `.SchLib`/`.PcbLib`, a database library
20
+ and KiCad libraries, from one plan.
21
+
22
+ Install it with `pip install ecadlib` (v0.3.1, Python 3.10+), or on Windows run the installer from the
23
+ [Releases page](https://github.com/oliBrowne/PCBLIVINGLIBRARY/releases). It is free and open source under the MIT
24
+ licence. Website: [pcblivinglibrary.vercel.app](https://pcblivinglibrary.vercel.app).
25
+
26
+ **Supported tools:** Altium (Altium 365 first) and KiCad are the supported targets: they get the testing, the
27
+ benchmarks and the fixes. The Fusion Electronics / EAGLE and EasyEDA Pro exports (section 11b) still work but are
28
+ **experimental**: kept as they are, not a focus, and they may be removed.
29
+
30
+ **Where it's going:** [docs/PRODUCT_SPEC.md](docs/PRODUCT_SPEC.md) says who ecadlib is for, what it must do and how
31
+ success is measured; [ROADMAP.md](ROADMAP.md) lists what has shipped and the next milestones (v0.3.1, v0.4.0, v0.5.0)
32
+ with their dates.
8
33
 
9
34
  ## Start here (no terminal needed)
10
35
 
@@ -17,7 +42,7 @@ who use the command line or work on ecadlib itself (section 38 covers how the in
17
42
  ## Quick start
18
43
 
19
44
  ```
20
- pip install -e . # from this checkout (Python 3.10+); `pip install ecadlib` once a release is on PyPI
45
+ pip install ecadlib # Python 3.10+ (or `pip install -e .` from this checkout)
21
46
  ecadlib demo # about a second, offline, no API keys: a small library in ./ecadlib-demo
22
47
  ecadlib init # a few questions -> ecadlib.toml, then a setup check and the next commands
23
48
  ecadlib librarian my_bom.csv -o ecadlib_workspace/librarian/my_bom # fill every missing part; `ecadlib status` shows it
@@ -46,7 +71,7 @@ See section 32 for the first-run commands in detail.
46
71
  Install it with pip (Python 3.10 or later):
47
72
 
48
73
  ```
49
- pip install ecadlib # once a release is on PyPI (see Releases below)
74
+ pip install ecadlib # from PyPI
50
75
  pip install "ecadlib[pdf]" # pypdf trims datasheets to --pages
51
76
  pip install -e ".[dev]" # from a checkout: editable, plus everything the test suite uses
52
77
  ```
@@ -95,7 +120,7 @@ That gives you a single `ecadlib` command (`python -m ecadlib` works too). There
95
120
  ```
96
121
 
97
122
  Its tools are `workspace_info`, `triage_bom` (with `summary_only` for large BOMs), `preview_footprint`, `approve_footprint`, `librarian_run` (fill every missing part, then the release check), `release_check` (with `allow` for parts a person has signed off), `held_parts`, `part_candidates` and `choose_part` (fix a held part), `approval_status` and `approve_part` (sign-off; `user` is required, the agent must pass the person's own name), `find_duplicates` (with `merge`), `build_altium_models`, `export_altium_dblib`, `altium365_plan`, `altium365_push` (dry run unless `dry_run` is false; held parts are never pushed), `sim_models` (SPICE/IBIS models; `attach` writes them) and `list_example_specs`.
98
- 3. **CLI**, for scripts and CI: `ecadlib triage`, `ecadlib footprint`, `ecadlib check`, `ecadlib footprint-bench`, `ecadlib dblib`, `ecadlib dblib-sync`, `ecadlib altium-lib`, `ecadlib altium365`, `ecadlib kicad-export` (the library as KiCad files, section 11), `ecadlib eagle-export` and `ecadlib easyeda-export` (Fusion Electronics / EAGLE and EasyEDA Pro, section 11b), `ecadlib resolve`, `ecadlib approve`, `ecadlib dedupe`, `ecadlib sourcing`, `ecadlib step-gen`, `ecadlib upkeep` (section 15), `ecadlib a365`, `ecadlib project`, `ecadlib where-used` and `ecadlib sim-models` (section 17), `ecadlib init` and `ecadlib rules` (section 18), `ecadlib demo` (section 32), `ecadlib kicad-pcm` and `ecadlib altium-pkg` (section 31), `ecadlib ai` (section 33), `ecadlib symbol` (section 19), `ecadlib datasheet`, `ecadlib team`, `ecadlib agent` (the library agent, section 20), `ecadlib agent-watch` (21), `ecadlib queue` (22), `ecadlib visual-check` (23), `ecadlib migrate` and `ecadlib rebuild` (35). Run `ecadlib <command> -h` for options. Each is described below; the older `python3 -m ecadlib.<module>` form still works.
123
+ 3. **CLI**, for scripts and CI: `ecadlib triage`, `ecadlib footprint`, `ecadlib check`, `ecadlib footprint-bench`, `ecadlib dblib`, `ecadlib dblib-sync`, `ecadlib altium-lib`, `ecadlib altium365`, `ecadlib kicad-export` (the library as KiCad files, section 11), `ecadlib eagle-export` and `ecadlib easyeda-export` (Fusion Electronics / EAGLE and EasyEDA Pro, experimental, section 11b), `ecadlib resolve`, `ecadlib approve`, `ecadlib dedupe`, `ecadlib sourcing`, `ecadlib step-gen`, `ecadlib upkeep` (section 15), `ecadlib a365`, `ecadlib project`, `ecadlib where-used` and `ecadlib sim-models` (section 17), `ecadlib init` and `ecadlib rules` (section 18), `ecadlib demo` (section 32), `ecadlib kicad-pcm` and `ecadlib altium-pkg` (section 31), `ecadlib ai` (section 33), `ecadlib symbol` (section 19), `ecadlib datasheet`, `ecadlib team`, `ecadlib agent` (the library agent, section 20), `ecadlib agent-watch` (21), `ecadlib queue` (22), `ecadlib visual-check` (23), `ecadlib migrate` and `ecadlib rebuild` (35). Run `ecadlib <command> -h` for options. Each is described below; the older `python3 -m ecadlib.<module>` form still works.
99
124
 
100
125
  The UI and MCP server write everything to one workspace folder (default `./ecadlib_workspace`). It has these subfolders:
101
126
  * `uploads/`
@@ -426,16 +451,27 @@ python3 -m tests.test_offline
426
451
  * `unverified (source unreachable)`, which is kept separate on purpose so a network failure is never reported as "needs generation"
427
452
  * **Providers** (`providers.py`, interface `lookup(line) -> SourceResult`, raising `NotConfigured`):
428
453
 
429
- | provider | auth needed | state in prototype |
430
- |---|---|---|
431
- | `jlcpcb_search` | none: anonymous JSON POST to `jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList` (taken from easyeda2kicad 1.0.1 `search_jlcpcb_components`; its docstring says "Works anonymously") | implemented; MPN → LCSC by exact normalised match |
432
- | `easyeda` | none: `GET https://easyeda.com/api/products/{LCSC}/components` with `Referer: https://easyeda.com/` | implemented; parses `result.dataStr.head.c_para`, `packageDetail.dataStr.shape` and `SVGNODE~{…c_etype: outline3D}` for 3D, following easyeda2kicad's importer |
433
- | `digikey` | OAuth2 client credentials (`DIGIKEY_CLIENT_ID/SECRET`), Product Information API v4 | stub |
434
- | `mouser` | `MOUSER_API_KEY` (Search API) | stub |
435
- | `nexar` | OAuth2 client credentials (`NEXAR_CLIENT_ID/SECRET`), GraphQL, quota-limited | stub |
436
- | `snapmagic` | partner token only, with no self-serve public API | stub |
437
- | `samacsys` | account plus Library Loader or partner access, with no documented public REST API | stub |
438
-
454
+ Default order: `nexar,digikey,mouser,jlcpcb_search,easyeda`. The official APIs come first; each is skipped with a
455
+ "not configured" note until its key is set. None of them hands out symbol or footprint geometry, so they identify
456
+ the part (maker, description, datasheet link, package, and the LCSC number when LCSC sells it) and the EasyEDA
457
+ route or the datasheet reader supplies the CAD data.
458
+
459
+ | provider | official? | auth needed | what it gives |
460
+ |---|---|---|---|
461
+ | `nexar` | yes (Octopart / Altium supply API) | OAuth2 client credentials (`NEXAR_CLIENT_ID/SECRET` or `NEXAR_SUPPLY_TOKEN`), GraphQL, quota-limited | MPN match, maker, description, best datasheet, LCSC number from LCSC's offer |
462
+ | `digikey` | yes (Product Information API v4) | OAuth2 client credentials (`DIGIKEY_CLIENT_ID/SECRET`) | MPN match, maker, description, datasheet, package |
463
+ | `mouser` | yes (Search API v1) | `MOUSER_API_KEY` | MPN match, maker, description, datasheet |
464
+ | `jlcpcb_search` | **no**: anonymous JSON POST to `jlcpcb.com/api/overseas-pcb-order/v1/shoppingCart/smtGood/selectSmtComponentList` (from easyeda2kicad 1.0.1) | none | MPN → LCSC, only when no official source found the LCSC number |
465
+ | `easyeda` | **no**: `GET https://easyeda.com/api/products/{LCSC}/components` with `Referer: https://easyeda.com/` | none | the symbol, footprint and 3D data (the only free source of ready CAD data) |
466
+ | `snapmagic` | partner token only, no self-serve public API | | stub |
467
+ | `samacsys` | account plus Library Loader or partner access, no documented public REST API | | stub |
468
+
469
+ * `[providers] unofficial = false` in `ecadlib.toml` keeps the two undocumented endpoints out entirely (triage, the
470
+ librarian's EasyEDA source, the UI). Parts then come from your libraries, the KiCad libraries, the standard
471
+ packages and the datasheet reader.
472
+ * `ecadlib sources-check` asks both unofficial endpoints about known parts (NE555DR, LM358DR, RP2040) and checks the
473
+ converter still gives the right pad and pin counts. `.github/workflows/sources-canary.yml` runs it every Monday and
474
+ keeps the raw answers as an artifact, so a change on their side shows up as a red run, not as broken user parts.
439
475
  * easyeda2kicad 1.0.1 calls `/components` with no `?version=`. The `?version=6.4.19.5` suffix comes from older releases; I kept it, and it is untested.
440
476
  * easyeda2kicad 1.0.1 also knows `pro.easyeda.com/api/components/searchByNumbers` (LCSC → v2 UUID, unauthenticated). Its other v2 endpoints are marked "requires auth" in its source.
441
477
  * I found no public, unauthenticated LCSC search endpoint. The `wmsc.lcsc.com` endpoints are undocumented and were also blocked here.
@@ -587,6 +623,10 @@ python3 -m ecadlib.altium365 plan examples/sample_bom.csv --triage out/triage_sa
587
623
  # add --models out/altium_models_kicad (section 6) -> 14 ready
588
624
  ```
589
625
 
626
+ **Altium can now build these files itself.** Every library written here also gets a plain-text recipe beside it
627
+ (`<file>.ecadbuild`), and the install script lets Altium rebuild each library from it with its own scripting API
628
+ before installing it. See [docs/altium-native.md](docs/altium-native.md) and `ecadlib altium-native`.
629
+
590
630
  No dependencies beyond the standard library. `cfb.py` writes the OLE2 container (v3, 512-byte sectors, mini stream, DIFAT for files over ~7 MB). `altium_lib.py` builds the Altium streams inside it. The record layouts are ported from **AltiumSharp** (C#, Apache-2.0; attribution in the file header and `NOTICE`). `altium-designer-mcp` (GPL-3.0) was read for notes only, and none of its code is used.
591
631
 
592
632
  What gets written:
@@ -790,7 +830,10 @@ Writes what the librarian built for Altium as a KiCad library: `mylib.kicad_sym`
790
830
  * **Checks:** every file parses with `ecadlib.sexpr`, each footprint passes release_check's footprint rule at the level the release check recorded, and symbol pins are checked against footprint pads. **In KiCad itself** (7.0.11, see section 28): the `fp-lib-table` resolves in a KiCad project and its footprints match the library; KiCad 7 refuses the KiCad 8 symbol library as such, and loads it with the two KiCad 8 tokens (`exclude_from_sim`, `generator_version`) removed, every pin, graphic and field unchanged. KiCad 8, 9 and 10 were not run here. A KiCad 7 symbol's `ki_description` becomes the `Description` field.
791
831
  * **License:** KiCad library content is CC-BY-SA 4.0. An export that includes some gets a `NOTICE.txt`, and the export stays out of git (`out/kicad/` is in `.gitignore`).
792
832
 
793
- ## 11b. `eagle_export.py` and `easyeda_pro_export.py`: Fusion Electronics / EAGLE and EasyEDA Pro
833
+ ## 11b. `eagle_export.py` and `easyeda_pro_export.py`: Fusion Electronics / EAGLE and EasyEDA Pro (experimental)
834
+
835
+ > **Experimental.** ecadlib focuses on Altium and KiCad. These two exports keep working and keep their read-back
836
+ > checks, but they get no new features and may be removed in a later release.
794
837
 
795
838
  ```
796
839
  ecadlib eagle-export out/librarian/plan.json -o out/fusion --name mylib [--kicad out/kicad] [--include-held] [--offline]
@@ -974,7 +1017,7 @@ python3 -m tests.test_sim_models
974
1017
  * an Altium 365 part list (an `altium365` state file, or JSON in the `a365_sync` shape `{name, folder, parameters, symbol, footprint}`): folders and parameters;
975
1018
  * KiCad `.kicad_sym`/`.pretty`, and BOM/parts spreadsheets (column names).
976
1019
 
977
- Names are pattern-mined per category (`RES_{package}_{value}_{tolerance}`, `R{package}_{value}`, `{mpn}` ...) with value style (`4K7`, `4.7k`, `4.7kΩ`) detected. The written file says, for every setting, where it came from and how confident the guess is. `init` asks a question only where the evidence is split (the best pattern under 60% with a runner-up at 20% or more); `--yes` takes the best guess. If `ecadlib.toml` already exists, `init` shows a diff, writes the proposal to `ecadlib.toml.proposed` and changes nothing unless you confirm or pass `--force` (the old file is kept as `.bak`).
1020
+ Names are pattern-mined per category (`RES_{package}_{value}_{tolerance}`, `R{package}_{value}`, `{mpn}` ...) with value style (`4K7`, `4.7k`, `4.7kΩ`) detected. The written file says, for every setting, where it came from and how confident the guess is. Confidence counts how much of the library shows the convention, not only how much of the evidence agrees: a setting backed by fewer than 3 parts, footprints or symbols, or a footprint layer seen in fewer than half of the footprints (one `.Designator` in 48 footprints), is low. Low settings are not written; they are listed as comments at the top of the file, and ecadlib's default stays. The courtyard is never learned onto a layer that holds the library's 3D bodies, and a `.Designator` on a mechanical layer is not proposed when the library has silkscreen on the overlay. `init` asks a question only where the evidence is split (the best pattern under 60% with a runner-up at 20% or more); `--yes` takes the best guess. If `ecadlib.toml` already exists, `init` shows a diff, writes the proposal to `ecadlib.toml.proposed` and changes nothing unless you confirm or pass `--force` (the old file is kept as `.bak`).
978
1021
 
979
1022
  The UI has a **Library rules** card (type the folders to scan, review the proposals with their evidence, edit the TOML, save; replacing an existing file asks first and shows the diff). The MCP server has `propose_rules` and `save_rules`.
980
1023
 
@@ -1,3 +1,3 @@
1
1
  """ecadlib - prototype tooling for an AI-assisted "living" ECAD component library."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.3.1"
@@ -247,6 +247,8 @@ def push(items, client, ws, dry_run=False, note="ecadlib sync", policy=None, rul
247
247
  from .approvals import push_block
248
248
  from .rules import house_item
249
249
  from .a365_sync import is_a365
250
+ # sign-off is checked on the item as it was approved (ecadlib's parameter names: the content hash covers them)
251
+ blocks = [push_block(i, policy) for i in items]
250
252
  # a pulled item already carries the workspace's own parameter names: renaming it would rename them there
251
253
  items = [i if is_a365(i) else house_item(i, rules) for i in items]
252
254
  defs = {} if dry_run else client.release_defs(ws)
@@ -255,14 +257,13 @@ def push(items, client, ws, dry_run=False, note="ecadlib sync", policy=None, rul
255
257
  if p not in uploaded:
256
258
  uploaded[p] = "<fileId>" if dry_run else client.upload(p)
257
259
  return uploaded[p]
258
- for it in items:
260
+ for it, block in zip(items, blocks):
259
261
  res = {"name": it["name"], "action": "", "detail": ""}
260
262
  if it.get("status") == "held": # release_check held it back: no release, no parameter update
261
263
  res["action"], res["detail"] = "skip", "held: " + "; ".join(it.get("hold_reasons") or ["release check"])
262
264
  results.append(res)
263
265
  continue
264
- block = push_block(it, policy) # sign-off policy not met, or a dedupe duplicate
265
- if block:
266
+ if block: # sign-off policy not met, or a dedupe duplicate
266
267
  res["action"], res["detail"] = "skip", block
267
268
  results.append(res)
268
269
  continue
@@ -325,7 +326,7 @@ def main(argv=None):
325
326
  from . import rules as RU
326
327
 
327
328
  if a.cmd == "plan":
328
- if Path(a.out).is_dir() or a.out.endswith(("/", "\\")): # -o out/a365: the plan file inside that folder
329
+ if Path(a.out).is_dir() or a.out.endswith(("/", "\\")) or not Path(a.out).suffix: # -o out/a365: the plan file inside that folder
329
330
  a.out = str(Path(a.out) / "plan.json")
330
331
  rules = RU.load(a.rules, start=Path(a.out).parent)
331
332
  lines, _ = read_bom(a.bom, RU.bom_columns(rules))