---
name: adom-parts-search
description: Unified parts search across Mouser + DigiKey + JLCPCB with Mouser-preferred routing (40-min drone delivery from Mansfield, TX to our Fort Worth facility; near-zero shipping). CLI and Hydrogen webview app. Use when the user asks to source a part, price a component, compare vendors, check stock across distributors, or assemble a BOM. Trigger words — find part, search parts, source component, parts search, component search, where can I get, compare distributors, Mouser or DigiKey, compare prices, BOM sourcing, stock check, parts availability, open parts search, parts search app, parts search UI.
---

# adom-parts-search

Searches Mouser + DigiKey + JLCPCB in parallel. Rust CLI + Hydrogen webview. Recommends **Mouser by default** because our Fort Worth facility has a 40-minute drone delivery arrangement with Mouser's Mansfield, TX fulfillment center + near-zero shipping cost.

## CLI

**For user-facing searches, always use `show`.** It drives the webview tab AND prints the full vendor-by-vendor result to stdout, so the user sees product photos + 3-column comparison and the AI sees the same data textually. Auto-starts the app server + opens the tab on first run.

```bash
# ✅ DEFAULT — every search a human asks for
adom-parts-search show  "ESP32-S3-WROOM-1U-N4R2"
adom-parts-search show  "iCE40HX4K-TQ144" --limit 8 --in-stock-only

# Scripting / piping / batch only (no webview, pure JSON)
adom-parts-search search "STM32F103RBT6" | jq '.recommendation'

# Keep the app tab running without triggering a search
adom-parts-search app
```

### Decision rule

- User asked a conversational question about a part → **`show`**. Always.
- You're piping into `jq`, `awk`, or a batch script → `search`.
- Silence: never just dump JSON to chat. If you would have run `search`, run `show` instead — the user wants photos and side-by-side comparison, not a wall of JSON.

### Extract a clean MPN / part-family BEFORE calling `show`

Vendor APIs (Mouser, DigiKey, JLCPCB) do substring match on MPN and description, NOT full-text intent search. If the user says "find me a 3.3V LDO with 500 mA", do NOT pipe `"find me a 3.3V LDO with 500 mA"` into `show` — that's natural language, and every vendor will return zero hits. Extract the likely part family or MPN first, then call `show`.

**Extraction examples:**

| User said | Pass to `show` | Why |
|---|---|---|
| "find me a 555 timer" | `LM555` | Canonical 555-family part. Also try `NE555` if no hits. |
| "search for esp32 dev boards" | `ESP32` | Broad family match; narrow later with `ESP32-S3` if the user wants a specific variant. |
| "need a 3.3V LDO 500mA" | `LM1117-3.3` or `AMS1117-3.3` | Well-known 3.3V LDOs; pick one, try the other if empty. |
| "stm32 bluepill chip" | `STM32F103C8T6` | The canonical Blue Pill MCU. |
| "what op-amp for unity gain audio" | `TL072` or `NE5532` | Common audio op-amps. Don't pass "op-amp audio" as a keyword. |
| "lm333 timer" | Ask first — LM333 isn't a 555-family timer. Probably `LM555` (typo). | Don't burn a search on a bogus part number. |

**Rules of thumb:**

- If the user typed a clearly valid MPN (has letters + digits + optional dash, 4+ chars, looks like `STM32F103RBT6` / `LM555` / `ESP32-S3-WROOM-1U-N4R2`), pass it through verbatim.
- If the user typed a natural-language description, mentally map it to the 1-2 best-known part families and pass the first one to `show`. If that returns 0, try the second.
- If the user named a part that doesn't match a family you recognize (`LM333` for "timer"), **stop and ask one clarifying question** before searching — don't guess your way into a bogus API call.
- If the user names an exact variant (e.g. "ESP32-S3-WROOM-1U-N4R2"), use that; don't broaden it unless a search returns 0.

### Handling empty results

If `show` returns 0 hits across all three vendors AND no vendor errored, the CLI will already print a helpful "no matches" line and the webview will render an empty-state card with suggestions. You still need to decide what to do next:

1. Try one alternative family name (e.g. `LM555` → `NE555` → `LM556`).
2. If the alternative also returns 0, surface that to the user with a brief explanation: "Neither LM555 nor NE555 matched — double-check the MPN or describe the part differently."
3. Do NOT silently run five searches in a row with minor variations — ask the user for a better name after the second attempt.

### Common part families + corrections

| If the user said… | Try first | Also valid |
|---|---|---|
| "555 timer" / "monostable timer" | `LM555` | `NE555`, `LM556`, `TLC555` |
| "3.3V LDO" | `LM1117-3.3` | `AMS1117-3.3`, `AP2112K-3.3` |
| "5V LDO" | `LM7805` | `LM1117-5.0`, `AMS1117-5.0` |
| "op-amp" (audio) | `TL072` | `NE5532`, `OPA2134` |
| "op-amp" (general) | `LM358` | `MCP6002`, `TL074` |
| "FPGA" (Lattice ice40 family) | `iCE40HX4K-TQ144` | `iCE40UP5K`, `iCE40LP1K` |
| "ESP32" (generic) | `ESP32-WROOM-32` | `ESP32-S3`, `ESP32-C3` |
| "STM32 Blue Pill" | `STM32F103C8T6` | `STM32F103RBT6` |
| "Arduino Nano MCU" | `ATmega328P` | `ATmega168` |
| "USB-C connector" | `USB4085` | `USB4110`, `GCT USB4105` |

