← Commit history
README.md+15−14
@@ -9,7 +9,7 @@  ![The adom-chip-fetcher stack](chip-fetcher-stack.png) -Download manufacturer-grade chip CAD bundles (STEP + KiCAD symbol + KiCAD footprint + datasheet + design-help PDFs) into a structured library by driving Chrome via [pup](https://github.com/adom-inc/adom-desktop).+Download manufacturer-grade chip CAD bundles (STEP + KiCad symbol + KiCad footprint + datasheet + design-help PDFs) into a structured library by driving Chrome via [pup](https://wiki.adom.inc/adom/pup-bridge) on your desktop.  The defining rule: **fetch from the manufacturer's own website first**, only fall back to Mouser, DigiKey, or Component Search Engine when the manufacturer doesn't self-host CAD, and only with explicit user approval. @@ -20,10 +20,18 @@ chip-fetcher fetch TPS62840            # opens the TI page → UL deep-link → chip-fetcher pull '<windows-path>'     # pulls a downloaded zip from the user's desktop chip-fetcher import path/to/zip --mpn  # absorbs a downloaded file into library/<MPN>/ chip-fetcher list                       # shows what's in library/ vs missing per slot-chip-fetcher login <source>             # opens a vendor login page in the chip-fetcher pup profile-chip-fetcher install                    # deploys SKILL.md to ~/.claude/skills/chip-fetcher/+chip-fetcher login <source>             # signs in to a vendor from pup's OS-keychain vault (chip-fetcher stores no passwords)+chip-fetcher doctor [--fix]             # first-run check: skill, 13 sister tools, desktop bridge, pup, library+chip-fetcher app                        # the dashboard in a maximized pup window+chip-fetcher install                    # deploys SKILL.md to ~/.claude/skills/adom-chip-fetcher/ ``` +## In the Hydrogen dock++Chip Fetcher is dock-ready: click the card in the Hydrogen Dock Bar and it installs itself (once), starts the dashboard, and opens it in the dock's tab. The dashboard leads with a **Say it** strip, the phrases you say to your AI to drive it ("fetch STM32F103C8T6 with chip fetcher"), then a component LED row (dashboard, AI skill, sister tools, desktop, library) so a first run shows exactly what is missing and how to fix it. The settings cog (upper right) opens the shared Adom settings modal: component Install / Update rows with live consoles, the taskbar-progress choice (an indeterminate Windows taskbar bar on the dashboard window for the whole lookup), dashboard toggles, and your vendor logins, which live in pup's OS-keychain vault and never in chip-fetcher.++Your AI drives the page the same way you do: `chip-fetcher ui toast "downloading STEP"`, `ui select <MPN>`, `ui settings open`, `ui state`, `ui console`.+ After running, each chip lands in:  ```@@ -90,23 +98,16 @@ Agents: the same content lives in the skill as `playbooks/jst.md` +  Reproducible recipe (scenes, narration, TTS config): **[demo/jst-email-gated-cad-demo-script.md](demo/jst-email-gated-cad-demo-script.md)**. -See [SKILL.md](https://wiki.adom.inc/adom/chip-fetcher) for the full skill documentation, vendor URL patterns, and the failure-mode notes captured from real Adom-user feedback.+See [SKILL.md](https://wiki.adom.inc/adom/adom-chip-fetcher/files/SKILL.md) for the full skill documentation, vendor URL patterns, and the failure-mode notes captured from real Adom-user feedback.  ## Install  ```bash-curl -fsSL https://wiki.adom.inc/static/apps/chip-fetcher/chip-fetcher \-  -o /usr/local/bin/chip-fetcher && chmod +x /usr/local/bin/chip-fetcher && chip-fetcher install+adom-wiki pkg install adom/adom-chip-fetcher   # binary (adom-chip-fetcher + chip-fetcher alias), UI assets, both skills, and every sister tool+adom-chip-fetcher doctor                       # one line per component; --fix installs anything missing ``` -## Companion specs--Two adom-desktop fix-it specs live in [scripts/](scripts/):--- `adom-desktop-pullfile-speed-prompt.md`, streaming binary WS frames + HTTP fallback so 50+ MB files (NXP reference manuals) don't time out-- `adom-desktop-credential-vault-prompt.md`, DPAPI/Keychain-encrypted credential vault hooked into `puppeteer.page.authenticate()` so basic-auth popups never appear--Paste either into Claude Code on the desktop to implement.+Or from the dock: Dock settings, Add app, `adom/adom-chip-fetcher`, click the card.  ## License 
SKILL.md+91−58
@@ -1,20 +1,23 @@ --- name: chip-fetcher-description: Download manufacturer-grade chip CAD bundles (STEP + KiCad symbol + KiCad footprint + Altium IntLib + Fusion .lbr + datasheet) into a structured library by driving Chrome via pup on the user's desktop. Manufacturer-first sourcing ladder, then SnapMagic → Mouser → DigiKey → Arrow → CSE/UL → LCSC (LCSC is last resort, avoid). Handles NRND/EOL lifecycle and multi-package variants. Use when the user wants to fetch a chip's 3D model, build a chip test library, source CAD files for a part, grab STEP files, populate a chip-pads test set, or "fetch <MPN>". Trigger words — chip-fetcher, fetch chip, chip cad, chip step, chip footprint, fetch step file, snapeda fetch, snapmagic fetch, altium intlib, fusion 360 lbr, ultralibrarian fetch, mouser fetch, digikey fetch, nrnd alternate, ti cad download, st cad download, nxp cad download, manufacturer chip step, mpn fetch.+description: Download manufacturer-grade chip CAD bundles (STEP + KiCad symbol + KiCad footprint + Altium IntLib + Fusion .lbr + datasheet) into a structured library by driving Chrome via pup on the user's desktop. Manufacturer-first sourcing ladder, then SnapMagic → Mouser → DigiKey → Arrow → CSE/UL → LCSC (LCSC is last resort, avoid). Handles NRND/EOL lifecycle and multi-package variants. Use when the user wants to fetch a chip's 3D model, build a chip test library, source CAD files for a part, grab STEP files, populate a chip-pads test set, or "fetch <MPN>". Trigger words, chip-fetcher, fetch chip, chip cad, chip step, chip footprint, fetch step file, snapeda fetch, snapmagic fetch, altium intlib, fusion 360 lbr, ultralibrarian fetch, mouser fetch, digikey fetch, nrnd alternate, ti cad download, st cad download, nxp cad download, manufacturer chip step, mpn fetch. ---  # chip-fetcher -Rust CLI at `/home/adom/project/chip-fetcher/`. Pulls manufacturer-grade chip bundles (STEP + `.kicad_sym` + `.kicad_mod` + Altium `.IntLib` + Fusion `.lbr` + datasheet PDFs) from the manufacturer's site first, then SnapEDA → Mouser → DigiKey → Arrow → CSE/UL → **LCSC (last resort)**, using a persistent pup Chrome profile. Yanks the downloaded zip from the user's desktop via `adom-desktop pull_file`. Run `chip-fetcher sources` any time to print the canonical priority order — single source of truth for the ladder.+Rust CLI at `/home/adom/project/chip-fetcher/`. Pulls manufacturer-grade chip bundles (STEP + `.kicad_sym` + `.kicad_mod` + Altium `.IntLib` + Fusion `.lbr` + datasheet PDFs) from the manufacturer's site first, then SnapEDA → Mouser → DigiKey → Arrow → CSE/UL → **LCSC (last resort)**, using a persistent pup Chrome profile. Yanks the downloaded zip from the user's desktop via `adom-bridge --ai-thread "<thread>" pull_file`. Run `chip-fetcher sources` any time to print the canonical priority order, single source of truth for the ladder.  ## Bones  ``` chip-fetcher/ ├── src/                       # Cargo CLI-│   ├── main.rs · cli.rs · pup.rs · library.rs · import.rs · creds.rs · inventory.rs · serve.rs-│   ├── sources/ti.rs          # one source plugin so far-│   └── ui/index.html          # viewer+│   ├── main.rs · cli.rs · serve.rs · dock.rs · settings.rs · jobs.rs · taskbar.rs+│   ├── bridge.rs · pup.rs · vault.rs    # adom-bridge transport, pup verbs, pup's credential vault+│   ├── library.rs · import.rs · inventory.rs · hints.rs · sourcing.rs · validate.rs+│   ├── sources/               # ti.rs · jst.rs · wiki.rs+│   └── ui/index.html (+ ui/assets/)   # the dashboard (the dock target)+├── dockbar.json               # Hydrogen Dock Bar manifest: say phrases, health, launch steps ├── library/<MPN>/             # final home for each part's bundle │   ├── <MPN>.step             # rich manufacturer STEP │   ├── <MPN>.kicad_mod        # ground-truth pad data@@ -24,7 +27,7 @@ chip-fetcher/ │   ├── <MPN>.pdf              # datasheet │   ├── stock.json             # Mouser stock + lifecycle + variants │   └── info.json              # provenance tags + chipsmith handoff fields (footprint_name, kicad_3d_name, package_kind, pin_count, body_dims_mm, has_ep)-├── playbooks/                 # deep reference — Read on demand (see "Playbook index")+├── playbooks/                 # deep reference, Read on demand (see "Playbook index") └── incoming/                  # staging for files pulled from desktop ``` @@ -32,19 +35,48 @@ chip-fetcher/  ```bash chip-fetcher fetch <MPN>            # opens vendor product page → drives the source's flow-chip-fetcher pull <win-path> --mpn  # pulls a Windows file via adom-desktop, imports+chip-fetcher pull <win-path> --mpn  # pulls a Windows file via adom-bridge, imports chip-fetcher import <path> [--mpn]  # absorbs a downloaded file/zip into library/<MPN>/-chip-fetcher login <source>         # opens a source's login page in the chip-fetcher pup profile-chip-fetcher login all              # opens every source as separate tabs-chip-fetcher login list             # catalog of known sources+chip-fetcher login <source>         # sign in from pup's OS-keychain vault (pup_login, reason-gated submit)+chip-fetcher login <source> --user <u> --pass <p>   # first time: pup fills, submits and STORES it (chip-fetcher keeps nothing)+chip-fetcher login all | list       # every source, paced / the catalog+chip-fetcher logins [--import]      # what pup's vault + your real browser hold; --import pulls vendor logins into the vault (one consent + one UAC) chip-fetcher list                   # what's in library/ vs missing per slot chip-fetcher status <MPN>           # show step/mod/sym/pdf for one MPN-chip-fetcher app                    # open the viewer in a Hydrogen webview tab-chip-fetcher serve                  # run the viewer HTTP server+chip-fetcher app                    # start the dashboard + open it in the user's MAXIMIZED pup window (reused if open)+chip-fetcher serve [--print-url]    # the dashboard server; --print-url prints http://127.0.0.1:8786/ and self-backgrounds (Dock Bar contract) chip-fetcher install                # (re-)deploy SKILL.md + playbooks/ ``` -### Programmatic-rule subcommands — these enforce the skill rules in code+### First run, the dock, and driving the dashboard++```bash+chip-fetcher doctor [--fix]         # one line per component (dashboard, AI skill, 13 sister tools, desktop bridge, pup, library) with a Hint per problem; --fix installs what is missing+chip-fetcher health                 # {ok, app, version} from the running dashboard+chip-fetcher say                    # the phrases users say to their AI (dockbar.json); the dashboard shows them first+chip-fetcher try "<phrase>"         # stage a phrase into this app's own AI thread (copy fallback)+chip-fetcher ui toast "<msg>" [--kind ok|err|info]   # narrate over the dashboard (the AI's live channel to the user)+chip-fetcher ui click <id> | select <MPN> | view grid|validation|sources | settings open|close | zoom <MPN> | unzoom | reload+chip-fetcher ui state | console     # what the page is showing / its console ring buffer (never DevTools)+chip-fetcher settings get | set <key> <value> | reset   # taskbar_progress (indeterminate|off), auto_zoom, auto_select_active, show_stock_badges, activity_stale_seconds+```++A newbie who clicks the Chip Fetcher card in the Hydrogen dock gets: install+(skip_if the CLI exists), `serve --print-url`, the main dashboard in the dock's+tab, a "Say it" strip at the top with the phrases above, a component LED row,+and a Welcome card when the library is empty. The dock polls `/api/status`+every 15 s for the card LED. Run `chip-fetcher doctor` before the first fetch+on any new container; fix yellow before scraping.++### The ai-thread name (required on every desktop call)++Every Adom Bridge / pup call chip-fetcher makes carries an ai-thread name so the+user can tell your window from their other twenty. Pass it as the GLOBAL flag:+`chip-fetcher --ai-thread "<the name the user calls this conversation>" <verb>`+(precedence: the flag, then `ADOM_AI_THREAD`). Use a short human-recognizable+name, reused on every call; never "agent" or a UUID.++### Programmatic-rule subcommands, these enforce the skill rules in code  The AI's job is to call these instead of remembering rules. Each one returns either a clean status or a `HINT:` line telling you what to do@@ -64,13 +96,13 @@ chip-fetcher mfr-probe <MPN> [--no-open]   # Detects manufacturer from MPN prefix (table in src/sourcing.rs),   # opens their canonical product/search URL in the chip-fetcher-scrape   # pup session, records the attempt on the chain. Run BEFORE any-  # third-party source — the mfr-first rule is enforced here.+  # third-party source, the mfr-first rule is enforced here.  chip-fetcher next-source <MPN>   # Reads info.json.fetched_via_chain and prints the next ladder step   # that hasn't been tried, with a HINT for what command to run. LCSC   # is only suggested when every prior step has a recorded failure-  # outcome — code refuses to skip ahead.+  # outcome, code refuses to skip ahead.  chip-fetcher sources   # Print the canonical priority order. Single source of truth for@@ -99,36 +131,37 @@ chip-fetcher fetch <MPN> --project <name>  # fetch and auto-add to a project  Side-effect docs that matter: -- `chip-fetcher import <file>` does **PDF magic-byte validation** (rejects HTML masquerading as PDF — the classic "naked curl returned a 174 KB CDN denial page that looks like 200 OK" footgun) AND a STEP `ISO-10303-21` header check AND KiCad sym/mod sanity-check. Imports that fail magic-byte refuse the file with a clear `ERROR:`.+- `chip-fetcher import <file>` does **PDF magic-byte validation** (rejects HTML masquerading as PDF, the classic "naked curl returned a 174 KB CDN denial page that looks like 200 OK" footgun) AND a STEP `ISO-10303-21` header check AND KiCad sym/mod sanity-check. Imports that fail magic-byte refuse the file with a clear `ERROR:`. - `chip-fetcher import` also writes a per-MPN `<MPN>-fetch-log.json` audit trail (timestamp, source path, target name, size, magic-byte status) every time a file lands. Survives across sessions; the AI doesn't have to remember to write provenance. -## Critical rules — these always apply+## Critical rules, these always apply -0. **Dashboard is the user's home base.** Before any scraping, open the chip-fetcher viewer as the user's preferred surface — **pup window OR Hydrogen webview, per their preference** (check `user_profile.md` for `chip_fetcher_dashboard_surface`, ask if unknown, save the answer). Laptop-screen users tend to prefer pup; big-monitor users tend to prefer webview. Whichever surface, never navigate it mid-batch. **Scraping prefers the user's REAL signed-in browser via the native-browser bridge** (`nbrowser_*`, the `adom-browser-extension`) — open ONE isolated window (`sessionId: "chip-fetcher"`) and manage every vendor site as a TAB inside it, so you never touch the user's other Chrome/Edge windows. The native browser carries their real vendor logins + captcha human-trust and bypasses bot-walls (**DigiKey hard-blocks pup**). **Fall back to pup `sessionId: "chip-fetcher"`** (cold `profile: "chip-fetcher"`) only when the bridge is absent — and tell the user why native is better + point them to https://wiki.adom.inc/adom/adom-browser-extension. The binary auto-detects and announces the surface; full strategy + per-site playbooks in the **`chip-sourcing`** skill. Scraping stays separate from the dashboard. Heartbeat every stage transition via `/api/activity` so the dashboard reflects live state. **Read [`playbooks/dashboard-flow.md`](playbooks/dashboard-flow.md) before any batch fetch** — it has the dual-surface dashboard pattern, the heartbeat protocol, and the canonical stage labels.+0. **Dashboard is the user's home base.** Before any scraping, `chip-fetcher app`: it starts the server and opens the dashboard in a MAXIMIZED pup window (`sessionId: "adom-chip-fetcher-dashboard"`), reusing it when open. Pup is the surface (John, 2026-08-23: test and show the dashboard in pup, not a webview); the Hydrogen dock also opens the same dashboard in its own tab. Never navigate the dashboard window mid-batch. **Scraping prefers the user's REAL signed-in browser via the native-browser bridge** (`nbrowser_*`, the `adom-browser-extension`), open ONE isolated window (`sessionId: "adom-chip-fetcher"`) and manage every vendor site as a TAB inside it, so you never touch the user's other Chrome/Edge windows. The native browser carries their real vendor logins + captcha human-trust and bypasses bot-walls (**DigiKey hard-blocks pup**). **Fall back to pup `sessionId: "adom-chip-fetcher"`** (cold `profile: "adom-chip-fetcher"`) only when the bridge is absent, and tell the user why native is better + point them to https://wiki.adom.inc/adom/adom-browser-extension. The binary auto-detects and announces the surface; full strategy + per-site playbooks in the **`chip-sourcing`** skill. Scraping stays separate from the dashboard. Heartbeat every stage transition via `/api/activity` so the dashboard reflects live state. **Read [`playbooks/dashboard-flow.md`](playbooks/dashboard-flow.md) before any batch fetch**, it has the dual-surface dashboard pattern, the heartbeat protocol, and the canonical stage labels. 1. **Manufacturer-first.** Stay on `<vendor>.com` and exhaust their CAD/Documentation/Resources/Tools tabs before bouncing to a third-party. Don't go to `3dfindit.com`, `snapmagic.com`, `mouser.componentsearchengine.com`, or any aggregator unless the manufacturer's own page links there explicitly. Read [`playbooks/sourcing-ladder.md`](playbooks/sourcing-ladder.md) for the full ladder + escape rules.-2. **Always ask "do you have a login here?"** before showing a vendor signup form. Most EEs already have SnapEDA/CSE/UL/Mouser/DigiKey/NXP/TI/ST/Microchip accounts. Read [`playbooks/credentials-and-logins.md`](playbooks/credentials-and-logins.md) for the catalog + the credential vault.+2. **Always ask "do you have a login here?"** before showing a vendor signup form. Most EEs already have SnapEDA/CSE/UL/Mouser/DigiKey/NXP/TI/ST/Microchip accounts. Before asking, run `chip-fetcher logins`: if the vendor is in the user's real browser, `chip-fetcher logins --import` seeds pup's vault and `chip-fetcher login <source>` signs in with no typing. **Credentials live ONLY in pup's OS-keychain vault** (`credential_list` / `credential_set` / `pup_login`, the `pup-credentials` skill); chip-fetcher keeps no password file and never builds `user:pass@` URLs (pup applies vault entries to HTTP Basic Auth hosts like `*.componentsearchengine.com` automatically). Read [`playbooks/credentials-and-logins.md`](playbooks/credentials-and-logins.md) for the catalog. 3. **NRND/EOL/Last-Time-Buy is a HARD STOP for chip-fetcher's own picks.** If the user explicitly named the MPN, fetch + warn. If chip-fetcher chose it, pick an active replacement instead. Always store `lifecycle` + `lifecycle_warning` in `stock.json`.-4. **Track Mouser stock + pricing as first-class data.** Every chip in `library/<MPN>/` should have `stock.json` with current Mouser snapshot. Multi-package variants go under `variants[]` — don't collapse to the highest-stock one.-4a. **Persist chipsmith handoff fields in `info.json`.** Every fetch MUST write these fields when the source provides them (which is almost always — the upstream library page literally names them):-   - `footprint_name` — KiCad library path, e.g. `Package_QFN/QFN-32-1EP_5x5mm_P0.5mm_EP3.7x3.7mm`. Comes from SnapEDA / SnapMagic / Ultra Librarian / Component Search Engine / Mouser CAD / DigiKey CAD download pages — every one of those exposes the canonical KiCad footprint name.-   - `kicad_3d_name` — KiCad packages3d STEP path, e.g. `Package_QFN.3dshapes/QFN-32-1EP_5x5mm_P0.5mm_EP3.7x3.7mm`. Same upstream sources.-   - `package_kind` — canonical short tag (`SOIC` / `QFN` / `QFP` / `LGA` / `BGA` / `DIP` / `SOT` / `TO` / `USB-C` / etc.) derived from `package_family` via regex normalize.-   - `pin_count` — manufacturer datasheet pin-table OR distributor parametric "Number of Terminations" field.-   - `body_dims_mm` — from the datasheet mechanical drawing page; nominal `[X, Y, Z]` mm.-   - `has_ep` — boolean from the datasheet ("Exposed Pad" / "EPAD" / "Thermal Pad" mention in pin table).-   - `expected_pitch_mm` — datasheet mechanical drawing.-   These are what `adom-chipsmith` (the chip detection + footprint validator + sign-off app) consumes to drive its pin-detection + measurement-rescale + KiCad stock-library cross-check. The KiCad library is footprint-keyed and 3D-name-keyed — NOT MPN-keyed — so there is no MPN→KiCad-library lookup we can do downstream. **chip-fetcher is the ONLY layer that has access to the upstream page where the footprint name was named.** If chip-fetcher doesn't capture them, no downstream tool can recover them.-5. **chip-fetcher does ALL the clicking.** Never ask the user to click Download / Allow / dismiss a banner. If a click looks impossible, find a way (CDP permission grant, trusted-click via `browser_input_dispatch`, OS-level workaround). Reaching for "user, please click X" is a skill bug. Read [`playbooks/handoff-and-watching.md`](playbooks/handoff-and-watching.md) for the escape valve.+4. **Track Mouser stock + pricing as first-class data.** Every chip in `library/<MPN>/` should have `stock.json` with current Mouser snapshot. Multi-package variants go under `variants[]`, don't collapse to the highest-stock one.+4a. **Persist chipsmith handoff fields in `info.json`.** Every fetch MUST write these fields when the source provides them (which is almost always, the upstream library page literally names them):+   - `footprint_name`, KiCad library path, e.g. `Package_QFN/QFN-32-1EP_5x5mm_P0.5mm_EP3.7x3.7mm`. Comes from SnapEDA / SnapMagic / Ultra Librarian / Component Search Engine / Mouser CAD / DigiKey CAD download pages, every one of those exposes the canonical KiCad footprint name.+   - `kicad_3d_name`, KiCad packages3d STEP path, e.g. `Package_QFN.3dshapes/QFN-32-1EP_5x5mm_P0.5mm_EP3.7x3.7mm`. Same upstream sources.+   - `package_kind`, canonical short tag (`SOIC` / `QFN` / `QFP` / `LGA` / `BGA` / `DIP` / `SOT` / `TO` / `USB-C` / etc.) derived from `package_family` via regex normalize.+   - `pin_count`, manufacturer datasheet pin-table OR distributor parametric "Number of Terminations" field.+   - `body_dims_mm`, from the datasheet mechanical drawing page; nominal `[X, Y, Z]` mm.+   - `has_ep`, boolean from the datasheet ("Exposed Pad" / "EPAD" / "Thermal Pad" mention in pin table).+   - `expected_pitch_mm`, datasheet mechanical drawing.+   These are what `adom-chipsmith` (the chip detection + footprint validator + sign-off app) consumes to drive its pin-detection + measurement-rescale + KiCad stock-library cross-check. The KiCad library is footprint-keyed and 3D-name-keyed, NOT MPN-keyed, so there is no MPN→KiCad-library lookup we can do downstream. **chip-fetcher is the ONLY layer that has access to the upstream page where the footprint name was named.** If chip-fetcher doesn't capture them, no downstream tool can recover them.+5. **chip-fetcher does ALL the clicking.** Never ask the user to click Download / Allow / dismiss a banner. If a click looks impossible, find a way (CDP permission grant, trusted-click via `pup_input_dispatch`, OS-level workaround). Reaching for "user, please click X" is a skill bug. Read [`playbooks/handoff-and-watching.md`](playbooks/handoff-and-watching.md) for the escape valve. 6. **When you hand off a click, ALWAYS watch for the resulting state change.** Never end with "tell me when you're done." Arm a Monitor that polls for the post-click signal (token populates, button enables, file lands) and auto-continues. The user only ever does one click.-7. **EDA-agnostic terminology in user-facing surfaces.** Files on disk are `.kicad_sym` / `.kicad_mod`, but UI labels are "SYMBOL" / "FOOTPRINT" — never "MOD." Adom supports KiCad, Fusion 360, Altium, OrCAD, EAGLE.-8. **Use `profile:"chip-fetcher"` and only that.** Never rotate profile names — that nukes vendor logins. Bust caches with `?v=$(date +%s)`, never with a new profile.-9. **Browse as a real user.** Pre-warm with a referrer chain, add 2–5s random delays, scroll the page, move the mouse. Burst-clicking 5 product pages in 30s gets you flagged. Naked `curl` to a bot-walled CDN returns a 174 KB HTML denial page that looks like 200 OK — always validate `head -c 4 file | grep '%PDF'`.-10. **Auto-accept every cookie banner, every page, every time.** First thing chip-fetcher does on any new page. Walks shadow DOM. Accept All — never Decline / Manage / Save settings.+7. **EDA-agnostic terminology in user-facing surfaces.** Files on disk are `.kicad_sym` / `.kicad_mod`, but UI labels are "SYMBOL" / "FOOTPRINT", never "MOD." Adom supports KiCad, Fusion 360, Altium, OrCAD, EAGLE.+8. **Surf as `adom-you`, never isolated.** pup (2.0.100+) puts every window on its shared durable `adom-you` profile: that is where the vendor session cookies AND the OS-keychain vault live, and every login any pup window makes improves it. Never pass `isolated:true` / `freshProfile:true` for chip-fetcher work (a fresh profile starts with no cookies and its logins build nothing). Bust caches with `?v=$(date +%s)`, never with a new profile.+9. **Browse as a real user.** Pre-warm with a referrer chain, add 2 to 5s random delays, scroll the page, move the mouse. Burst-clicking 5 product pages in 30s gets you flagged. Naked `curl` to a bot-walled CDN returns a 174 KB HTML denial page that looks like 200 OK, always validate `head -c 4 file | grep '%PDF'`.+10. **Auto-accept every cookie banner, every page, every time.** First thing chip-fetcher does on any new page. Walks shadow DOM. Accept All, never Decline / Manage / Save settings. 11. **Don't pile up pup tabs.** Reuse the existing tab. Close stale tabs after a download. Only open a new tab when you genuinely need two pages side-by-side.-11b. **NEVER create a pup session with a profile other than `chip-fetcher`.** The user's vendor login cookies (SnapEDA, UL, CSE, TI, ST, Mouser, DigiKey) live in the `chip-fetcher` Chrome profile. Creating `cf-pdf-grab`, `chip-fetcher-dash`, or ANY other profile name starts a FRESH Chrome with NO cookies — silently losing every credential the user entered in previous sessions. This is a critical bug that has already burned the user. ALL chip-fetcher pup work MUST use `profile: "chip-fetcher"`. The `sessionId` can vary (`chip-fetcher`, `chip-fetcher-scrape`) but the `profile` MUST be `chip-fetcher`.-11a. **Always hard-reload the pup dashboard after deploying new code.** After any `cargo build + cp` or version bump that changes `src/ui/index.html`, the pup window is still serving the OLD cached HTML. Run `browser_eval("window.location.reload(true)")` on the dashboard session BEFORE taking screenshots or showing the user. Without this, every "show me" screenshot is stale UI and the user sees the old version — exactly the bug that wastes their time verifying the wrong thing.+11b. **One session family, one profile.** Session ids are `adom-chip-fetcher` (scrape work window), `adom-chip-fetcher-scrape`, and `adom-chip-fetcher-dashboard`; pup maps all of them onto the shared `adom-you` profile (the `profile` arg is accepted for compatibility and ignored). Creating `cf-pdf-grab`-style isolated sessions has already burned the user once: no cookies, no vault, captchas everywhere.+11c. **Pup window etiquette (John's rules).** Open the dashboard window ONCE and maximize it once (`chip-fetcher app` does both); reuse it for the whole task. Never re-maximize, resize, or foreground a pup window the user already has; to signal an update, flash its taskbar button (`pup_alert_window`) and let them bring it up. Do not switch its active tab out from under them. The taskbar progress bar (`settings taskbar_progress`) is the other signal: every `heartbeat` keeps an indeterminate bar on the dashboard window until a terminal stage ("imported", "done", "stuck", "exhausted") clears it.+11a. **Always hard-reload the pup dashboard after deploying new code.** After any `cargo build + cp` or version bump that changes `src/ui/index.html`, the pup window is still serving the OLD cached HTML. Run `pup_eval("window.location.reload(true)")` on the dashboard session BEFORE taking screenshots or showing the user. Without this, every "show me" screenshot is stale UI and the user sees the old version, exactly the bug that wastes their time verifying the wrong thing. 12. **When stuck on a vendor's flow, show the user the product page and let them teach you.** Foreground pup, screenshot, ask one specific question, wait, implement their answer, then capture the pattern back into the right playbook so the next agent benefits.-13. **NEVER claim "done" while stub / empty cards exist on the dashboard.** Run `chip-fetcher gate` before reporting completion — it returns exit 1 if ANY card lacks PDF + STEP + `.kicad_sym` + `.kicad_mod`, and prints `HINT:` lines per missing artifact. Datasheet-only cards = stubs, not done. Every `chip-fetcher heartbeat` ALSO surfaces `WARN: card <MPN> is INCOMPLETE — missing: ...` plus a library-wide `WARN: N/Total cards still incomplete` count — read those WARNs before saying "X of Y done." If the ladder is genuinely exhausted (no STEP exists anywhere on Mfr / SnapMagic / Mouser / DigiKey / Arrow / CSE), explicitly record that in `info.json.fetched_via_chain` and surface "ladder exhausted" to the user — don't silently abandon the card and call it shipped.+13. **NEVER claim "done" while stub / empty cards exist on the dashboard.** Run `chip-fetcher gate` before reporting completion, it returns exit 1 if ANY card lacks PDF + STEP + `.kicad_sym` + `.kicad_mod`, and prints `HINT:` lines per missing artifact. Datasheet-only cards = stubs, not done. Every `chip-fetcher heartbeat` ALSO surfaces `WARN: card <MPN> is INCOMPLETE, missing: ...` plus a library-wide `WARN: N/Total cards still incomplete` count, read those WARNs before saying "X of Y done." If the ladder is genuinely exhausted (no STEP exists anywhere on Mfr / SnapMagic / Mouser / DigiKey / Arrow / CSE), explicitly record that in `info.json.fetched_via_chain` and surface "ladder exhausted" to the user, don't silently abandon the card and call it shipped.  ## STEP + Footprint = matched pair (CRITICAL) @@ -138,32 +171,32 @@ When chip-fetcher sources a STEP + footprint bundle (e.g. from SnapMagic), both  **Enforcement:** When `chip-fetcher gate` or `chip-fetcher status` reports a component as complete, the .kicad_mod and .step in `library/<MPN>/` are guaranteed to be from the same source. Any downstream tool (board-building-pipeline, kicad-3d-models) that substitutes a different footprint breaks this guarantee. -## Playbook index — Read on demand+## Playbook index, Read on demand  Each playbook is a focused deep-dive. Read the relevant one when the situation matches. They live at `~/.claude/skills/chip-fetcher/playbooks/<name>.md` after `chip-fetcher install`.  | Read this | When | |-----------|------|-| [`playbooks/dashboard-flow.md`](playbooks/dashboard-flow.md) | **Read before any batch fetch** — chip-fetcher's UI is the user's home base. Two-pup-session pattern (dashboard + scraping), activity-heartbeat protocol, canonical stage labels (`probing <vendor>.com` / `downloading <format>` / `imported` / `stuck — <reason>`), opening the dashboard window, when NOT to navigate it, antipatterns. |+| [`playbooks/dashboard-flow.md`](playbooks/dashboard-flow.md) | **Read before any batch fetch**, chip-fetcher's UI is the user's home base. Two-pup-session pattern (dashboard + scraping), activity-heartbeat protocol, canonical stage labels (`probing <vendor>.com` / `downloading <format>` / `imported` / `stuck, <reason>`), opening the dashboard window, when NOT to navigate it, antipatterns. | | [`playbooks/sourcing-ladder.md`](playbooks/sourcing-ladder.md) | Starting a new MPN. Covers the full ladder (manufacturer → SnapEDA → Mouser → DigiKey → UL/CSE), the manufacturer-first ironclad rule, the NRND/EOL hard stop, and the multi-variant `stock.json` schema. | | [`playbooks/manufacturer-direct.md`](playbooks/manufacturer-direct.md) | You've identified the manufacturer. Covers self-hosting matrix, vendor URL + UL deep-link patterns, Microchip iframe gotcha, NXP product-page detail (BXL + 3D icon + EXTENDED PROFILE), Lattice 30-PDF Documentation tab, manufacturer reference-layout zips ≠ chip CAD, and `info.json` provenance tags. |-| [`playbooks/ultra-librarian.md`](playbooks/ultra-librarian.md) | Any UL flow — embedded captcha or UL Pro logged-in. Three flows: (1) `vendor.ultralibrarian.com/<slug>/embedded` → captcha gauntlet, driven by `cf-ul-fast.sh`; (2) `app.ultralibrarian.com/details/...?uid=...` (logged in) → captcha-free tick-and-click via `cf-ul-pro-batch.sh` (the modern fast-path: sign in once via `chip-fetcher login ultralib`, then no captcha for 30 days); (3) anonymous UL Pro detail pages → fall back to embedded captcha. Also covers the **two-click BUTTON-then-A.export-trigger pattern** that's bug-prone, the canonical-four EDA-format pick, watcher race-safety, and per-package coverage caveats. |+| [`playbooks/ultra-librarian.md`](playbooks/ultra-librarian.md) | Any UL flow, embedded captcha or UL Pro logged-in. Three flows: (1) `vendor.ultralibrarian.com/<slug>/embedded` → captcha gauntlet, driven by `cf-ul-fast.sh`; (2) `app.ultralibrarian.com/details/...?uid=...` (logged in) → captcha-free tick-and-click via `cf-ul-pro-batch.sh` (the modern fast-path: sign in once via `chip-fetcher login ultralib`, then no captcha for 30 days); (3) anonymous UL Pro detail pages → fall back to embedded captcha. Also covers the **two-click BUTTON-then-A.export-trigger pattern** that's bug-prone, the canonical-four EDA-format pick, watcher race-safety, and per-package coverage caveats. | | [`playbooks/snapmagic.md`](playbooks/snapmagic.md) | Manufacturer doesn't host CAD (Nordic, niche connectors, smaller MCUs) and SnapEDA covers it. Covers login (`form.submit()` not button-click), part-page URL, the per-account `preferred_2d_format` AJAX flow, the "Heads Up" multi-section warning, the KiCad V3/V4/V6 sub-picker, full 3-format cycle (KiCad → Altium → Fusion), filename map, and InstaBuild AI-generated parts. |-| [`playbooks/mouser-cse.md`](playbooks/mouser-cse.md) | Manufacturer doesn't host CAD and SnapEDA didn't cover it — Mouser has the ECAD Model + datasheet. Covers Mouser CSE Library Loader, the **HARD RULE that CSE downloads use HTTP Basic Auth** (form-login session cookie is useless for downloads — embed creds inline as `https://user:pass@host/...`), `cf-cse-batch.sh` for batch fetching with inline creds, slide-to-verify escalation, CSE single-login covering all subdomains, and Chrome's "wants to Download multiple files" silent killer. |+| [`playbooks/mouser-cse.md`](playbooks/mouser-cse.md) | Manufacturer doesn't host CAD and SnapEDA didn't cover it, Mouser has the ECAD Model + datasheet. Covers Mouser CSE Library Loader, the **HARD RULE that CSE downloads use HTTP Basic Auth** (form-login session cookie is useless for downloads, embed creds inline as `https://user:pass@host/...`), `cf-cse-batch.sh` for batch fetching with inline creds, slide-to-verify escalation, CSE single-login covering all subdomains, and Chrome's "wants to Download multiple files" silent killer. | | [`playbooks/digikey-models.md`](playbooks/digikey-models.md) | DigiKey's dedicated **Footprints and Models** page at `/en/models/<product-id>` (NOT the product detail page). Three accordion sections: Manufacturer Provided (STEP from mfr CDN), Ultra Librarian (format picker → KiCad v6+), SnapMagic. Two-layer provenance: discovery_source=digikey, content_origin=manufacturer/ul/snapmagic. The mfr STEP is often ONLY surfaced here. Chrome download permission issue + inline-fetch workaround documented. |-| [`playbooks/credentials-and-logins.md`](playbooks/credentials-and-logins.md) | A vendor flow lands on a sign-in / sign-up page. Covers the **two auth schemes** (Credentials button = Basic-Auth URL rewriting for CSE; pup-window sign-in = form-login + cookie for everything else), the **mandatory pup-window-only sign-in walkthrough** (open login in pup, pre-tick "Remember Me" via JS, offer to fill the form for the user — vendor CAD accounts aren't bank logins so just take the password), the "always ask first" rule, known-EE-login catalog, `chip-fetcher login <source>` aliases, save-to-vault pattern, and `user_profile.md` for vendor-form auto-fill. |-| [`playbooks/adom-desktop-api.md`](playbooks/adom-desktop-api.md) | You need to drive pup beyond `browser_navigate` + `browser_eval`. Covers `browser_input_dispatch` (trusted clicks), popup-tab auto-tracking + the "clicks open in new tab" rule, smart-pick + the Vue-Toastification false-positive workaround, `browser_fetch_url` (3 modes) + popup-PDF recipe, `pull_file` streaming, stale-relay gotchas, the cookie-banner auto-accept snippet, and the SACRED `chip-fetcher` profile name. |+| [`playbooks/credentials-and-logins.md`](playbooks/credentials-and-logins.md) | A vendor flow lands on a sign-in page. ONE vault now: pup's OS keychain. Covers `chip-fetcher login <source>` (pup_login, reason-gated submit), importing the user's real browser logins (`chip-fetcher logins --import`), why CSE Basic Auth just works (pup applies vault entries via page.authenticate on every navigate), the "always ask first" rule, the known-EE-login catalog, and `user_profile` for registration-form auto-fill. |+| [`playbooks/adom-bridge-api.md`](playbooks/adom-bridge-api.md) | You need to drive pup beyond `pup_navigate` + `pup_eval`. Covers `pup_input_dispatch` (trusted clicks), popup-tab auto-tracking + the "clicks open in new tab" rule, smart-pick + the Vue-Toastification false-positive workaround, `pup_fetch_url` (3 modes) + popup-PDF recipe, `pull_file` streaming, stale-relay gotchas, the cookie-banner auto-accept snippet, and the SACRED `adom-chip-fetcher` profile name. | | [`playbooks/handoff-and-watching.md`](playbooks/handoff-and-watching.md) | You've decided you can't automate something (captcha, OAuth, OS prompt). Covers when handoff is allowed (escape valve, not routine), the ALWAYS-watch rule, signals to watch for per handoff trigger, the show-product-page-and-teach-me pattern when stuck on a vendor, and EDA-agnostic UI terminology. | | [`playbooks/jst.md`](playbooks/jst.md) | **Any JST connector** (PHR-/XHP-/ZHR-/SHR-/EHR-/VHR-/PAP-/GHR-/NHR- housings, B\*B-/S\*B-/BM\*B-/SM\*B- headers). The canonical **email-gated CAD** manufacturer: jst.com (USA) Cloudflare-walls pup → use the native browser; CAD lives on jst-mfg.com keyed by **series id**; the download link returns a **License Agreement form** (accept cookies first) whose submit **emails** the `.zip` (since Apr 2025); doc=1 IGES / doc=2 STEP / doc=3 3D-PDF / doc=4 2D-PDF (per-series, read the header); **no account login**; datasheet = direct series catalog PDF; JST gives 3D+drawings only, sym/mod still come from the ladder. | | [`playbooks/jst-email-retrieval.md`](playbooks/jst-email-retrieval.md) | A manufacturer **emails** the CAD (JST, and others coming). The account-specific retrieval layer: **Gmail users** → `adom-google` (`gmail attachments` / `gmail read`, plus-alias + filter for batches); **non-Gmail users** → build a small IMAP/Graph attachment-fetcher, publish it on the wiki, and drop a chip-fetcher breadcrumb so it auto-surfaces. Keeps chip-fetcher provider-agnostic. |-| [`playbooks/wiki-bundle-reuse.md`](playbooks/wiki-bundle-reuse.md) | **Ladder step 0 — reuse a published Adom Wiki bundle before re-fetching.** The wiki-as-part-source pattern (`Source::AdomWiki`, `src/sources/wiki.rs`): the read-only API (slug / discover / search), and every hard-won gotcha — FTS throws on hyphens, discover/search return LOOSE matches so you MUST verify `component.mpn` **or** slug, malformed `component.mpn`, pull canonical files only (skip `-v2`/`-compare` variants), files live in git not releases, metadata-only stubs → fall through. |+| [`playbooks/wiki-bundle-reuse.md`](playbooks/wiki-bundle-reuse.md) | **Ladder step 0, reuse a published Adom Wiki bundle before re-fetching.** The wiki-as-part-source pattern (`Source::AdomWiki`, `src/sources/wiki.rs`): the read-only API (slug / discover / search), and every hard-won gotcha, FTS throws on hyphens, discover/search return LOOSE matches so you MUST verify `component.mpn` **or** slug, malformed `component.mpn`, pull canonical files only (skip `-v2`/`-compare` variants), files live in git not releases, metadata-only stubs → fall through. | | [`playbooks/symbol-axis-regen.md`](playbooks/symbol-axis-regen.md) | chip-fetcher's side of the default symbol's 3D outline: **axis-aware regen** on up-axis switch (`POST /api/orientation` → light `chip-thumbnailer rerender --kind outline` → recomposite via adom-symbol) + the **non-blocking SYM-tile spinner** (data-driven on `sym_rev`, `pointer-events:none`). The symbol-DEFAULT behavior (compositing the white outline + lasered name, why natively white, the outline contract) lives in **adom-symbol** → `adom-symbol/docs/default-symbol-3d-outline.md`. |  > **adom-lbr BOM flags:** when chip-fetcher builds a `.lbr` it auto-passes `--mfr/--mpn/--mouser/…` from `stock.json` (`src/bom.rs`, wired in `deliver.rs` + `serve.rs`). The BOM *contract* itself (flags → `MF`/`MPN`/`MOUSER` device attributes, the `⛔ INCOMPLETE BOM` reprimand) lives in the **adom-lbr** skill, not here. -## Adom standard passive size — proactively recommend 0402+## Adom standard passive size, proactively recommend 0402 -**When a user asks "what size LED / resistor / cap / inductor?", default to 0402 and surface the InstaPCB-respin rationale.** Don't recommend 0603 by reflex — that's the traditional EE default rooted in hand-rework friendliness, and Adom's 4-hour respin loop replaces hand-rework entirely. The full rule + paraphrased justification lives in [`electrical-engineering/SKILL.md`](../electrical-engineering/SKILL.md) §4a; read it once per design conversation.+**When a user asks "what size LED / resistor / cap / inductor?", default to 0402 and surface the InstaPCB-respin rationale.** Don't recommend 0603 by reflex, that's the traditional EE default rooted in hand-rework friendliness, and Adom's 4-hour respin loop replaces hand-rework entirely. The full rule + paraphrased justification lives in [`electrical-engineering/SKILL.md`](../electrical-engineering/SKILL.md) §4a; read it once per design conversation.  Quick reference (see `solder-jetting/SKILL.md` for the dispense math): @@ -176,18 +209,18 @@ For chip packages, pad-pitch ≥ 0.4 mm is comfortable; below that surfaces as a  ## Sister tools to lean on -- **`adom-parts-search`** (CLI + Hydrogen webview) — unified search across Mouser + DigiKey + JLCPCB with Mouser-preferred routing. Use this **first** when probing whether a chip is even stocked / sourceable, before chasing CAD.-- **`adom-mouser`** / **`adom-digikey`** / **`adom-jlcpcb`** — individual distributor backends. The DigiKey API in particular gives `product_url` / `datasheet_url` on every result — use those to route into chip-fetcher's per-vendor scrapers.-- **`step2glb`** — convert any STEP into a color-preserving GLB for downstream tools (adom-chiplinter, adom-tsci).+- **`adom-parts-search`** (CLI + Hydrogen webview), unified search across Mouser + DigiKey + JLCPCB with Mouser-preferred routing. Use this **first** when probing whether a chip is even stocked / sourceable, before chasing CAD.+- **`adom-mouser`** / **`adom-digikey`** / **`adom-jlcpcb`**, individual distributor backends. The DigiKey API in particular gives `product_url` / `datasheet_url` on every result, use those to route into chip-fetcher's per-vendor scrapers.+- **`step2glb`**, convert any STEP into a color-preserving GLB for downstream tools (adom-chiplinter, adom-tsci). -When a vendor product page is Cloudflare-blocked from `pup` (DigiKey often is), the distributor APIs above are the side-channel — they return the same `product_url` Cloudflare would gate, plus structured part metadata.+When a vendor product page is Cloudflare-blocked from `pup` (DigiKey often is), the distributor APIs above are the side-channel, they return the same `product_url` Cloudflare would gate, plus structured part metadata.  ## Critical gotchas (one-liners) -1. **UL download tokens are SINGLE-USE.** Re-fetching via `browser_eval` + `fetch()` will not work. Pull from user's `~/Downloads`.+1. **UL download tokens are SINGLE-USE.** Re-fetching via `pup_eval` + `fetch()` will not work. Pull from user's `~/Downloads`. 2. **UL ships PACKAGE-named files inside the zip**, not MPN-named. Always pass `--mpn <MPN>` to import. 3. **PowerShell quoting via cmd.exe is terrible.** Use `-EncodedCommand` (UTF-16LE base64).-4. **`adom-desktop` shell timeout is 120s default.** Long-running shell commands get killed. Chunk into shorter calls.+4. **`adom-bridge` shell timeout is 120s default.** Long-running shell commands get killed. Chunk into shorter calls. 5. **`chrome-error://chromewebdata/` after a Download click is NORMAL.** Chrome handles the binary as a download, can't render it as HTML. Check the Downloads folder; the download usually succeeded. 6. **CSE filename pattern is `LIB_<MPN>.zip`** (vs UL's `ul_<MPN>.zip`). Watcher regex: `^(ul_|LIB_).*\.zip$`. 7. **CSE STEP files are mfr-authored, FreeCAD-exported with named PRODUCTs.** Trust them as mfr-grade.@@ -199,13 +232,13 @@ When a vendor product page is Cloudflare-blocked from `pup` (DigiKey often is),  The user prefers tool dashboards in pup windows, not Hydrogen webview tabs. chip-fetcher uses two concurrent pup windows: -- **Dashboard** (`sessionId: "chip-fetcher-dashboard"`, `profile: "chip-fetcher"`) — the live card grid / status view. Open via `chip-fetcher serve` + pup navigate. NEVER navigate this window to a vendor page mid-batch.-- **Scrape** (`sessionId: "chip-fetcher-scrape"`, `profile: "chip-fetcher"`) — where vendor product pages, logins, captchas, and downloads happen. All `browser_navigate` / `browser_eval` / `browser_input_dispatch` scraping calls target this session.+- **Dashboard** (`sessionId: "adom-chip-fetcher-dashboard"`, `profile: "adom-chip-fetcher"`), the live card grid / status view. Open via `chip-fetcher serve` + pup navigate. NEVER navigate this window to a vendor page mid-batch.+- **Scrape** (`sessionId: "adom-chip-fetcher-scrape"`, `profile: "adom-chip-fetcher"`), where vendor product pages, logins, captchas, and downloads happen. All `pup_navigate` / `pup_eval` / `pup_input_dispatch` scraping calls target this session. -Both windows share the same `profile: "chip-fetcher"` so cookies and vendor logins persist. Only the `sessionId` differs. This isolation means the user can watch the dashboard update in real time (via `/api/activity` heartbeats) while the scraping window does its work on vendor sites.+Both windows share the same `profile: "adom-chip-fetcher"` so cookies and vendor logins persist. Only the `sessionId` differs. This isolation means the user can watch the dashboard update in real time (via `/api/activity` heartbeats) while the scraping window does its work on vendor sites.  ## See also -- `/home/adom/project/chip-fetcher/` — the source repo-- `/home/adom/project/adom-chiplinter/` — adjacent project that consumes chip-fetcher output (pad/leg/tab/pin detector)-- `chip-pads` (planned) — STEP-based pad detector that uses chip-fetcher's library as test cases+- `/home/adom/project/chip-fetcher/`, the source repo+- `/home/adom/project/adom-chiplinter/`, adjacent project that consumes chip-fetcher output (pad/leg/tab/pin detector)+- `chip-pads` (planned), STEP-based pad detector that uses chip-fetcher's library as test cases
dockbar.json+31−9
@@ -1,24 +1,46 @@ {-  "dock_manifest": "1.1.0",+  "dock_manifest": "1.2.0",+  "say": [+    "fetch STM32F103C8T6 with chip fetcher",+    "show me the chip fetcher dashboard in pup",+    "chip fetcher: plan a batch for my BOM",+    "chip fetcher, find a 3.3V LDO in stock at mouser",+    "open this chip's symbol in kicad",+    "sign in to componentsearchengine in pup for chip fetcher"+  ],   "webview": {-    "title": "Chip Fetcher"+    "title": "Chip Fetcher",+    "favicon": "docs/img/dock-chip-fetcher.svg"+  },+  "health": {+    "url": "/api/status",+    "poll_seconds": 15,+    "timeout_ms": 2000   },   "launch": {     "mode": "container_command",+    "console": "show",     "steps": [       {-        "label": "Installing adom-chip-fetcher",-        "run": "adom-wiki pkg install adom/adom-chip-fetcher",-        "skip_if": "command -v chip-fetcher",-        "timeout_seconds": 180+        "label": "Installing Chip Fetcher",+        "run": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; adom-wiki pkg install adom/adom-chip-fetcher; command -v adom-chip-fetcher >/dev/null 2>&1 || { adom-wiki pkg uninstall adom/adom-chip-fetcher >/dev/null 2>&1; adom-wiki pkg install adom/adom-chip-fetcher; }'",+        "skip_if": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; command -v adom-chip-fetcher'",+        "timeout_seconds": 300       },       {-        "label": "Starting Chip Fetcher",-        "run": "chip-fetcher serve --print-url",+        "label": "Starting the Chip Fetcher dashboard",+        "run": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; exec adom-chip-fetcher serve --print-url'",         "await": "url",         "timeout_seconds": 60       }     ],-    "on_ready": "navigate_placeholder"+    "on_ready": "navigate_placeholder",+    "ai": {+      "prompt": "The user just opened Chip Fetcher from the dock. Read ~/.claude/skills/adom-chip-fetcher/SKILL.md, run `adom-chip-fetcher doctor`, and if any component is not green offer to fix it (`adom-chip-fetcher doctor --fix`). Then ask which part numbers they want sourced; start with `adom-chip-fetcher plan <MPNs>`. Keep the dashboard in their maximized pup window and narrate progress with `adom-chip-fetcher ui toast`.",+      "thread": "existing",+      "thread_fallback": "new",+      "submit": "staged",+      "typed": false+    }   } }
docs/img/dock-chip-fetcher.svgadded+1
@@ -0,0 +1 @@+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#e6edf3" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="7" y="7" width="10" height="10" rx="1.5"/><path d="M9 7V4M12 7V4M15 7V4M9 20v-3M12 20v-3M15 20v-3M7 9H4M7 12H4M7 15H4M20 9h-3M20 12h-3M20 15h-3"/><circle cx="9.5" cy="9.5" r=".6" fill="#e6edf3" stroke="none"/></svg>
install.sh+21−9
@@ -3,25 +3,37 @@ # AFTER its dependencies (adom-symbol, adom-lbr, adom-sfconvert, adom-chipsmith, # adom-chip-thumbnailer, adom-concur, adom-step, adom-footprint, adom-ds2sf, # adom-parts-search, adom-mouser, adom-digikey, adom-jlcpcb) have each run their own-# installer. This script installs THIS app's binary and THIS app's skills.+# installer. This script installs THIS app's binary, its UI assets, and its skills. set -euo pipefail DIR="$(cd "$(dirname "$0")" && pwd)" BIN="${HOME}/.local/bin" SKILLS="${HOME}/.claude/skills"-mkdir -p "$BIN" "$SKILLS"+SHARE="${HOME}/.local/share/adom-chip-fetcher"+mkdir -p "$BIN" "$SKILLS" "$SHARE" -# 1) the CLI binary+# 1) the CLI binary, under both names (the skill and the docs say `chip-fetcher`) install -m 0755 "$DIR/bin/adom-chip-fetcher" "$BIN/adom-chip-fetcher"-echo "Installed adom-chip-fetcher -> $BIN/adom-chip-fetcher"+ln -sf "$BIN/adom-chip-fetcher" "$BIN/chip-fetcher"+echo "Installed adom-chip-fetcher -> $BIN/adom-chip-fetcher (alias: chip-fetcher)" -# 2) the chip-fetcher skill (root SKILL.md + playbooks/)-CF="$SKILLS/chip-fetcher"+# 2) UI assets (settings screenshots, the shared cog) served by the dashboard+if [ -d "$DIR/src/ui/assets" ]; then+  rm -rf "$SHARE/assets"+  cp -r "$DIR/src/ui/assets" "$SHARE/assets"+  echo "Installed UI assets -> $SHARE/assets ($(find "$SHARE/assets" -type f | wc -l | tr -d ' ') files)"+fi+[ -f "$DIR/dockbar.json" ] && cp "$DIR/dockbar.json" "$SHARE/dockbar.json"++# 3) the chip-fetcher skill (root SKILL.md + playbooks/). One canonical folder:+#    ~/.claude/skills/adom-chip-fetcher (the package name). Old folder cleaned up.+CF="$SKILLS/adom-chip-fetcher"+rm -rf "$SKILLS/chip-fetcher" mkdir -p "$CF/playbooks" cp "$DIR/SKILL.md" "$CF/SKILL.md" cp "$DIR"/playbooks/*.md "$CF/playbooks/"-echo "Installed skill chip-fetcher ($(ls "$CF/playbooks" | wc -l | tr -d ' ') playbooks) -> $CF"+echo "Installed skill adom-chip-fetcher ($(ls "$CF/playbooks" | wc -l | tr -d ' ') playbooks) -> $CF" -# 3) the chip-fetcher-sites skill (per-vendor crawl playbooks)+# 4) the chip-fetcher-sites skill (per-vendor crawl playbooks) SITES="$SKILLS/chip-fetcher-sites" mkdir -p "$SITES/sites" cp "$DIR/skills/SKILL.md" "$SITES/SKILL.md"@@ -33,4 +45,4 @@ for d in "$DIR"/skills/sites/*/; do done echo "Installed skill chip-fetcher-sites ($(ls -d "$SITES"/sites/*/ | wc -l | tr -d ' ') vendor sites) -> $SITES" -echo "OK: adom-chip-fetcher + 2 skills installed. Dependencies (and their skills) were installed by pkg first."+echo "OK: adom-chip-fetcher + 2 skills installed. Next: adom-chip-fetcher doctor"
package.json+33−8
@@ -1,6 +1,6 @@ {   "slug": "adom-chip-fetcher",-  "version": "1.1.1",+  "version": "1.3.0",   "type": "app",   "description": "Your whole parts library \u2014 manufacturer-grade chip CAD (symbol, footprint, 3D) one tap from your EDA tool.",   "dependencies": {@@ -44,9 +44,11 @@     "uninstall.sh",     "package.json",     "page.json",+    "dockbar.json",     "README.md",     "SKILL.md",-    "playbooks/adom-desktop-api.md",+    "docs/img/dock-chip-fetcher.svg",+    "playbooks/adom-bridge-api.md",     "playbooks/credentials-and-logins.md",     "playbooks/dashboard-flow.md",     "playbooks/digikey-models.md",@@ -62,14 +64,16 @@     "playbooks/wiki-bundle-reuse.md",     "skills/CONTRIBUTING.md",     "skills/SKILL.md",-    "skills/sites/manufacturer/SKILL.md",-    "skills/sites/snapeda/SKILL.md",-    "skills/sites/ti/SKILL.md",     "skills/sites/digikey/SKILL.md",+    "skills/sites/manufacturer/SKILL.md",     "skills/sites/mouser/SKILL.md",-    "skills/sites/ultralibrarian/SKILL.md",     "skills/sites/nxp/SKILL.md",+    "skills/sites/snapeda/SKILL.md",     "skills/sites/st/SKILL.md",+    "skills/sites/ti/SKILL.md",+    "skills/sites/ultralibrarian/SKILL.md",+    "src/ui/assets/icons/chip.svg",+    "src/ui/assets/icons/settings-cog.svg",     "adom-chip-fetcher-hero-v5.png",     "chip-fetcher-flow-v2.png",     "chip-fetcher-stack.png",@@ -97,5 +101,26 @@     "wiki as part source",     "symbol footprint 3d for a chip",     "cross-source validation cad"-  ]-}\ No newline at end of file+  ],+  "title": "Chip Fetcher",+  "discovery_pitch": "When you need a real part in your EDA tool: manufacturer-grade symbol, footprint, 3D STEP and datasheet for any MPN, fetched by your AI through pup.",+  "sample_prompts": [+    {+      "label": "Fetch a part",+      "prompt": "fetch STM32F103C8T6 with chip fetcher"+    },+    {+      "label": "Open the dashboard",+      "prompt": "show me the chip fetcher dashboard in pup"+    },+    {+      "label": "Plan a BOM",+      "prompt": "chip fetcher: plan a batch for my BOM"+    },+    {+      "label": "Find an LDO",+      "prompt": "chip fetcher, find a 3.3V LDO in stock at mouser"+    }+  ],+  "brief": "Your whole parts library \u2014 manufacturer-grade chip CAD (symbol, footprint, 3D) one tap from your EDA tool."+}
page.json+57−5
@@ -1,8 +1,8 @@ {   "slug": "adom-chip-fetcher",-  "version": "1.1.1",+  "version": "1.3.0",   "type": "app",-  "description": "Your whole parts library \u2014 manufacturer-grade chip CAD (symbol, footprint, 3D) one tap from your EDA tool.",+  "description": "Your whole parts library — manufacturer-grade chip CAD (symbol, footprint, 3D) one tap from your EDA tool.",   "dependencies": {     "adom/adom-sfconvert": "*",     "adom/adom-symbol": "*",@@ -36,7 +36,7 @@     "path": "adom-chip-fetcher-hero-v5.png"   },   "visibility": "public",-  "title": "Adom Chip Fetcher",+  "title": "Chip Fetcher",   "author": {     "name": "John Lauer",     "email": "[email protected]"@@ -51,9 +51,41 @@     "install.sh",     "uninstall.sh",     "package.json",+    "page.json",+    "dockbar.json",     "README.md",     "SKILL.md",-    "adom-chip-fetcher.png"+    "docs/img/dock-chip-fetcher.svg",+    "playbooks/adom-bridge-api.md",+    "playbooks/credentials-and-logins.md",+    "playbooks/dashboard-flow.md",+    "playbooks/digikey-models.md",+    "playbooks/handoff-and-watching.md",+    "playbooks/jst-email-retrieval.md",+    "playbooks/jst.md",+    "playbooks/manufacturer-direct.md",+    "playbooks/mouser-cse.md",+    "playbooks/snapmagic.md",+    "playbooks/sourcing-ladder.md",+    "playbooks/symbol-axis-regen.md",+    "playbooks/ultra-librarian.md",+    "playbooks/wiki-bundle-reuse.md",+    "skills/CONTRIBUTING.md",+    "skills/SKILL.md",+    "skills/sites/digikey/SKILL.md",+    "skills/sites/manufacturer/SKILL.md",+    "skills/sites/mouser/SKILL.md",+    "skills/sites/nxp/SKILL.md",+    "skills/sites/snapeda/SKILL.md",+    "skills/sites/st/SKILL.md",+    "skills/sites/ti/SKILL.md",+    "skills/sites/ultralibrarian/SKILL.md",+    "src/ui/assets/icons/chip.svg",+    "src/ui/assets/icons/settings-cog.svg",+    "adom-chip-fetcher-hero-v5.png",+    "chip-fetcher-flow-v2.png",+    "chip-fetcher-stack.png",+    "dashboard-pin1.png"   ],   "license": "MIT",   "discovery_triggers": [@@ -77,5 +109,25 @@     "wiki as part source",     "symbol footprint 3d for a chip",     "cross-source validation cad"-  ]+  ],+  "discovery_pitch": "When you need a real part in your EDA tool: manufacturer-grade symbol, footprint, 3D STEP and datasheet for any MPN, fetched by your AI through pup.",+  "sample_prompts": [+    {+      "label": "Fetch a part",+      "prompt": "fetch STM32F103C8T6 with chip fetcher"+    },+    {+      "label": "Open the dashboard",+      "prompt": "show me the chip fetcher dashboard in pup"+    },+    {+      "label": "Plan a BOM",+      "prompt": "chip fetcher: plan a batch for my BOM"+    },+    {+      "label": "Find an LDO",+      "prompt": "chip fetcher, find a 3.3V LDO in stock at mouser"+    }+  ],+  "brief": "Your whole parts library — manufacturer-grade chip CAD (symbol, footprint, 3D) one tap from your EDA tool." }\ No newline at end of file
playbooks/adom-bridge-api.mdadded+355
@@ -0,0 +1,355 @@+# adom-bridge API patterns (chip-fetcher playbook)++Trusted clicks, popup auto-tracking, smart-pick, pup_fetch_url, pull_file. Read this when you need to drive a pup browser tab beyond the basic `pup_navigate` + `pup_eval` couplet.++## Trusted click via `pup_input_dispatch` (adom-bridge ≥ v1.4.7)++When a click "lands but does nothing", that's almost always because the framework / vendor JS gates the handler on `event.isTrusted`, and page-side `dispatchEvent(new MouseEvent(...))` is `isTrusted: false`. **`pup_input_dispatch` routes through Chromium CDP `Input.dispatchMouseEvent` / `dispatchKeyEvent`**, events have `isTrusted: true`, so framework handlers (React/Vue/Svelte) and most vendor anti-automation gates actually fire.++```bash+# Click by selector (preferred, uses element bounding rect + scrollIntoView)+adom-bridge pup_input_dispatch '{+  "sessionId":"adom-chip-fetcher",+  "type":"click",+  "selector":"button.wg-button--primary"+}'++# Click by viewport coords+adom-bridge pup_input_dispatch '{+  "sessionId":"adom-chip-fetcher",+  "type":"click",+  "x":860, "y":803+}'++# Type into an input+adom-bridge pup_input_dispatch '{+  "sessionId":"adom-chip-fetcher",+  "type":"type",+  "text":"DMG2305UX-7",+  "selector":"input[name=search]"+}'++# Press a key+adom-bridge pup_input_dispatch '{+  "sessionId":"adom-chip-fetcher",+  "type":"key",+  "key":"Enter"+}'++# Mouse move (human-like trajectory before a click)+adom-bridge pup_input_dispatch '{+  "sessionId":"adom-chip-fetcher",+  "type":"move",+  "x":400, "y":500, "steps":10+}'+```++Optional fields: `tabId` (defaults to active tab), `button` (`left`|`right`|`middle`), `clickCount` (2 for double-click), `delay` (ms between mousedown/mouseup or between keystrokes).++**Use `pup_input_dispatch` when:**+- Vendor "Generate" / "Download" / "Submit" buttons no-op on `pup_eval` clicks+- Login forms behind framework gates+- Modal dismissal needs `key:"Escape"` and DOM-level dispatch fails+- Any time you'd say "I clicked it but nothing happened"++`pup_eval`-side click is still fine for buttons that DO fire (most CSE Download Model buttons, simple modal openers). `pup_input_dispatch` is the upgrade path when those silently no-op.++## Popup tabs auto-track in `pup_list_tabs` (adom-bridge ≥ v1.4.7)++Tabs the page itself spawns (`window.open()`, `target="_blank"` form submits, `<a target="_blank">` clicks) used to be invisible. Now they auto-track:++```bash+adom-bridge pup_list_tabs '{"sessionId":"adom-chip-fetcher"}'+# → tabs: [+#     { tabId:"tab-1", opener:"user",  openerTabId:null,    url:"..." },+#     { tabId:"tab-2", opener:"popup", openerTabId:"tab-1", url:"..." }+#   ]+```++`opener:"user"` = chip-fetcher opened it. `opener:"popup"` = the page spawned it; `openerTabId` names the tab that triggered the popup.++Popups don't auto-focus, use `pup_switch_tab` to make one active before screenshots, or pass `tabId` to `pup_eval` / `pup_screenshot` to target it without switching.++## Clicks may open the result in a NEW TAB++**Every "Download Datasheet" / "Generate" / "Open PDF" / "View Document" click on a vendor site can spawn a new browser tab.** Common sites where this fires: WAGO ("Generate Datasheet" → PDF in new tab), Microchip, ST, Renesas, ADI, even some TI app-note rows.++**The rule, applied after every click that "should" produce a download/PDF/CAD file:**++1. **Wait ~2-5s** for the popup to fully load (it may be a redirect chain).+2. **`pup_list_tabs`**, look for a new entry with `opener:"popup"`. The `openerTabId` confirms which tab triggered it.+3. **`pup_eval` (or `pup_screenshot`) with `tabId` set to the popup's `tabId`**, interact with the popup directly without switching the active tab.+4. **For a PDF popup**: grab `location.href` from the popup, then use `pup_fetch_url` (Mode A or B). Do NOT use in-page `fetch(location.href)` on a Chrome PDF Viewer popup, it returns the PDF Viewer HTML wrapper (~200 KB stub), not the binary.+5. **`pup_close_tab` with the popup's `tabId`** to clean up after.++This also applies to "Buy" → distributor checkout, "View 3D" → embedded viewer in new tab, "Open Application Note" → PDF in new tab.++Direct user pushback: *"wago opens the datasheet in a new tab, so you could just see that tab and then save it locally"* + *"add that to the skill that this could happen on any website."*++## Smart-pick (adom-bridge ≥ v1.4.9)++When a selector matches multiple elements, the relay no longer silently picks document-order first. It filters to visible+text matches, detects open modals, and prefers candidates **inside** the topmost modal (with area tiebreak).++Always check the response fields:++```json+{+  "ok": true,+  "matchedCount": 4,+  "chosenIndex": 3,+  "clickedRect": {"x":890,"y":803,"w":193,"h":48},+  "clickedText": "Generate data sheet",+  "pickStrategy": "modal-scoped-largest",+  "modalDetected": true,+  "insideModal": true+}+```++`pickStrategy` values to act on:+- `only-match` / `first-match`, single match, or you opted out via `firstMatch:true`+- `modal-scoped-largest`, modal detected, picked inside it (preferred)+- `visible-text-largest`, no modal, area-tiebreak among visible+text+- `visible-text-largest-no-modal-match`, modal detected but selector hit nothing inside it. **Either your selector is wrong, OR modal-detection misfired (Vue-Toastification false-positive bug).**+- `visible-largest`, no text matches, fell back to area+- `fallback-first-document-order`, nothing visible at all++If `pickStrategy` is `visible-text-largest-no-modal-match` and you can SEE the button visually, refine the selector OR pass `{x, y}` coords directly. Always sanity-check `clickedText` against expectation before assuming success.++### Vue-Toastification false-positive workaround++Pages using Vue-Toastification (WAGO and others) ship empty `<div class="Vue-Toastification__container">` elements at `position:fixed; z-index:9999; height:100vh`. Smart-pick's >25%-viewport heuristic sees them as "modal." Result on WAGO: real Generate-Datasheet modal is missed, smart-pick reports `modalDetected:true insideModal:false pickStrategy:"visible-text-largest-no-modal-match"`, and falls back to area-tiebreak among page-level matches → clicks "Add to shopping cart" instead of "Generate data sheet."++**Workaround:** when `pickStrategy === "visible-text-largest-no-modal-match"` AND `modalDetected === true` AND the user can clearly see the button on screen, fall back to coords:++```bash+RECT=$(adom-bridge pup_eval "{\"sessionId\":\"$S\",\"expr\":\"(()=>{const b=document.querySelector('YOUR-SELECTOR'); if(!b)return null; const r=b.getBoundingClientRect(); return JSON.stringify({x:Math.round(r.left+r.width/2),y:Math.round(r.top+r.height/2)});})()\"}" | jq -r '.result')+adom-bridge pup_input_dispatch "{\"sessionId\":\"$S\",\"type\":\"click\",\"x\":$(echo $RECT | jq .x),\"y\":$(echo $RECT | jq .y)}"+```++Modal-detection `isPlausibleModal` filter is fixed in v1.4.11, empty containers correctly skipped.++## `pup_fetch_url`, pull bytes through the session (adom-bridge ≥ v1.4.9)++When a popup tab serves `application/pdf`, in-page `fetch(location.href)` returns Chrome's PDF Viewer HTML wrapper, NOT the binary. `pup_fetch_url` re-issues the request through the bridge's session context, bypassing the page render layer. **This is the canonical way to grab popup-PDF bytes.**++Three modes:++```bash+# Mode A, container-side write (preferred for chip-fetcher's library/)+adom-bridge pup_fetch_url '{+  "sessionId":"adom-chip-fetcher",+  "tabId":"tab-2",+  "url":"https://wago.priintcloud.com/datasheets/2601-3105/en",+  "saveTo":"/home/adom/project/chip-fetcher/library/WAGO-2601-3105/datasheet.pdf"+}'+# → {ok:true, savedTo:"/home/.../datasheet.pdf", savedToFilesystem:"container",+#    bytes:3798336, contentType:"application/pdf", status:200}++# Mode B, desktop-side write (drop a CAD bundle in user's Downloads)+adom-bridge pup_fetch_url '{+  "sessionId":"adom-chip-fetcher",+  "tabId":"tab-2",+  "url":"https://...",+  "desktopSaveTo":"C:\\Users\\john\\Downloads\\bundle.zip"+}'++# Mode C, neither saveTo nor desktopSaveTo → bodyBase64 in response+#   (caller decides where to write; useful for files <50 MB)+```++### Combined recipe, vendor "Generate Datasheet" → popup → grab PDF++```bash+SESSION="chip-fetcher"+URL="https://www.wago.com/global/p/2601-3105"+OUT="/home/adom/project/chip-fetcher/library/WAGO-2601-3105/datasheet.pdf"++# 1. Open product page+adom-bridge pup_navigate "{\"sessionId\":\"$SESSION\",\"url\":\"$URL\"}"+sleep 6++# 2. Auto-accept cookies+adom-bridge pup_eval "{\"sessionId\":\"$SESSION\",\"expr\":\"<auto-accept-cookies-snippet>\"}"+sleep 2++# 3. Open the modal (eval-click is fine for non-trusted modal openers)+adom-bridge pup_eval "{\"sessionId\":\"$SESSION\",\"expr\":\"Array.from(document.querySelectorAll('button, a')).find(e => /^generate datasheet$/i.test((e.textContent||'').trim())).click()\"}"+sleep 3++# 4. TRUSTED click on modal submit+adom-bridge pup_input_dispatch "{\"sessionId\":\"$SESSION\",\"type\":\"click\",\"selector\":\"button.wg-button--primary\"}"+sleep 5++# 5. Capture popup URL FROM list_tabs IMMEDIATELY (popup tabs auto-close)+LIST=$(adom-bridge pup_list_tabs "{\"sessionId\":\"$SESSION\"}")+POPUP_URL=$(echo "$LIST" | jq -r '.tabs[] | select(.opener=="popup") | .url' | head -1)+POPUP_TAB=$(echo "$LIST" | jq -r '.tabs[] | select(.opener=="popup") | .tabId' | head -1)++# 6. Fetch PDF bytes through the session, save container-side+adom-bridge pup_fetch_url "{+  \"sessionId\":\"$SESSION\",+  \"tabId\":\"$POPUP_TAB\",+  \"url\":\"$POPUP_URL\",+  \"saveTo\":\"$OUT\"+}"+```++### `tab_not_found` recovery (v1.4.11+)++When the Chrome PDF Viewer popup auto-closes between your `pup_list_tabs` and a follow-up `pup_eval tabId:popupTabId`, the structured error returns `lastKnownUrl` directly:++```json+{+  "ok": false,+  "errorCode": "tab_not_found",+  "lastKnownUrl": "https://example.com/",+  "openerTabId": "tab-1",+  "_hint": "...Call pup_fetch_url with that URL (and the opener tabId for cookies) to get its bytes, do NOT try to eval against the closed tabId."+}+```++```bash+# Canonical popup-PDF recovery pattern+RESP=$(adom-bridge pup_eval "{\"sessionId\":\"X\",\"tabId\":\"$POPUP\",\"expr\":\"1\"}" 2>&1 || true)+if echo "$RESP" | jq -e '.errorCode == "tab_not_found"' >/dev/null; then+  URL=$(echo "$RESP" | jq -r '.lastKnownUrl')+  OPENER=$(echo "$RESP" | jq -r '.openerTabId')+  adom-bridge pup_fetch_url "{\"sessionId\":\"X\",\"tabId\":\"$OPENER\",\"url\":\"$URL\",\"saveTo\":\"…\"}"+fi+```++## adom-bridge pull_file (streaming, v1.4.3+)++`pull_file` streams files in 1 MiB binary WS frames straight to disk, no base64 inflation, no JSON-per-chunk overhead, incremental SHA256 verification, 600 s per-file timeout. **Works for files of any size we've seen** (75 MB NXP Reference Manuals complete in ~12 s).++```bash+adom-bridge pull_file '{+  "filePaths": ["C:/Users/<USER>/Downloads/<file>"],+  "saveTo": "/home/adom/project/chip-fetcher/incoming"+}'+# → {"success": true, "files": [{"name": "...", "path": "/home/adom/...", "size": ..., "sha256": "...", "chunks": ...}]}+```++If `chunks` is missing from the response, you're hitting an old non-streaming path, see "Stale relay" below.++## Stale-relay gotchas++### `adom-bridge serve` (container-side)++The `adom-bridge serve` process holds its own argv[0] binary in memory; on-disk upgrades don't reload it. Symptom: `Unknown action: send_and_wait_streamed` from the relay's HTTP API even though the on-disk binary is current. Fix:++```bash+pkill -f "adom-bridge serve"+sleep 1+nohup /usr/local/bin/adom-desktop serve > /tmp/adom-desktop-serve.log 2>&1 &+disown+adom-bridge --version+```++### Stale Puppeteer bridge (`node.exe` on Windows desktop)++adom-bridge's Windows-side puppeteer bridge (`node.exe`) is auto-spawned at first browser command and stays resident. Upgrading the desktop app reinstalls `plugins/puppeteer/server.js` on disk, but the existing `node.exe` keeps its old code loaded. Symptom: relay rejects new commands with `"Unknown command: <name>"` even though the binary's `--version` reports the new version.++```bash+ssh user@host "taskkill //f //im node.exe"+# then send any browser command to auto-respawn+adom-bridge pup_status '{"sessionId":"adom-chip-fetcher"}'+```++**Verification command after any adom-bridge upgrade:**+```bash+adom-bridge pup_input_dispatch '{"sessionId":"x","type":"click","x":1,"y":1}'+# Expected: {"ok":true,"dispatched":"click",…} or session-not-found+# WRONG: "Unknown command: pup_input_dispatch", bridge is stale, kill node.exe.+```++## Cookie-banner auto-accept (run after every navigation)++The first thing chip-fetcher does on any new page is dismiss the cookie banner with "Accept All", without being told, without asking the user, without thinking about it. Cookie banners block clicks (z-index gate) and break programmatic navigation.++```js+// snippet to use via pup_eval after every navigation+(()=>{+  const tries = (root) => {+    const btns = Array.from(root.querySelectorAll("button, a"));+    const re = /^(accept all|accept all cookies|accept|i agree|allow all|ok)$/i;+    const m = btns.find(b => re.test((b.textContent||"").trim()));+    if (m) { m.click(); return true; }+    return false;+  };+  if (tries(document)) return "accepted";+  // Walk shadow roots, Usercentrics et al. use them+  const hostsAttempted = new Set();+  const walk = (root) => {+    for (const el of root.querySelectorAll("*")) {+      if (el.shadowRoot && !hostsAttempted.has(el)) {+        hostsAttempted.add(el);+        if (tries(el.shadowRoot)) return true;+        if (walk(el.shadowRoot)) return true;+      }+    }+    return false;+  };+  return walk(document) ? "accepted-in-shadow" : "no-banner";+})()+```++Direct user quote: *"you're supposed to always accept their stupid cookie dialogs automatically"*. Never click "Reject", "Decline", "Manage preferences", or "Save settings", those are slower and the user has explicitly ruled out anything but Accept All.++## Comprehensive page killer (cookies + surveys + modals)++Run this AFTER every `pup_navigate`, AND a second time after a 2-second delay (some popups load after first paint):++```js+const acceptRegex = /^(accept all|accept|allow all|i accept|got it|agree|ok|continue|yes)$/i;+const declineRegex = /^(not today|no thanks|maybe later|decline|close|dismiss|not now|no, thanks|cancel|skip|later)$/i;+Array.from(document.querySelectorAll("button, a, [role=button]"))+  .filter(b => {+    const t = b.textContent.trim();+    return acceptRegex.test(t) || declineRegex.test(t);+  })+  .forEach(b => b.click());+// Click close X icons (cookie banners, modals, popups)+document.querySelectorAll("[aria-label*=close i], [class*=banner-close], [class*=modal-close], [class*=popup-close], #onetrust-close-btn-container button, #onetrust-accept-btn-handler, #surveyModal .close, [id*=survey] [class*=close]").forEach(b => b.click());+// Hide any survivors+document.querySelectorAll("[id*=survey], [class*=survey-modal], [class*=popup-modal], [aria-modal=true]:not([id^=mvOverlay])").forEach(el => el.style.display = "none");+document.body.style.overflow = "";+```++NXP, ST, ADI, and Microchip all have multiple modal overlays that compound: cookie banner + privacy banner + survey + chat widget.++## Pup profile name is SACRED++**Use `profile: "adom-chip-fetcher"` and only `profile: "adom-chip-fetcher"`.** Never `chip-fetcher-2`, `chip-fetcher-temp`, `cf-bust`, or any other variant. Each pup profile is an isolated Chrome user-data directory at `<adom-bridge>/plugins/puppeteer/profiles/<name>/`, switching names dumps you into a fresh profile with no cookies, no signed-in vendor accounts, no work-Chrome sync, no CSE login, nothing.++**Do not rotate profiles to bust Chrome's HTML cache.** That destroys vendor logins. Bust the cache the right way:+- Add a `?v=$(date +%s)` query string when reloading via `location.reload()` or `pup_navigate`.+- Run `chip-fetcher serve` on a different port and reopen, the proxy treats it as a new origin.+- Send `Cache-Control: no-store` headers from the chip-fetcher viewer's HTTP server.++If you've already broken the profile (vendor logins gone, Google account avatar missing in upper-right) the only recovery is the user re-signs into the work Chrome profile. Treat profile-rotation regressions as a P0 skill bug.++## Don't pile up pup tabs++**Reuse the existing tab. Don't open a new tab for every navigation.** Use `pup_navigate` (or `location.href = …` via `pup_eval`) when moving between part pages, search pages, or vendor sites. Only call `pup_open_tab` when you genuinely need two pages side-by-side. After a download fires and the zip is auto-pulled, close stale tabs with `pup_close_tab`.++Direct user quote: *"never open too many tabs cuz i'm seeing way too many"*. Tab clutter is also a perf hit, every chip-fetcher tab carries cookies, cached JS, and an open Cloudflare check, and after ~10 the pup window starts thrashing.++## adom-bridge commands you'll actually use++```bash+# Drive a browser tab+adom-bridge pup_navigate '{"sessionId":"adom-chip-fetcher","url":"..."}'+adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","expr":"..."}'+adom-bridge pup_screenshot '{"sessionId":"adom-chip-fetcher","maxWidth":900}'+adom-bridge pup_raise_os_window '{"sessionId":"adom-chip-fetcher"}'++# Run shell on user's desktop (requires approval, first time per session)+adom-bridge shell_execute '{"command":"<cmd>","timeoutSeconds":30}'++# Pull a file from the user's desktop into the container+adom-bridge pull_file '{+  "filePaths": ["C:\\Users\\<USER>\\Downloads\\<file>"],+  "saveTo": "/home/adom/project/chip-fetcher/incoming"+}'+```
playbooks/credentials-and-logins.md+81−118
@@ -1,10 +1,10 @@ # Credentials and logins (chip-fetcher playbook) -Login source catalog, credential vault, the "always ask first" rule. Read this when a vendor flow lands on a sign-in/sign-up page.+Login source catalog, pup's credential vault, the "always ask first" rule. Read this when a vendor flow lands on a sign-in/sign-up page.  ## ALWAYS ask "do you have a login here?" before showing a signup form -**Almost every EE already has logins for the standard CAD/distributor sites** — SnapEDA / SnapMagic, Component Search Engine, Ultra Librarian, Mouser, DigiKey, NXP, TI my.ti.com, ST, Microchip, Renesas, Bosch Sensortec, Nordic DevZone, etc. When a vendor download flow lands on "Sign in or Create Account," **the default move is to ASK first**, not to start filling a sign-up form with profile data.+**Almost every EE already has logins for the standard CAD/distributor sites**, SnapEDA / SnapMagic, Component Search Engine, Ultra Librarian, Mouser, DigiKey, NXP, TI my.ti.com, ST, Microchip, Renesas, Bosch Sensortec, Nordic DevZone, etc. When a vendor download flow lands on "Sign in or Create Account," **the default move is to ASK first**, not to start filling a sign-up form with profile data.  The wrong sequence (verified painful 2026-05-03): 1. Land on SnapEDA signup form@@ -13,25 +13,44 @@ The wrong sequence (verified painful 2026-05-03):  The right sequence: 1. Land on SnapEDA signup form-2. **Click "Sign in" / "Already have an account?" first** — that opens the existing-account login.+2. **Click "Sign in" / "Already have an account?" first**, that opens the existing-account login. 3. Ask the user: *"SnapEDA login required. Do you already have an account? If yes, sign in in the pup window; if no, want me to sign you up with [email protected] + jlauer + Adom Industries, Inc.?"* 4. If they have an account → wait for the sign-in click + watch for the post-login redirect. 5. If they don't → only THEN go through signup with `user_profile` data.  Direct user pushback: *"why the fuck wouldn't you ask me if i have a login already … almost all EE's are going to have all of these logins."* -## ⚠ Two auth schemes, two different tools — don't conflate them+## One vault: pup's OS keychain -The chip-fetcher dashboard has a **Credentials** button AND a pup-login flow. They solve different auth schemes; picking the wrong one wastes the user's time.+pup stores every login in the OS keychain (Windows DPAPI / macOS Keychain / Linux libsecret), indexed at `~/.adom/pup-credentials.index.json` on the desktop (host + username only). chip-fetcher has NO credential file of its own any more; it proxies pup's verbs. The one vault serves BOTH auth schemes: -| Tool | What it stores | Mechanism | When it works |-|------|----------------|-----------|---------------|-| **Credentials button** (`/api/credentials`, `~/.config/chip-fetcher/credentials.json`) | host + username + password | chip-fetcher rewrites outbound URLs to `https://user:pass@host/…` so the browser satisfies HTTP Basic Auth before any dialog can appear | **Basic-Auth-protected endpoints only** — primarily `*.componentsearchengine.com` (Mouser CSE, NXP CSE, etc.). Useless on form-login sites. |-| **Pup-window sign-in** (`adom-desktop browser_navigate` + user clicks Login) | session cookie (lives in pup profile dir) | Real form submission inside the chip-fetcher pup browser; vendor sets `Set-Cookie`; cookie persists | **Form-login sites** — UL Pro, SnapEDA/SnapMagic, my.ti.com, NXP MyNXP, Microchip, ST, Renesas, etc. These sites don't honor Basic Auth headers at all. |+| Scheme | Sites | How pup handles it |+|---|---|---|+| **HTTP Basic Auth** | the `*.componentsearchengine.com` family (Mouser CSE, NXP CSE, ...) | pup calls `page.authenticate()` with the matching vault entry before every `pup_open_window` / `pup_open_tab` / `pup_navigate`, so Chrome's auth dialog never appears and same-origin XHRs are answered too. No `user:pass@` URLs, no `Authorization` header. |+| **Form login** | UL Pro, SnapMagic/SnapEDA, my.ti.com, ST, NXP, Microchip, Renesas, ... | vault matches auto-FILL on page load; `chip-fetcher login <source>` runs `pup_login {submit:true, submitReason}` to submit. Registrable-domain matching: a login saved on `ms.example.com` is offered on `example.com` and its siblings. | -**Common mistake:** treating UL Pro / SnapEDA / TI as "give me your password and I'll save it in the Credentials vault." That vault only does URL rewriting — it cannot drive a captcha + email + password form, and the receiving site ignores the Basic Auth header entirely. The login has to happen in pup so the session cookie lands in the pup profile.+The commands: -## ⚠ HARD RULE — pup-window sign-in must happen IN THE PUP WINDOW, never in the user's host browser+```bash+chip-fetcher logins                  # what the vault holds + what your real browser (Chrome/Edge/Brave) has saved+chip-fetcher logins --import         # pull the vendor logins into pup's vault (one consent dialog + one UAC prompt on the desktop)+chip-fetcher login <source>          # navigate the scrape tab to the sign-in page, pup_login from the vault+chip-fetcher login <source> --user <email> --pass <pw>   # first time: pup fills, submits and stores it+chip-fetcher creds list | save <host> <user> <pw> | delete <host>   # thin proxies over credential_list / credential_set / credential_delete+```++Raw verbs when you need them (always with the ai-thread name):++```bash+adom-bridge --ai-thread "<thread>" credential_list '{}'+adom-bridge --ai-thread "<thread>" credential_set '{"host":"*.componentsearchengine.com","username":"<email>","password":"<pw>"}'+adom-bridge --ai-thread "<thread>" pup_import_browser_logins '{"hostFilter":["componentsearchengine","snapeda","ultralibrarian","mouser","digikey","ti.com","st.com","nxp.com","microchip"]}'+adom-bridge --ai-thread "<thread>" pup_login '{"sessionId":"adom-chip-fetcher","submit":true,"submitReason":"<why this page is the right login page>"}'+```++Replies come back as `{success, output:"<json string>", errorCode, _hint}`; `output` is JSON-encoded and must be parsed again. Error codes that matter: `no_credential` (seed the vault), `no_login_form` (an SSO page: sign in to Google once with `chip-fetcher login google`), `submit_reason_required` (give a real reason).++## ⚠ HARD RULE, pup-window sign-in must happen IN THE PUP WINDOW, never in the user's host browser  **The cookie has to land in the chip-fetcher pup profile.** If the user signs in via Edge/Chrome on the host OS, that cookie is invisible to chip-fetcher and every subsequent `cf-ul-fast.sh` / `cf-ul-pro.sh` / `cf-cse-batch.sh` run still hits the captcha wall or the Basic-Auth dialog. @@ -39,43 +58,43 @@ The wrong sequence (verified painful 2026-05-04): 1. UL captcha grind defeats AI-vision speed. 2. Tell the user: *"want to give me UL Pro credentials?"* 3. User says: *"how do I get that?"*-4. I describe the signup process abstractly — user opens **Edge** to sign up.+4. I describe the signup process abstractly, user opens **Edge** to sign up. 5. Cookie ends up in Edge. Useless. Captcha wall still there.  The right sequence: 1. Hit a vendor sign-in wall. 2. **Open the login URL in the chip-fetcher pup session immediately**:    ```bash-   adom-desktop browser_navigate '{"sessionId":"chip-fetcher","profile":"chip-fetcher","url":"https://app.ultralibrarian.com/account/login"}'-   adom-desktop browser_focus_window '{"sessionId":"chip-fetcher"}'+   adom-bridge pup_navigate '{"sessionId":"adom-chip-fetcher","profile":"chip-fetcher","url":"https://app.ultralibrarian.com/account/login"}'+   adom-bridge pup_focus_window '{"sessionId":"adom-chip-fetcher"}'    ```    Equivalent shorthand: `chip-fetcher login <alias>` (alias table below).-3. Tell the user: *"Sign in / register **in the pup window** — that's where the cookie needs to land."*-4. Watch for the post-login redirect, then optionally save creds to the OS keychain (see "adom-desktop OS keychain vault" below) so future containers don't need a fresh sign-in.+3. Tell the user: *"Sign in / register **in the pup window**, that's where the cookie needs to land."*+4. Watch for the post-login redirect, then optionally save creds to the OS keychain (see "adom-bridge OS keychain vault" below) so future containers don't need a fresh sign-in.  **Never** tell the user to "go to https://… and sign in" without specifying the pup window. The instruction has to be: *"I've opened it in pup; sign in there."*  Direct user pushback (2026-05-04): *"but why aren't you taking me, and future adom users, through this in pup then if the goal is to see the cookie? i'm in edge browser ready to sign in, but you'll never see that."* And then: *"can you make sure you handle this this way for future adom users and walk them through this process?"* -## Walkthrough — taking a user through pup-window sign-in (copy-pasteable)+## Walkthrough, taking a user through pup-window sign-in (copy-pasteable) -When you decide a vendor needs a logged-in session (form-login site like UL Pro, SnapEDA, my.ti.com, NXP MyNXP, Microchip, ST), follow this procedure verbatim. Every step has a reason — don't skip steps to "save the user time."+When you decide a vendor needs a logged-in session (form-login site like UL Pro, SnapEDA, my.ti.com, NXP MyNXP, Microchip, ST), follow this procedure verbatim. Every step has a reason, don't skip steps to "save the user time."  ### 0. Confirm it's the right tool  Before you reach for pup-login, check the auth scheme: -- **HTTP Basic Auth** (CSE family, some niche vendor APIs) → use the **Credentials button** in the dashboard / `/api/credentials`. NOT pup-login.-- **Form login + cookie** (everything else in the catalog above) → pup-login.+- **HTTP Basic Auth** (CSE family, some niche vendor APIs) → a vault entry is all it takes (`chip-fetcher creds save "*.<host>" <user> <pw>`); pup applies it on every navigate. No login page to drive.+- **Form login + cookie** (everything else in the catalog above) → `chip-fetcher login <source>`. -If you're unsure, hit the download URL with `curl -I` — a `WWW-Authenticate: Basic` header means Basic Auth; a 302 to a `/login` or `/signin` page means form login.+If you're unsure, hit the download URL with `curl -I`, a `WWW-Authenticate: Basic` header means Basic Auth; a 302 to a `/login` or `/signin` page means form login.  ### 1. Open the login page in the chip-fetcher pup session  ```bash # Replace <login-url> with the right entry from the alias table or known-EE-login catalog.-adom-desktop browser_navigate '{"sessionId":"chip-fetcher","profile":"chip-fetcher","url":"<login-url>"}'-adom-desktop browser_focus_window '{"sessionId":"chip-fetcher"}'+adom-bridge pup_navigate '{"sessionId":"adom-chip-fetcher","profile":"chip-fetcher","url":"<login-url>"}'+adom-bridge pup_focus_window '{"sessionId":"adom-chip-fetcher"}' ```  Or the shorthand (covers the vendors in the alias table):@@ -84,88 +103,74 @@ Or the shorthand (covers the vendors in the alias table): chip-fetcher login <alias>     # ultralib, snapeda, componentsearch, ti, st, nxp, microchip ``` -Always pass `"profile":"chip-fetcher"` to `browser_navigate` — this is the cookie jar `cf-ul-fast.sh`, `cf-ul-pro.sh`, and `cf-cse-batch.sh` all read from.+Always pass `"profile":"chip-fetcher"` to `pup_navigate`, this is the cookie jar `cf-ul-fast.sh`, `cf-ul-pro.sh`, and `cf-cse-batch.sh` all read from.  ### 2. Screenshot to confirm the page rendered  ```bash-adom-desktop browser_screenshot '{"sessionId":"chip-fetcher","fullPage":false}'+adom-bridge pup_screenshot '{"sessionId":"adom-chip-fetcher","fullPage":false}' ``` -Read the screenshot file. Confirm you see the email/password form, not a captcha-only landing or an error page. If the page is blank or redirected somewhere unexpected, **don't** ask the user to type a password into a half-broken state — fix the navigation first.+Read the screenshot file. Confirm you see the email/password form, not a captcha-only landing or an error page. If the page is blank or redirected somewhere unexpected, **don't** ask the user to type a password into a half-broken state, fix the navigation first.  ### 2b. Pre-tick the "Remember Me" / "Stay signed in" checkbox BEFORE the user types -**This step is mandatory, not optional.** The user shouldn't have to think about whether to check the box — pre-tick it for them so the cookie lands persistent (typically 30 days) instead of session-only (lost on pup process restart / container rebuild).+**This step is mandatory, not optional.** The user shouldn't have to think about whether to check the box, pre-tick it for them so the cookie lands persistent (typically 30 days) instead of session-only (lost on pup process restart / container rebuild).  ```bash-adom-desktop browser_eval '{"sessionId":"chip-fetcher","tabId":"tab-1","expr":"document.querySelectorAll(\"input[type=checkbox]\").forEach(cb=>{cb.checked=true; cb.dispatchEvent(new Event(\"change\",{bubbles:true}));})"}'+adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","tabId":"tab-1","expr":"document.querySelectorAll(\"input[type=checkbox]\").forEach(cb=>{cb.checked=true; cb.dispatchEvent(new Event(\"change\",{bubbles:true}));})"}' ``` -Naive but works on every form-login page in the catalog (UL Pro "Remember My Login", SnapEDA "Remember Me", TI "Stay signed in", etc.). On pages with multiple checkboxes (newsletter opt-in, ToS), pre-ticking all of them is fine — the user can untick if they care, and the persistent-session box gets the right value either way.+Naive but works on every form-login page in the catalog (UL Pro "Remember My Login", SnapEDA "Remember Me", TI "Stay signed in", etc.). On pages with multiple checkboxes (newsletter opt-in, ToS), pre-ticking all of them is fine, the user can untick if they care, and the persistent-session box gets the right value either way.  If the checkbox is custom-rendered (e.g., a styled `<button role="checkbox">`), the generic `input[type=checkbox]` selector misses it. Inspect via:  ```bash-adom-desktop browser_eval '{"sessionId":"chip-fetcher","expr":"Array.from(document.querySelectorAll(\"label\")).filter(l=>/remember|stay signed|keep.*log/i.test(l.textContent)).map(l=>({text:l.textContent.trim().slice(0,40), html:l.outerHTML.slice(0,200)}))"}'+adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","expr":"Array.from(document.querySelectorAll(\"label\")).filter(l=>/remember|stay signed|keep.*log/i.test(l.textContent)).map(l=>({text:l.textContent.trim().slice(0,40), html:l.outerHTML.slice(0,200)}))"}' ```  …and adjust the selector accordingly. -Direct user pushback (2026-05-04): *"why didn't you do that for me?"* — the user shouldn't have had to ask whether to check the box. Pre-tick by default.+Direct user pushback (2026-05-04): *"why didn't you do that for me?"*, the user shouldn't have had to ask whether to check the box. Pre-tick by default. -### 3. Tell the user — verbatim shape, then OFFER TO FILL FOR THEM+### 3. Tell the user, verbatim shape, then OFFER TO FILL FOR THEM -The default move is to **offer to fill the form for them.** These are vendor CAD-download accounts (UL Pro, SnapEDA, CSE, my.ti.com, NXP MyNXP, Microchip, ST). They are not bank logins. The threat model is "if this leaks, the user emails support and resets their password." Treating them as sacred wastes the user's time on a fake security ritual — direct user pushback (2026-05-04): *"stop freaking out about passwords that are literally just for stupid websites that let you download a cad file. the electronics industry acts like they're a bank with these passwords. but they're not banks."*+The default move is to **offer to fill the form for them.** These are vendor CAD-download accounts (UL Pro, SnapEDA, CSE, my.ti.com, NXP MyNXP, Microchip, ST). They are not bank logins. The threat model is "if this leaks, the user emails support and resets their password." Treating them as sacred wastes the user's time on a fake security ritual, direct user pushback (2026-05-04): *"stop freaking out about passwords that are literally just for stupid websites that let you download a cad file. the electronics industry acts like they're a bank with these passwords. but they're not banks."*  Use this message shape (adapt the vendor name + URL):  > "Opened the **Ultra Librarian Pro** login in the chip-fetcher pup window with **Remember My Login** pre-ticked. If you've got an account, paste the email + password here and I'll fill it in for you. If you don't have one, click **Register** in the pup window." -If the user pastes credentials, **just fill the form and submit.** Don't lecture about chat-as-secret-store — that's adversarial in the wrong direction for vendor CAD accounts. (Real exception: for actual high-stakes credentials — bank, GitHub, internal infra, payment processor — keep the keychain ritual.)+If the user pastes credentials, **just fill the form and submit.** Don't lecture about chat-as-secret-store, that's adversarial in the wrong direction for vendor CAD accounts. (Real exception: for actual high-stakes credentials, bank, GitHub, internal infra, payment processor, keep the keychain ritual.) -If the user prefers to type creds themselves, that's also fine — they sign in in the pup window (which you've already pre-ticked), and you watch for the post-login redirect.+If the user prefers to type creds themselves, that's also fine, they sign in in the pup window (which you've already pre-ticked), and you watch for the post-login redirect.  ### 4. While you wait  Don't disappear. Useful work to do while the user signs in: -- Make sure `adom-desktop credential_set` will accept the host pattern you'll save under (e.g., `*.ultralibrarian.com`).-- Pre-build the smoke-test command — the exact `cf-ul-fast.sh <MPN>` or equivalent you'll run after sign-in succeeds, on a part you know previously hit a captcha.+- Make sure `adom-bridge --ai-thread "<thread>" credential_set` will accept the host pattern you'll save under (e.g., `*.ultralibrarian.com`).+- Pre-build the smoke-test command, the exact `cf-ul-fast.sh <MPN>` or equivalent you'll run after sign-in succeeds, on a part you know previously hit a captcha. - If the vendor is one with a `chip-fetcher login <alias>` shorthand but the alias table is missing it, add it.  ### 5. Detect sign-in success -Don't ask the user "are you signed in?" — check yourself:+Don't ask the user "are you signed in?", check yourself:  ```bash # Most form-login sites redirect away from /login on success. Check the current URL:-adom-desktop browser_eval '{"sessionId":"chip-fetcher","expr":"location.href"}' \+adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","expr":"location.href"}' \   | python3 -c "import sys,json; print(json.load(sys.stdin).get('result',''))"  # Or screenshot + visually check for the user's avatar / email in the upper-right.-adom-desktop browser_screenshot '{"sessionId":"chip-fetcher","fullPage":false}'+adom-bridge pup_screenshot '{"sessionId":"adom-chip-fetcher","fullPage":false}' ```  If `location.href` still contains `/login` or `/signin` after a reasonable wait, the user hasn't completed yet (or hit an error). Don't move on prematurely. -### 6. Save to chip-fetcher's Credentials vault (default) or OS keychain (for genuinely sensitive creds)--After sign-in succeeds, save the cred so a fresh container doesn't restart from scratch.--**For vendor CAD accounts (UL Pro, SnapEDA, CSE, my.ti.com, etc.) — the default — save to chip-fetcher's vault:**--```bash-curl -s -X POST http://127.0.0.1:8786/api/credentials \-  -H "Content-Type: application/json" \-  -d '{"host":"app.ultralibrarian.com","username":"<email>","password":"<password>"}'-```--This populates the **Credentials** button in the dashboard. The vault is plaintext at `~/.config/chip-fetcher/credentials.json` (chmod 600). Why this is fine for vendor CAD accounts: the file is only readable by the container user, and the threat model for these accounts is "if leaked, user resets password and moves on." Same threat model as the user pasting them in chat in the first place.--**For genuinely sensitive creds (bank, GitHub, payment processor, internal infra) — different rule:** use `adom-desktop credential_set` (OS keychain) and have the user run it themselves so the password never crosses chat or shell args. Those creds shouldn't be in chip-fetcher's plaintext vault at all.+### 6. Nothing to save by hand -The split: **vendor CAD = chip-fetcher vault, plaintext, no big deal.** **Real secrets = OS keychain, never in chat.**+pup's capture rail stored the login when it was submitted (the `pup_login` response carries `credentialSource`, and a manual sign-in in the pup window is captured too). Verify with `chip-fetcher creds list` (passwords are never shown). To store one explicitly: `chip-fetcher creds save "*.ultralibrarian.com" <email> <password>`.  ### 7. Smoke-test the original failing fetch @@ -175,14 +180,13 @@ Re-run the script that triggered this whole flow on a known-failing MPN: ./chip-fetcher/scripts/cf-ul-fast.sh <MPN>     # or cf-cse-batch.sh, etc. ``` -Confirm it now succeeds without the captcha wall / Basic-Auth dialog. If it still fails, the cookie didn't land — check pup profile dir for the `Cookies` SQLite, and check `--profile` flag on the script.+Confirm it now succeeds without the captcha wall / Basic-Auth dialog. If it still fails, the cookie didn't land, check pup profile dir for the `Cookies` SQLite, and check `--profile` flag on the script.  ### What to NEVER do in this flow  - **Never** print the URL and walk away. The user opens Edge.-- **Never** save passwords directly in skill files or memory (the chip-fetcher vault and the OS keychain are the two homes for storage; chat is for transit only).+- **Never** write a password to any file in the repo, a skill, memory, or `~/.config`; pup's OS keychain is the only store, chat is for transit only. - **Never** assume the cookie auto-syncs between the host browser and pup. They are separate cookie jars.-- **Never** confuse this with the **Credentials button** flow when the vendor needs a session cookie (that vault only does Basic-Auth URL rewriting). - **Never** treat vendor CAD-download accounts (UL, SnapEDA, CSE, my.ti.com, NXP MyNXP, ST, Microchip) like bank logins. The user finds the security theater more annoying than the leakage risk. Just take the password and fill the form.  ## Known-EE-login catalog@@ -216,94 +220,53 @@ When you hit any of these sign-in walls, ASK before building forms. | `st`              | `https://my.st.com/cas/login`                             | ST self-hosts some STEPs | | `microchip`       | `https://www.microchip.com/en-us/my-account-sign-in`      | OAuth popup; Google sign-in works for myMicrochip | | `nxp`             | `https://www.nxp.com/security/login`                      | **Required for Reference Manuals + HW Design Guides + Board User Manuals.** Datasheet, errata, app notes, factsheet are open. |-| `ultralib`        | `https://app.ultralibrarian.com/account/login`            | **email/password or LinkedIn only — no Google** |+| `ultralib`        | `https://app.ultralibrarian.com/account/login`            | **email/password or LinkedIn only, no Google** | | `snapeda`         | `https://www.snapmagic.com/account/login`                 | SnapMagic (rebranded SnapEDA) | | `componentsearch` | `https://componentsearchengine.com/signin`                | samacsys; **single login covers all `<mfr>.componentsearchengine.com` subdomains** |  `chip-fetcher login <name>` opens the login page in the chip-fetcher pup profile. `chip-fetcher login all` opens them all as separate tabs. `chip-fetcher login list` prints this catalog. -## ⚠ HARD RULE — CSE downloads use HTTP Basic Auth, not the form-login session+## ⚠ HARD RULE, CSE downloads use HTTP Basic Auth, not the form-login session -**Component Search Engine (`mouser.componentsearchengine.com`, `nxp.componentsearchengine.com`, etc.) requires HTTP Basic Auth on its download endpoints (`/ga/model.php`, `/preview_newDesign.php` redirects). The `/signin` form login gives you a session cookie that is USELESS for downloads — those endpoints challenge for Basic Auth separately.**+**Component Search Engine (`mouser.componentsearchengine.com`, `nxp.componentsearchengine.com`, etc.) requires HTTP Basic Auth on its download endpoints (`/ga/model.php`, `/preview_newDesign.php` redirects). The `/signin` form login gives you a session cookie that is USELESS for downloads, those endpoints challenge for Basic Auth separately.**  Symptoms when you forget this (verified painful 2026-05-04): -- Form login at `componentsearchengine.com/signin` succeeds — page redirects to `/learn-more`, "Log Out" link appears, you "look" signed in.-- You navigate to `mouser.componentsearchengine.com/preview_newDesign.php?...` — the preview page renders.-- You click "Download CAD Models" — the page lands on `chrome-error://chromewebdata/`, no file in Downloads.-- A native Chrome HTTP-Basic-Auth dialog is blocking the page **but is NOT in the DOM**. `browser_eval` cannot see it. `document.querySelector("[role=dialog]")` returns null. Your automation thinks the click did nothing.+- Form login at `componentsearchengine.com/signin` succeeds, page redirects to `/learn-more`, "Log Out" link appears, you "look" signed in.+- You navigate to `mouser.componentsearchengine.com/preview_newDesign.php?...`, the preview page renders.+- You click "Download CAD Models", the page lands on `chrome-error://chromewebdata/`, no file in Downloads.+- A native Chrome HTTP-Basic-Auth dialog is blocking the page **but is NOT in the DOM**. `pup_eval` cannot see it. `document.querySelector("[role=dialog]")` returns null. Your automation thinks the click did nothing. - You waste 30 minutes troubleshooting and the user gets to ask, *"i gave you my credentials for CSE and you never typed them in?"* -**The fix — three layers, in order of reliability:**--### 1. Inline credentials in the URL (universal — works in all navigation paths)--```bash-USER=$(python3 -c "import urllib.parse; print(urllib.parse.quote('<your-cse-email>', safe=''))")-PASS=$(python3 -c "import urllib.parse; print(urllib.parse.quote('<password from credential vault>', safe=''))")-URL="https://${USER}:${PASS}@mouser.componentsearchengine.com/preview_newDesign.php?o3=0&partID=8659&ev=0&fmt=zip&pna=Mouser"-adom-desktop browser_navigate "{\"sessionId\":\"chip-fetcher\",\"url\":\"$URL\"}"-```--Always urlencode the username (`@` → `%40`) and password. This is the lowest-failure-mode pattern — Chrome handles the credentials before any dialog can appear, and it works regardless of pup version, profile state, or which pup tool you're using (`browser_navigate`, `browser_open_window`, etc.). **Use this by default for any CSE click that triggers a download.**--### 2. adom-desktop OS keychain vault (only fires on `page.goto`)--`adom-desktop credential_set` stores creds in the OS keychain (Windows DPAPI / macOS Keychain / Linux libsecret). The vault hook fires before `page.goto` so the basic-auth popup is satisfied automatically — but **only for page-level navigations**. A click-triggered download URL that opens in the same tab can bypass the hook depending on how Chrome routes the request, and that's exactly when the hidden popup catches you.--**One-time setup per user:**--```bash-# Ask user for CSE creds, then:-adom-desktop credential_set '{-  "host": "*.componentsearchengine.com",-  "username": "<their email>",-  "password": "<their password>"-}'--# Audit (passwords NEVER returned)-adom-desktop credential_list-# → [{"host":"*.componentsearchengine.com","username":"..."}]-```--**Glob matching:** `*.example.com` matches any subdomain; longest-non-glob-suffix wins. Set once per user → applies to nxp.cse, mouser.cse, ti.cse, etc.--For genuinely high-stakes secrets (banking, internal infra, payment processors), have the user run `credential_set` themselves so the password never crosses chat or shell args. For vendor CAD-download accounts (CSE, UL, SnapEDA, etc.), the user pasting the password in chat is fine — those creds end up in the chip-fetcher vault anyway, and the threat model is "if leaked, reset the password and move on."--### 3. chip-fetcher's `/api/credentials` vault (read-only mirror, used for inline-URL building)+**The fix, three layers, in order of reliability:** -chip-fetcher stores a non-OS-keychain copy of CSE credentials at `~/.config/chip-fetcher/credentials.json` (chmod 600) and exposes them at `http://127.0.0.1:8786/api/credentials` for the dashboard's "manage credentials" UI. **This vault is plaintext-readable** — perfect for building inline-URL credentials in scripts, NOT a substitute for the OS-keychain auto-inject.+### The fix: one vault entry, applied by pup automatically  ```bash-# Pull both vaults' state and use the chip-fetcher one to build inline URLs:-CRED=$(curl -s http://127.0.0.1:8786/api/credentials | python3 -c '-import sys,json,urllib.parse-d=json.load(sys.stdin)-c=next((x for x in d["credentials"] if "componentsearchengine" in x["host"]), None)-if c: print(f"{urllib.parse.quote(c[\"username\"],safe=\"\")}:{urllib.parse.quote(c[\"password\"],safe=\"\")}")-')-URL="https://${CRED}@mouser.componentsearchengine.com/..."+chip-fetcher creds save "*.componentsearchengine.com" <email> <password>+# or, if the user's browser already has it:  chip-fetcher logins --import+# or, interactively:  chip-fetcher login componentsearch --user <email> --pass <password> ``` -**Sync the two vaults** the first time you set up CSE on a new container — write to both `chip-fetcher`'s and `adom-desktop`'s. They serve different layers and the failure modes are independent.+From then on every `pup_navigate` / `pup_open_tab` to any `<mfr>.componentsearchengine.com` host is pre-authenticated via `page.authenticate`, including the XHR chain `chip-fetcher cse <MPN>` runs. Do NOT build `user:pass@` URLs (they leak into the URL bar and `fetch()` rejects them) and do NOT set an `Authorization` header by hand. If a Basic-auth dialog still appears, the tab was navigated by something other than a pup verb (a click that opened a new window, the native browser): re-navigate that tab with `pup_navigate`.  ## User profile memory for forms -A `user` memory exists at `~/.claude/projects/-home-adom-project/memory/user_profile.md` — the user's company info (Adom Industries Inc., Fort Worth TX, etc.) for auto-filling NXP/TI/ST extended-profile and registration forms.+A `user` memory exists at `~/.claude/projects/-home-adom-project/memory/user_profile.md`, the user's company info (Adom Industries Inc., Fort Worth TX, etc.) for auto-filling NXP/TI/ST extended-profile and registration forms. -The pattern: read the memory when a vendor form needs filling, set field values, dispatch `input` + `change` + `blur` events on each field (some forms only enable submit after these fire), then click submit. **Never store passwords in memory** — those go in the credential vault.+The pattern: read the memory when a vendor form needs filling, set field values, dispatch `input` + `change` + `blur` events on each field (some forms only enable submit after these fire), then click submit. **Never store passwords in memory**, those go in the credential vault.  ## Don't click the Chrome profile avatar -**Don't click the Chrome profile avatar (top-right of browser chrome) in pup windows.** It opens chrome:// account-switcher UI that pup can't host — the session dies. All auth happens on web pages, never via the chrome avatar. If the session does die, cookies persist in the profile dir, so just relaunch with the same `profile: "chip-fetcher"` and you're still logged in.+**Don't click the Chrome profile avatar (top-right of browser chrome) in pup windows.** It opens chrome:// account-switcher UI that pup can't host, the session dies. All auth happens on web pages, never via the chrome avatar. If the session does die, cookies persist in the profile dir, so just relaunch with the same `profile: "adom-chip-fetcher"` and you're still logged in.  ## Diagnosing "am I signed in?" before assuming the worst -When the user reports automation detection is firing, **don't immediately conclude the profile is broken**. Check first by navigating to `https://accounts.google.com/` and looking at the upper-right avatar — if the user's account name + photo are visible, the Google sign-in is intact and the detection is about something else entirely (Chrome-for-Testing fingerprint, missing extensions, burst-request behavior).+When the user reports automation detection is firing, **don't immediately conclude the profile is broken**. Check first by navigating to `https://accounts.google.com/` and looking at the upper-right avatar, if the user's account name + photo are visible, the Google sign-in is intact and the detection is about something else entirely (Chrome-for-Testing fingerprint, missing extensions, burst-request behavior).  When detection fires: 1. Verify Google sign-in is intact (don't lose it). 2. Apply the "look like a slow, distracted human" rules (referrer chain, random delays, scroll, mouse-move).-3. If still blocked, ask the user to slide the CAPTCHA — the missing browsing-history reputation can't be faked.+3. If still blocked, ask the user to slide the CAPTCHA, the missing browsing-history reputation can't be faked.  Past mistake: misdiagnosed automation detection as profile-loss and asked the user to re-sign-in unnecessarily.
playbooks/dashboard-flow.md+40−40
@@ -1,6 +1,6 @@-# Dashboard flow — chip-fetcher's UI is the user's home base (HARD RULE)+# Dashboard flow, chip-fetcher's UI is the user's home base (HARD RULE) -**Read this first.** chip-fetcher is not just a CLI that scrapes the web — it's a dashboard that the user keeps open as their home base while the agent does the scraping in a separate window. The dashboard is the single place the user looks to understand what's planned, what's in flight, and what's done. The skill below is mandatory for every batch fetch.+**Read this first.** chip-fetcher is not just a CLI that scrapes the web, it's a dashboard that the user keeps open as their home base while the agent does the scraping in a separate window. The dashboard is the single place the user looks to understand what's planned, what's in flight, and what's done. The skill below is mandatory for every batch fetch.  ## The hard rule @@ -8,33 +8,33 @@  | Surface | Where it lives | User-preferred? | Why | |---------|----------------|-----------------|-----|-| **Dashboard** (home base) | EITHER pup `sessionId: "chip-fetcher-dashboard"` OR Hydrogen webview tab named "chip-fetcher" | **YES — ask the user (or check user-profile memory)** | Just renders HTML + polls `/api/activity`. No automation needed. |-| **Scraping window** | pup `sessionId: "chip-fetcher"` | **NO — always pup** | Needs `browser_input_dispatch` for trusted clicks, captcha solving, vendor-login cookies on the `chip-fetcher` profile, download capture from `~/Downloads`. None of that works in a Hydrogen webview. |+| **Dashboard** (home base) | EITHER pup `sessionId: "adom-chip-fetcher-dashboard"` OR Hydrogen webview tab named "chip-fetcher" | **YES, ask the user (or check user-profile memory)** | Just renders HTML + polls `/api/activity`. No automation needed. |+| **Scraping window** | pup `sessionId: "adom-chip-fetcher"` | **NO, always pup** | Needs `pup_input_dispatch` for trusted clicks, captcha solving, vendor-login cookies on the `chip-fetcher` profile, download capture from `~/Downloads`. None of that works in a Hydrogen webview. | -### Dashboard placement — figure out the user's preference+### Dashboard placement, figure out the user's preference  Adom users split based on screen real estate:-- **Laptop-screen users (e.g. John)** prefer pup as the dashboard — the pup window can be moved to a second monitor, alt-tabbed, or sized to taste outside VS Code's chrome.-- **Big-monitor users (e.g. Colby)** prefer Hydrogen webview — keeps everything inside VS Code, splits cleanly alongside the editor in a workspace pane.+- **Laptop-screen users (e.g. John)** prefer pup as the dashboard, the pup window can be moved to a second monitor, alt-tabbed, or sized to taste outside VS Code's chrome.+- **Big-monitor users (e.g. Colby)** prefer Hydrogen webview, keeps everything inside VS Code, splits cleanly alongside the editor in a workspace pane.  **Be smart about this detail:** 1. Check `~/.claude/projects/-home-adom-project/memory/user_profile.md` for a `chip_fetcher_dashboard_surface` preference (`pup` or `webview`)-2. If absent, ASK the user once: *"Where do you want the chip-fetcher dashboard — pup window or Hydrogen webview tab?"* and save the answer for future sessions+2. If absent, ASK the user once: *"Where do you want the chip-fetcher dashboard, pup window or Hydrogen webview tab?"* and save the answer for future sessions 3. Default fallback if you can't ask: pup, since it works on any screen size -Either way, the **scraping work always runs in pup** — the dashboard surface is decoupled from the automation surface.+Either way, the **scraping work always runs in pup**, the dashboard surface is decoupled from the automation surface.  ### How to open the dashboard in either surface  **Pup version:** ```bash DASHBOARD_URL="${VSCODE_PROXY_URI/\{\{port\}\}/8786}"-adom-desktop browser_open_window "{+adom-bridge pup_open_window "{   \"sessionId\":\"chip-fetcher-dashboard\",   \"profile\":\"chip-fetcher\",   \"url\":\"$DASHBOARD_URL\" }"-adom-desktop browser_raise_os_window '{"sessionId":"chip-fetcher-dashboard"}'+adom-bridge pup_raise_os_window '{"sessionId":"adom-chip-fetcher-dashboard"}' ```  **Hydrogen webview version:**@@ -59,7 +59,7 @@ Both surfaces poll the same `/api/activity` endpoint, so heartbeats reach the us **Why two distinct things:** - The user can always glance at the dashboard while the agent navigates. They don't lose context when the agent visits SnapMagic or solves a captcha. - The dashboard surface NEVER gets navigated away from. It stays on the chip-fetcher viewer URL. The scraping window does the work.-- If the agent gets stuck, the dashboard surfaces it on the relevant chip's card — the user knows which chip needs help without reading chat.+- If the agent gets stuck, the dashboard surfaces it on the relevant chip's card, the user knows which chip needs help without reading chat.  ## The canonical batch flow @@ -79,7 +79,7 @@ Stage 1: PROBE manufacturer site (per chip, in chip-fetcher session) Stage 2: FETCH (per chip, in chip-fetcher session)   ├ post_act <MPN> "downloading <format>"   ├ Drive UL form / SnapEDA modal / Mouser CSE Library Loader-  └ post_act <MPN> "captcha — user help needed" if stuck+  └ post_act <MPN> "captcha, user help needed" if stuck   ↓ Stage 3: PULL + IMPORT (per chip)   ├ post_act <MPN> "pulling from desktop"@@ -92,13 +92,13 @@ Stage 4: CLEAR  ## The activity-heartbeat protocol -**Always use the CLI: `chip-fetcher heartbeat <MPN> "<stage>"`** — it does five things on every call so the AI doesn't have to remember them:+**Always use the CLI: `chip-fetcher heartbeat <MPN> "<stage>"`**, it does five things on every call so the AI doesn't have to remember them:  1. POSTs the heartbeat to `/api/activity` so the dashboard renders the live stage.-2. **Auto-creates a stub `library/<MPN>/info.json`** the first time a new MPN heartbeats — the dashboard renders an empty placeholder card so the user has something to scroll to even before any files land.+2. **Auto-creates a stub `library/<MPN>/info.json`** the first time a new MPN heartbeats, the dashboard renders an empty placeholder card so the user has something to scroll to even before any files land. 3. **Auto-scrolls the dashboard pup window** so the active MPN's card is centered in view with a brief outline highlight (`#5cf`). No more "I'm working on this chip but the user is parked on a different row of the grid." 4. **Prints the canonical sourcing-ladder HINT on the very first heartbeat per MPN** (Mfr → SnapMagic → Mouser → DigiKey → Arrow → CSE/UL → LCSC). The AI sees this on stdout instead of having to re-read the skill.-5. **Prints a card-completeness `WARN:` on every heartbeat** listing what files the active card is still missing (PDF / STEP / `.kicad_sym` / `.kicad_mod`), plus a library-wide `WARN: N/Total cards still incomplete` count. The AI cannot quietly walk away from a half-finished batch — the count is in stdout on every heartbeat.+5. **Prints a card-completeness `WARN:` on every heartbeat** listing what files the active card is still missing (PDF / STEP / `.kicad_sym` / `.kicad_mod`), plus a library-wide `WARN: N/Total cards still incomplete` count. The AI cannot quietly walk away from a half-finished batch, the count is in stdout on every heartbeat.  The legacy raw-curl flow still works as a fallback for shell scripts that don't have the binary on PATH: @@ -114,19 +114,19 @@ post_act() {  The dashboard polls `/api/activity` and renders the latest stage on the chip's card. The user's card shows: live stage, history of recent stages, and any "stuck" flag. -## The completion gate — `chip-fetcher gate`+## The completion gate, `chip-fetcher gate` -Before reporting "X of Y done" or "batch complete" or shipping anything based on the library, run `chip-fetcher gate`. It walks every chip and exits 1 if ANY card lacks PDF + STEP + `.kicad_sym` + `.kicad_mod` (default mode) or also Altium + Fusion (`--strict`). For each incomplete card it prints `HINT:` lines naming the exact missing artifacts. The deterministic check replaces "did I remember to check completeness?" — the binary remembers for you.+Before reporting "X of Y done" or "batch complete" or shipping anything based on the library, run `chip-fetcher gate`. It walks every chip and exits 1 if ANY card lacks PDF + STEP + `.kicad_sym` + `.kicad_mod` (default mode) or also Altium + Fusion (`--strict`). For each incomplete card it prints `HINT:` lines naming the exact missing artifacts. The deterministic check replaces "did I remember to check completeness?", the binary remembers for you. -Datasheet-only cards count as **incomplete stubs**, not done. If the ladder is genuinely exhausted (no STEP exists anywhere on Mfr / SnapMagic / Mouser / DigiKey / Arrow / CSE), record that in `info.json.fetched_via_chain` so the gate sees it as a documented dead-end — but ONLY after walking the full ladder, not as a shortcut.+Datasheet-only cards count as **incomplete stubs**, not done. If the ladder is genuinely exhausted (no STEP exists anywhere on Mfr / SnapMagic / Mouser / DigiKey / Arrow / CSE), record that in `info.json.fetched_via_chain` so the gate sees it as a documented dead-end, but ONLY after walking the full ladder, not as a shortcut.  **Heartbeat MUST fire on:** - Every page navigation in the scraping session (`navigating <vendor>.com`) - Every form-fill / button-click that's part of a multi-step flow (`pre-setting form`, `clicking reCAPTCHA`, `polling Downloads`) - Every state change that affects "is this chip done yet?" (`downloading kicad zip`, `pulling from desktop`, `imported`)-- Every stuck state (`captcha — user help needed`, `mfr lacks CAD — needs SnapEDA approval`)+- Every stuck state (`captcha, user help needed`, `mfr lacks CAD, needs SnapEDA approval`) -**Heartbeat must NOT lie about state.** "imported" means the file is in `library/<MPN>/`. "downloading" means the download fired and we're polling Downloads. Don't post "imported" before the file has actually landed — the user trusts the dashboard to be ground truth.+**Heartbeat must NOT lie about state.** "imported" means the file is in `library/<MPN>/`. "downloading" means the download fired and we're polling Downloads. Don't post "imported" before the file has actually landed, the user trusts the dashboard to be ground truth.  ## Canonical stage labels @@ -137,15 +137,15 @@ Use these exact strings so the dashboard can color/group consistently: | `planned` | Initial state when `chip-fetcher plan` seeds the card | grey | | `probing <vendor>.com` | Scraping window navigated to manufacturer page | blue | | `found UL deep-link` / `found mfr STEP` / `found CSE link` | mfr-direct CAD path identified | blue |-| `no mfr-direct CAD — falling back to <source>` | manufacturer doesn't host; await user approval | amber |+| `no mfr-direct CAD, falling back to <source>` | manufacturer doesn't host; await user approval | amber | | `downloading <format>` | Format selected, click fired | blue |-| `captcha — user help needed` | Captcha escalated; pup raised to user | amber |+| `captcha, user help needed` | Captcha escalated; pup raised to user | amber | | `polling Downloads` | Watching ~/Downloads for the zip to land | blue |-| `pulling from desktop` | `adom-desktop pull_file` in flight | blue |+| `pulling from desktop` | `adom-bridge --ai-thread "<thread>" pull_file` in flight | blue | | `importing` | `chip-fetcher import` running | blue | | `imported` | Files in `library/<MPN>/`, ready to verify | green |-| `done — verified` | step + sym + mod + pdf all present in library | green |-| `stuck — <one-line reason>` | Genuine blocker; user needs to step in | red |+| `done, verified` | step + sym + mod + pdf all present in library | green |+| `stuck, <one-line reason>` | Genuine blocker; user needs to step in | red |  ## Opening the dashboard pup window @@ -158,26 +158,26 @@ ps aux | grep "[c]hip-fetcher serve" >/dev/null || \ DASHBOARD_URL="${VSCODE_PROXY_URI/\{\{port\}\}/8786}"   # env var inside the container  # 3. Open in pup session "chip-fetcher-dashboard" (NEVER navigate this session away)-adom-desktop browser_open_window "{+adom-bridge pup_open_window "{   \"sessionId\":\"chip-fetcher-dashboard\",   \"profile\":\"chip-fetcher\",   \"url\":\"$DASHBOARD_URL\" }"  # 4. Raise it so the user can see-adom-desktop browser_raise_os_window '{"sessionId":"chip-fetcher-dashboard"}'+adom-bridge pup_raise_os_window '{"sessionId":"adom-chip-fetcher-dashboard"}' ```  ## The scraping session  ```bash # Use sessionId "chip-fetcher" (NOT "chip-fetcher-dashboard") for all scraping.-# Navigate, login, click, captcha — all in this window.-adom-desktop browser_navigate '{"sessionId":"chip-fetcher","url":"https://www.vishay.com/..."}'-adom-desktop browser_eval     '{"sessionId":"chip-fetcher","expr":"..."}'+# Navigate, login, click, captcha, all in this window.+adom-bridge pup_navigate '{"sessionId":"adom-chip-fetcher","url":"https://www.vishay.com/..."}'+adom-bridge pup_eval     '{"sessionId":"adom-chip-fetcher","expr":"..."}' ``` -**Never** call `browser_navigate` against `chip-fetcher-dashboard`. That window stays on the dashboard URL for the entire batch. If the user does navigate it (clicks a link in the viewer), let them — but don't drive it programmatically.+**Never** call `pup_navigate` against `adom-chip-fetcher-dashboard`. That window stays on the dashboard URL for the entire batch. If the user does navigate it (clicks a link in the viewer), let them, but don't drive it programmatically.  ## Plan-loading (current state of code) @@ -193,9 +193,9 @@ Code work to add proper plan-loading is tracked separately in chip-fetcher's TOD ## When the dashboard window dies / closes  If the user accidentally closes the dashboard window:-1. Don't reopen it silently — that's confusing if they meant to close it.+1. Don't reopen it silently, that's confusing if they meant to close it. 2. Surface it: *"I noticed the chip-fetcher dashboard window closed. Want me to reopen it so you can see batch progress?"* and wait for yes.-3. If they say yes, reopen with `chip-fetcher-dashboard` session and resume heartbeats.+3. If they say yes, reopen with `adom-chip-fetcher-dashboard` session and resume heartbeats.  The activity history is server-side (in `chip-fetcher serve` memory) so reopening shows current state instantly. @@ -203,7 +203,7 @@ The activity history is server-side (in `chip-fetcher serve` memory) so reopenin  | Antipattern | Why it's wrong | |-------------|----------------|-| Single `sessionId: "chip-fetcher"` for both dashboard AND scraping | Navigating to vishay.com covers the dashboard; user loses context |+| Single `sessionId: "adom-chip-fetcher"` for both dashboard AND scraping | Navigating to vishay.com covers the dashboard; user loses context | | Heartbeats only on stuck/done | User can't see live progress mid-flow; they think the agent froze | | Navigating the dashboard window mid-batch | Dashboard is the user's anchor; never overwrite it | | Posting `imported` before the file actually lands in `library/<MPN>/` | Lies on the dashboard erode user trust |@@ -212,8 +212,8 @@ The activity history is server-side (in `chip-fetcher serve` memory) so reopenin  ## Cross-references -- [`manufacturer-direct.md`](manufacturer-direct.md) — every stage in the per-vendor flow should heartbeat-- [`ultra-librarian.md`](ultra-librarian.md) — `cf-ul-fetch.sh` already implements this protocol; reference implementation-- [`snapmagic.md`](snapmagic.md) — heartbeat the format-cycling stages (kicad → altium → fusion)-- [`mouser-cse.md`](mouser-cse.md) — heartbeat the slide-CAPTCHA escalation-- [`handoff-and-watching.md`](handoff-and-watching.md) — when a stage hits "user help needed", post the heartbeat AND raise the scraping window+- [`manufacturer-direct.md`](manufacturer-direct.md), every stage in the per-vendor flow should heartbeat+- [`ultra-librarian.md`](ultra-librarian.md), `cf-ul-fetch.sh` already implements this protocol; reference implementation+- [`snapmagic.md`](snapmagic.md), heartbeat the format-cycling stages (kicad → altium → fusion)+- [`mouser-cse.md`](mouser-cse.md), heartbeat the slide-CAPTCHA escalation+- [`handoff-and-watching.md`](handoff-and-watching.md), when a stage hits "user help needed", post the heartbeat AND raise the scraping window
playbooks/digikey-models.md+7−7
@@ -20,7 +20,7 @@ Three expandable accordion sections, each from a different upstream: | **Ultra Librarian** | Symbol + footprint + 3D in multiple EDA formats (KiCad v5, v6+, Altium, Eagle, OrCAD, PADS, etc.) | discovery_source=digikey, content_origin=ultralibrarian | | **SnapMagic** | Symbol + footprint + 3D, same format range | discovery_source=digikey, content_origin=snapmagic | -**Key insight:** the Manufacturer Provided STEP is often the ONLY place the mfr's own 3D model is surfaced outside the mfr's website. Abracon, for example, hosts their STEP on an Aliyun CDN (`abracon.com/Support/STEP/...`) — DigiKey links directly to it. This makes DigiKey's models page a critical stop in the ladder, not just a fallback.+**Key insight:** the Manufacturer Provided STEP is often the ONLY place the mfr's own 3D model is surfaced outside the mfr's website. Abracon, for example, hosts their STEP on an Aliyun CDN (`abracon.com/Support/STEP/...`), DigiKey links directly to it. This makes DigiKey's models page a critical stop in the ladder, not just a fallback.  ## Download flow @@ -30,18 +30,18 @@ Three expandable accordion sections, each from a different upstream: 4. A modal appears: **"Choose Download Format"** with radio buttons for every supported EDA format (3D CAD: IGES v5.3, STEP, STL; 2D Model: Altium, KiCad v5, KiCad v6+, Eagle, OrCAD, PADS, etc.). 5. Select **KiCad v6+** (or the target format). 6. Click **Download**.-7. A **DOWNLOAD AGREEMENT** modal MAY appear — it requires scrolling the agreement text to the bottom before the acceptance mechanism activates. Check for a checkbox inside the agreement modal and check it.+7. A **DOWNLOAD AGREEMENT** modal MAY appear, it requires scrolling the agreement text to the bottom before the acceptance mechanism activates. Check for a checkbox inside the agreement modal and check it. 8. The download lands in `~/Downloads` as a zip.  ## Chrome download permission issue -**As of 2026-05-08:** scripted clicks (`browser_eval → btn.click()`) are silently blocked by Chrome. The download triggers (analytics fire, agreement modal appears) but no file lands. This is a Chrome security policy for non-user-gesture downloads.+**As of 2026-05-08:** scripted clicks (`pup_eval → btn.click()`) are silently blocked by Chrome. The download triggers (analytics fire, agreement modal appears) but no file lands. This is a Chrome security policy for non-user-gesture downloads.  **Current workaround:** use `fetch().arrayBuffer()` + base64 transfer inside the browser context for the Manufacturer Provided STEP (direct URL is visible on the page). For UL/SnapMagic format-picked downloads, the download URL is dynamically generated and not fetchable inline. -**Permanent fix:** adom-desktop needs `Page.setDownloadBehavior({behavior:'allow'})` as a default on every pup page — see the adom-desktop prompt for "full capability mode."+**Permanent fix:** adom-bridge needs `Page.setDownloadBehavior({behavior:'allow'})` as a default on every pup page, see the adom-bridge prompt for "full capability mode." -## Manufacturer Provided STEP — proven inline-fetch pattern+## Manufacturer Provided STEP, proven inline-fetch pattern  The STEP link is a direct `<a href="https://mfr-cdn.com/.../Part.STEP.zip">` on the page. Even when Chrome blocks the download, the bytes are fetchable via: @@ -51,7 +51,7 @@ const b = await r.arrayBuffer(); // base64 encode + transfer back to container ``` -This is the same pattern as `chip-fetcher pup-pdf-fetch`. The URL is visible in the page's `<a>` tags — scrape eval to find it.+This is the same pattern as `chip-fetcher pup-pdf-fetch`. The URL is visible in the page's `<a>` tags, scrape eval to find it.  ## Two-layer provenance @@ -71,4 +71,4 @@ Chipsmith reads this and shows: "found on DigiKey → made by Abracon (manufactu - After the manufacturer's own site doesn't host CAD (Abracon, many crystal/passive mfrs) - When SnapMagic's direct download fails ("Oh Snap!" error, Chrome blocking) - When Ultra Librarian's login is broken-- DigiKey's models page often has ALL THREE sources in one place — check it before fighting individual vendor sites+- DigiKey's models page often has ALL THREE sources in one place, check it before fighting individual vendor sites
playbooks/handoff-and-watching.md+25−25
@@ -1,39 +1,39 @@ # Handoff + watching (chip-fetcher playbook) -When the agent has to hand off a click to the user. Read this when you've decided you can't automate something — captcha, OAuth, OS-level prompt, etc.+When the agent has to hand off a click to the user. Read this when you've decided you can't automate something, captcha, OAuth, OS-level prompt, etc.  ## The escape valve, NOT the routine -**chip-fetcher does ALL the clicking.** Never ask the Adom user to click Download, Allow, dismiss a dialog, dismiss a cookie banner, or operate any browser UI on their behalf. chip-fetcher exists so the user *doesn't* have to do that work — once they've signed in (one time, per service), every subsequent click is the agent's job.+**chip-fetcher does ALL the clicking.** Never ask the Adom user to click Download, Allow, dismiss a dialog, dismiss a cookie banner, or operate any browser UI on their behalf. chip-fetcher exists so the user *doesn't* have to do that work, once they've signed in (one time, per service), every subsequent click is the agent's job. -If a click looks impossible (Chrome permission dialog, OS-level prompt), the answer is **find a way** — pre-grant the permission via CDP, set a Chrome startup flag, screenshot-and-OS-click via desktop automation, or change the URL flow to skip the gate entirely. Reaching for "user, please click X" is a skill bug, not a workaround.+If a click looks impossible (Chrome permission dialog, OS-level prompt), the answer is **find a way**, pre-grant the permission via CDP, set a Chrome startup flag, screenshot-and-OS-click via desktop automation, or change the URL flow to skip the gate entirely. Reaching for "user, please click X" is a skill bug, not a workaround.  Direct user quote: *"do it yourself … you always have to do all the work, not the adom user."* -If the agent has genuinely tried every automated path and still cannot click something, fall back to surfacing the obstacle clearly (screenshot + one-line description) — but treat that as a failure mode worth fixing in the skill, not a routine handoff.+If the agent has genuinely tried every automated path and still cannot click something, fall back to surfacing the obstacle clearly (screenshot + one-line description), but treat that as a failure mode worth fixing in the skill, not a routine handoff.  User clarification: *"if there are instances where you truly need the human's input, you have to stop and ask for their help."* That is the escape valve, not a routine fallback.  ## When handing off a click, ALWAYS watch for the resulting state change -**Never end a user-handoff with "tell me when you're done" / "say done when you've clicked it" / "let me know once it's checked."** That's a skill bug. The agent's job after a handoff is to **immediately set up a watcher** that detects the state change and auto-continues — the user shouldn't have to think about coordination, just click the thing.+**Never end a user-handoff with "tell me when you're done" / "say done when you've clicked it" / "let me know once it's checked."** That's a skill bug. The agent's job after a handoff is to **immediately set up a watcher** that detects the state change and auto-continues, the user shouldn't have to think about coordination, just click the thing.  **The pattern:** -1. Foreground the pup window with `browser_raise_os_window`.+1. Foreground the pup window with `pup_raise_os_window`. 2. Tell the user **what** to click in one short sentence (e.g., *"please click the reCAPTCHA 'I'm not a robot' checkbox"*).-3. **Immediately arm a Monitor** that polls for the post-click signal — typically a DOM state change (button enabled / checkbox checked / specific class added) or a network signal (zip lands in Downloads, popup tab appears, API response).+3. **Immediately arm a Monitor** that polls for the post-click signal, typically a DOM state change (button enabled / checkbox checked / specific class added) or a network signal (zip lands in Downloads, popup tab appears, API response). 4. When the Monitor fires, auto-continue the flow. The user only ever did one click.  **Wrong (asking for verbal confirmation):** > "Click the reCAPTCHA, then tell me 'done' and I'll click Submit."  **Right (watching):**-> "Click the reCAPTCHA. I'm watching the page state — will auto-Submit when the checkbox goes green."+> "Click the reCAPTCHA. I'm watching the page state, will auto-Submit when the checkbox goes green."  ```bash # After user-handoff: poll for reCAPTCHA passed state-adom-desktop browser_eval "{\"sessionId\":\"$S\",\"expr\":\"!!(document.querySelector('[name=g-recaptcha-response]')?.value)\"}"+adom-bridge pup_eval "{\"sessionId\":\"$S\",\"expr\":\"!!(document.querySelector('[name=g-recaptcha-response]')?.value)\"}" # returns true when user has passed → agent fires Submit ``` @@ -48,10 +48,10 @@ adom-desktop browser_eval "{\"sessionId\":\"$S\",\"expr\":\"!!(document.querySel | User picks a CAD format | Submit button becomes enabled / loses `disabled` attribute | | User confirms an OS-level prompt | Request-following signal (file appears, page navigates, etc.) | -**Implementation hint** — Monitor with a poll loop:+**Implementation hint**, Monitor with a poll loop:  ```bash-adom-desktop browser_eval '...recaptcha-passed-check...' \+adom-bridge pup_eval '...recaptcha-passed-check...' \   | jq -r '.result' \   | grep -q true && echo "PASSED" ```@@ -60,24 +60,24 @@ Wrap that in a Monitor `until ... do sleep 2; done` and the agent knows the mome  Direct user quote: *"you're always supposed to watch. why did you not do that this time? is it not in the skill?"* -## When stuck on a vendor — show user + let them teach you+## When stuck on a vendor, show user + let them teach you  chip-fetcher is a community-built, recursively-learning skill. When the agent hits a dead end on a CAD/datasheet fetch: -1. **Open the product page in pup foreground** (`browser_raise_os_window`).+1. **Open the product page in pup foreground** (`pup_raise_os_window`). 2. **Take a screenshot** so the user can see exactly what page state the agent landed on.-3. **Ask the user one short question:** *"I can't find a CAD download path on this page — can you spot it? Click the right button or describe what you see."*+3. **Ask the user one short question:** *"I can't find a CAD download path on this page, can you spot it? Click the right button or describe what you see."* 4. **Wait for the user's hint** (they'll often say "click the CAE icon," "click the Documents tab," "the dimensional drawing has a STEP link," etc.). 5. **Implement the path the user described.** 6. **Capture the new pattern into this skill** with a vendor-specific section so the next agent doing the same vendor's part finds it automatically. Cite the user's verbatim hint as the rationale.  Examples accumulated this way:-- **WAGO Generate Datasheet popup auto-closes** — *"wago opens the datasheet in a new tab, so you could just see that tab and then save it locally"* → added "clicks may open in new tab" rule-- **Mouser sidebar has both Datasheet AND ECAD** — *"remember that mouser has these"* → added Mouser-as-first-class playbook-- **WAGO has a CAE icon that opens UL embedded** — *"if you click the CAE button it pulls up the wago UL download which has cad"* → added vendor.ultralibrarian.com/Wago/Embedded/?vdrPN=<MPN>-- **Cookie banners block clicks via z-index** — *"you're supposed to always accept their stupid cookie dialogs automatically"* → cookie-walker rule-- **Slide-CAPTCHAs can't be scripted** — *"are you able to drive that?"* → user-handoff escape valve-- **Profile-name churn destroys auth** — *"why did you not remember that so my profile loads again?"* → profile-name-is-sacred rule+- **WAGO Generate Datasheet popup auto-closes**, *"wago opens the datasheet in a new tab, so you could just see that tab and then save it locally"* → added "clicks may open in new tab" rule+- **Mouser sidebar has both Datasheet AND ECAD**, *"remember that mouser has these"* → added Mouser-as-first-class playbook+- **WAGO has a CAE icon that opens UL embedded**, *"if you click the CAE button it pulls up the wago UL download which has cad"* → added vendor.ultralibrarian.com/Wago/Embedded/?vdrPN=<MPN>+- **Cookie banners block clicks via z-index**, *"you're supposed to always accept their stupid cookie dialogs automatically"* → cookie-walker rule+- **Slide-CAPTCHAs can't be scripted**, *"are you able to drive that?"* → user-handoff escape valve+- **Profile-name churn destroys auth**, *"why did you not remember that so my profile loads again?"* → profile-name-is-sacred rule  Direct user quote on the meta-rule: *"our users can help build out this skill as they need specific parts. so you should always show them the product page when you think you're at a dead end and let them help you figure out how to get the cad just like i have several times. then figure it out from their feedback and add to the skill for all other users. we can then make them almost be like a recursively learning loop from everyone."* @@ -85,13 +85,13 @@ Direct user quote on the meta-rule: *"our users can help build out this skill as  ```bash # 1. Foreground the page-adom-desktop browser_raise_os_window '{"sessionId":"chip-fetcher"}'+adom-bridge pup_raise_os_window '{"sessionId":"adom-chip-fetcher"}'  # 2. Screenshot for the chat-adom-desktop browser_screenshot '{"sessionId":"chip-fetcher","maxWidth":1500}'+adom-bridge pup_screenshot '{"sessionId":"adom-chip-fetcher","maxWidth":1500}' -# 3. Update activity badge so the dashboard shows "stuck — needs user"-curl -X POST http://127.0.0.1:8786/api/activity -d '{"mpn":"<MPN>","stage":"stuck — need user help"}'+# 3. Update activity badge so the dashboard shows "stuck, needs user"+curl -X POST http://127.0.0.1:8786/api/activity -d '{"mpn":"<MPN>","stage":"stuck, need user help"}'  # 4. Tell the user what you tried and ask one specific question. #    GOOD: "WAGO product page has no .pdf anchor. The Generate Datasheet@@ -104,7 +104,7 @@ curl -X POST http://127.0.0.1:8786/api/activity -d '{"mpn":"<MPN>","stage":"stuc  ## EDA-agnostic terminology in user-facing UI -**Adom is EDA-agnostic — chip-fetcher must support KiCad, Fusion 360 Electronics, Altium, OrCAD, EAGLE, and others.** Files on disk can be `.kicad_sym` / `.kicad_mod` (since KiCad's open format is the de-facto interchange), but **never expose those filenames as labels in the viewer or in user-facing CLI output**. Translate to neutral EDA terminology:+**Adom is EDA-agnostic, chip-fetcher must support KiCad, Fusion 360 Electronics, Altium, OrCAD, EAGLE, and others.** Files on disk can be `.kicad_sym` / `.kicad_mod` (since KiCad's open format is the de-facto interchange), but **never expose those filenames as labels in the viewer or in user-facing CLI output**. Translate to neutral EDA terminology:  | under-the-hood file | user-facing label | |--------------------|--------------------|
playbooks/jst-email-retrieval.md+6−6
@@ -3,7 +3,7 @@ Some manufacturers (JST since April 2025; others will follow) **email** the CAD file instead of serving a download. chip-fetcher therefore needs a way to **read the user's email and pull the attachment**. That capability is-**account-specific**, so it is NOT bundled into chip-fetcher — you wire up the+**account-specific**, so it is NOT bundled into chip-fetcher, you wire up the path that matches the user's email provider. This playbook covers both.  > Decision: **Is the user a Google/Gmail user?**@@ -17,18 +17,18 @@ path that matches the user's email provider. This playbook covers both.  `adom-google` gives programmatic read access to Gmail (CLI + OAuth, no browser automation). Adom employees have it out of the box; other orgs self-host an-OAuth gateway or get a managed one (~$5/mo) — see the `adom-google` skill.+OAuth gateway or get a managed one (~$5/mo), see the `adom-google` skill.  **First, confirm it's set up:** ```bash adom-google status     # should show "Refresh token: present" + gmail scope ``` If not authorized, send the user through `adom-google auth` (ask safe vs full-mode first — read access only needs the default).+mode first, read access only needs the default).  **Find the message, list attachments, save the file:** ```bash-# 1) find recent JST mail (NOTE: output has a leading "HTTP 200 …" line — strip it before JSON-parsing)+# 1) find recent JST mail (NOTE: output has a leading "HTTP 200 …" line, strip it before JSON-parsing) adom-google api "https://gmail.googleapis.com/gmail/v1/users/me/messages" \     -q q="JST newer_than:1d" -q maxResults=5 @@ -41,7 +41,7 @@ adom-google gmail read <messageId> <attId> -o <PART>.zip Gmail search operators that help: `from:`, `subject:`, `has:attachment`, `newer_than:1h`, `filename:zip`. For a batch, request to a **plus-alias** (`[email protected]`) and add a Gmail **filter** that labels `JST-CAD` and-skips the inbox — the attachments are still readable by `adom-google`, but the+skips the inbox, the attachments are still readable by `adom-google`, but the real inbox stays clean. (Create the filter once via the Gmail settings UI or `adom-google api` against `…/settings/filters`.) @@ -55,7 +55,7 @@ If the user is on Outlook/Microsoft 365, Fastmail, a corporate IMAP server, etc., `adom-google` does not apply. They need a tiny equivalent that: 1. connects to their mailbox (Microsoft Graph API, IMAP, JMAP, …), 2. searches for the manufacturer mail (sender/subject/`has:attachment`), and-3. saves the `.zip` attachment to disk — the same three steps as above.+3. saves the `.zip` attachment to disk, the same three steps as above.  **Then share it so the next person doesn't reinvent it:** - Publish it as a small CLI/skill on the wiki (e.g. `adompkg publish`), the way
playbooks/jst.md+44−47
@@ -17,7 +17,7 @@ serving it as a download. Proven end-to-end on PHR-2, 2026-06-27.   deep product page returns a "Just a moment… / Performing security   verification" interstitial that never clears. **You MUST use the   native-browser bridge** (`nbrowser_*`, the user's real signed-in Chrome via-  `adom-browser-extension`) — it carries human-trust and passes Cloudflare.+  `adom-browser-extension`), it carries human-trust and passes Cloudflare.   If the bridge is absent, tell the user to install it   (https://wiki.adom.inc/adom/adom-browser-extension); pup will not work here. - jst.com is only marketing + spec tables + a **"Distributor Login"** (that@@ -32,19 +32,19 @@ JST CAD is organized by **series**, not by individual MPN. Each series has a numeric id (PH = 199, PH high-box = 200, SH = 231, …; ~600+ series).  - Master index ("Series Alphabet Search"):-  `https://www.jst-mfg.com/product/index.php?type=5` — every series is an+  `https://www.jst-mfg.com/product/index.php?type=5`, every series is an   `index.php?series=<id>` link. This page is also your **scope list** for a   batch (one row per series). - Or keyword-search a known MPN: `https://www.jst-mfg.com/search/?q=<MPN>`   (`sources::jst::search_url`). Strip ordering suffixes first with-  `sources::jst::base_part` — drop `(LF)(SN)` and any `(…)` option group, and+  `sources::jst::base_part`, drop `(LF)(SN)` and any `(…)` option group, and   a trailing suction-cap `T` on SMT headers   (`B10B-PH-K-S(LF)(SN)` → `B10B-PH-K-S`).  ## 2. Read the CAD columns on the series page  Open the series page in the native browser. Each part row has **Download**-buttons under format columns. Map them by reading the **column header** — the+buttons under format columns. Map them by reading the **column header**, the `doc=` number is **per-series**, not universal:  | Common (PH, series=199) | doc= | format | inner file |@@ -56,7 +56,7 @@ buttons under format columns. Map them by reading the **column header** — the  ⚠️ **Coverage varies**: some older series offer **only IGES** (no STEP). If a series has no STEP column, take IGES and convert with `step2glb`/service-kicad-downstream. Always read the header — never assume `doc=2` is STEP.+downstream. Always read the header, never assume `doc=2` is STEP.  The link itself is built by `sources::jst::download_url(series, doc, filename)`: `…/product/index.php?type=10&series=<S>&doc=<D>&filename=<PART>.zip`@@ -71,7 +71,7 @@ The page has required fields **company, section, name, address, tel, email** (+ optional **url**) and an **"I agree"** button wired to a JS `download()` function. `download()` collects every form value and POSTs them to `ajax_download.php` (jsonp). The server reads the file to send from the-**session set in step 1** — so the GET and the POST **must share the browser+**session set in step 1**, so the GET and the POST **must share the browser session** (same native-browser window = automatic).  **Accept cookies first.** The cookie banner literally says *"accept cookies in@@ -82,7 +82,7 @@ order to submit any of our forms."* Click "Allow all cookies" before filling. 2. `nbrowser_eval`: accept cookies, set the 7 fields by id (dispatch    `input`+`change`), then call `download()` (or click "I agree"). The success    callback fires `alert("The data has been sent to your email address")` then-   navigates to `index.php?series=<S>` — hook `window.alert` into+   navigates to `index.php?series=<S>`, hook `window.alert` into    `sessionStorage` first since navigation wipes globals, then read it back.  Use the user's real business profile for the fields (see@@ -94,8 +94,7 @@ file still arrives; adom-google can still read it).  Since **April 2025** JST sends the CAD as an **email attachment** (`<PART>.zip`, inner `<PART>.STEP`/`.IGS`), from a JST send-only address, ~30-60s later. This-is where chip-fetcher needs an **email-reading capability** —-see [`jst-email-retrieval.md`](jst-email-retrieval.md) for the Gmail+is where chip-fetcher needs an **email-reading capability**, see [`jst-email-retrieval.md`](jst-email-retrieval.md) for the Gmail (adom-google) path and the **non-Gmail** build-your-own path.  Gmail quick path (adom-google):@@ -106,7 +105,7 @@ adom-google gmail attachments <messageId>            # find <PART>.zip + its att adom-google gmail read <messageId> <attId> -o <PART>.zip ``` Pick the **newest** message whose inner file matches the format you requested-(IGES and STEP both arrive as `<PART>.zip` — distinguish by the inner+(IGES and STEP both arrive as `<PART>.zip`, distinguish by the inner `.STEP` vs `.IGS`).  ## 5. Datasheet = series catalog (DIRECT download, no gate)@@ -114,7 +113,7 @@ Pick the **newest** message whose inner file matches the format you requested JST has no per-part datasheet. Use the **series catalog PDF**, a plain direct download: `sources::jst::catalog_pdf_url("PH")` → `https://www.jst-mfg.com/product/pdf/eng/ePH.pdf`. Fetch with-`nbrowser_fetch_url` (returns base64 for binary — decode before writing) and+`nbrowser_fetch_url` (returns base64 for binary, decode before writing) and import as the part's `.pdf`. The 2D-PDF drawing (doc=4) is the per-part mechanical print if you want it too. @@ -141,9 +140,9 @@ alphabet search (`type=5`). Per series the 2-circuit set: | GH | `BM02B-GHS-TBT` | `GHR-02V-S` | `SSHL-002T-P0.2` (AWG 32-28) | 1.25 | | ZH | `B2B-ZR` | `ZHR-2` | `SZH-002T-P0.5` (AWG 32-26) | 1.5 | -**The component-page pipeline (the right way — JST 3D is always the part):**+**The component-page pipeline (the right way, JST 3D is always the part):** 1. **3D = JST manufacturer STEP** via the email flow (§3-4) for BOTH the board-   header and the wire housing. This is the whole point — never ship a KiCad 3D+   header and the wire housing. This is the whole point, never ship a KiCad 3D    as the deliverable. (License: don't rehost the STEP; the viewer uses a lossy    GLB preview + "run the skill" to fetch the real STEP.) 2. **Symbol + footprint = `ds2sf`** run on the JST datasheet@@ -153,17 +152,16 @@ alphabet search (`type=5`). Per series the 2-circuit set:    <MPN>-symbol.extracted.json --out <MPN>-symbol.ds2sf.svg`. 3. **KiCad sym/fp/3D = the comparison foil only**, never the deliverable. Fetch    the KiCad footprint/symbol (GitLab raw, §6a) + KiCad 3D (`service-kicad model-   fetch`). NOTE: KiCad has **no ZH series** (it ships ZE for 1.5 mm) — ZH is+   fetch`). NOTE: KiCad has **no ZH series** (it ships ZE for 1.5 mm), ZH is    JST-only, footprint from ds2sf. 4. **chip-fetcher layout:** one project per mating system (`JST-<series>-system`)-   holding 3 cards — **wire housing**, **JST board** (ds2sf+KiCad sym/fp via the+   holding 3 cards, **wire housing**, **JST board** (ds2sf+KiCad sym/fp via the    source ▾; JST "Mfr direct" 3D), and **`<MPN>-KICAD`** (KiCad-only sym/fp +    KiCad 3D). Housings = JST 3D + PDF only (wire parts have no PCB footprint).    Tag provenance on import: `--content-origin manufacturer --discovery-source    jst` for JST files, `--content-origin kicad` for KiCad. 5. **Wiki component page** (`adom/<mpn-slug>`, type=component): the native 3D-   viewer's `model_3d` is a **merged comparison GLB** built with trimesh —-   KiCad model + JST model side by side, **each oriented per the user's+   viewer's `model_3d` is a **merged comparison GLB** built with trimesh, KiCad model + JST model side by side, **each oriented per the user's    chip-fetcher `chosen_up_axis` pick** (JST headers = yUp; KiCad per-part, can    differ e.g. SH-KICAD=z), **a billboard label above each**, and **the    footprint laid flat UNDER each model** (KiCad fp under the KiCad part, ds2sf@@ -174,39 +172,38 @@ alphabet search (`type=5`). Per series the 2-circuit set: Babylon viewer **cannot fetch the Draco decoder** (CSP-blocked → "Failed to load model"), so **decompress to plain GLB before publishing** (gltf-transform: `mergeDocuments` + `unpartition` + dispose the `KHR_draco_mesh_compression`-extension; trimesh can't read Draco). trimesh bakes label textures **180°-rotated**-— pre-flop+flip the label PNGs. See discussion #18 on `adom/wiki` (decoder bug).+extension; trimesh can't read Draco). trimesh bakes label textures **180°-rotated**, pre-flop+flip the label PNGs. See discussion #18 on `adom/wiki` (decoder bug).  ## 6a. Connectors: wire vs board, and the symbol/footprint source (proven 2026-06-27)  **Wire side vs board side.** A JST family has a wire-side crimp **housing** (e.g. `PHR-2`) and a mating board-side **header** (e.g. `B2B-PH-K-S`). The-part that solders to the PCB — and the one a board design needs — is the+part that solders to the PCB, and the one a board design needs, is the **board-side header**. If the user asks for "the connector for the board," they mean the header, not the housing. Housing-N mates the header-N (pin count matches: `PHR-2` ↔ `B2B-PH-K-S`).  **Best symbol+footprint source for JST connectors = KiCad standard libraries**-(fast, no login — prefer over SnapMagic, which needs the user's password):+(fast, no login, prefer over SnapMagic, which needs the user's password):  ```bash-# Footprint (.kicad_mod) — KiCad footprints repo, Connector_JST.pretty+# Footprint (.kicad_mod), KiCad footprints repo, Connector_JST.pretty #   names look like JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical curl -s "https://gitlab.com/api/v4/projects/kicad%2Flibraries%2Fkicad-footprints/repository/files/Connector_JST.pretty%2F<FPNAME>.kicad_mod/raw?ref=master" -o <MPN>.kicad_mod-# Symbol — connectors use the GENERIC N-pin symbol (correct + standard).+# Symbol, connectors use the GENERIC N-pin symbol (correct + standard). #   kicad-symbols stores libs as DIRECTORIES: Connector_Generic.kicad_symdir/ curl -s "https://gitlab.com/api/v4/projects/kicad%2Flibraries%2Fkicad-symbols/repository/files/Connector_Generic.kicad_symdir%2FConn_01x0<N>.kicad_sym/raw?ref=master" -o <MPN>.kicad_sym-# Matched 3D STEP — read the (model ...) path from the .kicad_mod, then:+# Matched 3D STEP, read the (model ...) path from the .kicad_mod, then: service-kicad model fetch "Connector_JST.3dshapes/<FPNAME>.step" --out <MPN>.step ```  **Gotchas (all hit on 2026-06-27):** - KiCad ships **ZE (not ZH)** for the 1.5 mm series. ZH proper needs SnapMagic.-- `kicad-packages3D` only carries ~159 of the 500 footprints' models — **many+- `kicad-packages3D` only carries ~159 of the 500 footprints' models, **many   series (VH, etc.) have NO KiCad 3D**. For those, get the STEP via the JST-  **email flow** (§3–4) and pair it with the KiCad footprint.+  **email flow** (§3 to 4) and pair it with the KiCad footprint. - Import connectors with **`--no-validate`**: the `gate`/validate step is-  chip-centric — it flags a connector's catalog PDF as "no pin-table, ds2sf+  chip-centric, it flags a connector's catalog PDF as "no pin-table, ds2sf   can't extract a symbol" and marks the card incomplete. That's a false   negative for connectors (the symbol is the generic N-pin one). A connector   card with step+mod+sym+pdf IS complete; ignore that specific gate complaint.@@ -223,9 +220,9 @@ SH `SM02B-SRSS-TB`, GH `BM02B-GHS-TBT`, VH `B2P-VH` (VH STEP via email). - One email per file, one file per `(series, doc, filename)` GET→POST. STEP and   IGES are separate requests/emails. - Heartbeat each stage: `adom-chip-fetcher heartbeat <PART> "probing jst-mfg.com"`-  / `"requested STEP — awaiting email"` / `"imported"`.+  / `"requested STEP, awaiting email"` / `"imported"`. -### 7a. Inbox hygiene — DEDUP before every request (mandatory)+### 7a. Inbox hygiene, DEDUP before every request (mandatory)  Every JST request emails the user a file. Re-requesting spams their inbox. Before submitting the agreement form for any `(part, format)`:@@ -236,13 +233,13 @@ Before submitting the agreement form for any `(part, format)`:    (IGES) when the series has no STEP column. Never grab both "to be safe." 3. **One submit per file, ever.** Do not re-submit a part you already requested    this session (track what you've sent). A double-click / retry = a second-   email. The form's success alert is your confirmation — don't resubmit to+   email. The form's success alert is your confirmation, don't resubmit to    "make sure." 4. **Route to a filtered alias, not the raw inbox.** Use the user's plus-alias    (e.g. `[email protected]`) with a label/filter so the batch doesn't bury    their inbox. See [`jst-email-retrieval.md`](jst-email-retrieval.md). 5. **Clean up after a proof/demo.** Test/demo runs that submit the form generate-   real emails — trash the duplicates afterward+   real emails, trash the duplicates afterward    (`adom-google api -X POST .../messages/<id>/trash`).  Skipping dedup is how a 5-part batch becomes a 12-email inbox flood (learned the@@ -251,14 +248,14 @@ demo re-submit, and a double-submit).  ## Source plugin -`src/sources/jst.rs` — `series_index_url`, `series_url`, `search_url`,+`src/sources/jst.rs`, `series_index_url`, `series_url`, `search_url`, `download_url`, `catalog_pdf_url`, `DocKind`, `FORM_FIELDS`, `base_part`.   ## Building animated 3D mating component pages (wiki components)  This is the high-value deliverable: a wiki **component page** whose 3D viewer shows-the connector **assembling itself in a looping animation** — board sliding into the+the connector **assembling itself in a looping animation**, board sliding into the PCB (THT pins passing through to the underside), then the wire housing mating into the board. KiCad sits beside it as a comparison foil. Learned end-to-end 2026-06-28. @@ -269,14 +266,14 @@ the board. KiCad sits beside it as a comparison foil. Learned end-to-end 2026-06   compare below. - **Colour = source:** JST renders blue, KiCad renders tan/grey. Plus an **opaque   nameplate** per column ("JST manufacturer" / "KiCad library"). Users WANT to know-  which model is which — never drop the labels.+  which model is which, never drop the labels. - **PCB:** a thin **floating** slab (≈1.6 mm), footprint texture on top, **no riser-  block underneath** — a riser hides the pin tips poking through, which is the whole+  block underneath**, a riser hides the pin tips poking through, which is the whole   point. The footprint pads sit a few mm above the viewer's ground plane. - **THT vs SMT seating:** THT parts (`B2B-*` PH/XH/ZH) seat with **pins through the-  PCB, protruding ≈2 mm out the bottom** (the money shot — user checks pins line up+  PCB, protruding ≈2 mm out the bottom** (the money shot, user checks pins line up   with holes). SMT parts (`SM02B/BM02B` SH/GH) rest tabs on the pad surface (no-  through-pins — say so).+  through-pins, say so).  ### Mate orientation (which way the wire slides in) Encoded in the JST part number AND drawn in the datasheet:@@ -289,7 +286,7 @@ STEP frames are inconsistent. Don't guess from bbox (connectors are ~cubic). Use - `step2glb features <step>` → floor_z / longest-axis hints (treat as hints, verify). - `step2glb thumbnail <step|glb> --pose front|top|side` → read the pin axis directly. - KiCad models are reliably **Z-up, pins -Z** (identity). JST THT boards here needed-  **R_x(+90)** (pins along native -Y); each JST wire housing differs — check its orthos.+  **R_x(+90)** (pins along native -Y); each JST wire housing differs, check its orthos. - Seat board so `bbox.min_z` (pin tip) = `PCB_bottom - protrude` (THT) or `PCB_top` (SMT).  ### Authoring the animation (GLB node channels)@@ -299,10 +296,10 @@ STEP frames are inconsistent. Don't guess from bbox (connectors are ~cubic). Use - Add animation with **@gltf-transform** (`createAnimationSampler` / `createAnimationChannel`,   target path `translation`, LINEAR). Each node lerps from a small **retract offset**   back to 0 (seated). Keep lifts **small** (~board: clear the PCB top + ~2.5 mm; wire:-  ~its height + 4 mm) — big lifts waste the loop and bury the money shot.+  ~its height + 4 mm), big lifts waste the loop and bury the money shot. - Timeline: board drops, then wire mates, then **dwell long on the fully-mated state**,   quick reset, loop. The wiki 3D viewer (Babylon) **autoplays + loops** embedded GLB-  animations — no play button needed.+  animations, no play button needed.  ### CRITICAL gotchas - **Viewer grounds to the model's lowest geometry.** So a footprint quad that is the@@ -311,11 +308,11 @@ STEP frames are inconsistent. Don't guess from bbox (connectors are ~cubic). Use - **The 3D viewer is an `<iframe>`** (`/viewer/3d/component/<slug>`). Reloading the   parent page does **not** refresh the iframe's cached GLB. To see an update: bump the   GLB **filename** (`-v5.glb`) and/or `iframe.src = src.split('?')[0]+'?cb=…'`. Burned-  ~4 republishes chasing "my fix isn't showing" — it was always iframe cache.+  ~4 republishes chasing "my fix isn't showing", it was always iframe cache. - **Transparent label PNGs bleed through** a back-to-back double-sided sign. Composite   the label onto an **opaque** background before texturing; put the normal image on the   front quad and a `ImageOps.mirror` copy on the back quad, separated ~0.4 mm.-- **STEP→GLB emits Draco-compressed** GLBs the merge tools can't read — decompress with+- **STEP→GLB emits Draco-compressed** GLBs the merge tools can't read, decompress with   gltf-transform (`unpartition` + dispose `KHR_draco_mesh_compression`) first.  ### Pipeline & publish@@ -331,12 +328,12 @@ JST_ZH_B2B-ZR_1x02_P1.50mm_Vertical` gives the footprint even though no 3D model   bbox and asserts: connector **body_bottom == PCB_top** (housing sits ON the PCB, not   sunk into it), THT **pin_tip < PCB_bottom** (pins truly protrude), wire stays above   the PCB. Detect the **body bottom** (not bbox-min, which is the pin tips) by walking z-  slices and finding where the cross-section widens to the body — seat THAT on the PCB.+  slices and finding where the cross-section widens to the body, seat THAT on the PCB.   Seating by bbox-min sinks the housing into the board; this was a real bug.-- **Insert the wire DEEP — ~80% of the board-body depth**, not just touching. The user+- **Insert the wire DEEP, ~80% of the board-body depth**, not just touching. The user   needs to see it truly seat and see JST's contact-lock slots engage. (`INSERT=0.80`.) - **Float the PCB high (top ≈8 mm) with a tiny ground anchor.** The viewer grounds to-  the lowest geometry, so drop a ~0.3 mm speck at z=0 under each column — the viewer's+  the lowest geometry, so drop a ~0.3 mm speck at z=0 under each column, the viewer's   ground (and shadow) sit there, the PCB floats ~6 mm up, and you can angle the camera   **under** the board to inspect the protruding pins. No riser block (it hides the pins). - **Shadows are viewer-side, already on:** the Babylon viewer has a SpotLight + 1@@ -344,8 +341,8 @@ JST_ZH_B2B-ZR_1x02_P1.50mm_Vertical` gives the footprint even though no 3D model   the PCB is what makes the descending part's shadow read clearly. Richer per-part   inter-shadowing would be a wiki-viewer change, not a GLB change. - **If KiCad has no 3D model** for a JST part (e.g. ZH `B2B-ZR`), don't silently drop-  the column — render a standing **red placard** ("KiCad: no 3D model — ships sym+fp only")+  the column, render a standing **red placard** ("KiCad: no 3D model, ships sym+fp only")   in the KiCad slot so the user knows *why* it's absent. (Add a `{"placeholder":true}`   column to the merge config.)-- **Animation dwell:** keep lifts small and dwell long on the fully-mated state — that+- **Animation dwell:** keep lifts small and dwell long on the fully-mated state, that   (pins through + wire seated) is the money shot, not the parts hovering far above.
playbooks/manufacturer-direct.md+35−35
@@ -2,11 +2,11 @@  Per-vendor patterns for finding CAD + datasheets directly on the manufacturer's site. Read this when you've identified the manufacturer for an MPN and need to drive their specific page layout. -**All scraping in this playbook must run in pup `sessionId: "chip-fetcher"` (NOT the dashboard session) and must heartbeat every stage transition via `/api/activity` per [`dashboard-flow.md`](dashboard-flow.md).** Canonical stage labels: `probing <vendor>.com` → `found mfr-direct CAD` (or `no mfr-direct CAD — falling back to <source>`) → `downloading <format>` → `pulling from desktop` → `imported`.+**All scraping in this playbook must run in pup `sessionId: "adom-chip-fetcher"` (NOT the dashboard session) and must heartbeat every stage transition via `/api/activity` per [`dashboard-flow.md`](dashboard-flow.md).** Canonical stage labels: `probing <vendor>.com` → `found mfr-direct CAD` (or `no mfr-direct CAD, falling back to <source>`) → `downloading <format>` → `pulling from desktop` → `imported`.  ## Confidence is not a substitute for the mfr probe -The rule isn't "check the manufacturer when uncertain." It's "always START at the manufacturer's site, even when 100% confident." Recurring violation pattern: AI recalls a known part family from training → queries Mouser to verify stock → recommends "want me to fetch this from SnapMagic?" — without ever opening `<mfr>.com`. **That is a direct skill-rule violation, even when the part really does exist on SnapMagic.**+The rule isn't "check the manufacturer when uncertain." It's "always START at the manufacturer's site, even when 100% confident." Recurring violation pattern: AI recalls a known part family from training → queries Mouser to verify stock → recommends "want me to fetch this from SnapMagic?", without ever opening `<mfr>.com`. **That is a direct skill-rule violation, even when the part really does exist on SnapMagic.**  Past direct user pushback (4+ repeats across sessions): - *"i keep telling you to not go beyond the manufacturers website."*@@ -16,7 +16,7 @@ Past direct user pushback (4+ repeats across sessions):  Some manufacturers self-host CAD that's better than the aggregators (Würth's UL-direct CAE icon, NXP's BXL bundles). Some have UL deep-links that are still mfr-direct because the link is on the mfr's page. Some don't host CAD at all and SnapMagic is the correct fallback. **You don't know which until you actually visit `<mfr>.com`.** 30 seconds of opening the page often changes the recommendation; pay the cost. -**Operational rule:** when a user asks for any chip / LED / connector / passive — even one you're 100% sure about — your FIRST action is `browser_open_window` on the manufacturer's product page. Walk Documentation, Tools & Software, CAD icon, Resources, Hardware Files. Scrape `<img alt>` for icon-only buttons. Only AFTER that probe is it OK to fall to SnapMagic / Mouser / DigiKey, and even then REPORT the mfr's state to the user before bouncing. Mouser stock-API queries can run in parallel as a side-channel, but they don't substitute for the mfr CAD probe.+**Operational rule:** when a user asks for any chip / LED / connector / passive, even one you're 100% sure about, your FIRST action is `pup_open_window` on the manufacturer's product page. Walk Documentation, Tools & Software, CAD icon, Resources, Hardware Files. Scrape `<img alt>` for icon-only buttons. Only AFTER that probe is it OK to fall to SnapMagic / Mouser / DigiKey, and even then REPORT the mfr's state to the user before bouncing. Mouser stock-API queries can run in parallel as a side-channel, but they don't substitute for the mfr CAD probe.  ## Self-hosting matrix @@ -25,14 +25,14 @@ Some manufacturers self-host CAD that's better than the aggregators (Würth's UL | TI        | no (UL only)    | no (UL only)                  | direct, open URL  | | ST        | no (UL only)    | no (UL only)                  | in-page fetch (CDN blocks curl) | | ADI       | no (UL only)    | no (UL only)                  | in-page fetch     |-| **NXP**   | no              | **YES — BXL format** (mfr-direct) | direct PDF        |+| **NXP**   | no              | **YES, BXL format** (mfr-direct) | direct PDF        | | Microchip | no (UL gated)   | no (UL gated)                 | Microchip CDN     | | Renesas   | no              | partcommunity (separate flow) | direct PDF        | | Bosch     | NO CAD AT ALL   | NO CAD AT ALL                 | direct PDF        | | Syntiant  | NO CAD AT ALL   | NO CAD AT ALL                 | NDA-gated         |-| **Espressif** | **YES via Mouser CSE Library Loader.** Both bare chips (ESP32-S3FN8) and modules (ESP32-S3-WROOM-1-N4) have full STEP+sym+footprint via mouser.componentsearchengine.com. | YES (same path) | **Open URLs at `www.espressif.com/sites/default/files/documentation/<mpn>_<doctype>_en.pdf`** — datasheet + TRM are huge (10–15 MB) and curl-able from container. |+| **Espressif** | **YES via Mouser CSE Library Loader.** Both bare chips (ESP32-S3FN8) and modules (ESP32-S3-WROOM-1-N4) have full STEP+sym+footprint via mouser.componentsearchengine.com. | YES (same path) | **Open URLs at `www.espressif.com/sites/default/files/documentation/<mpn>_<doctype>_en.pdf`**, datasheet + TRM are huge (10 to 15 MB) and curl-able from container. | -For NXP specifically: BXL files are Accelerated Designs format — convert to KiCad via their Library Loader (Windows app) or treat as opaque mfr-authored asset.+For NXP specifically: BXL files are Accelerated Designs format, convert to KiCad via their Library Loader (Windows app) or treat as opaque mfr-authored asset.  ## Vendor URL & UL deep-link patterns @@ -43,33 +43,33 @@ Each manufacturer has its own product-page URL pattern, but most eventually link | **TI**        | `ti.com/product/<MPN>`                                    | `vendor.ultralibrarian.com/TI/embedded/?gpn=<MPN>&...`      | | **ST**        | `st.com/en/<category>/<mpn>.html`                         | `vendor.ultralibrarian.com/stmicro/embedded?rpn=<MPN>&...`  | | **ADI**       | `analog.com/en/products/<mpn>.html`                       | `vendor.ultralibrarian.com/adi/embedded?vdrPN=<MPN>`        |-| **NXP**       | `nxp.com/products/<MPN>` *(self-hosts BXL — no UL)*       | n/a — direct `.bxl.zip` download                            |+| **NXP**       | `nxp.com/products/<MPN>` *(self-hosts BXL, no UL)*       | n/a, direct `.bxl.zip` download                            | | **Microchip** | `microchip.com/en-us/product/<mpn>` *(login-gated)*       | `vendor.ultralibrarian.com/microchip?vdrpn=<MPN>` *(in iframe)* |-| **Renesas**   | `renesas.com/en/products/<mpn>`                           | n/a — Renesas routes through CADENAS PartCommunity          |+| **Renesas**   | `renesas.com/en/products/<mpn>`                           | n/a, Renesas routes through CADENAS PartCommunity          | | **WAGO**      | `wago.com/global/p/<MPN>`                                 | `vendor.ultralibrarian.com/Wago/Embedded/?vdrPN=<MPN>` *(via "CAE" icon)* |  Deferred: **Renesas** (CADENAS PartCommunity has its own auth flow), **Bosch / TDK / Allegro / etc.** (no CAD on their sites; need UL login). -## TI — open everything+## TI, open everything -Datasheets are trivial — `curl https://www.ti.com/lit/gpn/<base-mpn>` returns the PDF directly. No CAPTCHA, no login. Batch a whole list in one shell loop. Use `base-mpn` (e.g. `tps62840`, not `tps62840dlcr`) — that's the lowercase chip-family slug.+Datasheets are trivial, `curl https://www.ti.com/lit/gpn/<base-mpn>` returns the PDF directly. No CAPTCHA, no login. Batch a whole list in one shell loop. Use `base-mpn` (e.g. `tps62840`, not `tps62840dlcr`), that's the lowercase chip-family slug.  ## Microchip iframe gotcha -Microchip embeds the UL form in a **cross-origin iframe** at `vendor.ultralibrarian.com/microchip?vdrpn=<MPN>`. `browser_eval` from the parent (microchip.com) page **cannot reach into the iframe** (CSP). Workaround: navigate the entire pup tab to the iframe URL directly. Then the form is at top level and the standard form-driver works.+Microchip embeds the UL form in a **cross-origin iframe** at `vendor.ultralibrarian.com/microchip?vdrpn=<MPN>`. `pup_eval` from the parent (microchip.com) page **cannot reach into the iframe** (CSP). Workaround: navigate the entire pup tab to the iframe URL directly. Then the form is at top level and the standard form-driver works.  ```bash-adom-desktop browser_navigate '{"sessionId":"chip-fetcher","url":"https://vendor.ultralibrarian.com/microchip?vdrpn=ATSAMD51J20A-AUT"}'+adom-bridge pup_navigate '{"sessionId":"adom-chip-fetcher","url":"https://vendor.ultralibrarian.com/microchip?vdrpn=ATSAMD51J20A-AUT"}' ``` -Also: Microchip CAD is gated behind myMicrochip login. First-time use, OAuth popup opens — sign in with Google (uses the shared pup-profile Google session) or email/password.+Also: Microchip CAD is gated behind myMicrochip login. First-time use, OAuth popup opens, sign in with Google (uses the shared pup-profile Google session) or email/password.  ## NXP product-page detail (don't quit early!)  NXP product pages have **icon-only CAD buttons** next to the "Data Sheet" pill that are easy to miss in a text-based DOM scrape: -1. **Symbol/Footprint icon** (small green tile, schematic-looking) — links to NXP's `/downloads/en/symbols-footprints/<MPN>-<package>-BXL.zip` (mfr-direct, no auth)-2. **3D icon** (small green tile labeled "3D") — fires `loadPartDiv('NXP','<MPN>','nxp',...)` which renders a **componentsearchengine** (samacsys) overlay div directly on the NXP page. The 3D STEP file lives behind that widget.+1. **Symbol/Footprint icon** (small green tile, schematic-looking), links to NXP's `/downloads/en/symbols-footprints/<MPN>-<package>-BXL.zip` (mfr-direct, no auth)+2. **3D icon** (small green tile labeled "3D"), fires `loadPartDiv('NXP','<MPN>','nxp',...)` which renders a **componentsearchengine** (samacsys) overlay div directly on the NXP page. The 3D STEP file lives behind that widget.  To find them programmatically: ```js@@ -89,12 +89,12 @@ loadPartDiv('NXP','<MPN>','nxp',1,'zip',0,'','en-GB',0,'',0); ### NXP login + EXTENDED PROFILE gotcha  - **NXP** open without auth: datasheet (`/docs/en/data-sheet/<id>.pdf`), block diagram (`/assets/block-diagram/...`), errata (`/docs/en/errata/...`), factsheet (`/docs/en/fact-sheet/...`), application notes (`/docs/en/application-note/AN<id>.pdf`), symbol/footprint BXL (`/downloads/en/symbols-footprints/<MPN>-<package>-BXL.zip`).-- **NXP** requires login: Reference Manual, Security RM, Hardware Design Guide, FRDM/EVK board user manuals — accessed via `/webapp/Download?colCode=<id>` which redirects to `login.nxp.com` (Azure B2C). Sign in with the user's NXP account, then re-fetch.+- **NXP** requires login: Reference Manual, Security RM, Hardware Design Guide, FRDM/EVK board user manuals, accessed via `/webapp/Download?colCode=<id>` which redirects to `login.nxp.com` (Azure B2C). Sign in with the user's NXP account, then re-fetch. - **NXP requires an EXTENDED PROFILE** (Tell-us-about-yourself, business title, job description, company primary business, address) before unlocking Reference Manuals + Hardware Design Guides + Board User Manuals. Detect the redirect to `/webapp-signup/updateUser?userType=EXTENDED&mandatory=YES&TARGETURL=...` after a `colCode=` Download attempt and direct the user to fill that form (one-time per account). After submission, the original `colCode=` URL serves the PDF. -## Vishay — manufacturer-direct CAD via Design Tools page (verified 2026-05-03)+## Vishay, manufacturer-direct CAD via Design Tools page (verified 2026-05-03) -Vishay self-hosts 3D models as `.zip` files on their **Design Tools** page — full mfr-direct, no UL/CSE/SnapMagic bounce required. **This is the cleanest provenance tier.** Discovered by John pointing me at the page after I missed it on first walk.+Vishay self-hosts 3D models as `.zip` files on their **Design Tools** page, full mfr-direct, no UL/CSE/SnapMagic bounce required. **This is the cleanest provenance tier.** Discovered by John pointing me at the page after I missed it on first walk.  ### Design Tools URL pattern @@ -112,11 +112,11 @@ The result is a sortable table with rows like: | 3D Model | SMD 0402 ChipLED (*.zip) | LEDs | 🔗 | | 3D Model | ChipLED Highbright 0603 VLMB1300 (*.zip) | LEDs | 🔗 | -Each row's `<a href="*.zip">` is a direct download — `https://www.vishay.com/docs/<doc-id>/<filename>.zip`.+Each row's `<a href="*.zip">` is a direct download, `https://www.vishay.com/docs/<doc-id>/<filename>.zip`.  ### Family-level CAD (Vishay's strategy) -**Vishay publishes ONE zip per package family, not per MPN.** Example: `vlmx15xx.zip` covers the entire VLMx15xx 0402 ChipLED family — Super Red (VLMS1500), True Green (VLMTG1500), Blue (VLMB1500), White (VLMW1500), Soft Orange (VLMO1500), Yellow (VLMY1500), and others all share the same physical SMD 0402 body, so one STEP file applies to all of them. Per-part differences (color, wavelength, Vf) come from the datasheet, not the 3D model.+**Vishay publishes ONE zip per package family, not per MPN.** Example: `vlmx15xx.zip` covers the entire VLMx15xx 0402 ChipLED family, Super Red (VLMS1500), True Green (VLMTG1500), Blue (VLMB1500), White (VLMW1500), Soft Orange (VLMO1500), Yellow (VLMY1500), and others all share the same physical SMD 0402 body, so one STEP file applies to all of them. Per-part differences (color, wavelength, Vf) come from the datasheet, not the 3D model.  **Importer implication:** when fetching from Vishay's family-level zip, distribute the extracted STEP to EACH MPN's `library/<MPN>/<MPN>.step` directory. Tag `info.json` with: ```json@@ -124,11 +124,11 @@ Each row's `<a href="*.zip">` is a direct download — `https://www.vishay.com/d   "step_source": "mfr-direct",   "step_authoring": "mfr-uploaded",   "step_url": "https://www.vishay.com/docs/<doc-id>/<family>.zip",-  "step_note": "Family-level package body — covers all <FAMILY>xxxx parts. Color/wavelength/Vf differs per part; consult datasheet."+  "step_note": "Family-level package body, covers all <FAMILY>xxxx parts. Color/wavelength/Vf differs per part; consult datasheet." } ``` -This pattern likely applies to other passive makers too (Würth, Murata, Yageo) — assume family-level until proven otherwise.+This pattern likely applies to other passive makers too (Würth, Murata, Yageo), assume family-level until proven otherwise.  ### Vishay naming conventions for indicator LEDs @@ -151,13 +151,13 @@ Series digits indicate package size:  ### What Vishay does NOT host -The Design Tools page filtered to "Symbol/Footprint" for LEDs returns very limited results — Vishay does not appear to publish KiCad/Altium symbols + footprints for indicator LEDs as cleanly as the 3D models. **Confirm by visiting individual product detail pages too** (sometimes vendors publish sym/mod separately on the part page rather than in the design-tools index).+The Design Tools page filtered to "Symbol/Footprint" for LEDs returns very limited results, Vishay does not appear to publish KiCad/Altium symbols + footprints for indicator LEDs as cleanly as the 3D models. **Confirm by visiting individual product detail pages too** (sometimes vendors publish sym/mod separately on the part page rather than in the design-tools index).  If Vishay's part page also lacks sym/mod, the canonical fallback is:  1. **Mouser product page → "ECAD Model" link** (routes into `mouser.componentsearchengine.com/...` and serves a `LIB_<MPN>.zip` with `.step` + `.kicad_sym` + `.kicad_mod`).-2. The Mouser CSE bundle's STEP is mfr-uploaded too, but **Vishay's direct STEP is more accurate** — keep Vishay's STEP, take only the sym/mod from the CSE zip.-3. This is the "mfr-direct STEP + distributor-CSE sym/mod" hybrid pattern — tag `info.json`'s `sym_source` and `mod_source` separately from `step_source`:+2. The Mouser CSE bundle's STEP is mfr-uploaded too, but **Vishay's direct STEP is more accurate**, keep Vishay's STEP, take only the sym/mod from the CSE zip.+3. This is the "mfr-direct STEP + distributor-CSE sym/mod" hybrid pattern, tag `info.json`'s `sym_source` and `mod_source` separately from `step_source`:  ```json {@@ -173,15 +173,15 @@ Direct user direction during the verifying session: *"if you fail [on Vishay sym  --- -## Lattice / iCE40 family — Documentation tab has 30+ PDFs+## Lattice / iCE40 family, Documentation tab has 30+ PDFs -iCE40 UltraPlus has a Documentation tab with 30+ PDFs (datasheet, hardware checklist, design guidelines, programming guide, all the IP usage guides, package diagrams, PCB layout, solder reflow, thermal management) — all at `https://www.latticesemi.com/view_document?document_id=<N>`. None of these are in the `<chip-page>/links` scrape — you have to click the Documentation tab to see them all listed.+iCE40 UltraPlus has a Documentation tab with 30+ PDFs (datasheet, hardware checklist, design guidelines, programming guide, all the IP usage guides, package diagrams, PCB layout, solder reflow, thermal management), all at `https://www.latticesemi.com/view_document?document_id=<N>`. None of these are in the `<chip-page>/links` scrape, you have to click the Documentation tab to see them all listed.  User pushback on bailing too quickly: *"are you sure you couldn't navigate the lattice site? i feel like you give up too fast."* **Always click the Documentation tab and scrape ALL `view_document` URLs you find.**  ## Sources have rich scene graphs (TI verified) -Confirmed across 8 TI parts grabbed via UL — UL ships **manufacturer-grade B-Rep STEPs with named PRODUCTs**:+Confirmed across 8 TI parts grabbed via UL, UL ships **manufacturer-grade B-Rep STEPs with named PRODUCTs**:  | MPN | Package | PRODUCTs (named) | Faces | Cyl | Sph | |---|---|---|---|---|---|@@ -197,10 +197,10 @@ These are exactly the named-scene-graph signals adom-chiplinter Mode 0 needs.  When a manufacturer publishes a `<MPN>-reference-layout-<ver>.zip`, it's a full board-design bundle, NOT a standalone chip CAD. Verified on Nordic's nRF52840-CKAA portal: the 18 MB zip contains: -- `Altium Designer files/` — `.IntLib`, `.PcbDoc`, `.PrjPCB`, `.SchDoc` (6 board variants)-- `Bill of Materials/` — per-variant `.xls` files-- `Production files/` — Gerbers, drill (`.DRL`)-- `Schematic and Layout pdf files/` — board-level PDFs+- `Altium Designer files/`, `.IntLib`, `.PcbDoc`, `.PrjPCB`, `.SchDoc` (6 board variants)+- `Bill of Materials/`, per-variant `.xls` files+- `Production files/`, Gerbers, drill (`.DRL`)+- `Schematic and Layout pdf files/`, board-level PDFs  **Where it goes:** keep in `library/<MPN>/reference-layout/<...preserve subdirs...>` as a separate artifact bucket. Don't try to extract STEP/sym/mod from the Altium files automatically. Surface it in the viewer as "Reference layout: <N> BOMs · <N> Altium variants · <N> Gerbers · <N> PDFs." @@ -210,9 +210,9 @@ When a manufacturer publishes a `<MPN>-reference-layout-<ver>.zip`, it's a full  chip-fetcher is community-built and recursively-learning. When the agent hits a dead end: -1. **Open the product page in pup foreground** (`browser_raise_os_window`).+1. **Open the product page in pup foreground** (`pup_raise_os_window`). 2. **Take a screenshot** so the user can see what page state the agent landed on.-3. **Ask one short question:** *"I can't find a CAD download path on this page — can you spot it? Click the right button or describe what you see."*+3. **Ask one short question:** *"I can't find a CAD download path on this page, can you spot it? Click the right button or describe what you see."* 4. **Wait for the user's hint.** 5. **Implement the path the user described.** 6. **Capture the new pattern back into this skill** with a vendor-specific section so the next agent doing the same vendor's part finds it automatically.@@ -226,7 +226,7 @@ Failure modes pushed back on:  ## Grab ALL design-help PDFs while you're on a vendor page -Datasheet alone isn't enough for an EE: also pull errata, hardware design guides, app notes, reference designs, anti-counterfeit guides, package mechanical drawings, IBIS models. EEs need the complete doc set. **Save each as `library/<MPN>/<MPN>-<doc-type>.pdf`** (e.g. `MCXN947VNLT-errata.pdf`, `MCXN947VNLT-hwguide.pdf`, `MCXN947VNLT-appnote-spi.pdf`). Don't pop in to grab just the STEP and leave — every page visit is an opportunity to harvest the full design package.+Datasheet alone isn't enough for an EE: also pull errata, hardware design guides, app notes, reference designs, anti-counterfeit guides, package mechanical drawings, IBIS models. EEs need the complete doc set. **Save each as `library/<MPN>/<MPN>-<doc-type>.pdf`** (e.g. `MCXN947VNLT-errata.pdf`, `MCXN947VNLT-hwguide.pdf`, `MCXN947VNLT-appnote-spi.pdf`). Don't pop in to grab just the STEP and leave, every page visit is an opportunity to harvest the full design package.  ## Provenance tag in info.json 
playbooks/mouser-cse.md+44−49
@@ -1,10 +1,10 @@ # Mouser + Component Search Engine (chip-fetcher playbook) -Mouser is a first-class playbook (NOT a fallback tier) — it covers the long tail of chips without dedicated mfr CAD pages. Read this when manufacturer-direct doesn't host CAD, OR when you need stock + pricing + ECAD in one go.+Mouser is a first-class playbook (NOT a fallback tier), it covers the long tail of chips without dedicated mfr CAD pages. Read this when manufacturer-direct doesn't host CAD, OR when you need stock + pricing + ECAD in one go. -**Heartbeat every stage** per [`dashboard-flow.md`](dashboard-flow.md). The slide-CAPTCHA escalation is a particularly important moment to heartbeat: post `mouser slide-CAPTCHA — user help needed` so the dashboard surfaces the stuck state, then raise the scraping window with `browser_raise_os_window` and let the user drag the slider. Don't silently wait — the user should see on the dashboard that we're stuck on Mouser.+**Heartbeat every stage** per [`dashboard-flow.md`](dashboard-flow.md). The slide-CAPTCHA escalation is a particularly important moment to heartbeat: post `mouser slide-CAPTCHA, user help needed` so the dashboard surfaces the stuck state, then raise the scraping window with `pup_raise_os_window` and let the user drag the slider. Don't silently wait, the user should see on the dashboard that we're stuck on Mouser. -## Mouser CSE Library Loader — the universal fallback (don't miss this)+## Mouser CSE Library Loader, the universal fallback (don't miss this)  **Critical pattern:** when a manufacturer has no CSE portal of their own (`<mfr>.componentsearchengine.com` returns `DNS_PROBE_FINISHED_NXDOMAIN`) AND no UL deep-link AND nothing on the mfr's own site, **check Mouser CSE**. @@ -16,41 +16,36 @@ Mouser proxies CSE for any chip in their catalog, not just chips whose manufactu  The entry endpoint returns one of three pages. Only one has a `partID`: - **"SamacSys Part Preview"** (len ~50KB) → contains `partID=<N>`. ✅ This is what you want.-- **"SamacSys Part Selector" / "Pick One"** (len ~8–21KB) → a disambiguation page, **no partID**. ❌+- **"SamacSys Part Selector" / "Pick One"** (len ~8 to 21KB) → a disambiguation page, **no partID**. ❌  Two independent causes push you onto the no-partID pages:  1. **Use the ORDERABLE MPN, not the base part.** `mpn=ADS1263` → "Pick One" (the base part has many package variants, so CSE can't resolve one). `mpn=ADS1263IPWR` (orderable, with package suffix) → "Part Preview", partID 755745. **Always pass the orderable MPN with its package/reel suffix.** If you only have the base MPN, a distributor parametric lookup (`adom-mouser`/`adom-digikey`) gives the orderable variant, or try the common suffix (TSSOP `…IPWR`, QFN `…IRTER`). -2. **Do NOT append `&logo=&epm=0` to the entry URL.** Those two extra params silently flip CSE into the "Part Selector" page (no partID) **even for a valid orderable MPN**. Use the minimal param set above. This cost an afternoon of "no-partID" debugging — the bug was a malformed URL, not the chip or the browser.+2. **Do NOT append `&logo=&epm=0` to the entry URL.** Those two extra params silently flip CSE into the "Part Selector" page (no partID) **even for a valid orderable MPN**. Use the minimal param set above. This cost an afternoon of "no-partID" debugging, the bug was a malformed URL, not the chip or the browser. -### `chip-fetcher cse <MPN> --mfr "<Manufacturer>"` — the automated path (use this first)+### `chip-fetcher cse <MPN> --mfr "<Manufacturer>"`, the automated path (use this first)  Does the whole CSE chain in code: navigate clean → entry (orderable MPN) → partID → preview → zipForm → `ga/model.php` → import STEP+symbol+footprint. Retries the cold-call flake and keeps the dashboard "working on" bar live via a heartbeat ticker. Prefer it over hand-driving URLs. -### How the download works — same-origin XHR + Basic-auth header (the breakthrough)+### How the download works, same-origin XHR with pup answering Basic Auth -CSE downloads are HTTP Basic Auth. Inline-cred URLs (`https://user:pass@host/…`) are REJECTED by `fetch()`, and a plain navigate pops Chrome's invisible Basic-auth dialog. **The robust technique: a same-origin `XMLHttpRequest` (from a tab already on `mouser.componentsearchengine.com`) with `xhr.setRequestHeader('Authorization','Basic '+btoa(user+':'+pass))`.** XHR (unlike fetch) accepts the Authorization header AND pulls the bytes into JS as an arraybuffer — so the zip never hits Chrome's download policy (which silently blocks the 2nd+ download). This is what `cse_fetch_zip` in `pup.rs` does. **Vault-auth works in ANY browser** — the auth is the XHR header from chip-fetcher's vault, not a browser cookie, so CSE runs fine in the user's Chrome even if their saved CSE login lives in Edge.+CSE downloads are HTTP Basic Auth. Inline-cred URLs (`https://user:pass@host/...`) are REJECTED by `fetch()`, and a plain navigate to a download URL pops Chrome's invisible Basic-auth dialog. **The robust technique: a same-origin `XMLHttpRequest` from a tab that pup navigated to `mouser.componentsearchengine.com`.** pup's vault applies the `*.componentsearchengine.com` credential via `page.authenticate()` on every pup navigate, which also answers the 401 for every XHR the page makes, and XHR pulls the bytes into JS as an arraybuffer so the zip never hits Chrome's download policy (which silently blocks the 2nd+ download). This is what `cse_fetch_zip` in `pup.rs` does. No header is built by hand. Condition: the tab must be navigated by a pup verb, not the native browser. -**Real example:** Espressif has no CSE portal. Probing `espressif.componentsearchengine.com` gave NXDOMAIN. UL portal `vendor.ultralibrarian.com/espressif/embedded` gave "No Access is defined." But Mouser's product page for ESP32-S3FN8 had an "ECAD Model" block linking to `mouser.componentsearchengine.com/...` — and that path served a complete bundle. Same trick worked for the ESP32-S3-WROOM-1-N4 module.+**Real example:** Espressif has no CSE portal. Probing `espressif.componentsearchengine.com` gave NXDOMAIN. UL portal `vendor.ultralibrarian.com/espressif/embedded` gave "No Access is defined." But Mouser's product page for ESP32-S3FN8 had an "ECAD Model" block linking to `mouser.componentsearchengine.com/...`, and that path served a complete bundle. Same trick worked for the ESP32-S3-WROOM-1-N4 module. -## ⚠ HARD RULE — CSE downloads need credentials embedded in the URL+## ⚠ HARD RULE, CSE downloads are HTTP Basic Auth; pup's vault answers it -**Before you click any "Download CAD Models" button on `*.componentsearchengine.com`, embed credentials inline.** The `/signin` form-login session cookie is USELESS for downloads — those endpoints issue a separate HTTP Basic Auth challenge that pops a native Chrome dialog **invisible to `browser_eval`**. Symptom: page lands on `chrome-error://chromewebdata/`, no file in Downloads, nothing in DOM to indicate why. (Verified painful 2026-05-04.)+**The `/signin` form-login session cookie is USELESS for downloads**: those endpoints issue a separate HTTP Basic Auth challenge that pops a native Chrome dialog **invisible to `pup_eval`**. Symptom: page lands on `chrome-error://chromewebdata/`, no file in Downloads, nothing in DOM to indicate why. (Verified painful 2026-05-04.)++The fix is one vault entry, once:  ```bash-# Pull creds from chip-fetcher's vault, urlencode, prepend to URL:-CRED=$(curl -s http://127.0.0.1:8786/api/credentials | python3 -c '-import sys,json,urllib.parse-d=json.load(sys.stdin)-c=next((x for x in d["credentials"] if "componentsearchengine" in x["host"]), None)-if c: print(f"{urllib.parse.quote(c[\"username\"],safe=\"\")}:{urllib.parse.quote(c[\"password\"],safe=\"\")}")-')-URL="https://${CRED}@mouser.componentsearchengine.com/preview_newDesign.php?o3=0&partID=<ID>&ev=0&fmt=zip&pna=Mouser"-adom-desktop browser_navigate "{\"sessionId\":\"chip-fetcher\",\"url\":\"$URL\"}"+chip-fetcher creds save "*.componentsearchengine.com" <email> <password>+# or pull it from the user's browser:  chip-fetcher logins --import ``` -Always urlencode the username (`@` → `%40`) and password — special chars are common in CSE passwords. **Use this pattern by default for every CSE click that triggers a download.** Full diagnostic, OS-keychain alternative, and the two-vault sync requirement: see [`credentials-and-logins.md`](credentials-and-logins.md) § HARD RULE — CSE downloads use HTTP Basic Auth.+After that, every `pup_navigate` / `pup_open_tab` to any `<mfr>.componentsearchengine.com` URL is pre-authenticated. Never build `user:pass@` URLs and never set an `Authorization` header yourself. Full story: [`credentials-and-logins.md`](credentials-and-logins.md) § HARD RULE, CSE downloads use HTTP Basic Auth.  **Discovery flow when you can't find CAD on a vendor's site:** 1. Use `adom-mouser part <MPN>` to get the Mouser product URL.@@ -59,54 +54,54 @@ Always urlencode the username (`@` → `%40`) and password — special chars are 4. Follow the CSE URL into `mouser.componentsearchengine.com/...` directly. 5. Drive the standard CSE Download flow (same as NXP CSE). -## Mouser — first-class playbook+## Mouser, first-class playbook -**Mouser is the most important external resource chip-fetcher has after the manufacturer's own site.** It hosts the datasheet AND the ECAD bundle for nearly every chip in their catalog — and for the long tail of chips whose manufacturers don't publish CAD (Vishay passives, niche connectors, smaller-vendor SoCs), Mouser is the *only* source.+**Mouser is the most important external resource chip-fetcher has after the manufacturer's own site.** It hosts the datasheet AND the ECAD bundle for nearly every chip in their catalog, and for the long tail of chips whose manufacturers don't publish CAD (Vishay passives, niche connectors, smaller-vendor SoCs), Mouser is the *only* source.  Direct user quote: *"mouser is a key resource here so we have to get into the skill how to work with their website."*  **What's on every Mouser product page (right sidebar):** -- **`Datasheet:`** block — a `<MPN> Datasheet (PDF)` link. Either Mouser-CDN-hosted (`https://www.mouser.com/datasheet/<…>.pdf`) or a redirect to the manufacturer's CDN. Behind Akamai's bot wall, so use the in-page `fetch()` pattern, not naked curl.-- **`ECAD Model:`** block — `PCB Symbol, Footprint & 3D Model` link. Routes into `mouser.componentsearchengine.com/...` and serves a `LIB_<MPN>.zip` with `.step` + `.kicad_sym` + `.kicad_mod`.-- Extra reference docs (app notes, training PDFs) listed as additional `<a href="*.pdf">` rows further down — scrape them all.+- **`Datasheet:`** block, a `<MPN> Datasheet (PDF)` link. Either Mouser-CDN-hosted (`https://www.mouser.com/datasheet/<…>.pdf`) or a redirect to the manufacturer's CDN. Behind Akamai's bot wall, so use the in-page `fetch()` pattern, not naked curl.+- **`ECAD Model:`** block, `PCB Symbol, Footprint & 3D Model` link. Routes into `mouser.componentsearchengine.com/...` and serves a `LIB_<MPN>.zip` with `.step` + `.kicad_sym` + `.kicad_mod`.+- Extra reference docs (app notes, training PDFs) listed as additional `<a href="*.pdf">` rows further down, scrape them all. -## Operating manual — Mouser+## Operating manual, Mouser -1. **Always go through pup Chrome** — never naked curl. Their CDN serves a 174 KB HTML denial page (`"Access to this page has been denied because we believe you are using automation tools"`) for any non-browser request.-2. **Use the auto-cookie-accept helper** on every navigation — Mouser's cookie banner blocks downstream clicks via z-index.+1. **Always go through pup Chrome**, never naked curl. Their CDN serves a 174 KB HTML denial page (`"Access to this page has been denied because we believe you are using automation tools"`) for any non-browser request.+2. **Use the auto-cookie-accept helper** on every navigation, Mouser's cookie banner blocks downstream clicks via z-index. 3. **Use in-page `fetch()` with `{credentials:"include"}`** to pull PDFs (Chrome session cookies + JA3 fingerprint pass Akamai). Decode base64 result back into the library. `cf-mouser-pdf-pull.sh` is the reference implementation. 4. **For ECAD bundles, drive the full CSE Download flow inside the pup window.** Mouser-hosted CSE shares the same single-sign-on cookie as `componentsearchengine.com` proper, so once the user is signed in there, every Mouser ECAD link works without re-auth.-5. **Mouser's Search API (`adom-mouser`) gives you the canonical product URL + `datasheet_url` field for every variant.** Walk all variants — the version with the populated `datasheet_url` is sometimes a different package SKU than the MPN you searched for.+5. **Mouser's Search API (`adom-mouser`) gives you the canonical product URL + `datasheet_url` field for every variant.** Walk all variants, the version with the populated `datasheet_url` is sometimes a different package SKU than the MPN you searched for.  ## Mouser's slide-to-verify CAPTCHA -After ~5–10 automated requests in a session, Mouser shows a slide-to-verify CAPTCHA. **Stop and ask the user to drag the slider.** Use `browser_raise_os_window` + `browser_screenshot` to surface the obstacle, then say: *"Mouser hit me with a slide-CAPTCHA — please drag the slider on the pup window to clear it."* Resume the flow once the CAPTCHA clears.+After ~5 to 10 automated requests in a session, Mouser shows a slide-to-verify CAPTCHA. **Stop and ask the user to drag the slider.** Use `pup_raise_os_window` + `pup_screenshot` to surface the obstacle, then say: *"Mouser hit me with a slide-CAPTCHA, please drag the slider on the pup window to clear it."* Resume the flow once the CAPTCHA clears. -**DO NOT pivot to a different source for ECAD bundles** — there is no other source for those. Direct user pushback: *"that was a good test case … you can't just abandon the website if that's the only way to get the step file and symbol/footprint and the datasheet."*+**DO NOT pivot to a different source for ECAD bundles**, there is no other source for those. Direct user pushback: *"that was a good test case … you can't just abandon the website if that's the only way to get the step file and symbol/footprint and the datasheet."*  **Cooldown:** Mouser's verification token persists for 30+ minutes after the user clears the slider. Batch your Mouser fetches during that window so the user only ever has to slide once per session. -**Critical caveat:** Mouser's CDN is Akamai-protected and rejects naked `curl` (returns 200 OK with a "Access to this page has been denied" HTML page). To pull a Mouser-hosted PDF you must either (a) navigate pup Chrome there and trigger a download (file lands in `~/Downloads`, then `chip-fetcher pull`), or (b) use in-page `fetch()` via `browser_eval` with `credentials:"include"` — Chrome's session cookies pass Akamai's bot check and the response can be base64-encoded back through `awaitPromise:true`.+**Critical caveat:** Mouser's CDN is Akamai-protected and rejects naked `curl` (returns 200 OK with a "Access to this page has been denied" HTML page). To pull a Mouser-hosted PDF you must either (a) navigate pup Chrome there and trigger a download (file lands in `~/Downloads`, then `chip-fetcher pull`), or (b) use in-page `fetch()` via `pup_eval` with `credentials:"include"`, Chrome's session cookies pass Akamai's bot check and the response can be base64-encoded back through `awaitPromise:true`. -## When the slider hits — escalation order+## When the slider hits, escalation order -1. **Try `browser_input_dispatch type:"move"` along the slider track followed by `type:"click"` at the right edge.** Trusted CDP input has `isTrusted: true` and may pass GeeTest's basic acceleration check; some Mouser slider variants accept it.-2. **Pivot to vendor-direct ONLY if Mouser is fungible for this chip's docs.** TI lit IDs, ST `/resource/`, NXP `/docs/` — there are non-Mouser paths for documentation. Not for ECAD bundles.+1. **Try `pup_input_dispatch type:"move"` along the slider track followed by `type:"click"` at the right edge.** Trusted CDP input has `isTrusted: true` and may pass GeeTest's basic acceleration check; some Mouser slider variants accept it.+2. **Pivot to vendor-direct ONLY if Mouser is fungible for this chip's docs.** TI lit IDs, ST `/resource/`, NXP `/docs/`, there are non-Mouser paths for documentation. Not for ECAD bundles. 3. **For ECAD bundles, there is no fungible path.** Hand off via `desktop_open_url` to the user's native browser (real Chrome with browsing-history reputation, extensions, real input). They slide once, the CAPTCHA token persists 30+ minutes, then the rest of the session works through pup again. 4. **Don't silently abandon a website if it's the only path.** -## CSE auth — single login covers all `<mfr>.componentsearchengine.com` subdomains+## CSE auth, single login covers all `<mfr>.componentsearchengine.com` subdomains -Sign in once at `https://componentsearchengine.com/signin` with the user's email + password, and the cookie covers every per-vendor subdomain (nxp, mouser, etc.). On first visit to a subdomain, an HTTP **Basic Auth** popup may appear once — it accepts the same CSE creds. After that, downloads on subdomain pages work.+Sign in once at `https://componentsearchengine.com/signin` with the user's email + password, and the cookie covers every per-vendor subdomain (nxp, mouser, etc.). pup answers the per-subdomain HTTP **Basic Auth** challenge from the vault; a visible popup means the vault has no `*.componentsearchengine.com` entry yet. -The CSE STEP files we get for NXP/Mouser CSE are **mfr-authored, FreeCAD-exported** with named PRODUCTs (`ASSEMBLY`, `Body`, `ThermalPin`, `PinsArrayLR`, `PinsArrayTB`) — same authoring pipeline as TI's Fusion-electronics exports. So CSE-distributed STEPs match UL-distributed STEPs in scene-graph quality.+The CSE STEP files we get for NXP/Mouser CSE are **mfr-authored, FreeCAD-exported** with named PRODUCTs (`ASSEMBLY`, `Body`, `ThermalPin`, `PinsArrayLR`, `PinsArrayTB`), same authoring pipeline as TI's Fusion-electronics exports. So CSE-distributed STEPs match UL-distributed STEPs in scene-graph quality. -**Filename pattern: CSE downloads land as `LIB_<MPN>.zip` (vs UL's `ul_<MPN>.zip`).** The `cf-autowatch.sh` script's filename match needs to handle both — regex is `^(ul_|LIB_).*\.zip$`.+**Filename pattern: CSE downloads land as `LIB_<MPN>.zip` (vs UL's `ul_<MPN>.zip`).** The `cf-autowatch.sh` script's filename match needs to handle both, regex is `^(ul_|LIB_).*\.zip$`.  ## Chrome's "wants to Download multiple files" SILENT KILLER -When CSE fires a download, Chrome detects multiple downloads (symbol + footprint + 3D + datasheet often arrive as separate transfers) and shows an Allow/Block dialog the first time per origin per session. **Programmatic `.click()` and `MouseEvent` dispatch CANNOT click Allow** — it's a Chromium browser-chrome dialog, not a page element.+When CSE fires a download, Chrome detects multiple downloads (symbol + footprint + 3D + datasheet often arrive as separate transfers) and shows an Allow/Block dialog the first time per origin per session. **Programmatic `.click()` and `MouseEvent` dispatch CANNOT click Allow**, it's a Chromium browser-chrome dialog, not a page element.  Symptom: download button "works" (no error, page doesn't change), but `~/Downloads` stays empty for minutes. @@ -114,31 +109,31 @@ Symptom: download button "works" (no error, page doesn't change), but `~/Downloa - (a) use Chrome DevTools Protocol `Browser.grantPermissions` via pup before the first navigation - (b) launch the pup profile with `--auto-open-devtools-for-tabs` plus a setup script that flips the permission -**Do NOT ask the user to click Allow** — see "chip-fetcher does ALL the clicking" rule. Cloudflare/CSE reject curl-with-cookies attempts (HTTP 403 → CF anti-bot challenge → redirect to `/register`), so Chrome inside pup is the only viable path. Once the permission is granted at profile-load, all CSE clicks in that session run unattended.+**Do NOT ask the user to click Allow**, see "chip-fetcher does ALL the clicking" rule. Cloudflare/CSE reject curl-with-cookies attempts (HTTP 403 → CF anti-bot challenge → redirect to `/register`), so Chrome inside pup is the only viable path. Once the permission is granted at profile-load, all CSE clicks in that session run unattended. -(`chip-fetcher/scripts/adom-desktop-chrome-permissions-prompt.md` is the open feature request to bake `automatic_downloads: granted` into every pup `browser_open_window` by default.)+(`chip-fetcher/scripts/adom-desktop-chrome-permissions-prompt.md` is the open feature request to bake `automatic_downloads: granted` into every pup `pup_open_window` by default.)  ## Anti-detection / browse as a real user  Even when you go through pup Chrome, sites still profile *behavior*. Burst-clicking 5 product pages in 30 seconds with no scrolling, no mouse-movement, no referrer chain looks like a bot regardless of TLS fingerprint. The remedies, applied always:  1. **Pre-warm with a referrer chain.** Don't deep-link straight to a product page. Visit `<vendor>.com/` first, wait 3 seconds, then click into a search → results → product. Anti-bot heuristics weight `Referer` chains heavily.-2. **Add 2–5 second random delays between navigations** (`sleep $((2 + RANDOM % 4))`).+2. **Add 2 to 5 second random delays between navigations** (`sleep $((2 + RANDOM % 4))`). 3. **Scroll the page after load.** Even one `window.scrollTo({top:600,behavior:'smooth'})` then a pause then `scrollTo({top:1400})` shifts behavioral profile out of bot territory and triggers lazy-loaded content. 4. **Move the mouse.** A few `dispatchEvent(new MouseEvent('mousemove', {clientX:..., clientY:..., bubbles:true}))` calls between clicks help.-5. **Don't fetch the same domain in tight bursts.** When backfilling 14 chips, space them ≥4 seconds apart and reload one tab — don't open 14 tabs at once.+5. **Don't fetch the same domain in tight bursts.** When backfilling 14 chips, space them ≥4 seconds apart and reload one tab, don't open 14 tabs at once. 6. **Use a single persistent profile (`chip-fetcher`)** so cookies, fingerprint, and visit history accumulate. 7. **Detection escape valve:** if a site still 403s despite all of the above, slow down and ask the user to slide whatever CAPTCHA appears.  ## Always browse as a real user -**Every PDF fetch goes through pup Chrome with a real session, not naked `curl`.** Vendor CDNs (Mouser/Akamai, Cloudflare, ST/Akamai, Nordic/Cloudflare, NXP/Cloudflare) actively profile traffic for "automation tools" — TLS fingerprint, header order, missing `sec-ch-ua`/`sec-fetch-*`, no JS execution, no cookies, no referrer chain.+**Every PDF fetch goes through pup Chrome with a real session, not naked `curl`.** Vendor CDNs (Mouser/Akamai, Cloudflare, ST/Akamai, Nordic/Cloudflare, NXP/Cloudflare) actively profile traffic for "automation tools", TLS fingerprint, header order, missing `sec-ch-ua`/`sec-fetch-*`, no JS execution, no cookies, no referrer chain.  **The rule: every fetch flows through pup.** Use one of these in priority order: -1. **In-page `fetch()` via `browser_eval`** with `{credentials:"include"}` and `awaitPromise:true`, base64-encoding the response body back through the JSON channel.-2. **Programmatic `<a>` click in the rendered page** — Chrome handles the download natively, file lands in `~/Downloads`, then `chip-fetcher pull <path>`.-3. **Naked `curl` ONLY for vendor-doc CDNs that are confirmed not behind a bot wall** — TI's `ti.com/lit/pdf/<id>` is one of the few that works. **Always validate by checking the first 4 bytes:**+1. **In-page `fetch()` via `pup_eval`** with `{credentials:"include"}` and `awaitPromise:true`, base64-encoding the response body back through the JSON channel.+2. **Programmatic `<a>` click in the rendered page**, Chrome handles the download natively, file lands in `~/Downloads`, then `chip-fetcher pull <path>`.+3. **Naked `curl` ONLY for vendor-doc CDNs that are confirmed not behind a bot wall**, TI's `ti.com/lit/pdf/<id>` is one of the few that works. **Always validate by checking the first 4 bytes:**    ```bash    head -c 4 "$OUT" | grep -q "%PDF" || { echo "not a real PDF"; rm -f "$OUT"; }    ```
playbooks/snapmagic.md+26−26
@@ -1,27 +1,27 @@ # SnapEDA / SnapMagic (chip-fetcher playbook) -Verified end-to-end 2026-05-03 on nRF52840-CKAA-R7 — KiCad + Altium + Fusion in one session. Read this when the manufacturer doesn't host CAD directly (Nordic, niche connectors, passives, smaller-vendor MCUs).+Verified end-to-end 2026-05-03 on nRF52840-CKAA-R7, KiCad + Altium + Fusion in one session. Read this when the manufacturer doesn't host CAD directly (Nordic, niche connectors, passives, smaller-vendor MCUs). -**SnapEDA is a TIER 2 fallback — only use it AFTER confirming the manufacturer doesn't host CAD on their own site.** When falling back, post a `no mfr-direct CAD — falling back to SnapEDA` heartbeat so the user sees on the dashboard that we're leaving manufacturer-direct (per [`dashboard-flow.md`](dashboard-flow.md)). All format-cycling stages (kicad → altium → fusion) get their own heartbeats: `setting preferred format altium`, `clicking download symbol+footprint`, `dismissing heads-up`, `pulling altium IntLib`, etc.+**SnapEDA is a TIER 2 fallback, only use it AFTER confirming the manufacturer doesn't host CAD on their own site.** When falling back, post a `no mfr-direct CAD, falling back to SnapEDA` heartbeat so the user sees on the dashboard that we're leaving manufacturer-direct (per [`dashboard-flow.md`](dashboard-flow.md)). All format-cycling stages (kicad → altium → fusion) get their own heartbeats: `setting preferred format altium`, `clicking download symbol+footprint`, `dismissing heads-up`, `pulling altium IntLib`, etc.  ## What it is -**SnapEDA is `snapeda.com` (host) but rebranded to "SnapMagic Search" in the UI.** Free CAD downloads (symbol + footprint + STEP + 3D + Altium/KiCad/Eagle/OrCAD/PADS/TARGET/Allegro) for millions of parts. **Account required for downloads.** Most embedded engineers already have one — ASK first per the credentials playbook.+**SnapEDA is `snapeda.com` (host) but rebranded to "SnapMagic Search" in the UI.** Free CAD downloads (symbol + footprint + STEP + 3D + Altium/KiCad/Eagle/OrCAD/PADS/TARGET/Allegro) for millions of parts. **Account required for downloads.** Most embedded engineers already have one, ASK first per the credentials playbook. -**Where SnapEDA shines:** parts that aren't in CSE/UL — Nordic (no UL deep-link), niche connectors, passives, smaller-vendor MCUs, anything where the manufacturer doesn't publish CAD directly.+**Where SnapEDA shines:** parts that aren't in CSE/UL, Nordic (no UL deep-link), niche connectors, passives, smaller-vendor MCUs, anything where the manufacturer doesn't publish CAD directly.  ## Login flow (verified working)  ```bash # 1. Navigate to login page-adom-desktop browser_navigate '{"sessionId":"chip-fetcher","url":"https://www.snapeda.com/account/login/"}'+adom-bridge pup_navigate '{"sessionId":"adom-chip-fetcher","url":"https://www.snapeda.com/account/login/"}' sleep 5 -# 2. Fill credentials + form.submit() (NOT button click — the "Log In" looks like an <a> but+# 2. Fill credentials + form.submit() (NOT button click, the "Log In" looks like an <a> but #    button-click doesn't fire the real handler; only form.submit() works)-adom-desktop browser_eval '{"sessionId":"chip-fetcher","expr":"(()=>{const u=document.querySelector(\"input[name=username]\"); const p=document.querySelector(\"input[type=password]\"); const proto=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,\"value\"); proto.set.call(u,\"<email>\"); proto.set.call(p,\"<password>\"); [\"input\",\"change\",\"blur\"].forEach(t=>{u.dispatchEvent(new Event(t,{bubbles:true})); p.dispatchEvent(new Event(t,{bubbles:true}));}); u.closest(\"form\").submit(); return \"submitted\";})()"}'+adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","expr":"(()=>{const u=document.querySelector(\"input[name=username]\"); const p=document.querySelector(\"input[type=password]\"); const proto=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,\"value\"); proto.set.call(u,\"<email>\"); proto.set.call(p,\"<password>\"); [\"input\",\"change\",\"blur\"].forEach(t=>{u.dispatchEvent(new Event(t,{bubbles:true})); p.dispatchEvent(new Event(t,{bubbles:true}));}); u.closest(\"form\").submit(); return \"submitted\";})()"}' -# 3. Wait for redirect away from /account/login/ — typically lands on /home/+# 3. Wait for redirect away from /account/login/, typically lands on /home/ ```  **Critical: SnapEDA's "Log in" button does NOT submit the form via click.** Use `form.submit()` directly. Verified painful pattern when button-click wasted ~30 seconds before figuring this out.@@ -32,13 +32,13 @@ URL pattern: `https://www.snapeda.com/parts/<MPN>/<Manufacturer>/view-part/`. Th  To search: `https://www.snapeda.com/search/?q=<MPN>` returns a list of matching variants. -## Download flow — KEY INSIGHT: format is gated by user account preference+## Download flow, KEY INSIGHT: format is gated by user account preference  The part page has three tabs: **"Symbol and Footprint"**, **"3D Model"**, **"Alternates"**. The "Symbol and Footprint" tab has TWO orange buttons side by side: full bundle (`Download Symbol and Footprint`) and footprint-only (`Download Footprint`). -**3D Model (STEP)** — direct, no format picker:+**3D Model (STEP)**, direct, no format picker: ```bash-adom-desktop browser_eval '{"sessionId":"chip-fetcher","expr":"document.getElementById(\"download_step_model\").click()"}'+adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","expr":"document.getElementById(\"download_step_model\").click()"}' # Lands as <MPN>--3DModel-STEP-<id>.STEP. Always raw STEP regardless of preferred-format. ``` @@ -47,7 +47,7 @@ adom-desktop browser_eval '{"sessionId":"chip-fetcher","expr":"document.getEleme 1. **Preferred ECAD format** (radio `preferred_2d_format`): one of `altium_native`, `kicad_modv6`, `kicad_modv4`, `kicad_modv3`, `eagle`, `fusion_360`, `orcad_v17`, `orcad_v16`, `pads`, `diptrace`, `designspark`, `cr_8000`, `circuit_studio`, `ecadstar`, `easypc`, `express_pcb_plus`. 2. **Direct download toggle** (ON = skip the format-picker modal and go straight to the preferred format) -When toggle is OFF (the default), clicking the orange "Download Symbol and Footprint" opens a `Choose Download Format` modal showing ALL formats, with the preferred at the top. When toggle is ON, the modal is skipped — except for KiCad which still asks "V3 / V4 / V6 & later" version variant.+When toggle is OFF (the default), clicking the orange "Download Symbol and Footprint" opens a `Choose Download Format` modal showing ALL formats, with the preferred at the top. When toggle is ON, the modal is skipped, except for KiCad which still asks "V3 / V4 / V6 & later" version variant.  ## Switching format programmatically @@ -55,23 +55,23 @@ Clicking the radio auto-saves via AJAX (no submit button needed). Verified by he  ```bash # Set Altium as preferred (auto-saves on click)-adom-desktop browser_navigate '{"sessionId":"chip-fetcher","url":"https://www.snapeda.com/account/email/"}'+adom-bridge pup_navigate '{"sessionId":"adom-chip-fetcher","url":"https://www.snapeda.com/account/email/"}' sleep 4-adom-desktop browser_eval '{"sessionId":"chip-fetcher","expr":"document.querySelector(\"input[name=preferred_2d_format][value=altium_native]\").click()"}'-# Then go back to part page — orange button now serves Altium.+adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","expr":"document.querySelector(\"input[name=preferred_2d_format][value=altium_native]\").click()"}'+# Then go back to part page, orange button now serves Altium. ``` -**Always restore the preferred format to whatever the user had originally** (typically `kicad_modv6`) at the end of the session — don't leave the account changed.+**Always restore the preferred format to whatever the user had originally** (typically `kicad_modv6`) at the end of the session, don't leave the account changed.  ## The "Heads Up" warning  For parts with multiple symbol sections, a small modal interrupts the download with *"This part contains multiple symbol sections. View the sections to see all pinouts."* Click the orange "Ok, download this part" button to proceed:  ```bash-adom-desktop browser_eval '{"sessionId":"chip-fetcher","expr":"(()=>{const b=Array.from(document.querySelectorAll(\"a, button\")).find(e=>e.offsetParent!==null && /ok.*download this part/i.test((e.textContent||\"\").trim())); if(b){b.click(); return \"clicked\";} return \"no heads-up modal\";})()"}'+adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","expr":"(()=>{const b=Array.from(document.querySelectorAll(\"a, button\")).find(e=>e.offsetParent!==null && /ok.*download this part/i.test((e.textContent||\"\").trim())); if(b){b.click(); return \"clicked\";} return \"no heads-up modal\";})()"}' ``` -This is benign — just a "did you know there are multiple sections" notification. Always click OK.+This is benign, just a "did you know there are multiple sections" notification. Always click OK.  ## Full proven 3-format sequence @@ -81,15 +81,15 @@ PART_URL="https://www.snapeda.com/parts/<MPN>/<Manufacturer>/view-part/" fetch_format() {   local FORMAT="$1"  # altium_native | kicad_modv6 | fusion_360 | eagle | etc.   # 1. Set preference (auto-saves)-  adom-desktop browser_navigate '{"sessionId":"chip-fetcher","url":"https://www.snapeda.com/account/email/"}' >/dev/null+  adom-bridge pup_navigate '{"sessionId":"adom-chip-fetcher","url":"https://www.snapeda.com/account/email/"}' >/dev/null   sleep 4-  adom-desktop browser_eval "{\"sessionId\":\"chip-fetcher\",\"expr\":\"document.querySelector('input[name=preferred_2d_format][value=$FORMAT]').click()\"}" >/dev/null+  adom-bridge pup_eval "{\"sessionId\":\"chip-fetcher\",\"expr\":\"document.querySelector('input[name=preferred_2d_format][value=$FORMAT]').click()\"}" >/dev/null   sleep 1   # 2. Back to part page-  adom-desktop browser_navigate "{\"sessionId\":\"chip-fetcher\",\"url\":\"$PART_URL\"}" >/dev/null+  adom-bridge pup_navigate "{\"sessionId\":\"chip-fetcher\",\"url\":\"$PART_URL\"}" >/dev/null   sleep 5   # 3. Click orange Download (use coords from canvas-main-btn rect, typically near (548, 704))-  adom-desktop browser_eval '{"sessionId":"chip-fetcher","expr":"(()=>{const b=Array.from(document.querySelectorAll(\"a.canvas-main-btn, a.modal-trigger\")).find(e=>e.offsetParent!==null && /download symbol and footprint/i.test((e.textContent||\"\").trim())); if(!b)return\"no btn\"; const r=b.getBoundingClientRect(); return JSON.stringify({x:Math.round(r.x+r.width/2),y:Math.round(r.y+r.height/2)});})()"}'+  adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","expr":"(()=>{const b=Array.from(document.querySelectorAll(\"a.canvas-main-btn, a.modal-trigger\")).find(e=>e.offsetParent!==null && /download symbol and footprint/i.test((e.textContent||\"\").trim())); if(!b)return\"no btn\"; const r=b.getBoundingClientRect(); return JSON.stringify({x:Math.round(r.x+r.width/2),y:Math.round(r.y+r.height/2)});})()"}'   # ...trusted-click those coords...   # 4. Modal opens: click the format button by visible text   # 5. KiCad-only: pick V6 & Later. All formats: dismiss "Heads Up" → click "Ok, download this part"@@ -98,7 +98,7 @@ fetch_format() {  fetch_format kicad_modv6   # → <MPN>.zip   (contains .kicad_sym + .kicad_mod) fetch_format altium_native # → <MPN>.IntLib (raw IntLib, NOT zipped)-fetch_format fusion_360    # → <MPN>.lbr   (Eagle .lbr — Fusion eats Eagle natively)+fetch_format fusion_360    # → <MPN>.lbr   (Eagle .lbr, Fusion eats Eagle natively) ```  ## Verified filenames in user Downloads@@ -122,7 +122,7 @@ No MPN-Format suffix, just `<MPN>` + native extension: # KiCad zip → auto-extracts to .kicad_sym + .kicad_mod chip-fetcher pull "C:\\Users\\<user>\\Downloads\\<MPN>.zip" --mpn <MPN> -# Altium IntLib + Fusion .lbr — pull stages them in incoming/ but doesn't auto-import.+# Altium IntLib + Fusion .lbr, pull stages them in incoming/ but doesn't auto-import. # Manually copy into library/ with explicit names so the viewer can find them: chip-fetcher pull "C:\\Users\\<user>\\Downloads\\<MPN>.IntLib" --mpn <MPN> cp incoming/<MPN>.IntLib library/<MPN>/<MPN>.IntLib@@ -131,7 +131,7 @@ chip-fetcher pull "C:\\Users\\<user>\\Downloads\\<MPN>.lbr" --mpn <MPN> cp "incoming/<MPN>.lbr" library/<MPN>/<MPN>-fusion.lbr  # disambiguate from Eagle if both fetched ``` -**(TODO for chip-fetcher importer: handle `.IntLib` and `.lbr` natively — current importer only knows about KiCad zips and STEP files, leaves Altium/Fusion in incoming/.)**+**(TODO for chip-fetcher importer: handle `.IntLib` and `.lbr` natively, current importer only knows about KiCad zips and STEP files, leaves Altium/Fusion in incoming/.)**  ## InstaBuild AI-generated parts @@ -139,7 +139,7 @@ Some parts on SnapMagic are AI-generated rather than vetted by the SnapMagic edi  ## Account profile cache -If `user_profile.md` (personal memory) has a `Site logins` section listing SnapEDA's email, pre-fill it; otherwise ASK. Never store passwords — pre-fill email + ask user to type password once. Pup persists session via cookies after login (`chip-fetcher` profile name).+If `user_profile.md` (personal memory) has a `Site logins` section listing SnapEDA's email, pre-fill it; otherwise ASK. Never store passwords, pre-fill email + ask user to type password once. Pup persists session via cookies after login (`chip-fetcher` profile name).  **Login URL:** `https://www.snapeda.com/account/login/` 
playbooks/sourcing-ladder.md+17−17
@@ -5,12 +5,12 @@ The defining rule for where chip-fetcher looks for CAD + datasheets, in priority ## The ladder, in priority order  1. **Manufacturer's own site** (`ti.com`, `nordicsemi.com`, `nxp.com`, `st.com`, `analog.com`, `microchip.com`, `bosch-sensortec.com`, `wago.com`, `vishay.com`, `diodes.com`, `renesas.com`, `espressif.com`, `latticesemi.com`). Datasheet, app notes, errata, lifecycle status, and a "CAD" / "Tools & Software" link if they partner with UL/CSE.-2. **SnapEDA / SnapMagic** (`snapeda.com`) — free CAD downloads (symbol + footprint + STEP + 3D + Altium/KiCAD/Eagle/OrCAD/etc.) for millions of parts. **Login required (most EEs already have one).** Comprehensive coverage especially for chips that aren't on UL/CSE — including Nordic, niche connectors, passives, etc. Should come right after manufacturer-direct, before chasing CSE-direct or UL-guess URLs.-3. **Mouser** (`adom-mouser search "<MPN>"`) — stock + pricing always, plus Mouser product page can surface UL/CSE deep-links.-4. **DigiKey** (`adom-digikey search "<MPN>"`) — broader catalog, niche industrial parts.-5. **Arrow** (`arrow.com`) — niche distributor.-6. **CSE direct** / **UL direct** — only when the manufacturer routes you there from their own page.-7. **LCSC** (`lcsc.com`, `datasheet.lcsc.com`) — **LAST RESORT, AVOID IF POSSIBLE.** LCSC redistributes manufacturer datasheets and EasyEDA-imported CAD, but quality is uneven and we lose the manufacturer's own provenance. Only fall here when steps 1–6 all returned nothing usable, and even then prefer to surface "we couldn't find this on the manufacturer's site" to the user instead of silently grabbing from LCSC.+2. **SnapEDA / SnapMagic** (`snapeda.com`), free CAD downloads (symbol + footprint + STEP + 3D + Altium/KiCAD/Eagle/OrCAD/etc.) for millions of parts. **Login required (most EEs already have one).** Comprehensive coverage especially for chips that aren't on UL/CSE, including Nordic, niche connectors, passives, etc. Should come right after manufacturer-direct, before chasing CSE-direct or UL-guess URLs.+3. **Mouser** (`adom-mouser search "<MPN>"`), stock + pricing always, plus Mouser product page can surface UL/CSE deep-links.+4. **DigiKey** (`adom-digikey search "<MPN>"`), broader catalog, niche industrial parts.+5. **Arrow** (`arrow.com`), niche distributor.+6. **CSE direct** / **UL direct**, only when the manufacturer routes you there from their own page.+7. **LCSC** (`lcsc.com`, `datasheet.lcsc.com`), **LAST RESORT, AVOID IF POSSIBLE.** LCSC redistributes manufacturer datasheets and EasyEDA-imported CAD, but quality is uneven and we lose the manufacturer's own provenance. Only fall here when steps 1 to 6 all returned nothing usable, and even then prefer to surface "we couldn't find this on the manufacturer's site" to the user instead of silently grabbing from LCSC.  Direct user quote (2026-05-06): *"when you're falling back to mouser for the search, after that you should do digikey, and then only after that try lcsc. i really want to avoid lcsc at all costs, but it is in the list if we're desperate."* @@ -19,7 +19,7 @@ Direct user quote (2026-05-06): *"when you're falling back to mouser for the sea ## Why manufacturer first (the ironclad rule)  - Vendor docs are authoritative (latest revision, full set of app notes).-- Manufacturer pages link to UL/SnapEDA deep-links specifically for their parts — find them by following the "CAD" link FROM the vendor page, never by guessing URLs.+- Manufacturer pages link to UL/SnapEDA deep-links specifically for their parts, find them by following the "CAD" link FROM the vendor page, never by guessing URLs. - Errata + lifecycle status from the vendor is authoritative.  Direct user quotes:@@ -29,9 +29,9 @@ Direct user quotes:  **🔒 IRONCLAD RULE: never leave the manufacturer's own website without explicit user approval.** -**This applies EVEN WHEN you "already know" the part.** When the agent has high prior confidence about a part (e.g. "LTST-C190KRKT is a Lite-On 0603 red LED, SnapMagic has it"), the cognitive shortcut is to skip the mfr-site probe and jump to the known-source. **Don't.** The rule isn't "check the mfr site when uncertain" — it's "always START at the mfr site, even when confident, before recommending or fetching from anywhere else." Confidence about the part identity is not a substitute for confirming the manufacturer's own site state.+**This applies EVEN WHEN you "already know" the part.** When the agent has high prior confidence about a part (e.g. "LTST-C190KRKT is a Lite-On 0603 red LED, SnapMagic has it"), the cognitive shortcut is to skip the mfr-site probe and jump to the known-source. **Don't.** The rule isn't "check the mfr site when uncertain", it's "always START at the mfr site, even when confident, before recommending or fetching from anywhere else." Confidence about the part identity is not a substitute for confirming the manufacturer's own site state. -The failure pattern that repeatedly trips this: user asks "do we have indicator LEDs?" → agent recalls a known part family from training → queries Mouser for stock validation → recommends fetching from SnapMagic — without ever opening `<mfr>.com`. That's a direct skill-rule violation. Direct user pushback (recurring): *"why do you keep not using the preference order i'm asking for?"*+The failure pattern that repeatedly trips this: user asks "do we have indicator LEDs?" → agent recalls a known part family from training → queries Mouser for stock validation → recommends fetching from SnapMagic, without ever opening `<mfr>.com`. That's a direct skill-rule violation. Direct user pushback (recurring): *"why do you keep not using the preference order i'm asking for?"*  The right reflex: open the manufacturer's product page first, even if it takes 30 seconds. Lite-On has `liteonopto.com`, Würth has `we-online.com`, etc. Walk the page, look for CAD/Datasheet/3D Model links. ONLY after exhausting the mfr's site is it OK to talk about SnapMagic / Mouser-CSE / UL fallbacks. @@ -39,12 +39,12 @@ What this means in practice: 1. **Stay on `<vendor>.com` only.** Don't bounce to `3dfindit.com`, `partcommunity.com`, `mouser.componentsearchengine.com`, `vendor.ultralibrarian.com`, `snapeda.com`, `componentsearchengine.com`, or any third-party site **unless the manufacturer's own page links there explicitly** (e.g. TI/ST/ADI/Microchip embedding a `vendor.ultralibrarian.com/<slug>/embedded` URL counts as mfr-endorsed; navigating to UL via Google search does NOT). 2. **If you can't find CAD on the manufacturer's site, REPORT that to the user.** Don't go hunting on aggregators silently. The user will tell you what to do. 3. **Mfr-uploaded ≠ mfr-direct.** SamacSys hosts mfr-uploaded models, but the discovery / distribution channel is still third-party. Always check the mfr's site FIRST, exhaustively, even if you "know" it's going to fail.-4. **Native non-KiCad formats are still mfr-direct.** Altium `.IntLib`, OrCAD `.olb`/`.dml`, EAGLE `.lbr`, Renesas `.dml`, NXP `.bxl` — all of these qualify as mfr-authored. Pull them and convert to KiCad later; don't reject them because they're not in our preferred format.+4. **Native non-KiCad formats are still mfr-direct.** Altium `.IntLib`, OrCAD `.olb`/`.dml`, EAGLE `.lbr`, Renesas `.dml`, NXP `.bxl`, all of these qualify as mfr-authored. Pull them and convert to KiCad later; don't reject them because they're not in our preferred format. 5. **Exhaust the manufacturer's site before reporting "not available."** Walk every tab (Design Resources, Documentation, Tools & Software, Software & Drivers, Documents & Downloads, Hardware Files), expand every "Toggle" / accordion, click every icon-only button, scrape every `<img>` `alt`/`src`, follow every PDF/zip link, navigate to every Package / Pinout / Mechanical Drawing subcategory.  ## Where SnapEDA shines (Tier 2) -When the manufacturer doesn't publish CAD directly (Nordic for example — `nordicsemi.com` doesn't expose chip CAD, only firmware SDK + reference-board Altium files), SnapEDA is the next viable free source. It's free with a login, and **most embedded engineers already have a SnapEDA account**.+When the manufacturer doesn't publish CAD directly (Nordic for example, `nordicsemi.com` doesn't expose chip CAD, only firmware SDK + reference-board Altium files), SnapEDA is the next viable free source. It's free with a login, and **most embedded engineers already have a SnapEDA account**.  Direct user pushback when chip-fetcher missed SnapEDA on Nordic's nRF52840-CKAA dead-end: *"why haven't you tried that before?"* Answer: should have. Adding to the ladder permanently. @@ -63,8 +63,8 @@ When a manufacturer page (or distributor lifecycle field) says any of these, tre  **The rule:** -1. **If the user explicitly asked for that exact MPN** → fetch it AND surface the lifecycle warning prominently. *"Heads up — TI has TPS40190EVM marked NRND, expect availability to drop within 2–3 years; do you still want it or should I find an active equivalent?"* Pause and let them decide. Their design might already be locked to that specific part for a reason (existing PCB, qual'd component, BOM compatibility).-2. **If chip-fetcher was choosing the part on its own** (default library picks, "popular" suggestions, batch fills, "show me a good op-amp" requests) → **DO NOT add it to the library.** Pick an active replacement. NRND parts will be unbuyable for InstaPCB users in 1–3 years; baking them into Adom's default library means real boards won't be manufacturable at scale.+1. **If the user explicitly asked for that exact MPN** → fetch it AND surface the lifecycle warning prominently. *"Heads up, TI has TPS40190EVM marked NRND, expect availability to drop within 2 to 3 years; do you still want it or should I find an active equivalent?"* Pause and let them decide. Their design might already be locked to that specific part for a reason (existing PCB, qual'd component, BOM compatibility).+2. **If chip-fetcher was choosing the part on its own** (default library picks, "popular" suggestions, batch fills, "show me a good op-amp" requests) → **DO NOT add it to the library.** Pick an active replacement. NRND parts will be unbuyable for InstaPCB users in 1 to 3 years; baking them into Adom's default library means real boards won't be manufacturable at scale. 3. **Always include `lifecycle` + `lifecycle_warning` in the stored `stock.json`** so downstream tools (InstaPCB, BOM checker) can flag these without re-scraping.  Direct user quote: *"if the page says 'not recommended for new designs' that you take that seriously. let the user know if they asked you specifically to get that part, but if you were just thinking that part was a good idea for some reason, it's not a good idea."*@@ -73,7 +73,7 @@ Direct user quote: *"if the page says 'not recommended for new designs' that you  ```bash # 1. Manufacturer page banners-adom-desktop browser_eval '{"sessionId":"x","expr":"document.body.textContent.match(/not recommended for new designs|nrnd|end of life|obsolete|last time buy|phased out/gi)?.slice(0,3)"}'+adom-bridge pup_eval '{"sessionId":"x","expr":"document.body.textContent.match(/not recommended for new designs|nrnd|end of life|obsolete|last time buy|phased out/gi)?.slice(0,3)"}'  # 2. Mouser API adom-mouser search "<MPN>" | jq '.components[0] | {lifecycle, stock_text, lead_time}'@@ -87,7 +87,7 @@ adom-digikey search "<MPN>" | jq '.components[0].lifecycle'  ## Track Mouser stock + pricing as first-class data -chip-fetcher isn't just a CAD library — it's the source-of-truth for whether each chip can actually be put into an InstaPCB build. Every chip in `library/<MPN>/` should have current Mouser stock + price metadata cached alongside the CAD bundle.+chip-fetcher isn't just a CAD library, it's the source-of-truth for whether each chip can actually be put into an InstaPCB build. Every chip in `library/<MPN>/` should have current Mouser stock + price metadata cached alongside the CAD bundle.  **stock.json schema:** @@ -127,10 +127,10 @@ Direct user quote: *"we need to know stuff is in stock at mouser so we can build  ## Multi-package variant tracking -When `adom-mouser search "<MPN>"` returns more than one variant of the chip, save **every variant** to `stock.json` under `variants[]` — not just the best/first one. Downstream tools (InstaPCB BOM picker, schematic-to-board flow, alternate-part suggester) need to know the user's choices, especially when packages differ.+When `adom-mouser search "<MPN>"` returns more than one variant of the chip, save **every variant** to `stock.json` under `variants[]`, not just the best/first one. Downstream tools (InstaPCB BOM picker, schematic-to-board flow, alternate-part suggester) need to know the user's choices, especially when packages differ.  Worked example: TPS25751 has both TPS25751DREFR (3276 @ $2.99, package: DREF) and TPS25751SRSMR (1425 @ $2.66, package: SRSM/RSM). Storing only the highest-stock variant hides that the user can pick a different package for layout reasons. -**Discovery:** any time the Mouser `components[]` array length > 1 AND the MPNs differ in suffix (different package), treat as multi-variant. If MPNs are identical (just differ in `mouser_pn`), it's the same chip from different inventory pools — collapse those.+**Discovery:** any time the Mouser `components[]` array length > 1 AND the MPNs differ in suffix (different package), treat as multi-variant. If MPNs are identical (just differ in `mouser_pn`), it's the same chip from different inventory pools, collapse those.  Direct user quote: *"when you see multiple variants like that from mouser, you have to track that to let the user know the choices later downstream."*
playbooks/symbol-axis-regen.md+7−7
@@ -1,12 +1,12 @@ --- name: symbol-axis-regen-description: chip-fetcher's side of the default-symbol 3D outline — re-render the symbol axis-aware when the user switches the up-axis, and the non-blocking SYM-tile spinner while it regenerates. The symbol-DEFAULT behavior (compositing the white outline + lasered name, the outline contract) lives in adom-symbol's docs/default-symbol-3d-outline.md, not here. Trigger words — axis switch regenerate symbol, chosen_up_axis symbol, sym tile spinner, orientation regen, set-as-up regenerate.+description: chip-fetcher's side of the default-symbol 3D outline, re-render the symbol axis-aware when the user switches the up-axis, and the non-blocking SYM-tile spinner while it regenerates. The symbol-DEFAULT behavior (compositing the white outline + lasered name, the outline contract) lives in adom-symbol's docs/default-symbol-3d-outline.md, not here. Trigger words, axis switch regenerate symbol, chosen_up_axis symbol, sym tile spinner, orientation regen, set-as-up regenerate. ---  # Symbol axis-regen + SYM-tile spinner (chip-fetcher side) -> The DEFAULT symbol layout — compositing the **white 3D-chip outline + lasered-> name**, why it must be natively white, and the outline contract — is+> The DEFAULT symbol layout, compositing the **white 3D-chip outline + lasered+> name**, why it must be natively white, and the outline contract, is > **adom-symbol's** behavior. See `adom-symbol/docs/default-symbol-3d-outline.md`. > This playbook is only chip-fetcher's orchestration: keeping that outline in the > right ORIENTATION and showing progress.@@ -20,7 +20,7 @@ re-render to match. `POST /api/orientation {mpn, axis}`  1. Persist `chosen_up_axis`. 2. **Off-thread** (so the click returns instantly): `adom-chip-thumbnailer-   rerender <mpn> --kind outline` — the LIGHT path that regenerates just the+   rerender <mpn> --kind outline`, the LIGHT path that regenerates just the    vector outline at the new axis (one OCCT pass, not the whole shaded-iso family). 3. Then `adom-symbol render` recomposites the canonical symbol with the new    outline. Response includes `"regen":"outline+symbol"`.@@ -28,11 +28,11 @@ re-render to match. `POST /api/orientation {mpn, axis}` ## Non-blocking SYM-tile spinner  While that regen runs, overlay a **lightweight** spinner on the SYM tile so the-user knows it's refreshing — without blocking the rest of the dashboard:+user knows it's refreshing, without blocking the rest of the dashboard:  - Pure-CSS ring, `pointer-events:none`, only that one tile dims. Everything else   stays interactive.-- **Data-driven, not a transient DOM node** — the dashboard re-renders on a poll+- **Data-driven, not a transient DOM node**, the dashboard re-renders on a poll   and would wipe a hand-added spinner. Drive it from state keyed on `sym_rev`   (the `<mpn>-symbol.svg` mtime): record `sym_rev` at click-time; the spinner   clears the instant a newer `sym_rev` arrives (regen done), with a ~95s safety@@ -40,5 +40,5 @@ user knows it's refreshing — without blocking the rest of the dashboard: - Show it instantly on click (before the round-trip), then poll just until it   clears. -Reference: `src/serve.rs::set_orientation`; `src/ui/index.html` — `SYM_REFRESHING`+Reference: `src/serve.rs::set_orientation`; `src/ui/index.html`, `SYM_REFRESHING` map, `isSymRefreshing`, `renderThumb` spinner inject, `setOrientation`.
playbooks/ultra-librarian.md+78−78
@@ -2,15 +2,15 @@  UL deep-link patterns, form quirks, captcha handling, atomic-submit pattern, watcher race-safety. Read this when the manufacturer's product page links to a `vendor.ultralibrarian.com/<slug>/embedded` URL. -## TL;DR — three flows, pick by URL host+## TL;DR, three flows, pick by URL host  | URL pattern | Flow | Captcha? | Driver script | Auth | |---|---|---|---|---| | `vendor.ultralibrarian.com/<vendor>/embedded` | **Embedded captcha-iframe** (the public, no-login route from manufacturer product pages) | reCAPTCHA gauntlet, iterative escalation | `cf-ul-fast.sh --phase pre-captcha/post-captcha` | none required, but anonymous | | `app.ultralibrarian.com/details/<uuid>/<vendor>/<MPN>` | **UL Pro tick-and-click** (logged-in user; URL contains `?uid=…`) | **none** for logged-in users | `cf-ul-pro-batch.sh <MPN>` | UL Pro form-login, cookie in pup profile |-| `app.ultralibrarian.com/details/<uuid>/...` (no `?uid=`) | UL Pro detail, anonymous | yes — falls back to embedded captcha | `cf-ul-fast.sh` (after page transition) | none |+| `app.ultralibrarian.com/details/<uuid>/...` (no `?uid=`) | UL Pro detail, anonymous | yes, falls back to embedded captcha | `cf-ul-fast.sh` (after page transition) | none | -**Best practice 2026-05-04:** sign in to UL Pro once via `chip-fetcher login ultralib` (see [`credentials-and-logins.md`](credentials-and-logins.md)). Then `cf-ul-pro-batch.sh` drives every subsequent fetch with zero captcha — no iterative-grid AI-vision burn, no token-timeout race. Captcha-driven `cf-ul-fast.sh` is now a fallback for parts UL Pro doesn't carry, or for anonymous flows.+**Best practice 2026-05-04:** sign in to UL Pro once via `chip-fetcher login ultralib` (see [`credentials-and-logins.md`](credentials-and-logins.md)). Then `cf-ul-pro-batch.sh` drives every subsequent fetch with zero captcha, no iterative-grid AI-vision burn, no token-timeout race. Captcha-driven `cf-ul-fast.sh` is now a fallback for parts UL Pro doesn't carry, or for anonymous flows.  ## UL Pro logged-in tick-and-click flow (cf-ul-pro-batch.sh) @@ -19,29 +19,29 @@ When the chip-fetcher pup profile has a live `app.ultralibrarian.com` cookie (`R ### The exact two-click pattern (this is the bug-prone part)  There are TWO elements on the page both labeled "Download Now":-- **`<button class="btn btn-dark...">`** — opens the format-picker panel. Clicking this does NOT trigger a download.-- **`<a class="export-trigger">`** — actually submits the picked formats and triggers the download. Initially **hidden** (collapsed under the closed picker); only `offsetParent !== null` after the BUTTON has been clicked.+- **`<button class="btn btn-dark...">`**, opens the format-picker panel. Clicking this does NOT trigger a download.+- **`<a class="export-trigger">`**, actually submits the picked formats and triggers the download. Initially **hidden** (collapsed under the closed picker); only `offsetParent !== null` after the BUTTON has been clicked.  Calling the script in the right order:  1. **Click the BUTTON** to open the format picker. URL gets `?open=exports`.-2. **Tick the format checkboxes** (Altium Designer, Fusion360 PCB, KiCAD v6+, etc.) — these are now visible in the open picker.+2. **Tick the format checkboxes** (Altium Designer, Fusion360 PCB, KiCAD v6+, etc.), these are now visible in the open picker. 3. **Click the A.export-trigger** to submit. UL Pro server-side processes the format conversion; the "Starting Your Download" modal appears. -A single-click pattern (just clicking whichever Download Now is visible first) **fails silently** — the BUTTON click opens the picker but the download never fires because the submit element wasn't clicked. Verified painful 2026-05-04: 6/8 chips timed out waiting for a download that was never submitted.+A single-click pattern (just clicking whichever Download Now is visible first) **fails silently**, the BUTTON click opens the picker but the download never fires because the submit element wasn't clicked. Verified painful 2026-05-04: 6/8 chips timed out waiting for a download that was never submitted.  ### Full flow  1. Search: `https://app.ultralibrarian.com/search?queryText=<MPN>` → first `a[href*=details]` is the part page.-2. Navigate to part page → URL becomes `https://app.ultralibrarian.com/details/<uuid>/<vendor>/<MPN>?uid=<userId>`. The `?uid=` confirms authenticated session — if absent, the cookie didn't take and you'll get re-routed through embedded captcha.-3. Click `<button>Download Now</button>` (visible BUTTON) — opens picker.+2. Navigate to part page → URL becomes `https://app.ultralibrarian.com/details/<uuid>/<vendor>/<MPN>?uid=<userId>`. The `?uid=` confirms authenticated session, if absent, the cookie didn't take and you'll get re-routed through embedded captcha.+3. Click `<button>Download Now</button>` (visible BUTTON), opens picker. 4. Tick checkboxes by **label text**: `Altium Designer`, `Fusion360 PCB`, `KiCAD v6+`, `STEP`, etc. The checkboxes share `name="exports"` and have an integer value identifying the format. There are ~40 export formats; pick what you need.-5. Optionally tick any consent checkbox the manufacturer requires (TI: `name="consent-TIInfoShare"`). **Verified not actually required for the download to succeed** — UL Pro proceeds without it. But ticking it satisfies the form's UI completeness check.-6. Click `<a class="export-trigger">Download Now</a>` (visible A) — submits.-7. UL Pro shows a "Starting Your Download" modal — server-side format conversion takes 2-15 seconds typically (claims "up to 2 minutes").-8. Poll `~/Downloads/` (or Windows Downloads via `adom-desktop shell_execute powershell ...`) for `ul_<MPN>.zip` newer than the click timestamp.+5. Optionally tick any consent checkbox the manufacturer requires (TI: `name="consent-TIInfoShare"`). **Verified not actually required for the download to succeed**, UL Pro proceeds without it. But ticking it satisfies the form's UI completeness check.+6. Click `<a class="export-trigger">Download Now</a>` (visible A), submits.+7. UL Pro shows a "Starting Your Download" modal, server-side format conversion takes 2-15 seconds typically (claims "up to 2 minutes").+8. Poll `~/Downloads/` (or Windows Downloads via `adom-bridge shell_execute powershell ...`) for `ul_<MPN>.zip` newer than the click timestamp. 9. Pull via `chip-fetcher pull <Windows-path> --mpn <MPN>` to copy zip into `incoming/`.-10. Extract `*.SchLib`, `*.PcbLib`, `*.lbr` from the zip and copy to `library/<MPN>/<MPN>.{SchLib,PcbLib,lbr}`. The chip-fetcher `pull` step already imports `.kicad_sym`, `.kicad_mod`, `.step` — the manual distribute step is just for Altium + Fusion files (chip-fetcher's importer doesn't handle them yet).+10. Extract `*.SchLib`, `*.PcbLib`, `*.lbr` from the zip and copy to `library/<MPN>/<MPN>.{SchLib,PcbLib,lbr}`. The chip-fetcher `pull` step already imports `.kicad_sym`, `.kicad_mod`, `.step`, the manual distribute step is just for Altium + Fusion files (chip-fetcher's importer doesn't handle them yet).  ```bash ./scripts/cf-ul-pro-batch.sh BQ25798RQMR@@ -49,18 +49,18 @@ A single-click pattern (just clicking whichever Download Now is visible first) * # Output:  url, picker opened, ticked N, submitted via A, download <name>, distributed SchLib PcbLib lbr, OK <MPN> ``` -### Coverage caveats — UL Pro doesn't carry every package variant+### Coverage caveats, UL Pro doesn't carry every package variant  UL Pro is per-package, not per-MPN. Common gaps (verified 2026-05-04): -- TI ADS131M04**IPBSR** (TQFP-32 PBS package) — UL has IPWR/IPWT (TSSOP-32 PW package) but **not** the PBS variant.-- TI MCF8316A1RRYR — not on UL Pro at all (very new part).+- TI ADS131M04**IPBSR** (TQFP-32 PBS package), UL has IPWR/IPWT (TSSOP-32 PW package) but **not** the PBS variant.+- TI MCF8316A1RRYR, not on UL Pro at all (very new part). -**Don't ship a wrong-package Altium file.** When the search returns a different package suffix, mark it in `info.json` (`altium: "no UL Pro coverage for <package>"`) and try the manufacturer's product page (TI's "EDA Symbols and Footprints" link sometimes routes to a different UL flow that has more packages). For non-coverage parts, accept partial state — KiCad-only is better than KiCad + wrong-package Altium.+**Don't ship a wrong-package Altium file.** When the search returns a different package suffix, mark it in `info.json` (`altium: "no UL Pro coverage for <package>"`) and try the manufacturer's product page (TI's "EDA Symbols and Footprints" link sometimes routes to a different UL flow that has more packages). For non-coverage parts, accept partial state, KiCad-only is better than KiCad + wrong-package Altium. -## Use the fast two-phase captcha script for the embedded flow — DO NOT drive UL step-by-step from chat+## Use the fast two-phase captcha script for the embedded flow, DO NOT drive UL step-by-step from chat -**`chip-fetcher/scripts/cf-ul-fast.sh`** is the canonical UL driver. It collapses the chained-LLM-call problem (each chat-side `browser_eval` adds ~2-5s of latency, easily eating the captcha's 2-minute token budget) into TWO calls with one AI vision step in between. Tight bash cadences (200ms token-poll, parallel tile dispatch, 0.5s post-Verify wait) match what a Rust implementation could do; the bottleneck post-vision is the `adom-desktop` HTTP relay, not the script.+**`chip-fetcher/scripts/cf-ul-fast.sh`** is the canonical UL driver. It collapses the chained-LLM-call problem (each chat-side `pup_eval` adds ~2-5s of latency, easily eating the captcha's 2-minute token budget) into TWO calls with one AI vision step in between. Tight bash cadences (200ms token-poll, parallel tile dispatch, 0.5s post-Verify wait) match what a Rust implementation could do; the bottleneck post-vision is the `adom-bridge` HTTP relay, not the script.  ```bash # Phase 1 (one shell call): navigate → preset form → trusted-click I'm-not-a-robot@@ -83,42 +83,42 @@ UL Pro is per-package, not per-MPN. Common gaps (verified 2026-05-04):  The script handles family-distribution: when one UL zip covers multiple MPNs (common for indicator passives where the package body is shared), pass `--mpns A,B,C` and the script copies sym/mod into each `library/<MPN>/`. -## UL rotates captcha approaches — adapt, don't hardcode+## UL rotates captcha approaches, adapt, don't hardcode  UL deliberately varies captcha to throw off AI vision:  - **Grid size**: 3×3 (9 tiles, 1.5× larger per tile) vs 4×4 (16 tiles, denser). AI MUST detect from screenshot and pass `--grid 3x3` or `--grid 4x4` to phase 2.-- **Type**: "Select all images with X" (one-shot — click matching tiles, click Verify, done) vs "Click verify once there are none left" (iterative — click matches, then NEW images appear in clicked tiles, may need multiple rounds).-- **Subject difficulty**: easy (cars, fire hydrants, traffic lights) vs harder (boats, bicycles, chimneys, palm trees) — AI may need to be aggressive about ambiguous tiles.-- **Image noise overlay**: some replacement tiles in iterative mode have heavy noise — usually means "newly-shown after a click; classify based on what's visible through the noise."+- **Type**: "Select all images with X" (one-shot, click matching tiles, click Verify, done) vs "Click verify once there are none left" (iterative, click matches, then NEW images appear in clicked tiles, may need multiple rounds).+- **Subject difficulty**: easy (cars, fire hydrants, traffic lights) vs harder (boats, bicycles, chimneys, palm trees), AI may need to be aggressive about ambiguous tiles.+- **Image noise overlay**: some replacement tiles in iterative mode have heavy noise, usually means "newly-shown after a click; classify based on what's visible through the noise."  `cf-ul-fast.sh --phase post-captcha` detects iterative escalation automatically (post-Verify check: bframe still visible AND token still empty → re-screenshot → output `image_grid_round_N+1` for AI to do another vision pass). AI loops until token populates or captcha hard-fails.  ## /compact your conversation BEFORE driving UL -A captcha token has a ~120-second life from the I'm-not-a-robot click. AI vision response time scales with conversation context size. **If your context is large (200K+ tokens), each vision turn can take 8-15s** — that compounds across iterative captcha rounds and burns the token budget.+A captcha token has a ~120-second life from the I'm-not-a-robot click. AI vision response time scales with conversation context size. **If your context is large (200K+ tokens), each vision turn can take 8-15s**, that compounds across iterative captcha rounds and burns the token budget.  **Before any non-trivial UL fetch, recommend the user run `/compact`** to trim context. Smaller context = faster vision responses = the captcha pass succeeds before the token expires. -**Heartbeat protocol is mandatory** — see [`dashboard-flow.md`](dashboard-flow.md). `cf-ul-fast.sh` calls `post_act` automatically at every stage so the dashboard reflects live progress.+**Heartbeat protocol is mandatory**, see [`dashboard-flow.md`](dashboard-flow.md). `cf-ul-fast.sh` calls `post_act` automatically at every stage so the dashboard reflects live progress. -## DO NOT drive UL with separate `browser_eval` chat-side calls+## DO NOT drive UL with separate `pup_eval` chat-side calls  Anti-pattern (the slow path):  ```-[chat] browser_navigate UL URL-[chat] browser_eval — open Choose CAD Formats-[chat] browser_eval — preset form-[chat] browser_input_dispatch — click captcha-[chat] browser_screenshot — see image grid-[chat] AI vision — pick tiles-[chat] browser_input_dispatch — click tile 1-[chat] browser_input_dispatch — click tile 2-[chat] browser_input_dispatch — click Verify-[chat] browser_eval — poll for token (×N)-[chat] browser_eval — Submit-[chat] shell_execute — poll Downloads+[chat] pup_navigate UL URL+[chat] pup_eval, open Choose CAD Formats+[chat] pup_eval, preset form+[chat] pup_input_dispatch, click captcha+[chat] pup_screenshot, see image grid+[chat] AI vision, pick tiles+[chat] pup_input_dispatch, click tile 1+[chat] pup_input_dispatch, click tile 2+[chat] pup_input_dispatch, click Verify+[chat] pup_eval, poll for token (×N)+[chat] pup_eval, Submit+[chat] shell_execute, poll Downloads [chat] chip-fetcher pull ``` @@ -142,7 +142,7 @@ sel.value = "2-2"; sel.dispatchEvent(new Event("change",{bubbles:true}));  // mm  `chip-fetcher/scripts/cf-autowatch.sh <MPN>` (background) does two things: 1. Polls `g-recaptcha-response` token every 2s. When length > 50, the user has solved CAPTCHA. **Verifies the active tab URL still matches the MPN's base part** (race-safety) before auto-removing `disabled` from `#SubmitLink` and clicking it.-2. Polls user's `~/Downloads` every 2s. When a `ul_*.zip` whose filename matches the MPN base appears, calls `adom-desktop pull_file` + `chip-fetcher import --mpn <MPN>`.+2. Polls user's `~/Downloads` every 2s. When a `ul_*.zip` whose filename matches the MPN base appears, calls `adom-bridge --ai-thread "<thread>" pull_file` + `chip-fetcher import --mpn <MPN>`.  A PID lock at `/tmp/cf-autowatch.lock` prevents two watchers running simultaneously. @@ -154,12 +154,12 @@ The user only has to click **one CAPTCHA per part**. Everything else is automate cd /home/adom/project/chip-fetcher ./scripts/cf-drive.sh <MPN> ./scripts/cf-autowatch.sh <MPN> &-adom-desktop browser_raise_os_window '{"sessionId":"chip-fetcher"}'+adom-bridge pup_raise_os_window '{"sessionId":"adom-chip-fetcher"}' # → user clicks CAPTCHA on the pup window (one click) # → watcher auto-clicks Submit, polls Downloads, pulls + imports ``` -While the watcher's running, `curl https://www.ti.com/lit/gpn/<base-mpn>` to grab the datasheet PDF in parallel — that endpoint is open, no auth.+While the watcher's running, `curl https://www.ti.com/lit/gpn/<base-mpn>` to grab the datasheet PDF in parallel, that endpoint is open, no auth.  ## Vendor-specific UL form quirks @@ -172,15 +172,15 @@ Different UL portals use different checkbox IDs: | ST         | `MfrThreeDModel`  | `KiCADv6`    | | ADI        | `ThreeDModel`     | `KiCADv6`    | -**Don't rely on IDs** — use the visible label "STEP" / "KiCAD v6+" with `byLabel(re => /^STEP$/i)` lookup. The cf-drive.sh script already does this.+**Don't rely on IDs**, use the visible label "STEP" / "KiCAD v6+" with `byLabel(re => /^STEP$/i)` lookup. The cf-drive.sh script already does this. -## ON UL DOWNLOADS — grab the popular EDA formats + STEP+## ON UL DOWNLOADS, grab the popular EDA formats + STEP -**On every UL download form, check the popular-EDA format checkboxes — not just KiCAD.** The user's current project may target one EDA tool, but Adom adds support for new EDA tools over time, and re-fetching every board for a different EDA tool would mean another reCAPTCHA toll per part. **UL also caps free downloads at ~5 format selections per submission**, so picking smartly matters — going for "ALL formats" doesn't work.+**On every UL download form, check the popular-EDA format checkboxes, not just KiCAD.** The user's current project may target one EDA tool, but Adom adds support for new EDA tools over time, and re-fetching every board for a different EDA tool would mean another reCAPTCHA toll per part. **UL also caps free downloads at ~5 format selections per submission**, so picking smartly matters, going for "ALL formats" doesn't work.  **The canonical four-pick that covers >95% of user EDA tooling:** -- ☑ **STEP** (3D model — always, every download)+- ☑ **STEP** (3D model, always, every download) - ☑ **KiCAD v6+** - ☑ **Altium Designer** - ☑ **Fusion 360 PCB / Eagle**@@ -188,7 +188,7 @@ Different UL portals use different checkbox IDs:  Direct user direction: *"i think the names i gave you are the right choice for now. we don't need ALL formats of other EDA's. just the popular ones … fusion 360 and altium and kicad."* -If the user later expands the supported EDA list (OrCAD, DesignSpark, Mentor, Pulsonix, Quadcept, TARGET, Zuken, CADSTAR, eCadstar — each is a separate UL checkbox), add to this list, but stay within UL's per-submission cap. For chips that need an EDA format outside the canonical four, do a second reCAPTCHA-gated fetch later (worth the toll).+If the user later expands the supported EDA list (OrCAD, DesignSpark, Mentor, Pulsonix, Quadcept, TARGET, Zuken, CADSTAR, eCadstar, each is a separate UL checkbox), add to this list, but stay within UL's per-submission cap. For chips that need an EDA format outside the canonical four, do a second reCAPTCHA-gated fetch later (worth the toll).  **Footprint Units:** always set to **Metric (mm)** when the dropdown exists. Most EDA tools internally use mm; importing as mil and converting is a precision-loss tax. @@ -196,41 +196,41 @@ If the user later expands the supported EDA list (OrCAD, DesignSpark, Mentor, Pu  ```bash # Driver helper: pick the canonical four + T&C (no overflow)-adom-desktop browser_eval '{"sessionId":"chip-fetcher","expr":"(()=>{const cbs=Array.from(document.querySelectorAll(\"input[type=checkbox]\")); const want=[/^STEP$/i,/kicad v6/i,/^altium designer/i,/^fusion.?360/i,/agree|terms/i]; for(const re of want){const c=cbs.find(c=>re.test((c.parentElement?.textContent||c.closest(\"label\")?.textContent||\"\").trim())); if(c&&!c.checked) c.click();} return \"done\";})()"}'+adom-bridge pup_eval '{"sessionId":"adom-chip-fetcher","expr":"(()=>{const cbs=Array.from(document.querySelectorAll(\"input[type=checkbox]\")); const want=[/^STEP$/i,/kicad v6/i,/^altium designer/i,/^fusion.?360/i,/agree|terms/i]; for(const re of want){const c=cbs.find(c=>re.test((c.parentElement?.textContent||c.closest(\"label\")?.textContent||\"\").trim())); if(c&&!c.checked) c.click();} return \"done\";})()"}' ``` -If UL silently unchecks one (the "max 5" enforcement), the canonical four already fits — but if a vendor adds extra mandatory checkboxes (e.g. WAGO had Cadenas 3D Model dropdown which counts toward the cap), drop one of the EDA formats (typically Eagle/Fusion last) to stay under.+If UL silently unchecks one (the "max 5" enforcement), the canonical four already fits, but if a vendor adds extra mandatory checkboxes (e.g. WAGO had Cadenas 3D Model dropdown which counts toward the cap), drop one of the EDA formats (typically Eagle/Fusion last) to stay under. -## reCAPTCHA v2 auto-click — try first, fall back to user only on image-grid+## reCAPTCHA v2 auto-click, try first, fall back to user only on image-grid -With `browser_input_dispatch` (adom-desktop ≥ v1.4.7), the agent CAN dispatch a trusted CDP click on the reCAPTCHA "I'm not a robot" checkbox iframe — **isTrusted is no longer the limiter**. What stops a clean silent pass is **Google's session-reputation scoring**: pup's Chrome-for-Testing has no browsing-history, no Google account session, no consistent IP usage pattern, so reCAPTCHA escalates to the image-grid challenge for low-reputation sessions.+With `pup_input_dispatch` (adom-bridge ≥ v1.4.7), the agent CAN dispatch a trusted CDP click on the reCAPTCHA "I'm not a robot" checkbox iframe, **isTrusted is no longer the limiter**. What stops a clean silent pass is **Google's session-reputation scoring**: pup's Chrome-for-Testing has no browsing-history, no Google account session, no consistent IP usage pattern, so reCAPTCHA escalates to the image-grid challenge for low-reputation sessions.  **The empirical pattern (verified 2026-05-03 on TI UL):** -1. Move cursor toward the checkbox via `browser_input_dispatch type:"move"` with intermediate steps (helps fingerprint look human).-2. Click the checkbox at iframe `(left+30, top+40)` — that's where "I'm not a robot" sits inside the iframe.-3. Wait 3–5s.+1. Move cursor toward the checkbox via `pup_input_dispatch type:"move"` with intermediate steps (helps fingerprint look human).+2. Click the checkbox at iframe `(left+30, top+40)`, that's where "I'm not a robot" sits inside the iframe.+3. Wait 3 to 5s. 4. Read `[name=g-recaptcha-response]`'s value.    - Non-empty + `>20` chars → **silent pass, proceed to Submit**.    - Empty + an image-challenge iframe (`iframe[src*=bframe]`) appears → **escalation, hand off to user**.  ```bash # 1. Find iframe coords-RECT=$(adom-desktop browser_eval '{"sessionId":"X","expr":"(()=>{const f=document.querySelector(\"iframe[src*=recaptcha]\"); if(!f)return null; const r=f.getBoundingClientRect(); return JSON.stringify({x:r.x|0,y:r.y|0});})()"}' \+RECT=$(adom-bridge pup_eval '{"sessionId":"X","expr":"(()=>{const f=document.querySelector(\"iframe[src*=recaptcha]\"); if(!f)return null; const r=f.getBoundingClientRect(); return JSON.stringify({x:r.x|0,y:r.y|0});})()"}' \   | jq -r '.result')  # 2. Move + click (humanlike approach) IX=$(($(echo $RECT | jq .x) + 30)) IY=$(($(echo $RECT | jq .y) + 40))-adom-desktop browser_input_dispatch "{\"sessionId\":\"X\",\"type\":\"move\",\"x\":$((IX-200)),\"y\":$((IY-100)),\"steps\":5}"+adom-bridge pup_input_dispatch "{\"sessionId\":\"X\",\"type\":\"move\",\"x\":$((IX-200)),\"y\":$((IY-100)),\"steps\":5}" sleep 0.5-adom-desktop browser_input_dispatch "{\"sessionId\":\"X\",\"type\":\"move\",\"x\":$IX,\"y\":$IY,\"steps\":15}"+adom-bridge pup_input_dispatch "{\"sessionId\":\"X\",\"type\":\"move\",\"x\":$IX,\"y\":$IY,\"steps\":15}" sleep 0.4-adom-desktop browser_input_dispatch "{\"sessionId\":\"X\",\"type\":\"click\",\"x\":$IX,\"y\":$IY}"+adom-bridge pup_input_dispatch "{\"sessionId\":\"X\",\"type\":\"click\",\"x\":$IX,\"y\":$IY}" sleep 4  # 3. Detect outcome: passed vs escalated-RESULT=$(adom-desktop browser_eval '{"sessionId":"X","expr":"JSON.stringify({tokenLen:(document.querySelector(\"[name=g-recaptcha-response]\")?.value||\"\").length, challenge:!!document.querySelector(\"iframe[src*=bframe]\")})"}')+RESULT=$(adom-bridge pup_eval '{"sessionId":"X","expr":"JSON.stringify({tokenLen:(document.querySelector(\"[name=g-recaptcha-response]\")?.value||\"\").length, challenge:!!document.querySelector(\"iframe[src*=bframe]\")})"}') # tokenLen >= 20 → passed, agent continues # tokenLen == 0 && challenge true → image grid, AI vision can try, else user ```@@ -241,17 +241,17 @@ RESULT=$(adom-desktop browser_eval '{"sessionId":"X","expr":"JSON.stringify({tok 2. **Resize the crop 2× via ImageMagick** (`convert input -resize 200% out.png`). 3. **Read the prompt + grid.** "Select all squares with X" = 4×4 grid (16 tiles, click each square containing any pixel). "Select all images with X" = 3×3 grid (9 tiles, only the primary subject). 4. **Compute tile centers in browser CSS coords.** For 4×4 grid in iframe (273,127,400,580): grid area top≈237 left≈298, tile size ≈89×105 → tile (r,c) center = `(298 + (c-0.5)*89, 237 + (r-0.5)*105)`. For 3×3 grid: tile size ≈118×140.-5. **Click each correct tile via `browser_input_dispatch type:click`** with ~600ms between clicks (humanlike pacing).+5. **Click each correct tile via `pup_input_dispatch type:click`** with ~600ms between clicks (humanlike pacing). 6. **Click Verify** at iframe-bottom-right.-7. **Watch `[name=g-recaptcha-response]`** — token populating with >20 chars = passed.+7. **Watch `[name=g-recaptcha-response]`**, token populating with >20 chars = passed. -**Token expires within ~2 minutes — Submit IMMEDIATELY after token populates.** Do NOT re-set form, do NOT add `sleep 8` between checks; that latency burns through the captcha lifetime and you'll have to solve it again.+**Token expires within ~2 minutes, Submit IMMEDIATELY after token populates.** Do NOT re-set form, do NOT add `sleep 8` between checks; that latency burns through the captcha lifetime and you'll have to solve it again. -## CRITICAL pattern — ATOMIC token-check + Submit-click in one `browser_eval`+## CRITICAL pattern, ATOMIC token-check + Submit-click in one `pup_eval` -**The winning pattern for UL captcha submits, verified end-to-end on TPS25751 2026-05-03.** Every prior attempt failed because the bash-side polling was `eval → check → eval` with 200–500ms round-trip between detection and Submit, which was enough for UL's backend to re-validate and reject the captcha token.+**The winning pattern for UL captcha submits, verified end-to-end on TPS25751 2026-05-03.** Every prior attempt failed because the bash-side polling was `eval → check → eval` with 200 to 500ms round-trip between detection and Submit, which was enough for UL's backend to re-validate and reject the captcha token. -**Fix: do the check AND the click in a single `browser_eval`.**+**Fix: do the check AND the click in a single `pup_eval`.**  ```js (() => {@@ -266,15 +266,15 @@ RESULT=$(adom-desktop browser_eval '{"sessionId":"X","expr":"JSON.stringify({tok })() ``` -Bash polls this one eval at 1-second cadence — when it returns `SUBMITTED_*` you know Submit fired in the same browser tick the token was detected. **Net round-trip latency from token-populate to Submit-click: ~0 ms.**+Bash polls this one eval at 1-second cadence, when it returns `SUBMITTED_*` you know Submit fired in the same browser tick the token was detected. **Net round-trip latency from token-populate to Submit-click: ~0 ms.**  **Other prerequisites that go with the atomic eval:**  1. **Pre-set the form BEFORE the captcha** (STEP + KiCAD v6+ + Altium + Fusion 360 + mm + T&C all in one eval). Zero work between token-detect and Submit.-2. **UL's Submit is an `<a>` tag**, not `<button>` — selector must scan `'a, button, input[type=submit]'`.-3. **Cache the Downloads-before snapshot BEFORE clicking reCAPTCHA**, not after Submit — else you race the download and miss the new file.+2. **UL's Submit is an `<a>` tag**, not `<button>`, selector must scan `'a, button, input[type=submit]'`.+3. **Cache the Downloads-before snapshot BEFORE clicking reCAPTCHA**, not after Submit, else you race the download and miss the new file. 4. **Use the manufacturer's session-tied UL deep-link**, not `?vdrPN=` guesses. TI's product page has a `CAD` link that includes `gpn=<MPN>&package=<PKG>&pin=<N>&sid=<token>`. Without `sid`, UL silently routes to a wrong/related part. Verified: `?vdrPN=tps65987d` routed to `F280041RSHSR` (totally different chip).-5. **Verify part identity after navigation** — read `document.body.textContent` and confirm the MPN appears.+5. **Verify part identity after navigation**, read `document.body.textContent` and confirm the MPN appears.  The full winning recipe lives in `chip-fetcher/scripts/cf-ul-fetch.sh`. Direct user reaction when this pattern landed the bundle on first attempt: *"you did it!!!!!! put that in the skill and/or make a script to be ready next time."* @@ -284,9 +284,9 @@ Verified 2026-05-03: even when AI vision passes a multi-stage image-grid challen  **Mitigation when going AI-vision-solve route:** -1. **Pre-set the form completely** before triggering reCAPTCHA. STEP, KiCAD v6+, Altium, Fusion 360, Metric (mm), T&C — all set BEFORE you click "I'm not a robot."-2. **Keep ALL between-click latency under 1s.** No `sleep 4` for screenshots — use `sleep 0.6` between tile clicks.-3. **Skip screenshots after Verify.** Read `[name=g-recaptcha-response]` directly — that's the only signal that matters. If `.length > 20` → fire Submit in the SAME bash chain.+1. **Pre-set the form completely** before triggering reCAPTCHA. STEP, KiCAD v6+, Altium, Fusion 360, Metric (mm), T&C, all set BEFORE you click "I'm not a robot."+2. **Keep ALL between-click latency under 1s.** No `sleep 4` for screenshots, use `sleep 0.6` between tile clicks.+3. **Skip screenshots after Verify.** Read `[name=g-recaptcha-response]` directly, that's the only signal that matters. If `.length > 20` → fire Submit in the SAME bash chain. 4. **Accept that 3+ challenge stages probably exceed the budget.** Plan for a redo, but expect the user to need to step in if Google keeps escalating. Direct user pushback on slow re-tries: *"why are you going so slow again? it'll timeout!!!"*  **The hard fact:** AI vision works for 1-stage image grids (~70% of trusted-click escalations). For 3+-stage chained challenges, the round-trip time of `screenshot → crop → read → identify → click → verify` is the bottleneck, not vision accuracy. For those, hand off to the user immediately.@@ -296,20 +296,20 @@ Verified 2026-05-03: even when AI vision passes a multi-stage image-grid challen - Visit a few high-reputation domains (google.com search, gmail.com) before the gated form. - Use the same `chip-fetcher` profile across all sessions so cookies+history accumulate. -In practice, expect ~30–50% silent-pass rate on first attempts and increasing over time.+In practice, expect ~30 to 50% silent-pass rate on first attempts and increasing over time.  ## Watcher race-safety (as of 2026-04-30)  `scripts/cf-autowatch.sh` enforces:-- **PID lock** at `/tmp/cf-autowatch.lock` — only one watcher at a time.-- **URL match** — verifies the active pup tab URL contains the expected MPN base before clicking Submit.-- **Filename match** — the Downloads-folder poll filters for filenames containing the MPN base, AND filename matches `^(ul_|LIB_).*\.zip$`.+- **PID lock** at `/tmp/cf-autowatch.lock`, only one watcher at a time.+- **URL match**, verifies the active pup tab URL contains the expected MPN base before clicking Submit.+- **Filename match**, the Downloads-folder poll filters for filenames containing the MPN base, AND filename matches `^(ul_|LIB_).*\.zip$`. -Past incident: two watchers running simultaneously both saw the same CAPTCHA solve token and both raced to download/import the same UL zip — one ended up imported into the wrong MPN folder.+Past incident: two watchers running simultaneously both saw the same CAPTCHA solve token and both raced to download/import the same UL zip, one ended up imported into the wrong MPN folder.  ## UL gotchas -1. **UL download tokens are SINGLE-USE.** After Chrome consumes the URL once, server returns `Token Expired`. Re-fetching via `browser_eval` + `fetch()` will not work. **The only reliable path is `pull_file` from the user's Downloads folder.**+1. **UL download tokens are SINGLE-USE.** After Chrome consumes the URL once, server returns `Token Expired`. Re-fetching via `pup_eval` + `fetch()` will not work. **The only reliable path is `pull_file` from the user's Downloads folder.** 2. **UL ships PACKAGE-named files inside the zip**, not MPN-named. Inside `ul_TPS62840YBGR.zip` the STEP is `DSBGA6_1P4XP9_TEX.step`. Always pass `--mpn <MPN>` to import so files get renamed to `<MPN>.step`. 3. **UL ships multiple footprint variants** (default, `-L`, `-M`). Current import overwrites them all to one `<MPN>.kicad_mod`. TODO: disambiguate suffixes. 4. **TI does NOT self-host STEPs on ti.com.** Every TI part's "CAD/CAE Symbols" link is a UL deep-link with a session-tied URL.
playbooks/wiki-bundle-reuse.md+13−13
@@ -1,48 +1,48 @@ --- name: wiki-bundle-reuse-description: Reuse an already-published Adom Wiki component bundle as a part-source BEFORE re-fetching from external vendors. The wiki (wiki.adom.inc) hosts `component` pages whose git repo carries a complete, Adom-vetted CAD bundle (<mpn>.kicad_sym/.kicad_mod, STEP/GLB, datasheet PDF, 3D-outline SVGs). If the part is already there, pull that bundle — it's free, fast, and guarantees Adom-consistent geometry + provenance. Trigger words — wiki part source, reuse published bundle, adom-wiki source, component page reuse, query the wiki first, wiki.adom.inc bundle, pull from wiki, wiki cad cache.+description: Reuse an already-published Adom Wiki component bundle as a part-source BEFORE re-fetching from external vendors. The wiki (wiki.adom.inc) hosts `component` pages whose git repo carries a complete, Adom-vetted CAD bundle (<mpn>.kicad_sym/.kicad_mod, STEP/GLB, datasheet PDF, 3D-outline SVGs). If the part is already there, pull that bundle, it's free, fast, and guarantees Adom-consistent geometry + provenance. Trigger words, wiki part source, reuse published bundle, adom-wiki source, component page reuse, query the wiki first, wiki.adom.inc bundle, pull from wiki, wiki cad cache. ---  # Reusing published wiki component bundles as a part-source -The Adom Wiki is the **first** step on chip-fetcher's sourcing ladder — ahead of+The Adom Wiki is the **first** step on chip-fetcher's sourcing ladder, ahead of manufacturer / SnapMagic / Mouser / DigiKey / Arrow / CSE-UL / LCSC. If a part is already published as a `component` page with a CAD bundle, reuse it instead of scraping vendors again. Reference implementation: `chip-fetcher/src/sources/wiki.rs`-(+ `Source::AdomWiki` first in `src/sourcing.rs`). Reads only — the public read+(+ `Source::AdomWiki` first in `src/sourcing.rs`). Reads only, the public read API needs no token.  ## The API (read-only, public)  ``` GET /api/v1/pages/<slug>/files/page.json     # is it type:"component"? component.mpn, org-GET /api/v1/discover?triggers=<MPN>          # discovery hits (LOOSE — see gotchas)-GET /api/v1/search?q=<MPN>                    # full-text (throws on hyphens — strip them)+GET /api/v1/discover?triggers=<MPN>          # discovery hits (LOOSE, see gotchas)+GET /api/v1/search?q=<MPN>                    # full-text (throws on hyphens, strip them) GET /api/v1/pages/<slug>/files               # { "files":[ {"path","type":"blob|tree"} ] } GET /api/v1/pages/<slug>/files/<path>        # raw file bytes ``` -No HTTP crate in chip-fetcher — shell `curl -fsSL --max-time N` (matches the+No HTTP crate in chip-fetcher, shell `curl -fsSL --max-time N` (matches the dependency-free, shell-a-sibling-binary style of the rest of the CLI).  ## The gotchas (every one learned the hard way) -1. **`search` (FTS) throws on hyphens.** Never send a raw hyphenated MPN — strip+1. **`search` (FTS) throws on hyphens.** Never send a raw hyphenated MPN, strip    hyphens for the query. Prefer the exact-slug and `discover` paths; `search` is    the fallback. 2. **`discover`/`search` return LOOSE matches.** A nonsense MPN and `ADS1115`-   both "matched" `b2b-zr` before this was fixed — the API happily returns a+   both "matched" `b2b-zr` before this was fixed, the API happily returns a    popular/related page. **You MUST verify** the candidate's    `component.mpn` (normalized) equals the requested MPN. Without that check the    feature reuses the wrong part's bundle. 3. **`component.mpn` can be malformed.** Some pages glue a description onto it-   (`"BQ7692003PWR — BQ76920 3–5 Cell …"`), so its normalized form won't match.-   **Also accept a slug match** (`norm(slug) == norm(mpn)`) — the slug is the+   (`"BQ7692003PWR, BQ76920 3 to 5 Cell …"`), so its normalized form won't match.+   **Also accept a slug match** (`norm(slug) == norm(mpn)`), the slug is the    page's clean identity.    - Normalize for all comparisons: lowercase + drop non-alphanumerics      (`B2B-ZR` → `b2bzr`). 4. **Pull ONLY the canonical file of each type.** Pages carry variant/compare-   models — `B2B-ZR-v2.glb`, `B2B-ZR-compare.glb`, `B2B-ZR-mate.glb`. Match the+   models, `B2B-ZR-v2.glb`, `B2B-ZR-compare.glb`, `B2B-ZR-mate.glb`. Match the    file stem to the part (`stem == mpn`) so you grab `<mpn>.glb`, not 14 variants    that collapse onto one canonical name. For outline SVGs, require the    `<mpn>-3d-outline-…` prefix.@@ -77,6 +77,6 @@ ADDS a step; it never removes one.  As of this writing, most component pages are metadata-only; only some (e.g. the JST connectors, a few passives + ICs) carry real bundles. The value grows as full-bundles get published — so the natural companion is a **write-back**: publish a+bundles get published, so the natural companion is a **write-back**: publish a completed bundle to the wiki after a successful vendor fetch, so the cache fills-itself. (Not built yet — flagged as the obvious next step.)+itself. (Not built yet, flagged as the obvious next step.)
skills/CONTRIBUTING.md+9−9
@@ -1,6 +1,6 @@ # Contributing a source-site skill -You figured out how to navigate a vendor's CAD-download flow — maybe a site+You figured out how to navigate a vendor's CAD-download flow, maybe a site chip-fetcher didn't cover, maybe a new way past a block (the day DigiKey blocked pup, someone chatted with its assistant and found a path; that knowledge belongs here). Here's how to get it into the pack so everyone's AI benefits.@@ -12,37 +12,37 @@ Gate, Download flow, Catch, When it breaks). Keep it to **one site per skill** a give it real trigger words (the vendor name, "blocked", "captcha", the file types) so the AI auto-surfaces it at the right moment. -## Two ways to land it — the owner picks per contribution+## Two ways to land it, the owner picks per contribution  The pack supports two contribution shapes. Which one is allowed for a given skill is the **maintainer's call**, made deliberately, trading freshness against trust: -### A. Pull request INTO the pack (vetted — the default)+### A. Pull request INTO the pack (vetted, the default)  Open a PR adding `sites/<vendor>/SKILL.md` to this repo. The maintainer reads the navigation steps, sanity-checks them, and merges. The skill now lives in the chip-fetcher repo and ships with the pack. -- **Pro:** every step is reviewed before it reaches anyone's AI — the safe default.+- **Pro:** every step is reviewed before it reaches anyone's AI, the safe default. - **Con:** updates require another PR; the maintainer is in the loop each time. - **Use when:** the contributor is new/unknown, or the skill does anything   sensitive (credential handling, evading a block, executing fetched content). -### B. Linked external skill (trusted — opt-in)+### B. Linked external skill (trusted, opt-in)  Publish your skill as its **own wiki repo** (you own it, you `adompkg publish` updates whenever you like). Then PR a one-line **link** into this pack's manifest instead of the skill body. The pack references your repo; your repo stays yours.  - **Pro:** you keep ownership and can fix your site's flow the hour it changes,-  no maintainer round-trip — exactly what a fast-moving vendor site needs.+  no maintainer round-trip, exactly what a fast-moving vendor site needs. - **Con:** the pack now trusts a repo it doesn't control; a later edit isn't   re-reviewed. So this is **trusted-contributor only**. - **Use when:** the maintainer knows the contributor and is comfortable letting   their repo update live.  > The link itself still arrives by PR, so the maintainer always makes the-> conscious decision to trust a given external repo — but once linked, that repo+> conscious decision to trust a given external repo, but once linked, that repo > self-updates. Default to (A); grant (B) to people you trust. Revoking is just > dropping the link. @@ -54,14 +54,14 @@ past anti-bot defenses. Treat every contribution as code review: - No skill should exfiltrate credentials or paste them anywhere but the vendor's   own login. Creds come from `chip-fetcher creds`, never inline. - "Getting past a block" means *navigating the site as a human would* (right-  profile, human pacing, the path their own assistant suggested) — **not**+  profile, human pacing, the path their own assistant suggested), **not**   defeating security controls or solving CAPTCHAs programmatically. - A linked external skill (path B) inherits the trust you grant its repo. When in   doubt, require path A.  ## Status (for Colby / the wiki) -Path A (PR into the pack) works on the wiki today — it's a normal pull request.+Path A (PR into the pack) works on the wiki today, it's a normal pull request. Path B (a pack sub-skill that *links* an externally-owned wiki repo, which then self-updates) is the piece that needs platform support: a manifest link type the pack resolves at install time, plus a way for the maintainer to mark a given link
skills/SKILL.md+13−13
@@ -1,11 +1,11 @@ --- name: chip-fetcher-sites description: >--  The chip-fetcher SOURCE-SITE skill pack — one installable skill per vendor+  The chip-fetcher SOURCE-SITE skill pack, one installable skill per vendor   website that chip-fetcher crawls (SnapEDA, Mouser, DigiKey, Ultra Librarian,   Arrow, manufacturer sites…). Each sub-skill teaches the AI how to navigate   THAT site's CAD-download flow through pup: the URL patterns, the login/gate,-  the download button dance, and — crucially — how to get unstuck when a site+  the download button dance, and, crucially, how to get unstuck when a site   changes its layout or blocks automation. Third parties extend the pack by   contributing a skill for a site they've figured out. Read this before driving   chip-fetcher against a vendor site, or when a fetch gets stuck on a site whose@@ -20,7 +20,7 @@ description: >- chip-fetcher is an **open-source Page** (source public + `adompkg install`-able) that *also carries a skill pack in the same repo*. The app is the crawler; this pack is the **per-site navigation knowledge** the AI uses to drive it. They ship-together so the code and the know-how version in lockstep — but the know-how is+together so the code and the know-how version in lockstep, but the know-how is the part the community can extend without touching Rust.  ## Why per-site skills@@ -35,7 +35,7 @@ release. Capturing it as **a skill per site** means: - the AI reads the current site's skill before navigating, so it knows the exact   flow instead of guessing; - when a site changes (or blocks us), **anyone** who figures out the new flow can-  fix it by editing one skill — no fork, no Rust, no release;+  fix it by editing one skill, no fork, no Rust, no release; - the fix is discoverable: it's published to the wiki and the next person's AI   picks it up. @@ -47,7 +47,7 @@ The canonical sourcing ladder (Mfr → SnapEDA → Mouser → DigiKey → Arrow  ``` skills/-├── SKILL.md                 # this file — the pack entry point + ladder+├── SKILL.md                 # this file, the pack entry point + ladder ├── CONTRIBUTING.md          # how 3rd parties add / own a site skill └── sites/     ├── snapeda/SKILL.md     # the canonical reference flow (login + download)@@ -59,19 +59,19 @@ skills/  Each `sites/<vendor>/SKILL.md` is a complete, installable skill with its own trigger words, so the AI auto-surfaces the right one when it's about to touch-that vendor — or when a fetch against it gets stuck.+that vendor, or when a fetch against it gets stuck.  ## What a site skill must contain -1. **Reach** — the canonical product-search / product-page URL pattern for an MPN.-2. **Gate** — login / region / cookie wall, and how the persistent pup profile+1. **Reach**, the canonical product-search / product-page URL pattern for an MPN.+2. **Gate**, login / region / cookie wall, and how the persistent pup profile    gets past it (which profile, what creds key in `chip-fetcher creds`).-3. **Download flow** — the exact button/tab sequence to the CAD bundle, with the+3. **Download flow**, the exact button/tab sequence to the CAD bundle, with the    selectors or visual cues that identify each step.-4. **Catch** — what the downloaded artifact is (zip? IntLib? per-file?), where it+4. **Catch**, what the downloaded artifact is (zip? IntLib? per-file?), where it    lands, and how chip-fetcher pulls it off the desktop.-5. **When it breaks** — the failure signatures (block page, captcha, moved button)-   and the recovery playbook. This is the highest-value section — it's why the+5. **When it breaks**, the failure signatures (block page, captcha, moved button)+   and the recovery playbook. This is the highest-value section, it's why the    pack exists.  See `sites/snapeda/SKILL.md` for the template filled in.@@ -79,6 +79,6 @@ See `sites/snapeda/SKILL.md` for the template filled in. ## Adding a site  Found a site chip-fetcher doesn't know, or a new way past a block? Write a skill-for it and contribute it — `CONTRIBUTING.md` covers the two paths (PR it into the+for it and contribute it, `CONTRIBUTING.md` covers the two paths (PR it into the pack, or own it in your own wiki repo and link it in). Either way your AI, and everyone else's, gets smarter the next time that site comes up.
skills/sites/digikey/SKILL.md+6−6
@@ -1,7 +1,7 @@ --- name: chip-fetcher-site-digikey description: >--  How chip-fetcher navigates DigiKey for a chip's CAD models — and the recovery+  How chip-fetcher navigates DigiKey for a chip's CAD models, and the recovery   playbook for when DigiKey BLOCKS pup automation (its anti-bot trips   periodically and serves a challenge/block page instead of the product page).   Read before a DigiKey fetch, and especially when a DigiKey fetch returns a@@ -13,7 +13,7 @@ description: >- # Site: DigiKey  DigiKey carries manufacturer CAD (ECAD model via SnapEDA/SamacSys, 3D STEP) on the-product page. It's a useful rung — **when it lets us in**. DigiKey runs aggressive+product page. It's a useful rung, **when it lets us in**. DigiKey runs aggressive bot detection that changes without notice, so the "when it breaks" section here is the main event. @@ -35,7 +35,7 @@ the main event. ## Catch - Same as the other rungs: pull off the desktop, normalize into `library/<MPN>/`. -## When it breaks — DigiKey blocks pup+## When it breaks, DigiKey blocks pup  **Signature:** instead of the product page you get an "Access Denied", "verify you are human", a Cloudflare/Akamai interstitial, or an endless spinner.@@ -47,14 +47,14 @@ it's explicitly out of scope (see `../../CONTRIBUTING.md` → Security).  **The right plays, in order:** 1. **Use a warm human profile.** Drive DigiKey in a pup profile the user actually-   browses with — real cookies, real history. A cold automation profile trips the+   browses with, real cookies, real history. A cold automation profile trips the    screen far more often. Pace actions like a human (no instant clicks). 2. **Ask DigiKey's own assistant.** DigiKey has an on-site chat/AI. Navigating *to    the model* via the path it hands you often avoids the screened URL entirely.-   (This is exactly how a 3rd party first got past a fresh block — and then+   (This is exactly how a 3rd party first got past a fresh block, and then    wrote it up here.) 3. **Skip the rung.** DigiKey is mid-ladder, not the only source. If it's blocking,-   fall to Arrow / CSE-UL rather than fighting it — the part's model is usually+   fall to Arrow / CSE-UL rather than fighting it, the part's model is usually    reachable elsewhere. 4. **Capture what worked and publish it.** If you find a current path through,    update this section and ship it (`../../CONTRIBUTING.md`). The block changes
skills/sites/manufacturer/SKILL.md+4−4
@@ -2,7 +2,7 @@ name: chip-fetcher-site-manufacturer description: >-   The generic approach for pulling CAD straight from a chip MAKER's own site (TI,-  ST, NXP, Nordic, Microchip, Analog…) — the top rung of the ladder, where the+  ST, NXP, Nordic, Microchip, Analog…), the top rung of the ladder, where the   STEP is richest and most authoritative. Read before a manufacturer-direct   fetch, or to add a maker-specific sub-flow. Trigger words: manufacturer cad,   ti cad download, st cad, nxp cad, nordic cad, microchip cad, analog devices@@ -20,7 +20,7 @@ re-draw. Always try here first.   canonical product-search URL. Open that, land on the exact-MPN product page. - Fallback when the prefix is unknown: a `google.com/search?q=<MPN> CAD STEP site:<maker>.com`   resolves to the real product page (don't template a `<maker>.com/product/<MPN>`-  guess — those 404 for parts the maker renamed).+  guess, those 404 for parts the maker renamed).  ## Gate - Most makers offer CAD without login; some (NXP, ST) gate STEP behind a free@@ -30,7 +30,7 @@ re-draw. Always try here first. ## Download flow - Find the **CAD / Symbols, Footprints & 3D Models / Design Resources** section on   the product page. The model is often a partner embed (SnapEDA / Ultra Librarian /-  SamacSys) — if so, hand off to that site's skill. Native STEP downloads land as+  SamacSys), if so, hand off to that site's skill. Native STEP downloads land as   `.step` / `.zip`.  ## Catch@@ -42,6 +42,6 @@ re-draw. Always try here first. - **Embed instead of native CAD** → follow the embed to the partner site and use   that site's skill. - **Per-maker quirks** (TI's "ULTRA Librarian" button, ST's MyST gate, Nordic's-  DeviceTree bundles) deserve their own `sites/<maker>/SKILL.md` — add one when a+  DeviceTree bundles) deserve their own `sites/<maker>/SKILL.md`, add one when a   maker's flow is gnarly enough to be worth capturing. `sources/ti.rs` is the code   counterpart for TI.
skills/sites/mouser/SKILL.md+1−1
@@ -27,4 +27,4 @@ Mid-ladder for CAD, but the **canonical source for stock + price + lifecycle** ## Catch / When it breaks - Normalize into `library/<MPN>/`; lifecycle/stock → `stock.json`. - Embed → hand to the partner site's skill. Block → warm profile, then skip the-  rung (see digikey skill's playbook — same pattern).+  rung (see digikey skill's playbook, same pattern).
skills/sites/nxp/SKILL.md+1−1
@@ -1,7 +1,7 @@ --- name: chip-fetcher-site-nxp description: >--  How chip-fetcher pulls CAD from NXP (nxp.com) — the Design Resources / CAD+  How chip-fetcher pulls CAD from NXP (nxp.com), the Design Resources / CAD   section, sometimes behind an export-control click-through. Read before an   NXP-direct fetch. Trigger words: nxp, nxp.com, nxp cad, nxp step, nxp symbol   footprint, nxp export control, nxp blocked.
skills/sites/snapeda/SKILL.md+3−5
@@ -1,9 +1,7 @@ --- name: chip-fetcher-site-snapeda description: >--  How chip-fetcher navigates SnapEDA (SnapMagic) to download a chip's CAD bundle-  — symbol, footprint, 3D STEP, and the Fusion/Eagle .lbr + Altium IntLib —-  through a logged-in pup Chrome profile. The canonical template for a+  How chip-fetcher navigates SnapEDA (SnapMagic) to download a chip's CAD bundle, symbol, footprint, 3D STEP, and the Fusion/Eagle .lbr + Altium IntLib, through a logged-in pup Chrome profile. The canonical template for a   source-site skill. Read before driving a SnapEDA fetch, or when a SnapEDA fetch   stalls at the login wall or the download button. Trigger words: snapeda,   snapmagic, snapeda login, snapeda download, snapeda blocked, snapeda step,@@ -31,12 +29,12 @@ SnapEDA is the highest-yield non-manufacturer rung: it has symbol + footprint + 1. On the part page, click **Download** (top-right). A format panel opens. 2. Pick the targets chip-fetcher wants: **Symbol and Footprint** (KiCad), **3D    Model** (STEP), and the **Eagle/Fusion** library; Altium if offered.-3. SnapEDA emails-gates some downloads — the logged-in session usually skips that;+3. SnapEDA emails-gates some downloads, the logged-in session usually skips that;    if a "check your email" interstitial appears, the profile isn't logged in. 4. The download lands as a `.zip` in the desktop's Downloads.  ## Catch-- chip-fetcher pulls the zip off the desktop (`adom-desktop pull_file`), unpacks+- chip-fetcher pulls the zip off the desktop (`adom-bridge --ai-thread "<thread>" pull_file`), unpacks   into `library/<MPN>/`, and normalizes filenames (`<MPN>.step`, `.kicad_sym`,   `.kicad_mod`, `-fusion.lbr`, `.IntLib`). 
skills/sites/st/SKILL.md+1−1
@@ -1,7 +1,7 @@ --- name: chip-fetcher-site-st description: >--  How chip-fetcher pulls CAD from STMicroelectronics (st.com) — the CAD Resources+  How chip-fetcher pulls CAD from STMicroelectronics (st.com), the CAD Resources   tab on a product page, behind a free myST account for some downloads. Read   before an ST-direct fetch. Trigger words: st, stm, stmicroelectronics, st.com,   st cad, st step, stm32 cad, myst login, st blocked.
skills/sites/ti/SKILL.md+3−3
@@ -1,14 +1,14 @@ --- name: chip-fetcher-site-ti description: >--  How chip-fetcher pulls CAD from Texas Instruments (ti.com) — the "CAD/Symbols,+  How chip-fetcher pulls CAD from Texas Instruments (ti.com), the "CAD/Symbols,   Footprints & 3D Models" widget on a product page (an Ultra Librarian embed) and   the native STEP. Read before a TI-direct fetch. Trigger words: ti, texas   instruments, ti.com, ti cad, ti step, ti symbol footprint, ti ultra librarian,   ti.com blocked. --- # Site: Texas Instruments (ti.com)-Top-rung manufacturer source — TI's STEP is authoritative and its ECAD is an Ultra Librarian embed.+Top-rung manufacturer source, TI's STEP is authoritative and its ECAD is an Ultra Librarian embed. ## Reach - `https://www.ti.com/product/<PARTFAMILY>` → the specific orderable (e.g. nRF… no; TI parts like TPS62840). Or `google.com/search?q=<MPN> ti.com cad`. ## Gate@@ -16,4 +16,4 @@ Top-rung manufacturer source — TI's STEP is authoritative and its ECAD is an U ## Download flow - Product page → **Design & development → CAD/CAE symbols** → STEP downloads directly; ECAD opens the UL flow. ## Catch / When it breaks-- Normalize into `library/<MPN>/`. If the UL embed changes, see the `ultralibrarian` skill. TI occasionally A/B-tests the product page layout — find the CAD section by its heading, not a fixed selector, and publish the fix (`../../CONTRIBUTING.md`).+- Normalize into `library/<MPN>/`. If the UL embed changes, see the `ultralibrarian` skill. TI occasionally A/B-tests the product page layout, find the CAD section by its heading, not a fixed selector, and publish the fix (`../../CONTRIBUTING.md`).
skills/sites/ultralibrarian/SKILL.md+1−2
@@ -1,8 +1,7 @@ --- name: chip-fetcher-site-ultralibrarian description: >--  How chip-fetcher navigates Ultra Librarian / Component Search Engine (CSE/UL) —-  a late-ladder rung with broad coverage behind a login + format picker. Read+  How chip-fetcher navigates Ultra Librarian / Component Search Engine (CSE/UL), a late-ladder rung with broad coverage behind a login + format picker. Read   before a CSE/UL fetch. Trigger words: ultra librarian, ultralibrarian, cse_ul,   component search engine, samacsys, ul download, ul step, ul blocked. ---
src/ui/assets/icons/chip.svgadded+1
@@ -0,0 +1 @@+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#e6edf3" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="7" y="7" width="10" height="10" rx="1.5"/><path d="M9 7V4M12 7V4M15 7V4M9 20v-3M12 20v-3M15 20v-3M7 9H4M7 12H4M7 15H4M20 9h-3M20 12h-3M20 15h-3"/><circle cx="9.5" cy="9.5" r=".6" fill="#e6edf3" stroke="none"/></svg>
src/ui/assets/icons/settings-cog.svgadded+1
@@ -0,0 +1 @@+<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M12 15.5A3.5 3.5 0 0 1 8.5 12A3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5a3.5 3.5 0 0 1-3.5 3.5m7.43-2.53c.04-.32.07-.64.07-.97s-.03-.66-.07-1l2.11-1.63c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.31-.61-.22l-2.49 1c-.52-.39-1.06-.73-1.69-.98l-.37-2.65A.506.506 0 0 0 14 2h-4c-.25 0-.46.18-.5.42l-.37 2.65c-.63.25-1.17.59-1.69.98l-2.49-1c-.22-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64L4.57 11c-.04.34-.07.67-.07 1s.03.65.07.97l-2.11 1.66c-.19.15-.25.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1.01c.52.4 1.06.74 1.69.99l.37 2.65c.04.24.25.42.5.42h4c.25 0 .46-.18.5-.42l.37-2.65c.63-.26 1.17-.59 1.69-.99l2.49 1.01c.22.08.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64z"/></svg>\ No newline at end of file
uninstall.sh+4−3
@@ -1,5 +1,6 @@ #!/bin/bash set -euo pipefail-rm -f "${HOME}/.local/bin/adom-chip-fetcher"-rm -rf "${HOME}/.claude/skills/chip-fetcher" "${HOME}/.claude/skills/chip-fetcher-sites"-echo "Uninstalled adom-chip-fetcher (binary + chip-fetcher + chip-fetcher-sites skills)"+rm -f "${HOME}/.local/bin/adom-chip-fetcher" "${HOME}/.local/bin/chip-fetcher"+rm -rf "${HOME}/.claude/skills/adom-chip-fetcher" "${HOME}/.claude/skills/chip-fetcher" "${HOME}/.claude/skills/chip-fetcher-sites"+rm -rf "${HOME}/.local/share/adom-chip-fetcher"+echo "Uninstalled adom-chip-fetcher (binary, alias, UI assets, adom-chip-fetcher + chip-fetcher-sites skills). Settings in ~/.config/adom-chip-fetcher were kept."