← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.6.13\ No newline at end of file+1.6.14\ No newline at end of file
CHANGELOG.md+14
@@ -3,6 +3,20 @@ All notable changes to the Adom Desktop Fusion 360 bridge, newest first. Canonical: https://wiki.adom.inc/adom/adom-desktop-fusion-bridge · GitHub backup: `adom-inc/adom-desktop-fusion-bridge` +## 1.6.14 - 2026-07-03++- **#63 ROOT-CAUSED + FIXED: Fusion MOVED its add-in directory; we now install to ALL of them.**+  2025+ Fusion scans `%APPDATA%\Autodesk\FusionAddins\` and SILENTLY ignores the legacy+  `Autodesk Fusion[ 360]\API\AddIns\` dirs (proven live on a fresh install: add-in in the legacy dir+  never loaded through a full launch; moved to FusionAddins it came up in 20s, runOnStartup honored,+  zero manual enabling). `install_addin.py` now installs/uninstalls to EVERY known location+  (TARGET_CANDIDATES, newest first), `fusion_detect` scans all of them, and the add-in-not-responding+  errors carry the FusionAddins explanation + a stable `errorCode`.+- **⛔ NEVER ask the user to restart Fusion / enable the add-in - baked into code + skills.** Every+  hint that said "restart Fusion 360" / "enable via UTILITIES > ADD-INS" now instructs the AI to DO+  IT ITSELF (`fusion_stop` + `fusion_start`); the USER skill (convention 6), fusion-onboarding, and+  CLAUDE.md all carry the rule. Doing the work for the user is the point of the bridge.+ ## 1.6.13 - 2026-07-03  - **`bridge.json` dependencies described honestly.** Dropped `fusion360: ">=2.0.0"` - Fusion 360 is
CLAUDE.md+9−3
@@ -36,9 +36,15 @@ all of this and filed its own bridge bugs as "adom-desktop" requests. Don't.)  ## How you DEPLOY when you change your code (VERIFIED by watching v1.5.1 ship, 2026-06-28) -Fusion loads the add-in from **`%APPDATA%\Roaming\Autodesk\Autodesk Fusion[ 360]\API\AddIns\AdomBridge\`**,-NOT from this repo or the cache. `install_addin.py` (`_sync_directory`) copies `addin/AdomBridge` ->-there. The exact, watched-it-happen release sequence (AFTER publishing to the wiki):+Fusion loads the add-in from a PER-USER add-in dir that Autodesk has MOVED across versions:+**2025+ Fusion scans `%APPDATA%\Roaming\Autodesk\FusionAddins\AdomBridge\`**; older builds used+`%APPDATA%\Roaming\Autodesk\Autodesk Fusion[ 360]\API\AddIns\AdomBridge\`. An add-in in the+wrong dir is SILENTLY ignored (Fusion runs fine, port 8774 never comes up - issue #63, root-caused+live). `install_addin.py` therefore installs to ALL of them (TARGET_CANDIDATES); NEVER assume a+single path, and if a future Fusion stops loading the add-in, FIRST suspect the dir moved again+(hunt for new dirs under %APPDATA%\Autodesk yourself). NOT from this repo or the cache.+⛔ NEVER ask the user to restart Fusion or enable the add-in - YOU restart (fusion_stop ++fusion_start); runOnStartup does the rest. The exact, watched-it-happen release sequence (AFTER publishing to the wiki):  1. **`bridge_install {"manifestUrl": ".../adom-bridge-fusion-manifest.json"}`** - streams the new zip    into the CACHE via `method: in_place_merge`. Updates the cache ONLY; it does NOT touch Fusion's
SKILL.md+1−1
@@ -37,7 +37,7 @@ Branch on the stable `errorCode`, never the prose: 3. **Core verbs live in the table below; `fusion_describe '{}'` is the full machine-readable catalog.** Read the table to act fast; hit `describe` for completeness — don't guess a verb. 4. **Background by default — but NOTIFY on every active step.** This bridge drives the AI's workspace, not the user's screen: dismiss dialogs in the BACKGROUND (`fusion_close_window`, never `fusion_send_key`/Escape, which steals focus). **The instant the user must DO something you can't (Autodesk sign-in, 2FA, a UAC/elevation prompt, accepting a license, a manual choice), fire an AD `notify_user` (a toast, and a progress toast for long installs) so they're pulled in — never silently wait on a screen they aren't looking at.** Only foreground Fusion to SHOW the user something. See [fusion-driving](skills/fusion-driving/SKILL.md) and [fusion-onboarding](skills/fusion-onboarding/SKILL.md). 5. **`ok:true` is not enough — verify.** A long op that "times out" at ~60s is usually STILL RUNNING server-side (the relay caps the request, not the work). Verify by artifacts (the saved file / a screenshot / `fusion_get_app_state`), don't blindly retry.-6. **Reuse the running Fusion, don't relaunch.** Fusion is a singleton — `fusion_readiness` before `fusion_start` (especially after a compaction); if it's already `ready`, just drive. Relaunching churns state.+6. **Reuse the running Fusion — and when a restart IS needed, YOU do it, never the user.** Fusion is a singleton: `fusion_readiness` before `fusion_start`; if `ready`, just drive. But a stale/missing add-in or a wedged session needs a restart, and that is YOUR job — `fusion_stop` then `fusion_start`. NEVER tell the user "restart Fusion" / "enable the add-in in UTILITIES" — doing it for them is the entire point of this bridge. (Add-in installs land in ALL Fusion add-in dirs — 2025+ Fusion scans `%APPDATA%\Autodesk\FusionAddins`, older builds use `...\Autodesk Fusion[ 360]\API\AddIns` — so a restart always picks it up.) 7. **Surface `_hint` verbatim + branch on `errorCode`.** Every verb returns an actionable `_hint`; relay it to the user unchanged and key your logic off the stable `errorCode`, not the prose. 8. **Never write files to a shared cloud ROOT.** Uploads default to an `Adom AI Workspace` folder; see [fusion-cloud-hygiene](skills/fusion-cloud-hygiene/SKILL.md). 
adom-bridge-fusion-manifest.json+5−5
@@ -1,13 +1,13 @@ {   "manifest_version": 1,   "name": "fusion360",-  "version": "1.6.13",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.13/adom-bridge-fusion-v1.6.13.zip",-  "sha256": "708daafd1d5d66adebf1a77082ea77f606e0546c450252e78fff21f4be001c6f",-  "size": 302647,+  "version": "1.6.14",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.14/adom-bridge-fusion-v1.6.14.zip",+  "sha256": "cba2e918b3e4d90743675d1de369e82f781ddb49518af64ddedbf5ba24e324ad",+  "size": 304852,   "verbPrefixes": [     "fusion_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-07-03T17:30:00Z"+  "released_at": "2026-07-03T19:00:00Z" }\ No newline at end of file
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "fusion360",   "displayName": "Autodesk Fusion 360",-  "version": "1.6.13",+  "version": "1.6.14",   "description": "Drive Autodesk Fusion 360 from the cloud: launch Fusion, electronics board layout, design rules, exports (STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers/BOM/CPL), fast APS server-indexed cloud search plus browse/recent/file-info/versions, cloud file download/upload and folder creation, and in-app parametric modeling (fusion_run_modeling_script). Never-charge: APS calls are capped to the free tier.",   "homepage": "https://wiki.adom.inc/adom/adom-desktop-fusion-bridge",   "author": "Adom Inc.",
fusion_detect.py+23−8
@@ -31,8 +31,12 @@ WEBDEPLOY_BASE = _WEBDEPLOY_BASES[0] if _WEBDEPLOY_BASES else (     Path(os.environ.get("LOCALAPPDATA", "")) / "Autodesk" / "webdeploy" / "production" ) -# Add-ins can live in either of these directories (varies by Fusion version)+# EVERY known per-user add-in dir, newest convention first. Fusion MOVED this+# across versions and silently ignores the others (issue #63: an add-in in the+# legacy API\AddIns dir never loads on 2025+ Fusion, which scans FusionAddins -+# proven live on a fresh install). Track ALL of them; never assume one path. ADDINS_DIR_CANDIDATES = [+    Path(os.environ.get("APPDATA", "")) / "Autodesk" / "FusionAddins",     Path(os.environ.get("APPDATA", "")) / "Autodesk" / "Autodesk Fusion" / "API" / "AddIns",     Path(os.environ.get("APPDATA", "")) / "Autodesk" / "Autodesk Fusion 360" / "API" / "AddIns", ]@@ -116,11 +120,17 @@ def _find_addins_dir() -> Path | None:   def _check_addin_installed(addins_dir: Path | None) -> bool:-    """Check if the AdomBridge add-in is installed."""-    if not addins_dir:-        return False-    addin_dir = addins_dir / "AdomBridge"-    return (addin_dir / "AdomBridge.py").exists()+    """Check if the AdomBridge add-in is installed in ANY known add-in dir.++    Scans every ADDINS_DIR_CANDIDATES entry (not just the preferred one) - the+    copy that counts is whichever dir THIS Fusion version scans, and we can't+    know that a priori, so presence anywhere counts as installed."""+    for candidate in ADDINS_DIR_CANDIDATES:+        if (candidate / "AdomBridge" / "AdomBridge.py").exists():+            return True+    if addins_dir and (Path(addins_dir) / "AdomBridge" / "AdomBridge.py").exists():+        return True+    return False   def _is_fusion_running() -> bool:@@ -252,8 +262,13 @@ def ensure_fusion_running(fusion_info: dict, wait_addin: bool = False) -> dict |             if not wait_for_addin(timeout=10):                 return {                     "success": False,-                    "error": "Fusion 360 is running but AdomBridge add-in not responding. "-                             "Enable it: UTILITIES > ADD-INS > AdomBridge > Run on Startup + Run",+                    "error": "Fusion 360 is running but AdomBridge add-in not responding.",+                    "errorCode": "fusion_addin_not_responding",+                    "_hint": "Fix it YOURSELF - never ask the user: (1) run install_addin() so the "+                             "add-in lands in EVERY Fusion add-in dir (Fusion moved it to "+                             "%APPDATA%\\Autodesk\\FusionAddins on 2025+ builds; an add-in in the "+                             "legacy API\\AddIns dir is silently ignored - issue #63), then "+                             "(2) restart Fusion via fusion_stop + fusion_start so it rescans.",                 }         return None  # Already running (and add-in ready if requested) 
handlers/open_design.py+1−1
@@ -41,7 +41,7 @@ def _import_via_addin(file_path: str, timeout: int = 30) -> dict:         return {             "success": False,             "error": f"AdomBridge add-in not responding (port {ADDIN_PORT}): {e}. "-                     "Ensure the add-in is running in Fusion (UTILITIES > ADD-INS > AdomBridge).",+                     "The add-in is not responding - fix it YOURSELF (never ask the user): fusion_stop + fusion_start reloads it (installed to all add-in dirs incl. FusionAddins).",         }     except Exception as e:         return {"success": False, "error": f"Import request failed: {e}"}
install_addin.py+53−40
@@ -1,9 +1,20 @@ #!/usr/bin/env python3-"""Install the AdomBridge add-in into Fusion 360's AddIns directory.+"""Install the AdomBridge add-in into EVERY Fusion add-in location. -Copies the addin/AdomBridge/ folder to:-  %APPDATA%/Autodesk/Autodesk Fusion/API/AddIns/AdomBridge/-  (or Autodesk Fusion 360 variant)+Fusion has MOVED its per-user add-in directory across versions, and an add-in+in the wrong one is silently ignored (Fusion loads fine, the add-in never runs,+port 8774 stays down - issue #63, root-caused live on a fresh 2026 install):++  NEW (2025+):  %APPDATA%/Autodesk/FusionAddins/AdomBridge/          <- current Fusion scans THIS+  legacy:       %APPDATA%/Autodesk/Autodesk Fusion/API/AddIns/       <- older builds+  legacy:       %APPDATA%/Autodesk/Autodesk Fusion 360/API/AddIns/   <- oldest naming++Verified live (ADOMBASELINE, Fusion 2026.x): the add-in sat in the legacy+API/AddIns dir through a full launch and NEVER loaded; moved to FusionAddins it+came up in 20s with zero manual enabling (runOnStartup honored). So we install+to ALL locations - each Fusion version loads from the one it scans and ignores+the others (a duplicate instance would fail its 8774 bind and no-op). NEVER+assume a single path; scan all of TARGET_CANDIDATES.  Usage:     python install_addin.py@@ -17,8 +28,11 @@ from pathlib import Path # Source: the add-in bundled with this plugin SOURCE_DIR = Path(__file__).resolve().parent / "addin" / "AdomBridge" -# Possible target directories+# EVERY known Fusion add-in directory convention, newest first. Keep this list+# complete - see the module docstring for why (a single-path assumption caused+# the #63 silent-failure). FusionAddins is created even on a fresh profile. TARGET_CANDIDATES = [+    Path.home() / "AppData" / "Roaming" / "Autodesk" / "FusionAddins",     Path.home() / "AppData" / "Roaming" / "Autodesk" / "Autodesk Fusion" / "API" / "AddIns",     Path.home() / "AppData" / "Roaming" / "Autodesk" / "Autodesk Fusion 360" / "API" / "AddIns", ]@@ -74,58 +88,57 @@ def find_addins_dir() -> Path | None:   def install():-    """Install the AdomBridge add-in.+    """Install the AdomBridge add-in into EVERY known Fusion add-in location.++    ALL of TARGET_CANDIDATES get the add-in (see module docstring - Fusion moved+    the scan dir across versions and ignores the others, so single-path installs+    silently fail on the "wrong" version). Smart update per target: copies+    individual changed files rather than deleting the folder, preserving Fusion's+    "Run on Startup" metadata. -    Uses a smart update strategy: copies individual files from source to target-    rather than deleting the entire target folder. This preserves Fusion 360's-    "Run on Startup" setting which is stored in the AddIns directory metadata.+    NOTE for the AI running this: after install, YOU restart Fusion yourself+    (fusion_stop then fusion_start) so it rescans - NEVER ask the user to+    restart Fusion; doing things for the user is the entire point of the bridge.     """     if not SOURCE_DIR.exists():         print(f"ERROR: Source add-in not found at {SOURCE_DIR}")         sys.exit(1) -    addins_dir = find_addins_dir()-    if not addins_dir:-        print("ERROR: Could not determine Fusion 360 AddIns directory.")-        sys.exit(1)--    target = addins_dir / "AdomBridge"-     print(f"Source:  {SOURCE_DIR}")-    print(f"Target:  {target}")--    addins_dir.mkdir(parents=True, exist_ok=True)--    if target.exists():-        # Smart update: sync files from source to target without deleting the folder-        _sync_directory(SOURCE_DIR, target)-        print(f"Updated AdomBridge add-in at {target}")-    else:-        # Fresh install-        shutil.copytree(SOURCE_DIR, target)-        print(f"Installed AdomBridge add-in to {target}")-        print()-        # The manifest sets "runOnStartup": true, so Fusion auto-runs the add-in-        # on its next launch — no manual enabling needed. Just (re)start Fusion and-        # the bridge's HTTP server comes up automatically; verify with-        # fusion_addin_status. (Older Fusion builds may require a one-time enable-        # via UTILITIES > ADD-INS > AdomBridge > Run on Startup if it doesn't load.)-        print("Restart Fusion 360 — the add-in auto-runs on startup (runOnStartup:true).")+    installed_to = []+    for addins_dir in TARGET_CANDIDATES:+        target = addins_dir / "AdomBridge"+        try:+            addins_dir.mkdir(parents=True, exist_ok=True)+            if target.exists():+                _sync_directory(SOURCE_DIR, target)+                print(f"Updated: {target}")+            else:+                shutil.copytree(SOURCE_DIR, target)+                print(f"Installed: {target}")+            installed_to.append(str(target))+        except OSError as e:+            print(f"  skip {target}: {e}")      print()-    print("The add-in will start an HTTP server on port 8774.")+    print(f"Add-in present in {len(installed_to)} location(s); Fusion loads it from the one "+          "its version scans (runOnStartup:true) and ignores the rest.")+    print("Restart Fusion via the BRIDGE (fusion_stop then fusion_start) so it rescans - "+          "never ask the user to do it. The add-in serves http://127.0.0.1:8774.")+    return installed_to   def uninstall():-    """Uninstall the AdomBridge add-in."""+    """Uninstall the AdomBridge add-in from EVERY known location."""+    removed = 0     for candidate in TARGET_CANDIDATES:         target = candidate / "AdomBridge"         if target.exists():             shutil.rmtree(target)             print(f"Removed: {target}")-            return--    print("AdomBridge add-in is not installed.")+            removed += 1+    if not removed:+        print("AdomBridge add-in is not installed.")   def main():
page.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-desktop-fusion-bridge",-  "version": "1.6.14",+  "version": "1.6.15",   "type": "app",   "description": "Drive Autodesk Fusion 360 from the cloud via Adom Desktop: launch Fusion, electronics board layout, design rules, exports (STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers/BOM/CPL), fast APS server-indexed cloud search with browse/recent/versions, cloud download/upload and folder creation, and in-app parametric modeling. Never-charge: APS calls are capped to the free tier.",   "dependencies": {},
server.py+7−5
@@ -1178,9 +1178,11 @@ def _diagnose_addin_timeout(command: str, original_result: dict) -> dict:     # Health probe failed — add-in HTTP server is down     return {         "success": False,-        "error": "AdomBridge add-in not responding. It may have crashed. "-                 "Restart Fusion 360 or re-enable the add-in: "-                 "UTILITIES > ADD-INS > AdomBridge > Run",+        "error": "AdomBridge add-in not responding (it may have crashed).",+        "errorCode": "fusion_addin_not_responding",+        "_hint": "Fix it YOURSELF - never ask the user: restart Fusion via fusion_stop + "+                 "fusion_start (the bridge installs the add-in to ALL Fusion add-in dirs incl. "+                 "%APPDATA%\\Autodesk\\FusionAddins, and runOnStartup reloads it).",     }  @@ -1999,7 +2001,7 @@ def dispatch_command(command: str, args: dict) -> dict:                 "success": False,                 "error": "Fusion 360 is running but AdomBridge add-in not responding.",                 "errorCode": "fusion_addin_not_responding",-                "_hint": "The add-in may be disabled or crashed. Try fusion_start which will restart Fusion cleanly. If that fails, the user must manually enable it via UTILITIES > ADD-INS > AdomBridge > Run on Startup + Run.",+                "_hint": "The add-in isn't responding. Fix it YOURSELF - never ask the user: the bridge auto-installs the add-in to ALL Fusion add-in dirs (2025+ Fusion scans %APPDATA%/Autodesk/FusionAddins; the legacy API/AddIns dirs are silently ignored - issue #63), so restart Fusion via fusion_stop + fusion_start and it loads (runOnStartup). Verify with fusion_addin_status.",             }      # ── Busy gate: reject add-in commands immediately if a long command is running ──@@ -2177,7 +2179,7 @@ def _build_status() -> dict:     elif not addin_ok:         led, summary = "yellow", "Add-in not connected"         tooltip = ("Fusion 360 is running but the AdomBridge add-in isn't responding yet "-                   "(still loading, or needs enabling via UTILITIES > ADD-INS).")+                   "(still loading; if it persists the AI restarts Fusion via fusion_stop/start).")     else:         led, summary = "green", "Fusion ready"         tooltip = "Fusion 360 running, AdomBridge add-in connected. All fusion_* verbs available."
skills/fusion-onboarding/SKILL.md+8−3
@@ -67,9 +67,14 @@ viewing and a lot of work. Don't auto-enter their password; do drive the OTP/for  ## Step 4 — Complete the circuit: auto-install the AdomBridge add-in This is what lets the bridge inject commands into Fusion after launch.-- **The bridge does this for you:** on first dispatch, `server.py` auto-runs `install_addin` if-  the add-in isn't present (copies `addin/AdomBridge/` → Fusion's `API/AddIns/`). You can also-  force it: run the cache's `install_addin.py` (`python <bridges-cache>/fusion360/install_addin.py`).+- **The bridge does this for you:** on start, `server.py` auto-runs `install_addin`, which lands the+  add-in in **EVERY known Fusion add-in dir** — `%APPDATA%\Autodesk\FusionAddins\` (what 2025++  Fusion actually scans) AND the legacy `Autodesk Fusion[ 360]\API\AddIns\` dirs. ⚠ Fusion MOVED+  this directory across versions and silently ignores the others (an add-in only in the legacy dir+  never loads on current Fusion — issue #63, proven live on a fresh install). Never assume one path.+- **⛔ NEVER ask the user to restart Fusion or enable the add-in.** After installing, YOU restart it:+  `fusion_stop` then `fusion_start` — the add-in auto-loads (`runOnStartup:true`, verified live:+  port 8774 up 20s after restart, zero manual enabling). Asking the user to do it defeats the bridge. - **It auto-loads every launch:** `AdomBridge.manifest` has **`"runOnStartup": true`**, so Fusion   runs the add-in (and its localhost:8774 HTTP server) automatically on startup — **no manual   "enable" needed**. (Verified: after a Fusion restart the add-in comes back `addinReady:true`