Bill of Materials Integration — applying DevOps-style tooling to electronics projects.
bomi is a Python CLI for researching JLCPCB/LCSC parts and keeping a per-project BOM in version control. It caches part data in a shared local SQLite database and stores project selections in .bomi/project.yaml.
AI agents (Claude Code, Cursor, GitHub Copilot, etc.) can drive it directly — searching the catalog, comparing parts, updating the BOM, and pulling datasheet analysis without touching a browser. After a lot of iteration and many PCBs made, bomi proved to be a significant time saver: easier to track decisions, catch stock issues early, and keep BOM costs under control across revisions.
somebox.github.io/bomi — demos, examples, and getting-started guide
- Python
3.11+ uvfor install and development- Network access for live catalog search
- An OpenRouter API key only if you want datasheet analysis or summaries
git clone https://github.com/somebox/bomi.git
cd bomi
# Install the CLI globally as `bomi`
uv tool install -e .
# Or, to work on the repo
uv syncNote for asdf users:
uv tool installinstalls into uv's own tool environment, which may not be onPATHunder all asdf-managed Python shims. If you get "No preset version installed for command bomi", run directly with:uv run --directory /path/to/bomi bomi <command>Set
BOMI_PROJECT(see Project Resolution) to avoid needing--projecton every command.
Global config lives here:
- macOS:
~/Library/Application Support/bomi/config.yaml - Linux:
~/.local/share/bomi/config.yaml
Minimal config (see config.yaml.example in the repo):
openrouter_api_key: sk-or-v1-...Environment variables override config values:
export BOMI_OPENROUTER_API_KEY=sk-or-v1-...The shared cache database is stored alongside the config as parts.db.
# 0. Sync the category tree (one-time, cached for 24h)
bomi sync
# 1. Search the live catalog
bomi search "10k 0402 resistor"
# 2. Cache one or more exact parts
bomi fetch C8287 C25900
# 3. Query the local cache offline
bomi query --package 0402 --basic-only --attr "Resistance >= 10k"
# 4. Inspect or compare cached parts
bomi info C8287
bomi compare C8287 C25900
# 5. Analyze a cached part's datasheet with OpenRouter (default prompt covers key specs)
bomi analyze C8287info, compare, analyze, and datasheet all work from the local cache. If a part is missing, run bomi fetch <code> first.
Projects store their BOM in .bomi/project.yaml, which is meant to be committed with the rest of the design files.
cd my-pcb-project
bomi init --name "my-board" --description "Motor driver board"
# Add parts to the BOM (fetches from catalog if not already cached)
bomi select C8287 --ref R1 --qty 2 --notes "10k pull-up"
bomi select C1525 --ref C1 --qty 1 --notes "100nF bypass"
bomi select C1525 --ref C2 --qty 1 --notes "100nF bypass"
# Review the BOM
bomi list
bomi list --format json
bomi list --format csv
bomi list --check
# Project summary
bomi status
# Edit selections
bomi relabel R1 R3
bomi deselect C2bomi init currently:
- creates
.bomi/project.yaml - appends datasheet PDF ignore rules to
.gitignore
Project context is resolved in this order:
--project <path>BOMI_PROJECTenv var- walking up from the current directory to find
.bomi/project.yaml
If you're running bomi from outside the project directory (e.g. via uv run --directory), set BOMI_PROJECT so project commands work without --project on every call:
export BOMI_PROJECT=/path/to/my-pcb-project| Command | Notes |
|---|---|
sync |
Fetch and cache JLCPCB category tree (skips if <24h old, --force to refresh) |
categories [query] |
List cached categories, optionally filtered by name |
search <keyword> |
Live JLCPCB search, results are cached locally. --category filters server-side |
fetch <codes>... |
Cache exact LCSC codes |
query [keyword] |
Search the local cache only. --category filters by category |
info <designator-or-code> |
Show one cached part by project designator (for example R1) or LCSC code |
compare <codes>... |
Compare cached parts |
analyze <code> |
Analyze one cached datasheet with OpenRouter |
datasheet <codes>... |
Download PDFs and optionally generate markdown summaries |
db stats |
Show cache statistics |
db clear |
Clear the local cache |
| Command | Notes |
|---|---|
init |
Create .bomi/project.yaml in the current directory |
select <code> --ref REF |
Add a BOM entry, fetching the part if needed |
deselect <ref> |
Remove a BOM entry by reference |
relabel <old> <new> |
Rename a BOM entry reference |
list |
Show the BOM with cached part data (bom is an alias) |
status |
Show project summary, cost estimate, and warnings |
Most research commands support --format table|json|csv|markdown.
Commands that currently support --format:
searchfetchqueryinfocompareanalyzelistdb stats
Most JSON output uses this envelope:
{
"status": "ok",
"command": "search",
"count": 5,
"results": []
}list --format json and bom --format json both return { "status": "ok", "command": "...", "data": [...] }.
status is text-only today.
Filter syntax:
--attr "AttributeName operator value"Supported operators: >=, <=, >, <, =, and != (mainly for non-numeric attribute values).
Values support SI prefixes such as 10k, 100n, and 4.7u. Non-numeric values use string matching with = or !=.
Examples:
bomi search "0402 resistor" --attr "Resistance >= 10k"
bomi query --category "Chip Resistor" --attr "Resistance = 36k"
bomi query --attr "Capacitance <= 100n"
bomi search "RGB LED" --attr "Forward Current >= 100mA"
bomi query --category "Slide Switches" --attr "Circuit = SP3T"For package, minimum stock, maximum price (qty-1 tier), and --attr filters, the same rules are applied after a live search (in-memory, on normalized parts) and inside the local query path (SQL). The --basic-only and --preferred-only flags apply on the JLCPCB API for search only; they are enforced in SQL for query but are not re-applied locally after a search. --category for search resolves to an exact synced subcategory name for the API; for query it is a substring match on cached parts.category.
| What | Location |
|---|---|
| Global config | ~/Library/Application Support/bomi/config.yaml on macOS, ~/.local/share/bomi/config.yaml on Linux |
| Shared cache database | parts.db in the same global data directory |
| Project BOM | .bomi/project.yaml inside a PCB project |
| Optional project docs | docs/ inside your project |
src/bomi/
api.py HTTP client for JLCPCB search and detail endpoints
analysis.py datasheet download and OpenRouter analysis
categories.py category validation (query) and API name resolution (search)
cli.py Click command definitions and output orchestration
config.py config and path handling
db.py SQLite schema and persistence (Database supports `with ... as db`)
filters.py shared package/stock/price/--attr rules for search vs query
normalize.py API response normalization
output.py table/json/csv/markdown formatters and BOM views for list/bom
project.py project file and BOM handling
scrape.py category tree scraper for JLCPCB
search.py local cache query helpers
The static site under site/ includes terminal demos in site/presentation/. To regenerate casts and copy the Reveal deck after editing demo/generator/scenes.yaml or demo/presentation/index.html:
python build_site.pyThen commit the updated site/presentation/ files. GitHub Pages only publishes what is in the repo; it does not run this script. Details: demo/README.md.
docs/bomi-guide.md: short agent-oriented usage guide (also at somebox.github.io/bomi/guide.html)docs/features.md: backlog / future-feature ideas (not a roadmap)docs/examples.md: command examplesdocs/bomi-api-internals.md: current API notes and implementation boundariesdocs/sqlite-database-guide.md: local cache schema and query examples
uv sync
uv run pytest -vPRs are welcome. A good starting point is:
- run
uv sync - read
README.mdand the docs linked above - run
uv run pytest -v - keep behavior changes reflected in the docs
