608 lines
35 KiB
Markdown
608 lines
35 KiB
Markdown
---
|
|
name: library-manager
|
|
description: >-
|
|
Manage Vecmocon's component library. Extract parameters from a component datasheet PDF into the
|
|
per-typeid Excel template, check Gitea for a duplicate MPN_make, classify to a
|
|
typeid, update that typeid's template (versioning, changelog, backfill), fill a per-part
|
|
workbook, verify with a human, read the Altium symbol/footprint refs, and push a part folder to
|
|
Gitea. Can UPDATE an existing part instead of hard-stopping on a duplicate. WRITES the mandatory
|
|
SOP parameters (from the verified Excel) directly into the .SchLib symbol and COMPILES an Altium
|
|
integrated library (.IntLib) bundling symbol + footprint, so every part folder holds five files
|
|
(workbook, datasheet, .SchLib, .PcbLib, .IntLib). Every Description follows Vecmocon's strict
|
|
Altium Description Format. Use whenever the user uploads a datasheet, builds/updates a library
|
|
entry, adds a parameter, fills .SchLib parameters, builds an integrated library, or pushes to
|
|
Gitea. ALWAYS trigger on "\datasheet", "\library", "\library-manager", or "\schlib".
|
|
---
|
|
|
|
# Library Manager
|
|
|
|
Turn one component datasheet into a verified, versioned library entry in Gitea. The guiding
|
|
idea is honesty and traceability: every value lands in the right column and unit, anything
|
|
the datasheet doesn't state stays blank, and nothing reaches Gitea until a human has
|
|
confirmed it.
|
|
|
|
## Ask, don't assume
|
|
|
|
This is a deliberately **interactive** skill. At every decision point, **ask the user and
|
|
wait for an answer — do not assume a default and proceed.** In particular, always confirm:
|
|
the **make**; the **typeid/classification** you inferred; whether any **new parameters**
|
|
should be added to the template; the extracted **values** (the verification loop); that the
|
|
**symbol/footprint actually match this part**; and whether to **apply a new parameter to
|
|
existing parts** (backfill). When something is ambiguous, ask a specific question rather than
|
|
guessing. It is always better to ask one more question than to write the wrong thing into the
|
|
library.
|
|
|
|
One more thing to confirm when a part **already exists**: whether to **update it or stop**
|
|
(don't silently overwrite), and if updating, **which fields change** (values, symbol/footprint,
|
|
datasheet). An update still goes through the same verification loop before anything is pushed.
|
|
|
|
**Pushing is the exception — push automatically.** Once a part is verified and assembled (or a
|
|
template/version change has been made, or a verified update is ready), push to Gitea **on your
|
|
own, without asking for confirmation**. Never add a "shall I push?" step — just push, then tell
|
|
the user where it landed. (You still flag a genuine symbol/footprint mismatch, because that's a
|
|
correctness issue, not a push confirmation.)
|
|
|
|
## Inputs
|
|
|
|
- **A datasheet PDF whose filename is the MPN** (e.g. `BAT46WJ.pdf`). If it's a series
|
|
datasheet, search that exact MPN inside to read the correct variant.
|
|
- **The make** (manufacturer), given by the user. The `make` tag is the first word of the
|
|
manufacturer, alphanumerics only (Texas Instruments → `Texas`, Nexperia → `Nexperia`).
|
|
- **Later in the flow**, after verification: an Altium **symbol** (`.SchLib`) and
|
|
**footprint** (`.PcbLib`) file, provided by the user.
|
|
|
|
## The identifier: `MPN_make_typeid`
|
|
|
|
Every part folder, every per-part workbook, and column A of every sheet (`MPN_make_type`)
|
|
use the same tag:
|
|
|
|
```
|
|
<MPN>_<make>_<typeid> e.g. BAT46WJ_Nexperia_SCH
|
|
```
|
|
|
|
`typeid` is the part's type-ID code from the taxonomy (`references/taxonomy.md`, full source
|
|
`assets/template/Type_ID.xlsx`) — Schottky → `SCH`, MOSFET → `MOS`, LDO → `LDO`. In the new
|
|
template **each typeid is its own sheet** (125 of them). The broader **Class** (Diode,
|
|
Transistor, IC …) is used only to organise the library repo into top-level folders.
|
|
|
|
## Gitea layout (two repos)
|
|
|
|
```
|
|
skill repo/ this skill's own files (updated versions land here too)
|
|
|
|
library repo/
|
|
<Class>/ e.g. Diode, IC, Transistor, Resistor, ...
|
|
<MPN>_<make>_<typeid>/ e.g. BAT46WJ_Nexperia_SCH
|
|
<MPN>_<make>_<typeid>.xlsx this part's own one-row parameter sheet
|
|
<MPN>_data.pdf the datasheet
|
|
<symbol>.SchLib user-provided, with all parameters written in
|
|
<footprint>.PcbLib user-provided
|
|
<MPN>.IntLib integrated library (symbol + footprint, compiled by the skill)
|
|
```
|
|
|
|
**Every part folder holds five files** — the workbook, the datasheet, the `.SchLib` (with the
|
|
full parameter set written into it), the `.PcbLib`, and the compiled `.IntLib`. The `.SchLib`
|
|
parameter fill and the `.IntLib` build are **not optional** — they run on every part before the
|
|
push (see *Mandatory symbol parameters* and *Build the integrated library* below).
|
|
|
|
There is **no single master workbook** — each part carries its own sheet inside its folder.
|
|
Connection + repo names live in `config/gitea.env` (`SKILL_REPO`, `LIBRARY_REPO`), so runs
|
|
need no per-session token. If the host is unreachable, the git steps fail clearly and write
|
|
nothing.
|
|
|
|
## Who's running this (operator identity)
|
|
|
|
The whole org shares one Claude account and one Gitea token, so the skill can't tell who's
|
|
running it on its own. Instead it records the **operator's name** on everything it pushes, and
|
|
asks **only once per person**. Establish the identity at the very start of a run:
|
|
|
|
1. Read `~/.library-manager-id` on the operator's machine (via the desktop bridge):
|
|
`cat ~/.library-manager-id`.
|
|
2. **If it exists** (JSON like `{"name":"Priya Sharma","email":"priya@vecmocon.com"}`) → use it,
|
|
**don't ask**.
|
|
3. **If it does NOT exist → this person isn't onboarded yet, so ask once** (do this even though
|
|
the config carries a default, so a teammate is never silently logged as "admin"):
|
|
> "First time using the library skill on this machine — what name should your library
|
|
> changes be recorded under? (If you're the admin, just enter `admin`.) And your email,
|
|
> if you have one."
|
|
|
|
Then save it so it's remembered forever:
|
|
`echo '{"name":"<Name>","email":"<email>"}' > ~/.library-manager-id`. Every later run finds
|
|
it in step 1 and never asks again — the admin answers `admin` once, each teammate answers
|
|
their own name once, and from then on everyone is attributed correctly with no prompt.
|
|
4. **Only if you genuinely can't ask** (an unattended / scheduled run, or the machine isn't
|
|
reachable) fall back to the config `OPERATOR` default so the run isn't blocked — and say in
|
|
your summary that attribution used the default rather than a confirmed person.
|
|
|
|
Then carry that identity through the run:
|
|
|
|
- pass `--author "<Name> <<email>>"` to every `push-part` / `push-skill` / `commit-push`, and
|
|
- pass `--by "<Name>"` to `append_parameter`.
|
|
|
|
(Equivalent alternatives the scripts also read: `export LM_AUTHOR_NAME=... LM_AUTHOR_EMAIL=...`
|
|
for the session, or drop the same `~/.library-manager-id` file in the container home.)
|
|
|
|
**Identity precedence** (first one that's set wins): `--author` flag → `LM_AUTHOR_*` env →
|
|
per-person `~/.library-manager-id` → the per-install **`OPERATOR`** default in
|
|
`config/gitea.env`. So this admin install has `OPERATOR=admin`, meaning **every run/push here
|
|
is recorded as "admin" automatically**, with no file or prompt needed. A member's own
|
|
`~/.library-manager-id` (or an explicit `--author`) overrides that default with their real name.
|
|
|
|
This stamps the operator onto three things: the **commit author** (shown in `git log`, the
|
|
Gitea commit page, and `git blame`), the **commit message** (it ends with `(by <Name>)`, so the
|
|
name shows right in Gitea's activity feed), and a **By** column in the changelog. Honest limit:
|
|
the top-line "*X* pushed to main" in Gitea's activity still shows the **shared token owner** —
|
|
only giving each person their own Gitea token changes that bottom layer.
|
|
|
|
## Workflow
|
|
|
|
Run these in order. Each `python`/`bash` command is a helper in `scripts/`.
|
|
|
|
### 0. Sync the skill state from Gitea first — always
|
|
|
|
The skill's state (the template, the per-typeid versions, and the changelog) lives in the
|
|
**skill repo**, and it grows over time. A fresh install/session starts from the packaged v1
|
|
state, so if you don't sync first, a second template change wouldn't build on the first — the
|
|
versions would restart at v1 and the changelog would look like it only holds the latest change.
|
|
**So begin every run by pulling the current state:**
|
|
|
|
```bash
|
|
python scripts/gitea_components.py pull-skill
|
|
```
|
|
|
|
This copies `template.xlsx`, `versions.json`, and `CHANGELOG.xlsx` from the skill repo into the
|
|
local skill, so version bumps continue correctly (v2 → v3 → …) and the changelog stays
|
|
**cumulative from the very first change**.
|
|
|
|
### 1. Duplicate check first — before any real work
|
|
|
|
The part's presence is keyed on **MPN + make** (typeid not known yet). If it already exists,
|
|
stop; re-doing an existing part would only risk overwriting good data.
|
|
|
|
```bash
|
|
python scripts/gitea_components.py check-mpn --mpn <MPN> --make <make>
|
|
```
|
|
|
|
`ABSENT` (exit 0) → this is a **new part**; continue to step 2.
|
|
|
|
`EXISTS …` (exit 3) → the part is already in Gitea. Don't silently overwrite it, but don't
|
|
dead-end either — **ask the user whether they want to update the existing part or stop.** If
|
|
they want to revise it (new/corrected values, a swapped symbol/footprint, a newer datasheet),
|
|
go to *Updating an existing part*. If not, stop here. (If the user's request already said
|
|
"update"/"revise"/"fix" this part, take that as the answer and go straight to the update flow.)
|
|
|
|
### 2. Classify → typeid (and its Class)
|
|
|
|
Read the datasheet, identify the part, and match it to the closest subclass in
|
|
`references/taxonomy.md`; record its **typeid** (= the template sheet name). The **Class**
|
|
(for the library-repo folder) comes from the same taxonomy row —
|
|
`scripts/common.py:class_folder(typeid)` returns it (e.g. `SCH` → `Diode`).
|
|
|
|
### 3. Confirm the typeid's template (and add parameters if asked)
|
|
|
|
Check whether that typeid has a sheet in `assets/template/template.xlsx`.
|
|
|
|
- **No sheet for this typeid** → ask the user to upload the template sheet for it. Add it to
|
|
`assets/template/template.xlsx`, then push the updated skill files to the skill repo (see
|
|
*Pushing the skill repo*). Then continue.
|
|
- **Sheet exists** → print **all** of that sheet's parameters (its column headers) in the
|
|
chat and ask the user whether any new parameters should be added.
|
|
- **No** → go to step 4.
|
|
- **Yes** → collect the new parameter name(s), then:
|
|
|
|
```bash
|
|
python scripts/append_parameter.py --typeid <typeid> \
|
|
--param "New Parameter(unit)" [--param "Another(unit)"] \
|
|
--desc "why these were added" --by "<operator name>"
|
|
```
|
|
|
|
This appends the column(s) at the end of that typeid's sheet, **bumps that typeid's
|
|
Template Version and Skill Version together** (v1→v2 — see *Per-typeid versioning*),
|
|
and writes one row to the global changelog `assets/CHANGELOG.xlsx`. Then sync the updated
|
|
skill files + changelog to the skill repo with `push-skill` **automatically** (see
|
|
*Pushing the skill repo*) — that merges the new changelog row onto the one already in Gitea
|
|
rather than overwriting it.
|
|
|
|
Then ask: **should this change apply to the parts of this typeid already in Gitea?**
|
|
- **No** → go to step 4 (only the current part gets the new column).
|
|
- **Yes → backfill** (see *Backfilling existing parts*), then tell the user the previous
|
|
sheets were updated, and go to step 4.
|
|
|
|
### 4. Extract and fill the per-part workbook
|
|
|
|
Read every parameter the datasheet actually states into that typeid's columns, converting to
|
|
each header's unit. **Leave blanks where the datasheet is silent — an honest blank beats a
|
|
guess.**
|
|
|
|
The **`Description`** column is special: it is **not** free prose but a strict `_`-joined
|
|
engineering string — a type prefix, the defining parameters in a fixed order, package near the
|
|
end, optional AEC-Q last (e.g. `CHIP_RES_36kΩ_62.2mW_±0.1%_0402`, `CHIP_CAP_2.2uF_100v_±10%_1210_x7r`,
|
|
`SCH_100V_0.25A_SOD-323F`). `references/description_format.md` defines the format for **every**
|
|
type in the library: the four the Altium SOP spells out (Resistor, Capacitor, Zener, TVS) are
|
|
**strict**; the rest are the house extension on the same basis. Look up the part's type there,
|
|
build the Description to that format, and if a real datasheet doesn't fit the format cleanly,
|
|
follow the pattern and **flag the mismatch to the engineer** rather than bending it silently.
|
|
Collect the values into a small `part.json`:
|
|
|
|
```json
|
|
{"mpn":"BAT46WJ","manufacturer":"Nexperia","typeid":"SCH",
|
|
"values":{"Description":"SCH_100V_0.25A_SOD-323F","Forward Voltage(V)":"0.71",
|
|
"Reverse Voltage(V)":"100","Forward Current(A)":"0.25","Package":"SOD-323F"}}
|
|
```
|
|
|
|
(typeid `SCH` → prefix `SCH`, format `SCH_Vr_Io_Package`, so a 100 V / 250 mA Schottky in
|
|
SOD-323F becomes `SCH_100V_0.25A_SOD-323F`.)
|
|
|
|
```bash
|
|
python scripts/fill_templates.py part.json \
|
|
--template assets/template/template.xlsx --dest <stage>/<tag>/
|
|
```
|
|
|
|
This writes `<tag>.xlsx` with column A = the tag, **Skill Version (col B)** and **Template
|
|
Version (col C)** stamped from this typeid's current versions, and the four design columns
|
|
left blank for now.
|
|
|
|
The part workbook has up to **two sheets**:
|
|
|
|
- **Sheet 1** — the typeid's parameter sheet (the one filled row).
|
|
- **Sheet 2 — `Version History`** — added **only when this typeid has had a template/skill
|
|
update**. It lists the **cumulative** change history for that typeid (Date, Skill Version,
|
|
Template Version as `v1 → v2`, Description) — every change up to the version this file was
|
|
built at. So a part built at v3 shows both `v1 → v2` and `v2 → v3`; a still-at-v1 typeid has
|
|
no second sheet. The history is read from `assets/CHANGELOG.xlsx`, so make sure the local
|
|
changelog is current (it's kept in sync by `push-skill`) before building parts.
|
|
|
|
### 5. Human verification loop
|
|
|
|
Deliver the filled workbook to the user and ask them to verify it. If they report an error
|
|
or say it isn't right, **go back to step 4, re-read the datasheet more carefully, re-fill,
|
|
and hand it back.** Repeat until the user confirms it's verified. Nothing is pushed until
|
|
this passes — the engineer is the ground truth for the numbers.
|
|
|
|
### 6. Symbol + footprint → the design columns
|
|
|
|
Once verified, ask the user to upload the **symbol (`.SchLib`)** and **footprint
|
|
(`.PcbLib`)** files. Copy them into the staging part folder **under their proper names** (so
|
|
the Path columns match the files that actually get stored — strip any upload-staging prefix
|
|
the environment may have added), then read all four design values in one shot:
|
|
|
|
```bash
|
|
cp <uploaded_symbol> <stage>/<tag>/<symbol_name>.SchLib
|
|
cp <uploaded_footprint> <stage>/<tag>/<footprint_name>.PcbLib
|
|
python scripts/altium_refs.py design \
|
|
--symbol <stage>/<tag>/<symbol_name>.SchLib \
|
|
--footprint <stage>/<tag>/<footprint_name>.PcbLib > design.json
|
|
```
|
|
|
|
This produces (verified against real Ultra-Librarian exports):
|
|
|
|
- **Library Ref** = the component name **inside** the `.SchLib` (e.g. `CGA3E3X7R1H474K080AE`)
|
|
- **Library Path** = the `.SchLib` file name
|
|
- **Footprint Ref** = the **base** pattern inside the `.PcbLib`; Altium ships IPC density
|
|
variants (`-L` / `-M` / `-N`) alongside the base, and the base is the one used (e.g.
|
|
`CAP_CGA3_TDK`, not `CAP_CGA3_TDK-L`)
|
|
- **Footprint Path** = the `.PcbLib` file name
|
|
|
|
These Ref names come from *inside* the files and can differ from the MPN or filename. If a Ref
|
|
comes back `null` (or a footprint shows several unrelated candidates), ask the user to confirm
|
|
the name from Altium's properties and edit `design.json`. Then re-fill so the columns land:
|
|
|
|
```bash
|
|
python scripts/fill_templates.py part.json \
|
|
--template assets/template/template.xlsx --dest <stage>/<tag>/ --design design.json
|
|
```
|
|
|
|
Once you have the symbol and its Library Ref, **always** write the full parameter set into the
|
|
`.SchLib` from the verified per-part Excel — see *Mandatory symbol parameters* below. **This is
|
|
compulsory on every run; do not ask the engineer whether to do it — just do it.** It is how the
|
|
verified workbook values (and the Description) land in the Altium symbol's properties, and the
|
|
`.IntLib` is then built from this enriched symbol.
|
|
|
|
### 7. Assemble the part folder — build the `.IntLib`, then five files
|
|
|
|
The staging folder `<tag>/` should hold the per-part `<tag>.xlsx`, the datasheet (name it
|
|
`<MPN>_data.<ext>`), the enriched symbol (`.SchLib` with parameters written in, step 6), and the
|
|
footprint (`.PcbLib`). Now **compile the integrated library** from the enriched symbol + footprint
|
|
so the folder carries all **five** files — see *Build the integrated library* below:
|
|
|
|
```bash
|
|
python scripts/build_intlib.py --schlib <stage>/<tag>/<sym>.SchLib \
|
|
--pcblib <stage>/<tag>/<fp>.PcbLib --out <stage>/<tag>/<MPN>.IntLib
|
|
```
|
|
|
|
### 8. Push to the library repo, under the part's Class
|
|
|
|
```bash
|
|
python scripts/gitea_components.py push-part --folder <stage>/<tag> --typeid <typeid> \
|
|
--author "<operator name> <<operator email>>"
|
|
```
|
|
|
|
This places the folder at `components/<Class>/<tag>/` — creating the Class folder if it
|
|
doesn't exist yet, or pushing into it if it does — and commits and pushes. Confirm to the
|
|
user where it landed.
|
|
|
|
## Mandatory symbol parameters (.SchLib)
|
|
|
|
The SOP (§5) requires every schematic symbol to carry a fixed parameter set in its Altium
|
|
properties — `Manufacturer`, `Manufacturer Part`, `Value`, `Tolerance`, `Operating
|
|
Temperature`, `ROHS`, `Datasheet`, `Process`, `Vecmocon Part Code`, and the two second-source
|
|
fields — with the **Comment** set to the MPN. The skill stamps these onto the symbol from the
|
|
verified workbook. **This step is compulsory on every part build — never ask whether to fill the
|
|
`.SchLib` parameters; always do it** before assembling the folder and building the `.IntLib`.
|
|
|
|
This runs **as its own task too**, not only inside new-part creation: whenever the user hands
|
|
over one or more `.SchLib` files and wants their parameters filled/updated (e.g. "\schlib", "add
|
|
these parameters to this symbol", "update the schlib"), read the datasheet/specs for each part,
|
|
build its `params.json`, and run `scripts/schlib_write.py` per file — same steps as below.
|
|
|
|
The skill writes these **directly into the `.SchLib` in pure Python** with
|
|
`scripts/schlib_write.py` (it rebuilds the OLE around the enlarged `Data` stream, preserving
|
|
every other byte, and strips the Ultra-Librarian `Manufacturer_Name` / `Manufacturer_Part_Number`
|
|
defaults that duplicate the SOP fields). Three kinds of value:
|
|
|
|
- **Read from the datasheet** (don't just echo given text — open the PDF and fill the real,
|
|
verified values): `Value` = the **value only** in shorthand (e.g. `1u`, `12p`, `10k` — no
|
|
package), plus `Manufacturer`, `Manufacturer Part`, `Operating Temperature`, `Tolerance`,
|
|
`ROHS`, `Datasheet`, and `Process` by inference from the package.
|
|
- **Leave blank for now** — `Manufacturer 2` / `Manufacturer Part 2` (the second source). Don't
|
|
populate these by default; they stay hidden in Altium until filled later. (An optional
|
|
cross-reference search to find a second source is documented in
|
|
`references/schlib_parameters.md` but is currently off — only do it if the engineer asks.)
|
|
- **Ask the engineer** — only `Vecmocon Part Code` (internal, not derivable).
|
|
|
|
Leave any genuinely-unknown field blank — the SOP hides blank parameters, so a gap simply stays
|
|
empty until filled. The full method for the second-source search is in
|
|
`references/schlib_parameters.md`.
|
|
|
|
Write the **full parameter set** — the typeid template's engineering columns **plus** the SOP
|
|
params above (see `references/schlib_parameters.md`) — and **source it from the verified per-part
|
|
Excel** so the symbol and the workbook can never disagree. After the sheet is verified (step 5),
|
|
pass that `<tag>.xlsx` to the writer with `--from-xlsx`: every engineering column **and the
|
|
Description** are read straight out of it and written into the `.SchLib`. Then layer on the
|
|
**SOP-only** fields that aren't template columns (the `Value` shorthand, `Manufacturer Part`,
|
|
`Operating Temperature`, `ROHS`, `Datasheet`, `Process`, `Vecmocon Part Code`, and the blank
|
|
second-source pair) via a small `params.json`; on any name collision the `params.json` value
|
|
wins. Pass `--typeid` too, so any template column the datasheet left silent is still present
|
|
(blank):
|
|
|
|
```bash
|
|
python scripts/schlib_write.py --schlib <in>.SchLib \
|
|
--from-xlsx <stage>/<tag>/<tag>.xlsx --params params.json \
|
|
--out <stage>/<tag>/<sym>.SchLib --typeid <TYPEID>
|
|
```
|
|
|
|
(`--params` is optional if the Excel already carries everything you need; `--from-xlsx` is
|
|
optional if you'd rather hand-build the whole set in `params.json` — give at least one.) The
|
|
`Description` written onto the symbol (and into the component's ComponentDescription field) is
|
|
the exact one from the Excel, which was built to `references/description_format.md`.
|
|
|
|
Deliver the resulting `.SchLib`; the engineer opens it in Altium once to confirm it loads, then
|
|
**Saves to Server** with a revision note. The full parameter set, each value's source, the
|
|
`params.json` shape (incl. the `remove` list), and the mini-stream size caveat are in
|
|
`references/schlib_parameters.md` — read it before building the parameter set. If a file doesn't
|
|
round-trip, fall back to `scripts/altium_params.py` (emits an Altium DXP script to apply the same
|
|
parameters from inside Altium). Always have the engineer confirm the file opens in Altium — the
|
|
skill writes Altium's own binary format, so Altium is the final validator.
|
|
|
|
## Build the integrated library (.IntLib)
|
|
|
|
Every part also gets a compiled **integrated library** — one file that bundles the schematic
|
|
symbol (with the parameters written in) and its footprint, so the component resolves in Altium
|
|
with no separate `.PcbLib` to locate. `scripts/build_intlib.py` compiles it in pure Python, no
|
|
Altium needed, and it's the **fifth file** in every part folder.
|
|
|
|
```bash
|
|
python scripts/build_intlib.py --schlib <stage>/<tag>/<sym>.SchLib \
|
|
--pcblib <stage>/<tag>/<fp>.PcbLib --out <stage>/<tag>/<MPN>.IntLib
|
|
```
|
|
|
|
How it works and what it needs:
|
|
|
|
- Feed it the **enriched `.SchLib`** (after `schlib_write.py` has written the parameters in) so the
|
|
integrated symbol carries the full parameter set and the correct ComponentDescription.
|
|
- The symbol **must contain a footprint model link** (an Altium RECORD=45 `ModelName` /
|
|
`ModelType=PCBLIB` in its `Data` stream — Ultra-Librarian and Altium exports include this). The
|
|
builder reads that link to know which footprint to bind, and errors clearly if it's absent — in
|
|
that case the symbol has no footprint assigned, so fix the symbol (or re-export it) first.
|
|
- It builds the `.IntLib` as an OLE compound file with five streams — the embedded `.schlib` and
|
|
`.pcblib` (zlib-compressed at Altium's default level), plus `LibCrossRef.Txt`, `Parameters .bin`,
|
|
and `Version.Txt` — reusing a bundled container skeleton
|
|
(`assets/templates/intlib_container.IntLib`) for the exact directory layout Altium expects, and a
|
|
**FAT-first** compound-file writer (`build_intlib.write_cfb`). Both the outer container and the
|
|
embedded symbol are written FAT-first — this matters: a FAT-last layout re-opens fine in olefile
|
|
and even standalone in Altium, but Altium's **IntLib extractor** throws "Stream read error" on it.
|
|
The two embedded libraries are compressed at zlib's **default level** (`0x789c`); Altium's
|
|
decompressor rejects other levels (e.g. level-9 `0x78da`).
|
|
- The builder self-validates: it re-opens the output, decompresses both embedded libraries, and
|
|
confirms they round-trip and that the cross-reference names the symbol + footprint. Even so,
|
|
Altium is the final validator — have the engineer open the `.IntLib` once (or, as a guaranteed
|
|
fallback, compile a `.LibPkg` in Altium from the same `.SchLib` + `.PcbLib`).
|
|
|
|
## Per-typeid versioning
|
|
|
|
Versioning is **per typeid**, not global. Each typeid carries its own `template_version` and
|
|
`skill_version` in `assets/template/versions.json` (both start at 1). When a parameter is
|
|
added to a typeid, that typeid gets a new template, so its `template_version` bumps — and on
|
|
the back of that its `skill_version` bumps too (v1→v2). **Only that typeid moves**; every
|
|
other typeid keeps its versions. Those two numbers are exactly what `fill_templates` stamps
|
|
into that typeid's rows (cols B and C), so a row always records the template/skill version it
|
|
was built against. `append_parameter.py` does the bump; `common.py` is the single source for
|
|
reading and writing these numbers.
|
|
|
|
## The changelog
|
|
|
|
`append_parameter.py` maintains one **global** changelog as an Excel workbook at
|
|
`assets/CHANGELOG.xlsx` (sheet `Changelog`, styled green header). Every time a
|
|
typeid's template/version changes, one row is appended with columns
|
|
**Date | Typeid | Skill Version | Template Version | Description** — the version columns hold
|
|
the new versions, and Description is your note (or the parameter(s) added if you gave none).
|
|
|
|
The changelog lives in the **skill repo in Gitea** as well, and it is **cumulative from the
|
|
first change onward**. Two things keep it that way: at the start of a run `pull-skill` (step 0)
|
|
brings the current changelog down so a new change appends to the full history, and on push
|
|
`push-skill` **merges** the new local rows onto the changelog already in Gitea — appended,
|
|
never overwritten. So the Gitea copy is the growing, authoritative history across machines and
|
|
sessions; the merged file is copied back locally so the two stay in sync. If you ever see the
|
|
Gitea changelog with only the latest change, it means step 0 (`pull-skill`) was skipped.
|
|
|
|
## Backfilling existing parts
|
|
|
|
When the user wants a newly-added parameter applied to parts of that typeid already in Gitea:
|
|
|
|
```bash
|
|
python scripts/gitea_components.py checkout --dest work/
|
|
python scripts/gitea_components.py list-type --typeid <typeid> --root work/ --json
|
|
```
|
|
|
|
`list-type` lists every existing part of that typeid with the files in its folder — including
|
|
its datasheet, which is co-located. For each one: read that datasheet, re-extract the values
|
|
(including the new parameter), and rebuild its per-part sheet in place:
|
|
|
|
```bash
|
|
python scripts/fill_templates.py <that_part>.json \
|
|
--template assets/template/template.xlsx --dest work/<Class>/<that_tag>/
|
|
```
|
|
|
|
Because `fill_templates` uses the current template and current versions, each rebuilt sheet
|
|
picks up the new column and the bumped version automatically. When all are done, push once
|
|
and tell the user the previous sheets were updated:
|
|
|
|
```bash
|
|
python scripts/gitea_components.py commit-push --root work/ --message "backfill <param> into <typeid>"
|
|
```
|
|
|
|
## Updating an existing part
|
|
|
|
When the duplicate check finds the part (or the user asks to revise one that's already in
|
|
Gitea), you're **editing a part in place** rather than assembling a new one. The mechanics are
|
|
the same edit-in-a-checkout pattern as backfill — the difference is you're changing one part's
|
|
own data, not applying a template change across a whole typeid. The guiding rule doesn't
|
|
change: nothing is overwritten until the engineer has verified the new version.
|
|
|
|
1. **Locate the part in a checkout you can commit.** Clone the library repo, then find the
|
|
part's folder and typeid (recovered from its tag):
|
|
|
|
```bash
|
|
python scripts/gitea_components.py checkout --dest work/
|
|
python scripts/gitea_components.py find-part --mpn <MPN> --make <make> --root work/ --json
|
|
```
|
|
|
|
`find-part` returns the part's Class, tag, typeid, the editable folder path under `work/`,
|
|
and the files in it (its current `<tag>.xlsx`, datasheet, symbol, footprint). If it prints
|
|
`NOT FOUND` (exit 4), the part isn't actually there — treat it as a **new** part and go back
|
|
to the normal add flow from step 2.
|
|
|
|
2. **Decide with the user what's changing.** A part update can revise any of: the **parameter
|
|
values** (re-read the datasheet, or a corrected/newer one), the **symbol/footprint**, or the
|
|
**datasheet PDF** itself. Ask which, so you only touch what's meant to change and leave the
|
|
rest of the folder intact.
|
|
|
|
3. **Apply the change in place, in `work/<Class>/<tag>/`.** A key thing to understand first:
|
|
`fill_templates` **rewrites the whole data row**, so it fills the four design columns
|
|
(`Library Ref/Path`, `Footprint Ref/Path`) from the `--design` map you give it — and leaves
|
|
them **blank if you don't**. When you rebuild a sheet, always re-supply the design values, or
|
|
you'll silently wipe the symbol/footprint refs that were already there. The symbol and
|
|
footprint files live in the part folder, so re-deriving them is cheap: point `altium_refs`
|
|
at whatever the folder will hold **after** your change.
|
|
|
|
- **Values** (re-read the datasheet, or a corrected/newer one) → read the current
|
|
`<tag>.xlsx` and the folder's datasheet so you start from what's there, re-extract into a
|
|
fresh `part.json` (Description still built to `references/description_format.md`),
|
|
re-derive the design columns from the folder's existing symbol/footprint, and rebuild the
|
|
sheet in place **with `--design`**:
|
|
|
|
```bash
|
|
python scripts/altium_refs.py design \
|
|
--symbol work/<Class>/<tag>/<symbol>.SchLib \
|
|
--footprint work/<Class>/<tag>/<footprint>.PcbLib > design.json
|
|
python scripts/fill_templates.py part.json \
|
|
--template assets/template/template.xlsx --dest work/<Class>/<tag>/ --design design.json
|
|
```
|
|
|
|
Because `fill_templates` reads the **current** template and versions, the rebuilt sheet
|
|
keeps this typeid's version stamp (a part-data fix isn't a template change, so nothing
|
|
bumps) and picks up any columns the typeid has gained — while the `--design` map carries
|
|
the existing symbol/footprint refs through unchanged. (If a part somehow has no
|
|
symbol/footprint yet, there's nothing to preserve — rebuild without `--design`.)
|
|
- **Symbol/footprint** → copy the new `.SchLib`/`.PcbLib` into the folder under their proper
|
|
names (replacing the old ones), then re-derive against the **new** files and rebuild with
|
|
`--design` exactly as above (this is step 6 of the add flow). Still flag a genuine
|
|
symbol/footprint-vs-part mismatch — that's a correctness issue.
|
|
- **Datasheet** → drop the newer PDF in as `<MPN>_data.<ext>`, replacing the old one. If the
|
|
values should reflect it, also redo the values step above.
|
|
|
|
4. **Verify, then push.** Hand the rebuilt `<tag>.xlsx` back and run the same human
|
|
verification loop (step 5) — the engineer is still the ground truth. Once confirmed, commit
|
|
the checkout and push (attributed to the operator, with a message that says it's an update):
|
|
|
|
```bash
|
|
python scripts/gitea_components.py commit-push --root work/ \
|
|
--message "update <tag>: <what changed> (by <operator name>)" \
|
|
--author "<operator name> <<operator email>>"
|
|
```
|
|
|
|
The authored commit and message are the record of the revision (visible in `git log`, the
|
|
Gitea commit view and `git blame`); the changelog stays reserved for template/version
|
|
changes, not per-part data fixes. Tell the user what changed and where it landed.
|
|
|
|
## Pushing the skill repo
|
|
|
|
When skill files change (a new typeid template, a parameter add, a version/changelog bump),
|
|
push the skill's own files to the skill repo with `push-skill` **automatically** (no
|
|
confirmation):
|
|
|
|
```bash
|
|
python scripts/gitea_components.py push-skill --author "<operator name> <<operator email>>" \
|
|
--message "Sync skill files + changelog"
|
|
```
|
|
|
|
`push-skill` clones the skill repo, copies the skill files in with the **`GIT_TOKEN` blanked
|
|
out** (the real token never leaves the machine), and **merges** `CHANGELOG.xlsx` — appending
|
|
this run's new rows onto the changelog already in Gitea so earlier entries are preserved — then
|
|
writes the merged changelog back locally. (The older `push_to_gitea.sh` still exists for a
|
|
plain flat push, but it does not merge the changelog or blank the token, so prefer
|
|
`push-skill` for the skill repo.)
|
|
|
|
## Resources
|
|
|
|
- `assets/template/template.xlsx` — the master template: one sheet per **typeid** (125),
|
|
source of every sheet's headers, styling and order. Columns A/B/C are always
|
|
`MPN_make_type` / `Skill Version` / `Template Version`; `Library Ref/Path`,
|
|
`Footprint Ref/Path` and `Manufacturer` sit near the end.
|
|
- `assets/template/Type_ID.xlsx` + `references/taxonomy.md` — Class → Subclass → Type ID.
|
|
- `references/description_format.md` — Vecmocon's Altium **Description Format** (the `_`-joined
|
|
engineering string for each part's Description column). Defines a format for **every** type:
|
|
the four SOP-defined ones (RES/CAP/Zener/TVS) are strict, the rest are the house extension on
|
|
the same basis. Read it before filling any Description.
|
|
- `references/schlib_parameters.md` — the SOP **mandatory symbol parameters** (§5) for the
|
|
`.SchLib`: the parameter set, where each value comes from, and how the generated Altium script
|
|
stamps them onto the symbol.
|
|
- `assets/template/versions.json` — per-typeid `template_version` + `skill_version`.
|
|
- `assets/CHANGELOG.xlsx` — global version/parameter changelog (created on first add;
|
|
merged into the skill repo's copy in Gitea by `push-skill`).
|
|
- `scripts/common.py` — taxonomy loader (`load_taxonomy`, `class_folder`), version store
|
|
(`get_versions`, `version_labels`, `bump_versions`), and the tag helper (`part_tag`).
|
|
- `scripts/fill_templates.py` — build one per-part `<tag>.xlsx` (version-stamped); reused for
|
|
backfill.
|
|
- `scripts/append_parameter.py` — append parameter(s) to a typeid, bump its versions, write
|
|
the changelog.
|
|
- `scripts/altium_refs.py` — read Library/Footprint Ref from `.SchLib`/`.PcbLib`.
|
|
- `scripts/schlib_write.py` — write the SOP mandatory parameters **directly into a `.SchLib`**
|
|
(pure-Python OLE rebuild; removes the Ultra-Librarian `Manufacturer_Name` /
|
|
`Manufacturer_Part_Number` defaults). Primary path; see `references/schlib_parameters.md`.
|
|
- `scripts/altium_params.py` — fallback: generate an Altium DelphiScript that stamps the same
|
|
parameters onto a `.SchLib` from inside Altium (DXP → Run Script).
|
|
- `scripts/build_intlib.py` — compile a component's `.SchLib` + `.PcbLib` into an Altium
|
|
**integrated library** (`.IntLib`) in pure Python (FAT-first OLE writer + Altium-level zlib);
|
|
the fifth file in every part folder. Needs the enriched `.SchLib` (parameters written) with a
|
|
footprint model link. Uses `assets/templates/intlib_container.IntLib` as the container skeleton.
|
|
- `assets/templates/intlib_container.IntLib` — a known-good single-component `.IntLib` reused
|
|
purely as the OLE container skeleton by `build_intlib.py` (all its streams are overwritten).
|
|
- `scripts/gitea_components.py` — `check-mpn`, `find-part` (locate an existing part to
|
|
update), `checkout`, `list-type`, `place-part`, `commit-push`, `push-part` (library repo),
|
|
and `push-skill` (skill repo: token-blanked push + append-only changelog merge).
|
|
- `scripts/push_to_gitea.sh` — push a folder's contents to a Gitea repo (used for the skill
|
|
repo).
|
|
- `config/gitea.env` — host, user, token, and the `SKILL_REPO` / `LIBRARY_REPO` names
|
|
(**secret** — do not push the token).
|