Metadata-Version: 2.4
Name: jlcpcb
Version: 0.1.0
Summary: Python client and agent-native CLI for the JLCPCB SMT parts catalogue (search, detail, datasheets, facets)
Project-URL: Homepage, https://github.com/youseiushida/jlcpcb
Project-URL: Repository, https://github.com/youseiushida/jlcpcb
Project-URL: Issues, https://github.com/youseiushida/jlcpcb/issues
Project-URL: Documentation, https://github.com/youseiushida/jlcpcb#readme
Project-URL: Changelog, https://github.com/youseiushida/jlcpcb/releases
Author: Yosei Ushida
License-Expression: MIT
License-File: LICENSE
Keywords: agent-native,bom,cli,datasheet,electronics,jlcpcb,lcsc,pcb,smt
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Manufacturing
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Hardware
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: click>=8.1
Requires-Dist: httpx[http2]>=0.28
Requires-Dist: pydantic>=2.6
Provides-Extra: dev
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest-xdist>=3; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.22; extra == 'dev'
Description-Content-Type: text/markdown

# jlcpcb

[![Tests](https://github.com/youseiushida/jlcpcb/actions/workflows/test.yml/badge.svg)](https://github.com/youseiushida/jlcpcb/actions/workflows/test.yml)
[![Live API](https://github.com/youseiushida/jlcpcb/actions/workflows/live.yml/badge.svg)](https://github.com/youseiushida/jlcpcb/actions/workflows/live.yml)
[![PyPI version](https://img.shields.io/pypi/v/jlcpcb.svg)](https://pypi.org/project/jlcpcb/)
[![Python versions](https://img.shields.io/pypi/pyversions/jlcpcb.svg)](https://pypi.org/project/jlcpcb/)
[![PyPI downloads](https://img.shields.io/pypi/dm/jlcpcb.svg)](https://pypi.org/project/jlcpcb/)
[![License: MIT](https://img.shields.io/github/license/youseiushida/jlcpcb.svg)](LICENSE)
[![Agent-native CLI](https://img.shields.io/badge/CLI-agent--native-blueviolet)](src/jlcpcb/skills/SKILL.md)
[![pre-commit](https://img.shields.io/badge/code%20style-detroit--school%20tests-success)](tests/)

Python client **and CLI** for the JLCPCB SMT parts catalogue. Search, filter,
fetch part detail, download datasheets and product images, and pull EasyEDA
symbols / footprints — all via the same internal JSON endpoints the JLCPCB
SPA uses.

See `docs/RESEARCH.md` for the reverse-engineered API specification, and
`src/jlcpcb/skills/SKILL.md` for the agent-side workflow guide.

## Library quick start

```python
from jlcpcb import JLCPCB

with JLCPCB() as c:
    result = c.search(keyword="STM32F103C8T6", page_size=10)
    detail = c.get_by_code("C8734")
    pdf = c.download_datasheet("C8734")
    brands_by_letter = c.brands()
```

## CLI quick start

The CLI follows the **10 Principles for Agent-Native CLIs** (Trevin Chow,
May 2026). Highlights:

* Non-interactive by default. No prompts, no hangs.
* JSON output when stdout is piped (the agent case); plain table when a TTY
  is attached. `--json` and `--table` force either.
* Stable exit-code taxonomy: `0/2/3/4/5/6/7/8/9` — visible in
  `jlcpcb agent-context | jq .exit_codes`.
* Enumerating errors: every enum rejection names the valid set in the
  message and suggests a working invocation.
* `--limit` default 25 (server cap 72), with `truncated` / `hint` in the JSON
  envelope.
* Cross-CLI vocabulary: `list` / `get` / `--force` / `--json` / `--limit` —
  banned synonyms (`info`, `ls`, `--format=json`, `--skip-confirmations`)
  are statically enforced (`tests/cli/test_vocabulary.py`).
* Three-layer introspection: `--help`, `jlcpcb agent-context`,
  `jlcpcb skill-path` → `SKILL.md`.
* Persistent profiles (`profile save/list/get/delete`).
* Job ledger (`job list/get/prune`) ready for future long-running ops.
* `--deliver=stdout|file:<path>|webhook:<url>` for download artefacts,
  atomic file write, structured refusal on unknown schemes.
* `feedback "..."` records to `~/.jlcpcb/feedback.jsonl`; POSTs to
  `$JLCPCB_FEEDBACK_ENDPOINT` if set.

```bash
# search
jlcpcb part list -q STM32F103C8T6 --limit 5 --in-stock

# single detail
jlcpcb part get C8734

# download datasheet to file
jlcpcb part datasheet download C8734                     # ./C8734.pdf
jlcpcb part datasheet download C8734 --deliver=stdout > out.pdf
jlcpcb part datasheet download C8734 --deliver=webhook:https://example.com/hook

# categories / brands / hot / fx
jlcpcb category list --limit 10
jlcpcb category get 2973
jlcpcb brand list --letter A --limit 5
jlcpcb hot list
jlcpcb fx list

# profiles
jlcpcb profile save mcus --library expand --in-stock --limit 50
jlcpcb --profile=mcus part list -q STM32

# agent introspection
jlcpcb agent-context | jq '.commands.part.subcommands.list.flags'
cat $(jlcpcb skill-path | jq -r .path)/SKILL.md
```

## Install

```
pip install -e .[dev]
```

The `jlcpcb` console_scripts entry is wired automatically by `pip install`.
Without install you can run the CLI in-tree via:

```
PYTHONPATH=src python -m jlcpcb.cli <args>
```

## Test

```
pytest                       # 188 tests, ~40 s (live tests skipped)
JLCPCB_LIVE=1 pytest         # 224 tests, ~110 s (includes 36 live tests)
```

The test suite is **Detroit-school**: unit and component tests exercise real
collaborators (Pydantic models, filter builders, the SSR parser, the Click
app) and only stub the HTTP transport via `respx` with fixtures captured
from the live API. Integration tests hit `jlcpcb.com` directly.

| Layer | Files | Tests |
| --- | --- | --- |
| Library unit | `tests/unit/test_*.py` | 67 |
| Library component (HTTP stubbed) | `tests/component/test_client.py` | 40 |
| Library integration (live HTTP) | `tests/integration/test_live.py` | 25 |
| CLI unit | `tests/cli/test_{delivery,profiles,jobs_and_feedback,vocabulary}.py` | 39 |
| CLI component (Click + HTTP stubbed) | `tests/cli/test_commands.py` | 42 |
| CLI integration (live HTTP) | `tests/cli/test_live.py` | 11 |
| **Total** | | **224** |

`tests/cli/test_vocabulary.py` is the policy enforcer the article calls out:
banned verbs and flag synonyms fail the test the moment they enter the
command tree, so the CLI surface can't silently drift away from the
convention.

To refresh the captured HTTP fixtures from the live API:

```
PYTHONPATH=src python scripts/capture_fixtures.py
PYTHONPATH=src python scripts/trim_manufacturers.py
```
