fritzkit 0.0.0-stage → 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 (328) hide show
  1. package/.claude-plugin/marketplace.json +12 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/CHANGELOG.md +8 -0
  4. package/LICENSE +21 -0
  5. package/README.md +429 -2
  6. package/dist/cli/commands/doctor.d.ts +4 -0
  7. package/dist/cli/commands/doctor.js +7 -0
  8. package/dist/cli/commands/doctor.js.map +1 -0
  9. package/dist/cli/commands/export.d.ts +17 -0
  10. package/dist/cli/commands/export.js +81 -0
  11. package/dist/cli/commands/export.js.map +1 -0
  12. package/dist/cli/commands/kicad.d.ts +10 -0
  13. package/dist/cli/commands/kicad.js +22 -0
  14. package/dist/cli/commands/kicad.js.map +1 -0
  15. package/dist/cli/commands/parts.d.ts +22 -0
  16. package/dist/cli/commands/parts.js +46 -0
  17. package/dist/cli/commands/parts.js.map +1 -0
  18. package/dist/cli/commands/prune.d.ts +14 -0
  19. package/dist/cli/commands/prune.js +67 -0
  20. package/dist/cli/commands/prune.js.map +1 -0
  21. package/dist/cli/commands/render.d.ts +24 -0
  22. package/dist/cli/commands/render.js +62 -0
  23. package/dist/cli/commands/render.js.map +1 -0
  24. package/dist/cli/commands/schema.d.ts +10 -0
  25. package/dist/cli/commands/schema.js +35 -0
  26. package/dist/cli/commands/schema.js.map +1 -0
  27. package/dist/cli/commands/ui.d.ts +14 -0
  28. package/dist/cli/commands/ui.js +67 -0
  29. package/dist/cli/commands/ui.js.map +1 -0
  30. package/dist/cli/commands/validate.d.ts +24 -0
  31. package/dist/cli/commands/validate.js +91 -0
  32. package/dist/cli/commands/validate.js.map +1 -0
  33. package/dist/cli/context.d.ts +81 -0
  34. package/dist/cli/context.js +139 -0
  35. package/dist/cli/context.js.map +1 -0
  36. package/dist/cli/main.d.ts +2 -0
  37. package/dist/cli/main.js +8 -0
  38. package/dist/cli/main.js.map +1 -0
  39. package/dist/cli/program.d.ts +21 -0
  40. package/dist/cli/program.js +158 -0
  41. package/dist/cli/program.js.map +1 -0
  42. package/dist/cli/raster.d.ts +4 -0
  43. package/dist/cli/raster.js +9 -0
  44. package/dist/cli/raster.js.map +1 -0
  45. package/dist/core/compile/breadboard-util.d.ts +12 -0
  46. package/dist/core/compile/breadboard-util.js +107 -0
  47. package/dist/core/compile/breadboard-util.js.map +1 -0
  48. package/dist/core/compile/breadboard.d.ts +60 -0
  49. package/dist/core/compile/breadboard.js +513 -0
  50. package/dist/core/compile/breadboard.js.map +1 -0
  51. package/dist/core/compile/fzp.d.ts +12 -0
  52. package/dist/core/compile/fzp.js +183 -0
  53. package/dist/core/compile/fzp.js.map +1 -0
  54. package/dist/core/compile/icon.d.ts +17 -0
  55. package/dist/core/compile/icon.js +27 -0
  56. package/dist/core/compile/icon.js.map +1 -0
  57. package/dist/core/compile/index.d.ts +4 -0
  58. package/dist/core/compile/index.js +62 -0
  59. package/dist/core/compile/index.js.map +1 -0
  60. package/dist/core/compile/pcb.d.ts +39 -0
  61. package/dist/core/compile/pcb.js +164 -0
  62. package/dist/core/compile/pcb.js.map +1 -0
  63. package/dist/core/compile/schematic.d.ts +69 -0
  64. package/dist/core/compile/schematic.js +240 -0
  65. package/dist/core/compile/schematic.js.map +1 -0
  66. package/dist/core/compile/style.d.ts +52 -0
  67. package/dist/core/compile/style.js +72 -0
  68. package/dist/core/compile/style.js.map +1 -0
  69. package/dist/core/compile/types.d.ts +50 -0
  70. package/dist/core/compile/types.js +15 -0
  71. package/dist/core/compile/types.js.map +1 -0
  72. package/dist/core/export/bin.d.ts +61 -0
  73. package/dist/core/export/bin.js +221 -0
  74. package/dist/core/export/bin.js.map +1 -0
  75. package/dist/core/export/fzpz.d.ts +37 -0
  76. package/dist/core/export/fzpz.js +121 -0
  77. package/dist/core/export/fzpz.js.map +1 -0
  78. package/dist/core/export/index.d.ts +5 -0
  79. package/dist/core/export/index.js +6 -0
  80. package/dist/core/export/index.js.map +1 -0
  81. package/dist/core/export/install.d.ts +77 -0
  82. package/dist/core/export/install.js +183 -0
  83. package/dist/core/export/install.js.map +1 -0
  84. package/dist/core/export/prune.d.ts +72 -0
  85. package/dist/core/export/prune.js +165 -0
  86. package/dist/core/export/prune.js.map +1 -0
  87. package/dist/core/fritzing.d.ts +22 -0
  88. package/dist/core/fritzing.js +77 -0
  89. package/dist/core/fritzing.js.map +1 -0
  90. package/dist/core/identity.d.ts +29 -0
  91. package/dist/core/identity.js +70 -0
  92. package/dist/core/identity.js.map +1 -0
  93. package/dist/core/image/bbox.d.ts +27 -0
  94. package/dist/core/image/bbox.js +24 -0
  95. package/dist/core/image/bbox.js.map +1 -0
  96. package/dist/core/image/index.d.ts +8 -0
  97. package/dist/core/image/index.js +9 -0
  98. package/dist/core/image/index.js.map +1 -0
  99. package/dist/core/image/key.d.ts +72 -0
  100. package/dist/core/image/key.js +317 -0
  101. package/dist/core/image/key.js.map +1 -0
  102. package/dist/core/image/prepare.d.ts +31 -0
  103. package/dist/core/image/prepare.js +98 -0
  104. package/dist/core/image/prepare.js.map +1 -0
  105. package/dist/core/image/remover.d.ts +17 -0
  106. package/dist/core/image/remover.js +51 -0
  107. package/dist/core/image/remover.js.map +1 -0
  108. package/dist/core/index.d.ts +16 -0
  109. package/dist/core/index.js +17 -0
  110. package/dist/core/index.js.map +1 -0
  111. package/dist/core/kicad/footprint.d.ts +104 -0
  112. package/dist/core/kicad/footprint.js +317 -0
  113. package/dist/core/kicad/footprint.js.map +1 -0
  114. package/dist/core/kicad/index.d.ts +25 -0
  115. package/dist/core/kicad/index.js +197 -0
  116. package/dist/core/kicad/index.js.map +1 -0
  117. package/dist/core/kicad/sexpr.d.ts +35 -0
  118. package/dist/core/kicad/sexpr.js +163 -0
  119. package/dist/core/kicad/sexpr.js.map +1 -0
  120. package/dist/core/resolve.d.ts +113 -0
  121. package/dist/core/resolve.js +301 -0
  122. package/dist/core/resolve.js.map +1 -0
  123. package/dist/core/schema/part.d.ts +995 -0
  124. package/dist/core/schema/part.js +278 -0
  125. package/dist/core/schema/part.js.map +1 -0
  126. package/dist/core/schema/part.schema.json +1122 -0
  127. package/dist/core/svg.d.ts +20 -0
  128. package/dist/core/svg.js +56 -0
  129. package/dist/core/svg.js.map +1 -0
  130. package/dist/core/units.d.ts +28 -0
  131. package/dist/core/units.js +42 -0
  132. package/dist/core/units.js.map +1 -0
  133. package/dist/core/validate/checker.d.ts +46 -0
  134. package/dist/core/validate/checker.js +142 -0
  135. package/dist/core/validate/checker.js.map +1 -0
  136. package/dist/core/validate/doctor.d.ts +25 -0
  137. package/dist/core/validate/doctor.js +156 -0
  138. package/dist/core/validate/doctor.js.map +1 -0
  139. package/dist/core/validate/explain.d.ts +10 -0
  140. package/dist/core/validate/explain.js +135 -0
  141. package/dist/core/validate/explain.js.map +1 -0
  142. package/dist/core/validate/index.d.ts +21 -0
  143. package/dist/core/validate/index.js +96 -0
  144. package/dist/core/validate/index.js.map +1 -0
  145. package/dist/core/validate/structural.d.ts +74 -0
  146. package/dist/core/validate/structural.js +664 -0
  147. package/dist/core/validate/structural.js.map +1 -0
  148. package/dist/core/workspace.d.ts +69 -0
  149. package/dist/core/workspace.js +213 -0
  150. package/dist/core/workspace.js.map +1 -0
  151. package/dist/mcp/jsonpatch.d.ts +30 -0
  152. package/dist/mcp/jsonpatch.js +145 -0
  153. package/dist/mcp/jsonpatch.js.map +1 -0
  154. package/dist/mcp/main.d.ts +2 -0
  155. package/dist/mcp/main.js +5 -0
  156. package/dist/mcp/main.js.map +1 -0
  157. package/dist/mcp/server.d.ts +28 -0
  158. package/dist/mcp/server.js +310 -0
  159. package/dist/mcp/server.js.map +1 -0
  160. package/dist/server/api.d.ts +72 -0
  161. package/dist/server/api.js +507 -0
  162. package/dist/server/api.js.map +1 -0
  163. package/dist/server/events.d.ts +28 -0
  164. package/dist/server/events.js +134 -0
  165. package/dist/server/events.js.map +1 -0
  166. package/dist/server/http.d.ts +36 -0
  167. package/dist/server/http.js +127 -0
  168. package/dist/server/http.js.map +1 -0
  169. package/dist/server/index.d.ts +65 -0
  170. package/dist/server/index.js +193 -0
  171. package/dist/server/index.js.map +1 -0
  172. package/dist/ui/assets/index-DLU8opyI.js +63 -0
  173. package/dist/ui/assets/index-GhUA-T3b.css +1 -0
  174. package/dist/ui/index.html +14 -0
  175. package/package.json +89 -4
  176. package/skills/fritzkit/SKILL.md +690 -0
  177. package/skills/fritzkit/reference/examples.md +275 -0
  178. package/skills/fritzkit/reference/part-json.md +333 -0
  179. package/skills/fritzkit/scripts/measure.py +197 -0
  180. package/tools/fzp-checker/.python-version +1 -0
  181. package/tools/fzp-checker/README.md +45 -0
  182. package/tools/fzp-checker/check_fzp.py +48 -0
  183. package/tools/fzp-checker/pyproject.toml +26 -0
  184. package/tools/fzp-checker/uv.lock +266 -0
  185. package/tools/fzp-checker/vendor/LICENSE.txt +8 -0
  186. package/tools/fzp-checker/vendor/UPSTREAM.md +25 -0
  187. package/tools/fzp-checker/vendor/fzp_checker.py +14 -0
  188. package/tools/fzp-checker/vendor/scripts/checks/__init__.py +26 -0
  189. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/__init__.cpython-313.pyc +0 -0
  190. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/check_missing_leg_ids.cpython-313.pyc +0 -0
  191. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/connector_numbering.cpython-313.pyc +0 -0
  192. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checker_runner.cpython-313.pyc +0 -0
  193. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checkers.cpython-313.pyc +0 -0
  194. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_svg_checkers.cpython-313.pyc +0 -0
  195. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_utils.cpython-313.pyc +0 -0
  196. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_checkers.cpython-313.pyc +0 -0
  197. package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_utils.cpython-313.pyc +0 -0
  198. package/tools/fzp-checker/vendor/scripts/checks/check_missing_leg_ids.py +44 -0
  199. package/tools/fzp-checker/vendor/scripts/checks/checkascii.py +27 -0
  200. package/tools/fzp-checker/vendor/scripts/checks/checkcase.py +156 -0
  201. package/tools/fzp-checker/vendor/scripts/checks/checkcopies.py +104 -0
  202. package/tools/fzp-checker/vendor/scripts/checks/connector_numbering.py +79 -0
  203. package/tools/fzp-checker/vendor/scripts/checks/connectors_misnumbered.py +228 -0
  204. package/tools/fzp-checker/vendor/scripts/checks/explain_errors.md +95 -0
  205. package/tools/fzp-checker/vendor/scripts/checks/fzp_checker_runner.py +717 -0
  206. package/tools/fzp-checker/vendor/scripts/checks/fzp_checkers.py +1069 -0
  207. package/tools/fzp-checker/vendor/scripts/checks/fzp_svg_checkers.py +62 -0
  208. package/tools/fzp-checker/vendor/scripts/checks/fzp_utils.py +222 -0
  209. package/tools/fzp-checker/vendor/scripts/checks/inspector_fc51.png +0 -0
  210. package/tools/fzp-checker/vendor/scripts/checks/invisibleconnectors.py +118 -0
  211. package/tools/fzp-checker/vendor/scripts/checks/nolayeridcheck.txt +758 -0
  212. package/tools/fzp-checker/vendor/scripts/checks/requirements.txt +3 -0
  213. package/tools/fzp-checker/vendor/scripts/checks/svg_checkers.py +953 -0
  214. package/tools/fzp-checker/vendor/scripts/checks/svg_utils.py +96 -0
  215. package/tools/fzp-checker/vendor/scripts/checks/test_checkers.py +839 -0
  216. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_missing.fzp.test +3 -0
  217. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_present.fzp.test +4 -0
  218. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_id.fzp.test +8 -0
  219. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_node_members.fzp.test +7 -0
  220. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_present_valid.fzp.test +8 -0
  221. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_missing_attributes.fzp.test +16 -0
  222. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_present_valid.fzp.test +14 -0
  223. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_invalid.fzp.test +70 -0
  224. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_valid.fzp.test +70 -0
  225. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_refs_valid.fzp.test +53 -0
  226. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_mixed.fzp.test +11 -0
  227. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_silkscreen_nested.fzp.test +11 -0
  228. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_style_attr.fzp.test +11 -0
  229. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_white_stroke.fzp.test +11 -0
  230. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_wrong_color.fzp.test +11 -0
  231. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_inherited.fzp.test +11 -0
  232. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_legacy.fzp.test +12 -0
  233. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_nested_copper.fzp.test +12 -0
  234. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_none.fzp.test +11 -0
  235. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_standard.fzp.test +12 -0
  236. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_uppercase.fzp.test +11 -0
  237. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/css_connector.fzp.test +100 -0
  238. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_missing.fzp.test +3 -0
  239. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_present.fzp.test +4 -0
  240. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/duplicate_ids.fzp.test +27 -0
  241. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_missing.fzp.test +6 -0
  242. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_present_valid.fzp.test +6 -0
  243. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/font_size.fzp.test +46 -0
  244. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_invalid_format.fzp.test +4 -0
  245. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_missing.fzp.test +4 -0
  246. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_present_valid.fzp.test +4 -0
  247. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_absent.fzp.test +15 -0
  248. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_present.fzp.test +15 -0
  249. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/hybrid_connectors.fzp.test +232 -0
  250. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/invalid_xml.fzp.test +12 -0
  251. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_match.fzp.test +29 -0
  252. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_mismatch.fzp.test +30 -0
  253. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_invalid.fzp.test +11 -0
  254. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_valid.fzp.test +11 -0
  255. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/matrix_transform.fzp.test +8 -0
  256. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_connector_refs.fzp.test +63 -0
  257. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_leg_ids.fzp.test +34 -0
  258. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_missing.fzp.test +4 -0
  259. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_non_alnum.fzp.test +4 -0
  260. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_present.fzp.test +4 -0
  261. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_absent.fzp.test +4 -0
  262. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_present.fzp.test +4 -0
  263. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_too_short.fzp.test +4 -0
  264. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_invalid.fzp.test +19 -0
  265. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_namedview.fzp.test +19 -0
  266. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_valid.fzp.test +19 -0
  267. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/pcb_only.fzp.test +48 -0
  268. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_name.fzp.test +6 -0
  269. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_value.fzp.test +6 -0
  270. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_present.fzp.test +6 -0
  271. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_missing.fzp.test +3 -0
  272. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_present.fzp.test +11 -0
  273. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/stroke_test.fzp.test +60 -0
  274. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/template_svgs.fzp.test +35 -0
  275. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_missing.fzp.test +3 -0
  276. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_present.fzp.test +4 -0
  277. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_ids.fzp.test +27 -0
  278. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_duplicates.fzp.test +7 -0
  279. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_unique.fzp.test +7 -0
  280. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/valid_xml.fzp.test +249 -0
  281. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_invalid_format.fzp.test +4 -0
  282. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_missing.fzp.test +3 -0
  283. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_present_valid.fzp.test +4 -0
  284. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_missing.fzp.test +3 -0
  285. package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_present.fzp.test +8 -0
  286. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/connector_refs_valid_breadboard.svg +7 -0
  287. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/duplicate_ids_breadboard.svg +13 -0
  288. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_absent_breadboard.svg +7 -0
  289. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_present_breadboard.svg +7 -0
  290. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/hybrid_connectors_breadboard.svg +102 -0
  291. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_invalid.svg +13 -0
  292. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_valid.svg +13 -0
  293. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/matrix_transform.svg +11 -0
  294. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_connector_refs_breadboard.svg +8 -0
  295. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_leg_ids_breadboard.svg +9 -0
  296. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_match_breadboard.svg +9 -0
  297. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_mismatch_breadboard.svg +9 -0
  298. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/unique_ids_breadboard.svg +13 -0
  299. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/hybrid_connectors_icon.svg +78 -0
  300. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_invalid.svg +4 -0
  301. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_namedview.svg +9 -0
  302. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_valid.svg +7 -0
  303. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/plain_pcb.svg +10 -0
  304. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/connector_refs_valid_pcb.svg +10 -0
  305. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_mixed.svg +8 -0
  306. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_silkscreen_nested.svg +10 -0
  307. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_style_attr.svg +6 -0
  308. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_white_stroke.svg +6 -0
  309. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_wrong_color.svg +6 -0
  310. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_inherited.svg +7 -0
  311. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_legacy.svg +10 -0
  312. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_nested_copper.svg +9 -0
  313. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_none.svg +7 -0
  314. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_standard.svg +10 -0
  315. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_uppercase.svg +7 -0
  316. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/css_connector.svg +25 -0
  317. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/font_size.svg +54 -0
  318. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/hybrid_connectors_pcb.svg +35 -0
  319. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/missing_connector_refs_pcb.svg +14 -0
  320. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/pcb-arduino-shield.svg +25 -0
  321. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/stroke_test.svg +12 -0
  322. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_match_pcb.svg +12 -0
  323. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_mismatch_pcb.svg +12 -0
  324. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/connector_refs_valid_schematic.svg +7 -0
  325. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/hybrid_connectors_schematic.svg +59 -0
  326. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/missing_connector_refs_schematic.svg +8 -0
  327. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_match_schematic.svg +6 -0
  328. package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_mismatch_schematic.svg +6 -0
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "fritzkit",
3
+ "owner": { "name": "pmaxhogan" },
4
+ "description": "Claude Code marketplace for the fritzkit plugin",
5
+ "plugins": [
6
+ {
7
+ "name": "fritzkit",
8
+ "source": "./",
9
+ "description": "Build Fritzing parts from a product link: workflow skill plus the fritzkit MCP server"
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "fritzkit",
3
+ "version": "0.1.0",
4
+ "description": "Build first-class Fritzing parts (.fzpz) from a product link, datasheet, photos or KiCad footprint: a skill for the research workflow plus the fritzkit MCP server that compiles, validates, renders, exports and installs the part.",
5
+ "author": { "name": "Max Hogan", "url": "https://github.com/pmaxhogan" },
6
+ "homepage": "https://github.com/pmaxhogan/fritzkit#readme",
7
+ "repository": "https://github.com/pmaxhogan/fritzkit",
8
+ "license": "MIT",
9
+ "keywords": ["fritzing", "fzpz", "electronics", "parts", "breadboard", "mcp"],
10
+ "mcpServers": {
11
+ "fritzkit": { "command": "npx", "args": ["-y", "fritzkit", "mcp"] }
12
+ }
13
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ Short notes per release. Releases are cut automatically on every green push to main.
4
+
5
+ ## Unreleased
6
+
7
+ - Scaffold from stack standards.
8
+ - Agent docs: Claude Code skill (skills/fritzkit), plugin manifest, README, docs/part-json.md field reference and docs/agent-workflow.md.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Max Hogan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,430 @@
1
- # Temporary Holding Version
1
+ # fritzkit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![ci](https://github.com/pmaxhogan/fritzkit/actions/workflows/ci.yml/badge.svg)](https://github.com/pmaxhogan/fritzkit/actions/workflows/ci.yml)
4
+ [![fzp-checker](https://github.com/pmaxhogan/fritzkit/actions/workflows/fzp-checker.yml/badge.svg)](https://github.com/pmaxhogan/fritzkit/actions/workflows/fzp-checker.yml)
5
+
6
+ fritzkit compiles a small JSON description of a board (`part.json`: outline in mm, every
7
+ pad and pin with its name, role and position, buses for shared nets) into a first-class
8
+ Fritzing part: breadboard, schematic, PCB and icon views that pass Fritzing's own part
9
+ checker, seat on the 0.1 in breadboard grid and keep their connector ids across versions so
10
+ placed parts can be swapped without losing wires. It ships as a CLI, an MCP server and a
11
+ Claude Code skill, so you can hand Claude a product link and get a `.fzpz` back in My Parts.
12
+ fritzkit itself is offline and deterministic; the agent does the reading and measuring.
13
+
14
+ ## Install
15
+
16
+ ```
17
+ npx fritzkit --help # run without installing
18
+ npm i -g fritzkit # or install the `fritzkit` command globally
19
+ ```
20
+
21
+ Requirements:
22
+
23
+ - Node.js 22 or newer
24
+ - [Fritzing](https://fritzing.org/) 1.0.7 (to use the parts; `install` and `prune` manage its user parts folder)
25
+ - optional: [uv](https://docs.astral.sh/uv/) for `validate --checker`, which runs Fritzing's own part checker
26
+
27
+ `fritzkit doctor` checks all of these.
28
+
29
+ ## 60-second quickstart
30
+
31
+ ```
32
+ fritzkit init # create the workspace (~/fritzkit/parts)
33
+ fritzkit new my-breakout --title "My breakout" # <workspace>/my-breakout/part.json + assets/
34
+ # edit ~/fritzkit/parts/my-breakout/part.json (see the reference below)
35
+ fritzkit validate my-breakout # schema + structural checks
36
+ fritzkit render my-breakout --png # PNG of every view into my-breakout/build/
37
+ fritzkit export my-breakout # -> my-breakout/<moduleId>.fzpz
38
+ fritzkit install my-breakout # into Fritzing's My Parts (close Fritzing first)
39
+ ```
40
+
41
+ Then start Fritzing; the part is in the **My Parts** bin.
42
+
43
+ ## Use it from Claude Code
44
+
45
+ As a plugin (skill + MCP server in one step):
46
+
47
+ ```
48
+ claude plugin marketplace add pmaxhogan/fritzkit
49
+ claude plugin install fritzkit@fritzkit
50
+ ```
51
+
52
+ (Inside a session: `/plugin marketplace add pmaxhogan/fritzkit`, then
53
+ `/plugin install fritzkit@fritzkit`.)
54
+
55
+ Or just the MCP server:
56
+
57
+ ```
58
+ claude mcp add --scope user fritzkit -- npx -y fritzkit mcp
59
+ ```
60
+
61
+ and, for the workflow, copy `skills/fritzkit/` from this repo into `~/.claude/skills/fritzkit/`.
62
+
63
+ Then ask: "Make me a Fritzing part for https://www.amazon.com/dp/XXXXXXXXXX". The skill
64
+ tells Claude how to fetch the listing and images, measure the board, name every pad, write
65
+ `part.json`, check the render against the photo, and export/install the part.
66
+
67
+ ## The agent workflow
68
+
69
+ 1. Gather sources with the agent's own tools (listing via Amazon's mobile URL with a plain
70
+ `curl`, gallery images from the page's `hiRes` URLs, datasheet PDFs, manufacturer wiki, Digikey page, or a KiCad footprint).
71
+ 2. Determine the outline in mm from dimension callouts or a drawing, cross-checked.
72
+ 3. Identify every pad: silkscreen name, role, net (buses), connector kind, position measured
73
+ from the photo at a known px/mm.
74
+ 4. Write `part.json`, `validate`, `render --png`, compare the render with the photo, fix,
75
+ repeat.
76
+ 5. `export`, then `install` (Fritzing closed) or hand over the `.fzpz`; `prune` old versions.
77
+ 6. Report what was measured, what was estimated, and what the user should verify.
78
+
79
+ Long form: [docs/agent-workflow.md](docs/agent-workflow.md). The skill itself:
80
+ [skills/fritzkit/SKILL.md](skills/fritzkit/SKILL.md).
81
+
82
+ ## part.json reference
83
+
84
+ Every field, type, default and the coordinate frame: [docs/part-json.md](docs/part-json.md).
85
+ The JSON Schema is printed by `fritzkit schema`. In short: lengths in mm, origin at the
86
+ top-left corner of the outline, x right, y down (like a top-down photo), positions are pad
87
+ centres.
88
+
89
+ Example, the [Seeed Studio XIAO ESP32-C6](https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/)
90
+ (a portrait board: top view, USB-C at the TOP short edge, two columns of 7 castellated pads
91
+ down the long edges, a 2-pad BAT header on the back, GND pads tied by a bus). Trimmed; the
92
+ full, measured spec is [examples/xiao-esp32-c6](examples/xiao-esp32-c6/README.md):
93
+
94
+ ```json
95
+ {
96
+ "$schema": "https://raw.githubusercontent.com/pmaxhogan/fritzkit/main/src/core/schema/part.schema.json",
97
+ "slug": "xiao-esp32-c6",
98
+ "title": "Seeed Studio XIAO ESP32-C6",
99
+ "version": 1,
100
+ "label": "U",
101
+ "tags": ["xiao", "esp32-c6", "seeed", "castellated"],
102
+ "description": "Seeed Studio XIAO ESP32-C6, 21 x 17.8 mm. Top (component) view, USB-C at the TOP short edge, the two columns of 7 castellated 0.1 in pads along the long edges: D0..D6 down the left edge, 5V/GND/3V3/D10/D9/D8/D7 down the right edge. BAT+ and BAT- are pads on the BACK, drawn where they sit seen through the board. BAT- and the side GND pad are the same net (Seeed's KiCad symbol names that pad GND), hence the GND bus.",
103
+ "sources": ["https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/"],
104
+ "properties": {
105
+ "mpn": "XIAO ESP32C6",
106
+ "type": "module",
107
+ "chip": "ESP32-C6"
108
+ },
109
+ "outline": {
110
+ "widthMm": 17.8,
111
+ "heightMm": 21,
112
+ "shape": "rounded",
113
+ "cornerRadiusMm": 1.5
114
+ },
115
+ "artwork": {
116
+ "style": "vector",
117
+ "boardColor": "#1A1A1A",
118
+ "components": [
119
+ {
120
+ "kind": "rect",
121
+ "xMm": 4.4,
122
+ "yMm": -1.5,
123
+ "wMm": 9,
124
+ "hMm": 7.4,
125
+ "color": "#B8B8B8",
126
+ "rxMm": 0.8,
127
+ "note": "USB-C receptacle, overhangs the top edge by 1.5 mm (KiCad footprint silk)"
128
+ },
129
+ {
130
+ "kind": "rect",
131
+ "xMm": 3,
132
+ "yMm": 6.8,
133
+ "wMm": 11.8,
134
+ "hMm": 10.4,
135
+ "color": "#6E6E6E",
136
+ "rxMm": 0.4,
137
+ "note": "ESP32-C6 RF shield can"
138
+ }
139
+ ]
140
+ },
141
+ "headers": [
142
+ {
143
+ "key": "left",
144
+ "kind": "castellated",
145
+ "cols": 7,
146
+ "orientation": "vertical",
147
+ "xMm": 1.28,
148
+ "yMm": 2.88,
149
+ "pins": [
150
+ {
151
+ "key": "d0",
152
+ "name": "D0",
153
+ "role": "analog",
154
+ "description": "D0 / A0: GPIO0 (LP_GPIO0), ADC"
155
+ },
156
+ {
157
+ "key": "d1",
158
+ "name": "D1",
159
+ "role": "analog",
160
+ "description": "D1 / A1: GPIO1 (LP_GPIO1), ADC"
161
+ },
162
+ {
163
+ "key": "d2",
164
+ "name": "D2",
165
+ "role": "analog",
166
+ "description": "D2 / A2: GPIO2 (LP_GPIO2), ADC"
167
+ },
168
+ {
169
+ "key": "d3",
170
+ "name": "D3",
171
+ "role": "io",
172
+ "description": "D3: GPIO21 (SDIO_DATA1)"
173
+ },
174
+ {
175
+ "key": "d4",
176
+ "name": "D4",
177
+ "role": "i2c",
178
+ "description": "D4 / SDA: GPIO22, I2C data (SDIO_DATA2)"
179
+ },
180
+ {
181
+ "key": "d5",
182
+ "name": "D5",
183
+ "role": "i2c",
184
+ "description": "D5 / SCL: GPIO23, I2C clock (SDIO_DATA3)"
185
+ },
186
+ {
187
+ "key": "d6",
188
+ "name": "D6",
189
+ "role": "uart",
190
+ "description": "D6 / TX: GPIO16, UART transmit"
191
+ }
192
+ ]
193
+ },
194
+ {
195
+ "key": "right",
196
+ "kind": "castellated",
197
+ "cols": 7,
198
+ "orientation": "vertical",
199
+ "xMm": 16.52,
200
+ "yMm": 2.88,
201
+ "pins": [
202
+ {
203
+ "key": "5v",
204
+ "name": "5V",
205
+ "role": "power",
206
+ "description": "5V (VBUS): USB-C 5 V in/out. No voltage here when running from the battery"
207
+ },
208
+ {
209
+ "key": "gnd",
210
+ "name": "GND",
211
+ "role": "gnd",
212
+ "description": "Ground"
213
+ },
214
+ {
215
+ "key": "3v3",
216
+ "name": "3V3",
217
+ "role": "power",
218
+ "description": "3V3 (3V3_OUT): 3.3 V regulator output"
219
+ },
220
+ {
221
+ "key": "d10",
222
+ "name": "D10",
223
+ "role": "spi",
224
+ "description": "D10 / MOSI: GPIO18, SPI data out"
225
+ },
226
+ {
227
+ "key": "d9",
228
+ "name": "D9",
229
+ "role": "spi",
230
+ "description": "D9 / MISO: GPIO20, SPI data in"
231
+ },
232
+ {
233
+ "key": "d8",
234
+ "name": "D8",
235
+ "role": "spi",
236
+ "description": "D8 / SCK: GPIO19, SPI clock"
237
+ },
238
+ {
239
+ "key": "d7",
240
+ "name": "D7",
241
+ "role": "uart",
242
+ "description": "D7 / RX: GPIO17, UART receive"
243
+ }
244
+ ]
245
+ },
246
+ {
247
+ "key": "bat",
248
+ "kind": "castellated",
249
+ "cols": 2,
250
+ "orientation": "horizontal",
251
+ "xMm": 7.11,
252
+ "yMm": 16.03,
253
+ "pad": {
254
+ "shape": "rect",
255
+ "wMm": 1.1,
256
+ "hMm": 2.5,
257
+ "smd": true
258
+ },
259
+ "pins": [
260
+ {
261
+ "key": "bat+",
262
+ "name": "BAT+",
263
+ "label": "B+",
264
+ "role": "power",
265
+ "description": "Li-ion battery plus (VBAT), pad on the BACK; charged by the on-board SGM40567 charger"
266
+ },
267
+ {
268
+ "key": "bat-",
269
+ "name": "BAT-",
270
+ "label": "B-",
271
+ "role": "gnd",
272
+ "description": "Battery minus, pad on the BACK. Same net as GND"
273
+ }
274
+ ]
275
+ }
276
+ ],
277
+ "buses": [
278
+ {
279
+ "id": "GND",
280
+ "pins": ["gnd", "bat-"]
281
+ }
282
+ ]
283
+ }
284
+ ```
285
+
286
+ More examples (an ESC with wire leads, a relay module, a KiCad import):
287
+ [skills/fritzkit/reference/examples.md](skills/fritzkit/reference/examples.md).
288
+
289
+ ## CLI reference
290
+
291
+ ```
292
+ fritzkit [--workspace <dir>] [--json] <command>
293
+ ```
294
+
295
+ `<part>` is a slug in the workspace (default `~/fritzkit/parts`, or `FRITZKIT_WORKSPACE`) or
296
+ a path to a part folder (`validate` and `render` also accept a `.fzpz`). `--json` prints machine-readable output.
297
+
298
+ | command | what it does |
299
+ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
300
+ | `init` | create the workspace folder |
301
+ | `new <slug> [--title <t>]` | create `<workspace>/<slug>/part.json` (a placeholder spec) and `assets/` |
302
+ | `list` | list the parts in the workspace |
303
+ | `validate <part> [--checker]` | schema and structural checks; `--checker` also runs Fritzing's part checker via uv and prints a `fzp checker: ran (...)` or `skipped - ...` line; a `.fzpz` path checks the package only |
304
+ | `render <part> [--view <v>] [--out <dir>] [--png]` | write `<view>.svg` for one view or all four (breadboard, schematic, pcb, icon) into `<part>/build/`; `--png` ALSO writes `<view>.png` at 300 dpi |
305
+ | `export <part> [--out <file>] [--install] [--force]` | build the `.fzpz` (next to part.json by default); refuses on validator errors unless `--force`; `--install` also installs it |
306
+ | `install <part> [--out <file>] [--force]` | same as `export --install`; refuses while Fritzing is running |
307
+ | `prune <family-or-slug> [--keep <moduleId>] [--yes]` | list older installed versions of a family (dry run); `--yes` deletes them, keeping the newest |
308
+ | `doctor` | check Node, uv, the Fritzing install and its parts folder, sharp |
309
+ | `schema [--out <file>]` | print or write the part.json JSON Schema |
310
+ | `import-kicad <file> --slug <slug>` | create a part from a `.kicad_mod` footprint with exact pad geometry |
311
+ | `mcp` | run the MCP server on stdio |
312
+ | `ui [part] [--port <n>]` | local web editor for part.json with live previews |
313
+
314
+ Exit codes: 0 ok, 1 error, 2 the `ui` app is not built (run `npm run ui:build`), 3 Fritzing is running (install/prune).
315
+
316
+ ## MCP tools
317
+
318
+ Run with `npx -y fritzkit mcp` (stdio). Same core as the CLI.
319
+
320
+ | tool | purpose |
321
+ | ------------------------- | -------------------------------------------------------------- |
322
+ | `fritzkit_init_workspace` | create the workspace |
323
+ | `fritzkit_list_parts` | list parts |
324
+ | `fritzkit_new_part` | create a part folder with a placeholder spec |
325
+ | `fritzkit_get_part` | read a part's part.json |
326
+ | `fritzkit_set_part` | replace part.json with a whole spec |
327
+ | `fritzkit_patch_part` | apply JSON-patch operations to part.json |
328
+ | `fritzkit_add_pins` | append pins |
329
+ | `fritzkit_validate` | schema + structural checks (optionally the Fritzing checker) |
330
+ | `fritzkit_render` | render a view as SVG text or a PNG image the agent can look at |
331
+ | `fritzkit_export` | build the `.fzpz` |
332
+ | `fritzkit_install` | install into Fritzing's user parts folder (Fritzing closed) |
333
+ | `fritzkit_prune` | remove old installed versions of a family |
334
+ | `fritzkit_import_kicad` | create a part from a `.kicad_mod` |
335
+ | `fritzkit_schema` | the part.json JSON Schema |
336
+ | `fritzkit_doctor` | environment check |
337
+
338
+ ## Acceptance
339
+
340
+ The part pipeline was verified end to end on a real product: a fresh agent built the
341
+ [HW-391 2S 20A balance board](examples/hw391-2s-20a-bms/README.md) from its Amazon listing
342
+ using only the skill and the CLI, every pad landed within 0.75 mm of a hidden golden table, and
343
+ the local acceptance run (`npm run e2e`) rendered it in Fritzing and wired it on a
344
+ breadboard. Screenshots of the GUI run (the part in My Parts, wired, saved) are in
345
+ [docs/acceptance/](docs/acceptance/); the example's own notes and measured-vs-golden table are in
346
+ [examples/hw391-2s-20a-bms/README.md](examples/hw391-2s-20a-bms/README.md).
347
+
348
+ ## FAQ
349
+
350
+ **Why does every export get a new moduleId?** Fritzing refuses to import a part whose
351
+ moduleId it already has ("Part module ID must be unique"). The moduleId is
352
+ `fritzkit-<slug>-<hash8>`, where the hash covers part.json and the assets, so every change
353
+ imports cleanly. Use `fritzkit prune <family> --yes` to remove the old versions.
354
+
355
+ **How do I swap a placed part for a newer version?** All versions share a family (the slug)
356
+ and differ in the variant (`v<version> <hash8>`). Select the part in a sketch and pick the
357
+ newer variant in the Inspector. Connector ids come from the persistent `pinIds` map in
358
+ part.json, so pins that still exist keep their ids and wires stay attached.
359
+
360
+ **Why must Fritzing be closed for `install`?** Fritzing scans its user parts folder only at
361
+ startup, so a part copied in while it runs would not show up (and it may write the same
362
+ files). `install` and `prune` refuse while Fritzing is running and say why.
363
+
364
+ **Where are parts installed?** Into Fritzing's user parts folder, one folder per moduleId:
365
+ `%APPDATA%\Fritzing\Fritzing\local_parts\user\<moduleId>\` on Windows,
366
+ `~/Library/Application Support/Fritzing/Fritzing/local_parts/user/` on macOS,
367
+ `~/.local/share/Fritzing/Fritzing/local_parts/user/` on Linux. This is the same layout
368
+ Fritzing's own File > Open import writes; the part shows up in My Parts at the next start.
369
+
370
+ **Should I use the photo artwork style?** Usually not. The default vector style looks like
371
+ Fritzing's own parts, stays sharp, is small and survives Fritzing's Parts Editor. The photo
372
+ style (`artwork.style: "photo"`) embeds the listing photo with vector pads on top; Fritzing's
373
+ Parts Editor drops embedded rasters if you edit the part, it pixelates when zoomed, and
374
+ Gerber export strips images (the PCB view is always vector).
375
+
376
+ **Can it do the PCB view?** Yes: a real footprint from the pad table (through-hole pads on
377
+ both copper layers with their drill size, SMD and castellated pads on top copper, the
378
+ outline on silkscreen). Positions are only as good as the measurements in part.json, so
379
+ verify against a datasheet drawing or calipers before fabricating a board.
380
+
381
+ ## Development
382
+
383
+ ```
384
+ npm ci
385
+ npm test # vitest with coverage
386
+ npm run lint # eslint
387
+ npm run format:check # prettier
388
+ npm run check-ascii # every tracked file must be ASCII
389
+ npm run build # tsc to dist/
390
+ npm run fritzkit -- <args> # run the CLI from source
391
+ ```
392
+
393
+ `tools/fzp-checker/` vendors Fritzing's `fzp_checker` (from fritzing-parts 1.0.7) for CI:
394
+ `uv run --project tools/fzp-checker check-fzp <file.fzpz>`. `npm run e2e` is a local-only
395
+ acceptance harness that drives a real Fritzing install; it needs a desktop session with
396
+ Fritzing 1.0.7 installed and is not run in CI. `.mcp.json` points Claude Code at the MCP
397
+ server from source when you work in this repo. Keep its server name `fritzkit`: the plugin
398
+ manifest declares a server with the same name, which replaces the dev entry when the plugin
399
+ is installed, so installers run `npx -y fritzkit mcp` and never the source checkout.
400
+
401
+ ## Releasing
402
+
403
+ `release.yml` runs after every green CI run on `main` (plus a daily cron) and publishes a
404
+ patch release, the same setup as `pmaxhogan/tasker-mcp`. The version is computed from the
405
+ registry (latest published patch + 1, never below the `major.minor.0` floor in
406
+ `package.json`) and is not committed back. The job installs with `--ignore-scripts` and no
407
+ dependency cache, and npm attaches provenance.
408
+
409
+ Auth is npm trusted publishing (OIDC) only; no token is stored. Until the repository
410
+ variable `NPM_TRUSTED_PUBLISHING` is `true`, the job runs `npm publish --dry-run` and skips
411
+ the tag and the GitHub Release. One-time setup:
412
+
413
+ 1. The package must exist before a trusted publisher can be attached. Publish the first
414
+ version by hand: `npm login`, `npm ci`, `npm run build`, `npm publish --access public`
415
+ (npm asks for 2FA once).
416
+ 2. Attach the trusted publisher, allowing direct publishes (2FA once):
417
+
418
+ ```bash
419
+ npm trust github fritzkit --repo pmaxhogan/fritzkit --file release.yml --allow-publish
420
+ ```
421
+
422
+ 3. Arm the workflow: `gh variable set NPM_TRUSTED_PUBLISHING --body true -R pmaxhogan/fritzkit`.
423
+
424
+ From then on every green push to `main` publishes `fritzkit@0.1.x`, tags `v0.1.x` and
425
+ creates a GitHub Release with the tarball.
426
+
427
+ ## License
428
+
429
+ fritzkit itself is MIT (see [LICENSE](LICENSE)). Parts generated with fritzkit are
430
+ released under CC-BY-SA 3.0, the license of Fritzing's own parts library.
@@ -0,0 +1,4 @@
1
+ /** `fritzkit doctor`: what this machine has and lacks. */
2
+ import { type DoctorReport } from "../../core/validate/index.js";
3
+ import type { CommandContext, CommandResult } from "../context.js";
4
+ export declare function doctorCommand(ctx: CommandContext): Promise<CommandResult<DoctorReport>>;
@@ -0,0 +1,7 @@
1
+ /** `fritzkit doctor`: what this machine has and lacks. */
2
+ import { doctor, formatDoctor } from "../../core/validate/index.js";
3
+ export async function doctorCommand(ctx) {
4
+ const report = await doctor({ env: ctx.env, ...(ctx.doctorOptions ?? {}) });
5
+ return { exitCode: report.ok ? 0 : 1, text: formatDoctor(report), data: report };
6
+ }
7
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,OAAO,EAAE,MAAM,EAAE,YAAY,EAAqB,MAAM,8BAA8B,CAAC;AAGvF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAmB;IACrD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACnF,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { type InstallResult } from "../../core/export/install.js";
2
+ import { type Issue } from "../../core/validate/index.js";
3
+ import { type CommandContext, type CommandResult } from "../context.js";
4
+ export interface ExportOptions {
5
+ out?: string;
6
+ install?: boolean;
7
+ /** Export (and install) even when the structural validator reports errors. */
8
+ force?: boolean;
9
+ }
10
+ export interface ExportData {
11
+ moduleId: string;
12
+ fzpz: string;
13
+ issues: Issue[];
14
+ installed?: Pick<InstallResult, "installedDir" | "alreadyInstalled" | "files" | "warnings">;
15
+ }
16
+ export declare function exportCommand(part: string, opts: ExportOptions, ctx: CommandContext): Promise<CommandResult<ExportData>>;
17
+ export declare function installCommand(part: string, opts: Omit<ExportOptions, "install">, ctx: CommandContext): Promise<CommandResult<ExportData>>;
@@ -0,0 +1,81 @@
1
+ /** `fritzkit export <part> [--out] [--install] [--force]` and `fritzkit install <part>`. */
2
+ import { join, resolve } from "node:path";
3
+ import { writeFzpz } from "../../core/export/fzpz.js";
4
+ import { FritzingRunningError, installPart, } from "../../core/export/install.js";
5
+ import { formatIssues, validateCompiled, validateFzpzFile, } from "../../core/validate/index.js";
6
+ import { CliError, cwdOf, EXIT_FRITZING_RUNNING, fritzingPathsFor, fritzingRunningMessage, isRunningOf, resolveTarget, } from "../context.js";
7
+ import { compileDir, warningIssues } from "./validate.js";
8
+ async function install(source, fzpz, ctx) {
9
+ try {
10
+ return await installPart(source, {
11
+ paths: fritzingPathsFor(ctx.env),
12
+ isRunning: isRunningOf(ctx),
13
+ });
14
+ }
15
+ catch (e) {
16
+ if (e instanceof FritzingRunningError) {
17
+ throw new CliError(`${fritzingRunningMessage("install")}\nThe .fzpz is ready at ${fzpz}.`, EXIT_FRITZING_RUNNING);
18
+ }
19
+ throw e;
20
+ }
21
+ }
22
+ export async function exportCommand(part, opts, ctx) {
23
+ if (opts.out !== undefined && !/\.fzpz$/i.test(opts.out)) {
24
+ throw new CliError(`--out must name a .fzpz file (got "${opts.out}")`);
25
+ }
26
+ const target = await resolveTarget(part, ctx);
27
+ let moduleId;
28
+ let fzpz;
29
+ let issues;
30
+ let source;
31
+ if (target.kind === "fzpz") {
32
+ if (!opts.install) {
33
+ throw new CliError(`${target.path} is already a .fzpz; use \`fritzkit install\` to install it`);
34
+ }
35
+ issues = validateFzpzFile(target.path);
36
+ fzpz = target.path;
37
+ source = target.path;
38
+ moduleId = "";
39
+ }
40
+ else {
41
+ const compiled = await compileDir(target.dir, ctx);
42
+ moduleId = compiled.identity.moduleId;
43
+ issues = [...validateCompiled(compiled), ...warningIssues(compiled.warnings)];
44
+ fzpz = opts.out ? resolve(cwdOf(ctx), opts.out) : join(target.dir, `${moduleId}.fzpz`);
45
+ source = compiled;
46
+ }
47
+ const errors = issues.filter((i) => i.level === "error");
48
+ if (errors.length > 0 && !opts.force) {
49
+ return {
50
+ exitCode: 1,
51
+ text: `${formatIssues(issues)}\nnot exported: fix the errors above or pass --force`,
52
+ data: { moduleId, fzpz: "", issues },
53
+ };
54
+ }
55
+ const lines = [];
56
+ if (issues.length > 0)
57
+ lines.push(formatIssues(issues));
58
+ if (target.kind === "dir") {
59
+ await writeFzpz(source, fzpz);
60
+ lines.push(`wrote ${fzpz}`, `moduleId ${moduleId}`);
61
+ }
62
+ const data = { moduleId, fzpz, issues };
63
+ if (opts.install) {
64
+ const r = await install(source, fzpz, ctx);
65
+ data.moduleId = r.moduleId;
66
+ data.installed = {
67
+ installedDir: r.installedDir,
68
+ alreadyInstalled: r.alreadyInstalled,
69
+ files: r.files,
70
+ warnings: r.warnings,
71
+ };
72
+ if (target.kind === "fzpz")
73
+ lines.push(`moduleId ${r.moduleId}`);
74
+ lines.push(...r.warnings.map((w) => `warning: ${w}`), `${r.alreadyInstalled ? "re-installed" : "installed"} to ${r.installedDir}`, "start Fritzing: the part is in the My Parts bin (search for its title)");
75
+ }
76
+ return { exitCode: 0, text: lines.join("\n"), data };
77
+ }
78
+ export function installCommand(part, opts, ctx) {
79
+ return exportCommand(part, { ...opts, install: true }, ctx);
80
+ }
81
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../../../src/cli/commands/export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type CommandContext, type CommandResult } from "../context.js";
2
+ export declare function importKicadCommand(file: string, opts: {
3
+ slug: string;
4
+ title?: string;
5
+ }, ctx: CommandContext): Promise<CommandResult<{
6
+ slug: string;
7
+ dir: string;
8
+ partJson: string;
9
+ pins: number;
10
+ }>>;
@@ -0,0 +1,22 @@
1
+ /** `fritzkit import-kicad <file.kicad_mod> --slug <slug> [--title <t>]`. */
2
+ import { resolve } from "node:path";
3
+ import { importKicadFile } from "../../core/kicad/index.js";
4
+ import { partDir, SCHEMA_URL } from "../../core/workspace.js";
5
+ import { cwdOf, workspaceOf } from "../context.js";
6
+ import { createPartFolder } from "./parts.js";
7
+ export async function importKicadCommand(file, opts, ctx) {
8
+ const dir = partDir(workspaceOf(ctx), opts.slug);
9
+ const fragment = await importKicadFile(resolve(cwdOf(ctx), file), {
10
+ slug: opts.slug,
11
+ ...(opts.title ? { title: opts.title } : {}),
12
+ });
13
+ const raw = { $schema: SCHEMA_URL, ...fragment };
14
+ const { partJson } = await createPartFolder(dir, raw);
15
+ const pins = raw.pins?.length ?? 0;
16
+ return {
17
+ exitCode: 0,
18
+ text: `created ${partJson} (${pins} pad${pins === 1 ? "" : "s"})\nnext: name the pins, then run: fritzkit validate ${opts.slug}`,
19
+ data: { slug: opts.slug, dir, partJson, pins },
20
+ };
21
+ }
22
+ //# sourceMappingURL=kicad.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kicad.js","sourceRoot":"","sources":["../../../src/cli/commands/kicad.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,WAAW,EAA2C,MAAM,eAAe,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAY,EACZ,IAAsC,EACtC,GAAmB;IAEnB,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,EAAE;QAChE,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7C,CAAC,CAAC;IACH,MAAM,GAAG,GAAkB,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,QAAQ,EAAE,CAAC;IAChE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC;IACnC,OAAO;QACL,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,WAAW,QAAQ,KAAK,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,uDAAuD,IAAI,CAAC,IAAI,EAAE;QAChI,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC/C,CAAC;AACJ,CAAC"}