Implement the penpot_list_library tool with name-to-id resolution and unit tests #192

Closed
opened 2026-09-14 23:09:54 +00:00 by david · 1 comment
Owner

Summary

Implement the penpot_list_library tool: return a file's design-system inventory — colours, typographies and components — as resolved name → id lookups, including duplicate-name detection. This is the lookup table the composition primitives use to turn colour: "flip7-gold" or typography: "h2" into the asset ids that make a shape's fill or text linked to the library rather than hard-coded.

Background

Depends on: #191

penpot_get_file already returns the raw asset records. This tool is the semantic projection of them: the agent works with names, the extension resolves ids. The testable core is the resolution logic, not the HTTP call.

Confirmed on Penpot 2.17: assets live at data.colors, data.typographies and data.components on the file; each record has id and name. Components additionally carry path and mainInstanceId/mainInstancePage, which the instancing primitive (a later milestone) needs.

Two real-world wrinkles this tool must handle rather than hide:

  1. Duplicate names. A library can contain two colours named flip7-gold. Resolution must refuse to guess: report the ambiguity with both ids and let the caller disambiguate.
  2. Linked libraries. A file that consumes an imported library does not own that library's components — they live in the library file. get-file-libraries/link-file-to-library govern that relationship and are built and validated in the library import/export milestone. Here, treat linked-library discovery as best-effort and optional: if a listing can be obtained, show it in a clearly separated section, but never let it fail the tool or appear in the acceptance criteria.

Documentation Required

A separate process downloads these into the listed folders before this issue is implemented. Check the folders for the actual reference material before starting.

docs/reference/penpot-api/

docs/reference/pi-coding-agent/ and docs/reference/typebox/

Implementation Details

Create extensions/penpot/src/tools/library.ts plus a pure resolution module — extensions/penpot/src/resolve.ts — and register the tool in src/index.ts.

Pure resolution (src/resolve.ts) — the unit-tested core

  • buildIndex(assets: {id, name}[])Map<name, Asset[]>.
  • resolveAsset(assets, name){ ok: true, asset } | { ok: false, reason: "missing" | "ambiguous", candidates?: Asset[] }.
    • Missing → the error lists the available names (bounded, e.g. first N) so a typo is immediately obvious.
    • Ambiguous → the error lists every candidate id for that name, never an arbitrary pick.
  • Name matching is exact and case-sensitive by default, with an optional { caseInsensitive: true } used by the tool's fuzzy parameter when the caller asks for it. Keep both behaviours tested.

Tool (penpot_list_library)

Parameters:

  • file_id (string, required).
  • library_file_ids (array of strings, optional) — extra library files whose assets should be merged in, labelled by source file id (explicit rather than implicit, so behaviour never depends on unpublished linking state).
  • include_components (boolean, optional, default true) — whether to list components.

Behaviour:

  1. Read the file's library (get-file, asset fields only — do not fetch page objects).
  2. Build the name → id index per asset kind (colours, typographies, components).
  3. Text output: a compact table per kind, name → id, alphabetical, with duplicates marked and a banner warning listing every ambiguous name.
  4. details carries the structured index: { fileId, colors: {...}, typographies: {...}, components: {...}, ambiguousNames: [...] }.
  5. library_file_ids: fetch each named file's assets and merge under a per-source heading; a failure for one library file is reported, not fatal.
  6. Best-effort only: attempt linked-library discovery; if it fails or returns nothing, omit the section silently.

Acceptance Criteria

  • penpot_list_library is registered and callable.
  • It lists colours, typographies and components with name → id, alphabetically.
  • resolveAsset returns { ok: false, reason: "missing" } with a bounded list of available names for an unknown name.
  • resolveAsset returns { ok: false, reason: "ambiguous" } with every candidate id when a name occurs more than once; it never picks one silently.
  • Duplicate names are visibly flagged in the tool output and in details.ambiguousNames.
  • library_file_ids merges the named files' assets, labelled by source; a failure for one file does not fail the tool.
  • include_components: false omits the components section.
  • Page objects are never fetched or returned.
  • Unit tests for resolveAsset/buildIndex are green with no network access.

Test Plan

node --test extensions/penpot/src/resolve.test.ts extensions/penpot/src/tools/library.test.ts

Live check (requires PENPOT_URL/PENPOT_TOKEN):

  • Call penpot_list_library on the scratch file and confirm the names/ids match the Penpot UI's Assets panel.
  • Add a second colour with a duplicate name in the UI, re-run, and confirm the ambiguity is reported with both ids.
