← Commit history

0.9.275: kicad_adom_library_heal (wiki #71 follow-up, John's call: merge): reconcile a split Adom library by merging every symbol from the orphaned bridge-default file into the registered file KiCad reads, one quote-aware add behind the balance gate with a .bak; dryRun previews; repoint mode kept as the other answer. Never runs on its own. Idempotent, refuses an unbalanced source, never edits or deletes the orphan.

John Lauer ·b8c6256df9 ·1mo ago ·parent 68e6c70
3 files changed +65−2
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.274\ No newline at end of file+0.9.275\ No newline at end of file
adom_library.py+63
@@ -422,3 +422,66 @@ def add_symbol(kicad_info: dict, symbol_text: str) -> dict:         "added": added_names,         "lib_path": str(lib_path),     }+++def heal_split(kicad_info: dict, mode: str = "merge", dry_run: bool = False) -> dict:+    """Reconcile a split Adom library (wiki #71). John's call, 2026-09-03: MERGE.++    merge:   copy every top-level symbol that exists in the orphaned (bridge+             default) file but not in the registered file INTO the registered+             file, one quote-aware add_symbol at a time, each behind the+             refuse-to-write balance gate with a .bak. The registered file stays+             the one KiCad reads; nothing is deleted; the orphan is left as-is.+    repoint: rewrite the sym-lib-table row's uri to the orphan. Not chosen; kept+             so the other answer is one argument away and not a code change.++    Never runs on its own: the bridge cannot know which of two libraries is the+    user's real one. This exists because a human said which."""+    rep = library_split_report(kicad_info)+    if not rep.get("split"):+        return {"success": True, "healed": False, "reason": "no split", **rep}+    registered = Path(rep["registeredPath"])+    orphan = Path(rep["bridgeDefaultPath"])+    if mode == "repoint":+        paths = _get_paths(kicad_info)+        table_path = paths[0]+        table = LibTable.parse_file(str(table_path))+        row = next((e for e in table.entries if e.name == ADOM_LIB_NAME), None)+        if row is None:+            return {"success": False, "error": "no Adom row to repoint"}+        if not dry_run:+            row.uri = str(orphan).replace("\\", "/")+            table.write_file(str(table_path))+        return {"success": True, "healed": not dry_run, "mode": "repoint", "dryRun": dry_run,+                "rowNowPointsAt": str(orphan).replace("\\", "/"),+                "_hint": "KiCad reads the repointed file after its next library load (restart)."}+    if mode != "merge":+        return {"success": False, "error": f"unknown mode {mode!r}", "allowed": ["merge", "repoint"]}+    try:+        orphan_text = orphan.read_text(encoding="utf-8")+        _parse_library_nodes(orphan_text)       # balance gate on the SOURCE too+    except Exception as e:  # pylint: disable=broad-except+        return {"success": False, "error": f"orphan library does not parse cleanly, refusing to merge from it: {e}"}+    have = {n.lower() for n in list_symbols(registered)}+    to_add = [(name, text) for name, text in _extract_top_level_symbols(orphan_text)+              if name.lower() not in have]+    skipped = [name for name, _ in _extract_top_level_symbols(orphan_text) if name.lower() in have]+    if dry_run:+        return {"success": True, "healed": False, "mode": "merge", "dryRun": True,+                "wouldAdd": [n for n, _ in to_add], "alreadyPresent": skipped,+                "registeredPath": str(registered), "orphanPath": str(orphan)}+    added, failed = [], []+    for name, text in to_add:+        # add_symbol takes a WHOLE .kicad_sym file (it re-parses it with the+        # quote-aware walker), so wrap the single top-level block in a minimal+        # library. Row-first _get_paths means it lands in the registered file.+        wrapped = "(kicad_symbol_lib (version 20231120) (generator \"adom-heal\")\n" + text + "\n)\n"+        r = add_symbol(kicad_info, wrapped)+        (added if r.get("success") else failed).append(name if r.get("success") else {"symbol": name, "error": r.get("error")})+    after = library_split_report(kicad_info)+    return {"success": not failed, "healed": True, "mode": "merge", "dryRun": False,+            "added": added, "alreadyPresent": skipped, "failed": failed,+            "registeredSymbolsAfter": after.get("registeredSymbols"),+            "_hint": ("Merged into the file KiCad reads. A running KiCad sees them after its next "+                      "library load (kicad_rescan_libraries force:true, or restart). The orphan file "+                      "was not modified or deleted.")}
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.274",+  "version": "0.9.275",   "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.",   "homepage": "https://wiki.adom.inc/adom/adom-bridge",   "author": "Adom Inc.",