Implement the penpot_get_file tool (pages, objects and library assets) with unit tests #191

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

Summary

Implement the penpot_get_file tool: read a design file's revision, pages, page objects and library assets (colours, typographies, components) into a structured shape the agent can reason about. This is the read side that every write path checks against, and the source of the revn the commit step tracks.

Background

Depends on: #190

Confirms the file shape on Penpot 2.17, validated against a live instance:

  • get-file returns library assets at data.colors, data.typographies and data.components.
  • Page content comes via data.pages (an ordered list of page ids) plus data.pagesIndex (id → { id, name, objects }), where objects is the shape id → shape map.
  • The response carries the file's revn (revision) and vern (version). The commit step needs revn; stale writes are accepted by the server, so the extension must track it itself.
  • get-file-fragment exists for cheaper targeted reads (a subset of pages/objects) — use it, or expose it, when a full file read would be wasteful for large files.

A freshly created file has one page containing a Root Frame at id 00000000-0000-0000-0000-000000000000.

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/file.ts (and, if it grows, a src/fileShape.ts for the types) and register the tool in src/index.ts.

Parameters:

  • file_id (string, required) — the file to read.
  • page_id (string, optional) — restrict the returned page content to one page.
  • include_objects (boolean, optional, default false) — whether to include page object trees. Default off, because a real design file's object map is large and rarely needed in full by an agent that already knows what it wrote.
  • summary_only (boolean, optional, default true) — return counts and names rather than full asset records.

Behaviour:

  1. Call get-file (or get-file-fragment when page_id/include_objects allow a cheaper read) and normalise into a stable internal shape: { id, name, revn, vern, pages: [{ id, name, objectCount }], colors: [{ id, name, color, opacity }], typographies: [{ id, name, ... }], components: [{ id, name, path, mainInstanceId, mainInstancePage }] }.
  2. Always include revn and vern in details — later steps depend on this being available and accurate.
  3. Text output: file name/id, revision, page count and names, and asset counts with names (not raw hex/JSON dumps). When summary_only is false, include the asset records.
  4. Truncate/summarise defensively: never emit the entire objects map into the text channel, even when include_objects is true — emit a per-page object count and the id→name tree up to a bounded depth, and put the full structure in details only if it stays within a reasonable size.
  5. Handle a non-existent file_id with the categorised not-found message, not a crash.

Acceptance Criteria

  • penpot_get_file is registered and callable.
  • It returns the file's revn and vern in details.
  • Page names and ids are returned in page order, each with an object count.
  • Colours, typographies and components are listed with their names and ids.
  • The full objects map is never dumped into the text output, regardless of include_objects.
  • page_id restricts page content to one page; an unknown page_id returns a clear error.
  • A non-existent file id returns a readable not-found error.
  • Unit tests with a captured/recorded real get-file payload (checked in as a fixture) parse correctly, with no network access.

Test Plan

node --test extensions/penpot/src/tools/file.test.ts

Live check (requires PENPOT_URL/PENPOT_TOKEN):

  • Call penpot_get_file on the scratch project's file and compare page names and asset names against the Penpot UI.
  • Confirm the reported revn matches a subsequent get-file call after a UI edit to the file.
  • Call with a bogus file_id and confirm the error is readable.
