← Commit history
README.md+4
@@ -97,6 +97,10 @@ Not in your rail? Dock settings (the gear at the top of the rail), Add app, | `adom-digikey` (service-digikey) | Recommended | DigiKey column and the fallback when Mouser cannot ship | | `adom-jlcpcb` (service-jlcpcb) | Optional | JLCPCB assembly-library column | +**No vendor API key, ever.** Mouser, DigiKey and JLCPCB credentials are company-owned and live only inside Adom's service containers. What a search needs is your **Adom identity**, so each search is attributed to a real Adom user: in an Adom container it is read automatically from `/var/run/adom/api-key`; elsewhere set `ADOM_API_KEY` to your Adom session token.++**Exact part first.** Search an exact MPN and the recommendation is that exact part whenever it is in stock, at Mouser first and then DigiKey. A packaging or grade variant (`DSER` vs `DSET`, a `-Q100` automotive part) is a different orderable part, so it can only ever appear as an explicit, amber-flagged **substitution**, never as a silent winner.+ Client-only: it owns no API keys and runs no service of its own; it federates the three vendor backends and renders. At least one backend must be reachable; a missing one shows a friendly error and the recommendation reasons around it.
SKILL.md+20−1
@@ -9,6 +9,14 @@ Searches Mouser + DigiKey + JLCPCB in parallel. Rust CLI + Hydrogen webview. Rec  **Quota rule: NEVER search in a loop.** Every search spends the shared daily vendor API quota (Mouser's free tier is about 1000/day for everyone combined). One search per user question. Never retry a rate-limited vendor on a timer, never poll /search, never re-run a search to refresh a page (the page re-renders from `GET /state` for free). The server answers an identical repeat within 5 minutes from its own cache, but do not lean on that: just do not loop. +## Identity and access (read this first)++**You never need a Mouser, DigiKey or JLCPCB API key, and you must never ask a user for one.** Those are company-owned credentials that live only inside Adom's service containers. Parts Search owns no vendor keys and runs no vendor service; it federates Adom's three hosted backends.++Every search DOES need an **Adom identity**, because each search is attributed to a real Adom user so shared vendor quota can be accounted for. It is automatic: in an Adom container the app reads `/var/run/adom/api-key` (the Adom session token) by itself; anywhere else set `ADOM_API_KEY` to the user's Adom session token. The backends validate the token against Adom's identity service, so the identity is proven rather than self-reported.++If a vendor column says **"search needs your Adom identity"**, the machine presented no valid Adom token: sign in to Adom or set `ADOM_API_KEY`. It is never a vendor key. If it says **"could not verify your identity right now"**, that is an Adom-side outage, not the user's credential: retry shortly.+ ## 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.@@ -108,11 +116,22 @@ The `search` output is a unified JSON:     "mpn": "iCE40HX4K-TQ144",     "unit_price": "$10.20",     "stock": 42,-    "reason": "Mouser has it in stock and is the preferred vendor: fastest, lowest-cost fulfillment."+    "reason": "Mouser has the exact part in stock and is the preferred vendor: fastest, lowest-cost fulfillment.",+    "requested_mpn": "iCE40HX4K-TQ144",+    "exact_match": true   } } ``` +### Exact part first, substitutions never silent++When the query is an exact MPN, the recommendation is **that exact part** whenever it is in stock: first at Mouser, then at DigiKey (the right part from the second vendor beats the wrong part from the first). Vendor result order and stock depth never outrank exactness.++- `exact_match: true` means `mpn` is precisely what was asked for.+- If the exact part is out of stock everywhere and a variant is offered instead, `exact_match` is `false` and a **`substitution`** string is present, naming both parts. The one-line summary says `SUBSTITUTION, NOT the requested <mpn>` and the page shows an amber warning.+- **Always relay a `substitution` to the user, and never order or design in a substituted part without their say-so.** A packaging suffix (`DSER` vs `DSET`) or a grade variant (`-Q100` automotive) is a different orderable part: different reel, different qualification, sometimes a different datasheet. Punctuation is part of an MPN (`74LVC2G17GV,125`), so it is compared exactly; only case and surrounding whitespace are ignored.+- A family or keyword query (`LM358`, `10k 0402`) matches no hit exactly, so `requested_mpn` is absent and the Mouser-preferred vendor order applies as before.+ Each `hit` includes `image_url` for the webview: use `show` when the user needs to eyeball product photos (Claude Code terminal can't show images).  ## The page
bin/adom-parts-search
⋯ 1 unchanged line ⋯
install.sh

  
package.json+41−2
@@ -1,6 +1,6 @@ {   "slug": "adom-parts-search",-  "version": "2.0.4",+  "version": "2.0.14",   "type": "app",   "description": "One search box across Mouser, DigiKey and JLCPCB. Parallel queries, side-by-side product photos, and a Mouser-preferred recommendation that reasons around stock and lead time. Dock Bar ready: serve, health, settings, Say it.",   "dependencies": {@@ -38,5 +38,44 @@     "SKILL.md",     "BUILD-SKILL.md",     "CHANGELOG.md"+  ],+  "changelog": "Module 2.4.0: the panel gutter check now measures against the containing block",+  "discovery_triggers": [+    "parts search",+    "find part",+    "source component",+    "source part",+    "compare distributors",+    "compare vendors",+    "Mouser or DigiKey",+    "BOM sourcing",+    "price this part",+    "where can I get",+    "check stock",+    "parts availability",+    "fastest delivery",+    "cheapest part",+    "component search",+    "part lookup",+    "price breaks"+  ],+  "discovery_pitch": "Search Mouser, DigiKey and JLCPCB in parallel with product photos; Mouser-preferred best-source pick. Say 'with parts search' to your AI.",+  "sample_prompts": [+    {+      "label": "source an STM32F103RBT6",+      "prompt": "source an STM32F103RBT6"+    },+    {+      "label": "where can I get an ESP32-S3-WROOM cheape",+      "prompt": "where can I get an ESP32-S3-WROOM cheapest and fastest"+    },+    {+      "label": "compare Mouser, DigiKey and JLCPCB for a",+      "prompt": "compare Mouser, DigiKey and JLCPCB for an LM555"+    },+    {+      "label": "price this BOM across distributors",+      "prompt": "price this BOM across distributors"+    }   ]-}+}\ No newline at end of file
page.json+49−8
@@ -4,7 +4,7 @@   "slug": "adom-parts-search",   "title": "Parts Search",   "brief": "One search box across Mouser, DigiKey and JLCPCB: parallel queries, side-by-side product photos, and a Mouser-preferred recommendation that reasons around stock and lead time.",-  "version": "2.0.4",+  "version": "2.0.14",   "install": {     "binary_name": "adom-parts-search",     "install_dir": "",@@ -36,10 +36,22 @@     "path": "screenshots/hero.png"   },   "sample_prompts": [-    "source an STM32F103RBT6",-    "where can I get an ESP32-S3-WROOM cheapest and fastest",-    "compare Mouser, DigiKey and JLCPCB for an LM555",-    "price this BOM across distributors"+    {+      "label": "source an STM32F103RBT6",+      "prompt": "source an STM32F103RBT6"+    },+    {+      "label": "where can I get an ESP32-S3-WROOM cheape",+      "prompt": "where can I get an ESP32-S3-WROOM cheapest and fastest"+    },+    {+      "label": "compare Mouser, DigiKey and JLCPCB for a",+      "prompt": "compare Mouser, DigiKey and JLCPCB for an LM555"+    },+    {+      "label": "price this BOM across distributors",+      "prompt": "price this BOM across distributors"+    }   ],   "discovery_triggers": [     "parts search",@@ -54,12 +66,41 @@     "where can I get",     "check stock",     "parts availability",-    "fastest delivery"+    "fastest delivery",+    "cheapest part",+    "component search",+    "part lookup",+    "price breaks"   ],-  "discovery_pitch": "Search Mouser + DigiKey + JLCPCB in parallel from one interface, with product photos and a Mouser-preferred best-source pick.",+  "discovery_pitch": "Search Mouser, DigiKey and JLCPCB in parallel with product photos; Mouser-preferred best-source pick. Say 'with parts search' to your AI.",   "metadata": {     "platform": "linux"   },   "category": "tools",-  "org": "adom"+  "org": "adom",+  "description": "One search box across Mouser, DigiKey and JLCPCB. Parallel queries, side-by-side product photos, and a Mouser-preferred recommendation that reasons around stock and lead time. Dock Bar ready: serve, health, settings, Say it.",+  "dependencies": {+    "adom/adom-mouser": "*",+    "adom/adom-digikey": "*",+    "adom/adom-jlcpcb": "*"+  },+  "needs_sudo": false,+  "scripts": {+    "install": "./install.sh",+    "uninstall": "./uninstall.sh"+  },+  "files": [+    "bin/**",+    "skills/**",+    "docs/icon.svg",+    "dockbar.json",+    "install.sh",+    "uninstall.sh",+    "package.json",+    "README.md",+    "SKILL.md",+    "BUILD-SKILL.md",+    "CHANGELOG.md"+  ],+  "changelog": "Module 2.4.0: the panel gutter check now measures against the containing block" }\ No newline at end of file
skills/adom-parts-search/SKILL.md+20−1
@@ -9,6 +9,14 @@ Searches Mouser + DigiKey + JLCPCB in parallel. Rust CLI + Hydrogen webview. Rec  **Quota rule: NEVER search in a loop.** Every search spends the shared daily vendor API quota (Mouser's free tier is about 1000/day for everyone combined). One search per user question. Never retry a rate-limited vendor on a timer, never poll /search, never re-run a search to refresh a page (the page re-renders from `GET /state` for free). The server answers an identical repeat within 5 minutes from its own cache, but do not lean on that: just do not loop. +## Identity and access (read this first)++**You never need a Mouser, DigiKey or JLCPCB API key, and you must never ask a user for one.** Those are company-owned credentials that live only inside Adom's service containers. Parts Search owns no vendor keys and runs no vendor service; it federates Adom's three hosted backends.++Every search DOES need an **Adom identity**, because each search is attributed to a real Adom user so shared vendor quota can be accounted for. It is automatic: in an Adom container the app reads `/var/run/adom/api-key` (the Adom session token) by itself; anywhere else set `ADOM_API_KEY` to the user's Adom session token. The backends validate the token against Adom's identity service, so the identity is proven rather than self-reported.++If a vendor column says **"search needs your Adom identity"**, the machine presented no valid Adom token: sign in to Adom or set `ADOM_API_KEY`. It is never a vendor key. If it says **"could not verify your identity right now"**, that is an Adom-side outage, not the user's credential: retry shortly.+ ## 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.@@ -108,11 +116,22 @@ The `search` output is a unified JSON:     "mpn": "iCE40HX4K-TQ144",     "unit_price": "$10.20",     "stock": 42,-    "reason": "Mouser has it in stock and is the preferred vendor: fastest, lowest-cost fulfillment."+    "reason": "Mouser has the exact part in stock and is the preferred vendor: fastest, lowest-cost fulfillment.",+    "requested_mpn": "iCE40HX4K-TQ144",+    "exact_match": true   } } ``` +### Exact part first, substitutions never silent++When the query is an exact MPN, the recommendation is **that exact part** whenever it is in stock: first at Mouser, then at DigiKey (the right part from the second vendor beats the wrong part from the first). Vendor result order and stock depth never outrank exactness.++- `exact_match: true` means `mpn` is precisely what was asked for.+- If the exact part is out of stock everywhere and a variant is offered instead, `exact_match` is `false` and a **`substitution`** string is present, naming both parts. The one-line summary says `SUBSTITUTION, NOT the requested <mpn>` and the page shows an amber warning.+- **Always relay a `substitution` to the user, and never order or design in a substituted part without their say-so.** A packaging suffix (`DSER` vs `DSET`) or a grade variant (`-Q100` automotive) is a different orderable part: different reel, different qualification, sometimes a different datasheet. Punctuation is part of an MPN (`74LVC2G17GV,125`), so it is compared exactly; only case and surrounding whitespace are ignored.+- A family or keyword query (`LM358`, `10k 0402`) matches no hit exactly, so `requested_mpn` is absent and the Mouser-preferred vendor order applies as before.+ Each `hit` includes `image_url` for the webview: use `show` when the user needs to eyeball product photos (Claude Code terminal can't show images).  ## The page
uninstall.sh