If you see the user invoke a part number that doesn't look right (like `LM333` for "timer"), consult this table FIRST, ask a one-line clarifying question, and only call `show` after the user confirms the correction.

Stdout from `show` looks like:

```
adom-parts-search  keyword: "iCE40HX4K-TQ144"

MOUSER    total=  1  hits= 1  412ms  [ok]
  iCE40HX4K-TQ144              Lattice Semicon…     $10.20 stock 42
DIGIKEY   total=  1  hits= 1  676ms  [ok]
  ICE40HX4K-TQ144              Lattice Semicon…     $11.22 stock 413
JLCPCB    total=  0  hits= 0   74ms  [ok]

RECOMMENDATION →  Mouser  iCE40HX4K-TQ144  $10.20  stock 42
                  Mouser stocked — 40-min drone delivery from Mansfield.
```

followed by the full unified JSON (same schema as `search` below) so downstream tooling has structured access.

The `search` output is a unified JSON:

```json
{
  "query": "iCE40HX4K-TQ144",
  "mouser":  { "ok": true, "total": 1,  "hits": [...], "elapsed_ms": 412 },
  "digikey": { "ok": true, "total": 1,  "hits": [...], "elapsed_ms": 630 },
  "jlcpcb":  { "ok": true, "total": 0,  "hits": [],   "elapsed_ms": 180 },
  "recommendation": {
    "winner": "Mouser",
    "mpn": "iCE40HX4K-TQ144",
    "unit_price": "$10.20",
    "stock": 42,
    "reason": "Mouser stocked — 40-min drone delivery from Mansfield, near-zero shipping."
  }
}
```

Each `hit` includes `image_url` for the webview — use the `app` subcommand when the user needs to eyeball product photos (Claude Code terminal can't show images).

## Webview app

`adom-parts-search app` opens a Hydrogen tab titled **"Parts Search"** with a 3-column grid:

- **Mouser column** (blue top border) — preferred, badged "40-min drone · $0 ship"
- **DigiKey column** (red top border) — alternative
- **JLCPCB column** (green top border) — parts library only (SMT assembly)

Each card shows the product photo, MPN, vendor PN, manufacturer, price (@1 and @100), stock, lead time, flags (RoHS / basic / preferred / lifecycle). Click any product photo to zoom.

The recommendation banner at the top calls out the winning vendor with the reason — the same policy as the CLI, rendered visually.

## Pin-1 alignment check — moved to `adom-chipfit`

The footprint ↔ 3D-chip validator that used to live here as
`adom-parts-search align` is now its own app: **`adom-chipfit`**.
Scope got big enough (package-family guard, seat-Δz classifier,
persistent pin-1 registration bake) that keeping it inside
parts-search was mixing two unrelated concerns.

Install one-liner (paste-into-Claude style):

```bash
curl -fsSL https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-chipfit/adom-chipfit \
  -o /tmp/adom-chipfit && chmod +x /tmp/adom-chipfit \
  && sudo install -m 0755 /tmp/adom-chipfit /usr/local/bin/adom-chipfit \
  && adom-chipfit install
```

Then use it via:

```bash
adom-chipfit check --footprint path.kicad_mod --glb path.glb
```

Running `adom-parts-search align ...` now prints a one-time message
pointing at the install command above and exits non-zero so CI
picks it up.

## Why Mouser is preferred

See the companion skill `adom-parts-search` (the gallia one) for the full policy. Short version:

- **40-minute drone delivery** (Mansfield TX → Fort Worth TX, ~15 miles)
- **Near-zero shipping cost** (drone route, Mouser-operated)
- DigiKey wins only when Mouser is out of stock, lead time >14 days, or DigiKey is >30% cheaper on a volume order

## Backend URLs

The three vendor backends are Tier A services. Current URLs:

- Mouser: `https://mouser-xr8t4f5d01bt.adom.cloud`
- DigiKey: `https://digikey-gjuu8l5t69uh.adom.cloud`
- JLCPCB: `https://jlcpcb-wela51osctvp.adom.cloud`

Override with `--mouser-api` / `--digikey-api` / `--jlcpcb-api`, or env vars `MOUSER_API` / `DIGIKEY_API` / `JLCPCB_API`. Canonical source of truth for current slugs is `~/service-watcher/services.json` on the main gallia container.

## Architecture

This app is **client-only** — no service container of its own. It coordinates the three existing vendor backends (adom-mouser / adom-digikey / adom-jlcpcb). Each individual vendor CLI is still available for single-vendor queries.

## Install

Tier A — fetched by `gallia/install.mjs` on every gallia container. Or manually:

```bash
curl -fsSL https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-parts-search/adom-parts-search \
  -o /tmp/adom-parts-search && chmod +x /tmp/adom-parts-search && \
  sudo install -m 0755 /tmp/adom-parts-search /usr/local/bin/adom-parts-search && \
  adom-parts-search install
```