## Summary Implement the `penpot_list_library` tool: return a file's design-system inventory — colours, typographies and components — as resolved **name → id** lookups, including duplicate-name detection. This is the lookup table the composition primitives use to turn `colour: "flip7-gold"` or `typography: "h2"` into the asset ids that make a shape's fill or text *linked* to the library rather than hard-coded. ## Background **Depends on:** #191 `penpot_get_file` already returns the raw asset records. This tool is the semantic projection of them: the agent works with names, the extension resolves ids. The testable core is the resolution logic, not the HTTP call. Confirmed on Penpot 2.17: assets live at `data.colors`, `data.typographies` and `data.components` on the file; each record has `id` and `name`. Components additionally carry `path` and `mainInstanceId`/`mainInstancePage`, which the instancing primitive (a later milestone) needs. Two real-world wrinkles this tool must handle rather than hide: 1. **Duplicate names.** A library can contain two colours named `flip7-gold`. Resolution must refuse to guess: report the ambiguity with both ids and let the caller disambiguate. 2. **Linked libraries.** A file that consumes an imported library does not own that library's components — they live in the library file. `get-file-libraries`/`link-file-to-library` govern that relationship and are built and validated in the library import/export milestone. Here, treat linked-library discovery as **best-effort and optional**: if a listing can be obtained, show it in a clearly separated section, but never let it fail the tool or appear in the acceptance criteria. ## Documentation Required A separate process downloads these into the listed folders before this issue is implemented. Check the folders for the actual reference material before starting. **`docs/reference/penpot-api/`** - `<PENPOT_URL>/api/main/doc/openapi.json` — confirm `get-file` (assets) and `get-file-libraries`/`has-file-libraries` (linked libraries) parameters on this instance. - https://raw.githubusercontent.com/penpot/penpot/2.17.2/common/src/app/common/types/file.cljc — `colors`, `typographies`, `components` collections on the file. - https://raw.githubusercontent.com/penpot/penpot/2.17.2/common/src/app/common/types/color.cljc, .../typography.cljc, .../component.cljc, .../components_list.cljc — the asset record fields available for name → id resolution. - https://help.penpot.app/user-guide/design-systems/libraries/ — how Penpot presents a file's library and connected libraries to a designer (for accurate wording in the output). **`docs/reference/pi-coding-agent/`** and **`docs/reference/typebox/`** - https://pi.dev/docs/latest/extensions — tool registration and result shape. - https://github.com/sinclairzx81/typebox — parameter schema helpers for an optional array of library file ids. ## Implementation Details Create `extensions/penpot/src/tools/library.ts` plus a pure resolution module — `extensions/penpot/src/resolve.ts` — and register the tool in `src/index.ts`. ### Pure resolution (`src/resolve.ts`) — the unit-tested core - `buildIndex(assets: {id, name}[])` → `Map<name, Asset[]>`. - `resolveAsset(assets, name)` → `{ ok: true, asset } | { ok: false, reason: "missing" | "ambiguous", candidates?: Asset[] }`. - Missing → the error lists the available names (bounded, e.g. first N) so a typo is immediately obvious. - Ambiguous → the error lists every candidate id for that name, never an arbitrary pick. - Name matching is exact and case-sensitive by default, with an optional `{ caseInsensitive: true }` used by the tool's `fuzzy` parameter when the caller asks for it. Keep both behaviours tested. ### Tool (`penpot_list_library`) Parameters: - `file_id` (string, **required**). - `library_file_ids` (array of strings, optional) — extra library files whose assets should be merged in, labelled by source file id (explicit rather than implicit, so behaviour never depends on unpublished linking state). - `include_components` (boolean, optional, default `true`) — whether to list components. Behaviour: 1. Read the file's library (`get-file`, asset fields only — do not fetch page objects). 2. Build the name → id index per asset kind (colours, typographies, components). 3. Text output: a compact table per kind, `name → id`, alphabetical, with duplicates marked and a banner warning listing every ambiguous name. 4. `details` carries the structured index: `{ fileId, colors: {...}, typographies: {...}, components: {...}, ambiguousNames: [...] }`. 5. `library_file_ids`: fetch each named file's assets and merge under a per-source heading; a failure for one library file is reported, not fatal. 6. Best-effort only: attempt linked-library discovery; if it fails or returns nothing, omit the section silently. ## Acceptance Criteria - [ ] `penpot_list_library` is registered and callable. - [ ] It lists colours, typographies and components with `name → id`, alphabetically. - [ ] `resolveAsset` returns `{ ok: false, reason: "missing" }` with a bounded list of available names for an unknown name. - [ ] `resolveAsset` returns `{ ok: false, reason: "ambiguous" }` with every candidate id when a name occurs more than once; it never picks one silently. - [ ] Duplicate names are visibly flagged in the tool output and in `details.ambiguousNames`. - [ ] `library_file_ids` merges the named files' assets, labelled by source; a failure for one file does not fail the tool. - [ ] `include_components: false` omits the components section. - [ ] Page objects are never fetched or returned. - [ ] Unit tests for `resolveAsset`/`buildIndex` are green with no network access. ## Test Plan ```bash node --test extensions/penpot/src/resolve.test.ts extensions/penpot/src/tools/library.test.ts ``` Live check (requires `PENPOT_URL`/`PENPOT_TOKEN`): - Call `penpot_list_library` on the scratch file and confirm the names/ids match the Penpot UI's Assets panel. - Add a second colour with a duplicate name in the UI, re-run, and confirm the ambiguity is reported with both ids.
david closed this issue 2026-09-15 00:12:35 +00:00
Author
Owner

pi-loop opened and merged a pull request for this issue: #225

pi-loop opened and merged a pull request for this issue: https://git.excelera.net/david/pi-extensions-and-skills/pulls/225
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
david/pi-extensions-and-skills#192
No description provided.