## Summary Implement the `penpot_get_file` tool: read a design file's revision, pages, page objects and library assets (colours, typographies, components) into a structured shape the agent can reason about. This is the read side that every write path checks against, and the source of the `revn` the commit step tracks. ## Background **Depends on:** #190 Confirms the file shape on Penpot 2.17, validated against a live instance: - `get-file` returns library assets at `data.colors`, `data.typographies` and `data.components`. - Page content comes via `data.pages` (an ordered list of page ids) plus `data.pagesIndex` (id → `{ id, name, objects }`), where `objects` is the shape id → shape map. - The response carries the file's `revn` (revision) and `vern` (version). The commit step needs `revn`; stale writes are accepted by the server, so the extension must track it itself. - `get-file-fragment` exists for cheaper targeted reads (a subset of pages/objects) — use it, or expose it, when a full file read would be wasteful for large files. A freshly created file has one page containing a `Root Frame` at id `00000000-0000-0000-0000-000000000000`. ## 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` — authoritative parameters and response shapes for `get-file` and `get-file-fragment` on this instance. - https://raw.githubusercontent.com/penpot/penpot/2.17.2/common/src/app/common/types/file.cljc — the `File` / `FileData` / `PagesList` schema: what `colors`, `typographies`, `components`, `pages` and `pagesIndex` actually contain. Pin to the Penpot tag matching the instance. - https://raw.githubusercontent.com/penpot/penpot/2.17.2/common/src/app/common/types/page.cljc — the page/`objects` shape referenced by `pagesIndex`. - https://raw.githubusercontent.com/penpot/penpot/2.17.2/common/src/app/common/types/color.cljc and .../typography.cljc and .../component.cljc — the asset record shapes. - https://help.penpot.app/user-guide/design-systems/libraries/ — product-level meaning of a file's library (colours, typographies, components). **`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 — `Type.Object`/`Type.Optional` for the parameter schema; `Type.Array` for repeated ids. ## Implementation Details Create `extensions/penpot/src/tools/file.ts` (and, if it grows, a `src/fileShape.ts` for the types) and register the tool in `src/index.ts`. Parameters: - `file_id` (string, **required**) — the file to read. - `page_id` (string, optional) — restrict the returned page content to one page. - `include_objects` (boolean, optional, default `false`) — whether to include page object trees. Default off, because a real design file's object map is large and rarely needed in full by an agent that already knows what it wrote. - `summary_only` (boolean, optional, default `true`) — return counts and names rather than full asset records. Behaviour: 1. Call `get-file` (or `get-file-fragment` when `page_id`/`include_objects` allow a cheaper read) and normalise into a stable internal shape: `{ id, name, revn, vern, pages: [{ id, name, objectCount }], colors: [{ id, name, color, opacity }], typographies: [{ id, name, ... }], components: [{ id, name, path, mainInstanceId, mainInstancePage }] }`. 2. Always include `revn` and `vern` in `details` — later steps depend on this being available and accurate. 3. Text output: file name/id, revision, page count and names, and asset counts with names (not raw hex/JSON dumps). When `summary_only` is false, include the asset records. 4. Truncate/summarise defensively: never emit the entire `objects` map into the text channel, even when `include_objects` is true — emit a per-page object count and the id→name tree up to a bounded depth, and put the full structure in `details` only if it stays within a reasonable size. 5. Handle a non-existent `file_id` with the categorised `not-found` message, not a crash. ## Acceptance Criteria - [ ] `penpot_get_file` is registered and callable. - [ ] It returns the file's `revn` and `vern` in `details`. - [ ] Page names and ids are returned in page order, each with an object count. - [ ] Colours, typographies and components are listed with their names and ids. - [ ] The full `objects` map is never dumped into the text output, regardless of `include_objects`. - [ ] `page_id` restricts page content to one page; an unknown `page_id` returns a clear error. - [ ] A non-existent file id returns a readable `not-found` error. - [ ] Unit tests with a captured/recorded real `get-file` payload (checked in as a fixture) parse correctly, with no network access. ## Test Plan ```bash node --test extensions/penpot/src/tools/file.test.ts ``` Live check (requires `PENPOT_URL`/`PENPOT_TOKEN`): - Call `penpot_get_file` on the scratch project's file and compare page names and asset names against the Penpot UI. - Confirm the reported `revn` matches a subsequent `get-file` call after a UI edit to the file. - Call with a bogus `file_id` and confirm the error is readable.
david closed this issue 2026-09-15 00:04:48 +00:00
Author
Owner

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

pi-loop opened and merged a pull request for this issue: https://git.excelera.net/david/pi-extensions-and-skills/pulls/224
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#191
No description provided.