@penpair/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -212
- package/dist/cli-main.d.ts +7 -0
- package/dist/cli-main.js +466 -0
- package/dist/cli.js +2 -455
- package/dist/docker-profile.d.ts +114 -0
- package/dist/docker-profile.js +18 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +8 -1
- package/dist/instance/init.d.ts +9 -6
- package/dist/instance/init.js +18 -58
- package/dist/instance/setup.d.ts +3 -0
- package/dist/instance/setup.js +8 -9
- package/dist/local-profile.d.ts +95 -0
- package/dist/local-profile.js +8 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/package.json +2 -7
- package/skills/penpair-setup/SKILL.md +11 -11
- package/skills/penpair-setup/references/web-or-docker.md +2 -2
- package/docs/compatibility.md +0 -29
- package/docs/getting-started.md +0 -97
- package/docs/guide.md +0 -93
- package/docs/releasing.md +0 -45
package/README.md
CHANGED
|
@@ -1,219 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
<img src="https://raw.githubusercontent.com/niklas-westman/penpair/main/docs/assets/logo.png" alt="penpair" width="120" height="120">
|
|
3
|
-
</p>
|
|
1
|
+
# @penpair/core
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
**The library and engine behind [penpair](https://github.com/niklas-westman/penpair): connect AI agents and people to a Penpot design file, safely.**
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
This package has no command of its own. For the command, install one of:
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
so people and agents work on the same design, safely and repeatably.
|
|
12
|
-
</p>
|
|
7
|
+
- [`@penpair/web`](https://www.npmjs.com/package/@penpair/web): Penpot on the web (design.penpot.app or your team's own). The default.
|
|
8
|
+
- [`@penpair/docker`](https://www.npmjs.com/package/@penpair/docker): the same, plus Penpot in Docker on your computer.
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
<a href="https://www.npmjs.com/package/@penpair/core"><img alt="npm" src="https://img.shields.io/npm/v/@penpair/core?color=0A5CFF&label=%40penpair%2Fcore"></a>
|
|
16
|
-
<a href="https://github.com/niklas-westman/penpair/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/niklas-westman/penpair/actions/workflows/ci.yml/badge.svg"></a>
|
|
17
|
-
<a href="https://github.com/niklas-westman/penpair/blob/main/LICENSE"><img alt="MIT licence" src="https://img.shields.io/badge/licence-MIT-0A5CFF"></a>
|
|
18
|
-
<img alt="Node 22 or later" src="https://img.shields.io/badge/node-%E2%89%A522-0A5CFF">
|
|
19
|
-
</p>
|
|
10
|
+
Both include everything in this package. Install this one directly only to use the library from your own code: the connection, the safe write runner, the snippet registry, `defineSnippet`, the skills and `runCli`.
|
|
20
11
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<a href="#quick-start">Quick start</a> ·
|
|
24
|
-
<a href="#web-or-docker">Web or Docker</a> ·
|
|
25
|
-
<a href="#add-penpair-to-your-repo">Add to your repo</a> ·
|
|
26
|
-
<a href="#packages">Packages</a> ·
|
|
27
|
-
<a href="#use-it-with-marwes">Marwes</a> ·
|
|
28
|
-
<a href="#check-each-page-with-reflection">Reflection</a> ·
|
|
29
|
-
<a href="#for-an-ai-agent">For an AI agent</a>
|
|
30
|
-
</p>
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
penpair makes a Penpot design file the **source of truth** for your project's UI, and makes your AI assistant dependable at working in it. Prompt for a whole page; the assistant reads the real design, builds with tested operations that look first, write once and read back, and follows written-down rules for how to work in Penpot. You review the result in Penpot as usual, and [Reflection](#check-each-page-with-reflection) can check the page it later builds in code against the design.
|
|
35
|
-
|
|
36
|
-
| | |
|
|
37
|
-
| --- | --- |
|
|
38
|
-
| **Connect it** | Point it at Penpot on the web, or your own, with one guided command and a check that it works. |
|
|
39
|
-
| **Drive it** | Inspect, build, audit, export, archive and manage design tokens through Penpot's MCP and plugin API. |
|
|
40
|
-
| **Know it** | The rules and the quirks of the tool ship with the code as skills your agent follows. |
|
|
41
|
-
| **Extend it** | Plug in your component library, your checks and your own snippets. The core stays neutral. |
|
|
42
|
-
|
|
43
|
-
## Why penpair
|
|
44
|
-
|
|
45
|
-
An AI assistant can already reach Penpot through its MCP server: it gets one tool that runs code inside your open file. That is power without hands-on knowledge, and designing a whole page that way fails in small, expensive ways. penpair is what makes it dependable, so you can prompt for a page and get a page.
|
|
46
|
-
|
|
47
|
-
**What goes wrong with a bare connection**, all met while building real pages:
|
|
48
|
-
|
|
49
|
-
- The first export after a change is often a stale render, so the assistant "sees" a defect that is not there, or misses one that is.
|
|
50
|
-
- A token binding appears seconds after the call, so a correct write reads back as "not bound", and applying the same token twice removes it.
|
|
51
|
-
- A request over about 110 KB is refused, a switched page takes effect only after the script ends, and a sleeping browser tab raises an error even though the write already landed.
|
|
52
|
-
- Every new session rediscovers these, and a plain write cannot tell "done" from "half done".
|
|
53
|
-
|
|
54
|
-
**What penpair adds:**
|
|
55
|
-
|
|
56
|
-
| | What you get | Why it matters for a full page |
|
|
57
|
-
| --- | --- | --- |
|
|
58
|
-
| **Skills** | The traps, the flat auto-layout rules, token binding and screen-design judgment, written for the agent and installed into your repo | The assistant starts out knowing what took us hours to learn, and follows your component library and tokens instead of inventing styles |
|
|
59
|
-
| **A safe write protocol** | Look first, write once, read back from scratch, then look at an export | A page is built in verified steps; a half-finished change is reported, not assumed |
|
|
60
|
-
| **Stable exports and an audit** | Exports that wait for a stable render; a check for unbound colours, spacing off your scale, overflow and blank text | The assistant can judge its own page and show you evidence |
|
|
61
|
-
| **One-command setup and `doctor`** | A connection that is checked, a key that never leaves your machine, an agent-readable `doctor --json` | A new teammate or a new agent is productive in minutes |
|
|
62
|
-
|
|
63
|
-
**What a prompt can look like** once it is set up:
|
|
64
|
-
|
|
65
|
-
> Design the candidate list page for tablet portrait as master and detail. Use our list and card components, bind every value to tokens, keep the header from the other pages. Check it, export it and show me.
|
|
66
|
-
|
|
67
|
-
The assistant reads the project's skills, inspects the file, builds the page in one verified write, exports it (twice, so the render is stable), audits it and shows you the frames. You review in Penpot as usual. The protocol and the rules come from building a full page, in desktop, tablet and phone variants, on a real product.
|
|
68
|
-
|
|
69
|
-
penpair does not design for you, and it has no "make me a page" button. It makes the assistant that does the designing reliable, repeatable and checkable.
|
|
70
|
-
|
|
71
|
-
## Quick start
|
|
72
|
-
|
|
73
|
-
> **New here, or not a terminal person?** Follow the step-by-step guide with the browser steps: **[Getting started →](https://github.com/niklas-westman/penpair/blob/main/docs/getting-started.md)**
|
|
74
|
-
|
|
75
|
-
You need [Node.js](https://nodejs.org) 22 or later and a terminal. In your project's folder:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
npx penpair setup
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
It asks where your Penpot is and does the rest, saying in plain words what it is doing and what it is waiting for:
|
|
82
|
-
|
|
83
|
-
1. writes a small config you can commit (`penpair.config.json`, no secrets),
|
|
84
|
-
2. takes your personal key **without showing it** and keeps it in a git-ignored `.env.local`,
|
|
85
|
-
3. waits while you connect the plugin in your browser (**File, MCP Server, Connect**),
|
|
86
|
-
4. reads your file once to prove it works,
|
|
87
|
-
5. installs the rules your AI assistant follows.
|
|
88
|
-
|
|
89
|
-
Run it again any time; it continues where it stopped. `npx penpair doctor` tells you what works, what does not and what to do about it.
|
|
90
|
-
|
|
91
|
-
## Web or Docker
|
|
92
|
-
|
|
93
|
-
Two ways to run Penpot, one way to connect. Pick one; you can switch later.
|
|
94
|
-
|
|
95
|
-
| | **On the web** (recommended) | **In Docker, on your computer** |
|
|
96
|
-
| --- | --- | --- |
|
|
97
|
-
| What | [design.penpot.app](https://design.penpot.app) (free account) or your team's own Penpot | A private Penpot per project on your machine |
|
|
98
|
-
| Install | Nothing | Docker Desktop, a 2 GB first download |
|
|
99
|
-
| Good for | Getting going fast, sharing with a team by invite | Offline work, data that stays with you, an instance you can throw away |
|
|
100
|
-
| Backup | Penpot's version history and its `.penpot` export | `penpair backup`, `backup verify`, `restore` |
|
|
101
|
-
| Set it up | `npx penpair setup --web` | `npx penpair setup --docker` |
|
|
102
|
-
|
|
103
|
-
The full comparison, what is checked and how to switch: **[Web or Docker →](https://github.com/niklas-westman/penpair/blob/main/packages/core/skills/penpair-setup/references/web-or-docker.md)**
|
|
104
|
-
|
|
105
|
-
## Add penpair to your repo
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
cd your-project
|
|
109
|
-
npx penpair setup # asks: web or this computer
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Then commit what it made (not `.env.local`; it is already in `.gitignore`):
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
git add penpair.config.json .gitignore .skills .agents .claude
|
|
116
|
-
git commit -m "Add penpair"
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Everyone who works with the assistant does their own `npx penpair connect`: the key belongs to a person, not to the repo. To pin penpair for the team, add it as a dev dependency: `npm i -D penpair`. To write your own Penpot operations, use the library: `npm i -D @penpair/core` and `import { defineSnippet } from "@penpair/core"`.
|
|
120
|
-
|
|
121
|
-
## Packages
|
|
122
|
-
|
|
123
|
-
| Package | What it is | Status |
|
|
124
|
-
| --- | --- | --- |
|
|
125
|
-
| [`penpair`](https://www.npmjs.com/package/penpair) | The command: `npx penpair …` | 0.1.0 |
|
|
126
|
-
| [`@penpair/core`](https://www.npmjs.com/package/@penpair/core) | The library, the command, the skills, and both ways of running Penpot | 0.1.0 |
|
|
127
|
-
| `@penpair/docker` | The Docker way of running Penpot, as its own opt-in package | Planned. Today it is inside `@penpair/core` and you choose with `setup --docker`; web users will then not carry it. |
|
|
128
|
-
| `@penpair/marwes` | The adapter for the Marwes component library | Planned, see below |
|
|
129
|
-
|
|
130
|
-
## Use it with Marwes
|
|
131
|
-
|
|
132
|
-
Using [Marwes UI](https://marwes.io), the themeable component library for React, Vue and Svelte? Connecting Marwes is the recommended setup: your Penpot file then speaks the same tokens and components as your code, so what is designed is what gets built. The adapter, `@penpair/marwes`, is the next package on the plan: it will import the Marwes tokens into your Penpot file and check that frames use them. Until it ships, set up penpair as above and name Marwes as your library in the project facts the setup skill asks you to fill in; the assistant then works against Marwes's tokens and components.
|
|
133
|
-
|
|
134
|
-
## Check each page with Reflection
|
|
135
|
-
|
|
136
|
-
Designing a page is half the job; the other half is knowing the assistant built what the design says. We recommend pairing penpair with [Reflection](https://github.com/niklas-westman/reflection) (`reflection-check` on npm), which renders your real UI in a browser and compares it with an approved image, state by state, and shows you the difference.
|
|
137
|
-
|
|
138
|
-
```
|
|
139
|
-
prompt ──▶ the assistant designs the page in Penpot ──▶ penpair export ──▶ an approved image per page and state
|
|
140
|
-
│
|
|
141
|
-
you review ◀── Reflection: the rendered page against that image, with a diff ◀── the assistant builds the page in code
|
|
12
|
+
```ts
|
|
13
|
+
import { defineSnippet } from "@penpair/core";
|
|
142
14
|
```
|
|
143
15
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
- **The design is the reference, not the assistant's memory.** The exported frame is what the delivery is measured against, so a misreading of the design shows up as a visible difference, not as a surprise in review.
|
|
147
|
-
- **Each page and state is one piece of evidence.** You look at a diff for the list, the open card, the empty state, the phone variant, instead of re-reading the whole change.
|
|
148
|
-
- **Nothing passes on a promise.** Reflection is review-first: a mismatch is shown to you, and an approved image only changes when you say so.
|
|
149
|
-
|
|
150
|
-
**The hand-off is one config block and one command.** List the frames of a page that are the design of a route, in `penpair.config.json`:
|
|
151
|
-
|
|
152
|
-
```json
|
|
153
|
-
"baselines": {
|
|
154
|
-
"page": "Candidate",
|
|
155
|
-
"root": "tests/baselines",
|
|
156
|
-
"frames": [
|
|
157
|
-
{ "frame": "F / candidates", "id": "candidates-desktop", "route": "candidates" },
|
|
158
|
-
{ "frame": "F tablet / candidates", "id": "candidates-tablet", "route": "candidates" }
|
|
159
|
-
]
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
```bash
|
|
164
|
-
npx penpair baselines # look: which images are new, the same, or changed in the design
|
|
165
|
-
npx penpair baselines --apply # write the new images, a manifest and reflection-cases.json
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
penpair exports each frame at a stable render, writes `tests/baselines/<id>.png`, and generates the Reflection cases for you: each frame's exact size becomes the viewport, so the capture and the reference have the same dimensions, and each check carries `ref: "penpair:<id>"` to join it back to the frame. In your `reflection.config.ts` you add the generated viewports to the route and spread the generated `visualSmoke` into the browser contract (a working example is in the [guide](https://github.com/niklas-westman/penpair/blob/main/docs/guide.md#baselines-for-a-check)), then run `npx reflection run --config reflection.config.ts --mode smoke`.
|
|
169
|
-
|
|
170
|
-
A changed design is never silently approved: when a frame no longer matches its image, `penpair baselines` says `changed` and leaves the image alone until you run `--apply --update`, which is the act of approving the new design. We ran this chain end to end (Penpot frame, `penpair baselines`, Reflection, browser, diff) and the check, its diff image and its `ref` all came out right.
|
|
171
|
-
|
|
172
|
-
One thing to know before you rely on it: the percentage is not a verdict. A correct implementation already differs from a Penpot render by about 1 to 2% on text alone (fonts, anti-aliasing), and a faint colour shift or a one-line copy change can sit below that. Read the diff image and Reflection's diagnostics; they point at the likely cause. What is still planned is the richer link, where each page's states are read from Penpot and shown in Reflection's own viewer (through `reflection-projection`, already tried on a real page of 30 states).
|
|
173
|
-
|
|
174
|
-
## For an AI agent
|
|
175
|
-
|
|
176
|
-
If you were told to set penpair up, read [the setup skill](https://github.com/niklas-westman/penpair/blob/main/packages/core/skills/penpair-setup/SKILL.md) first and follow it. In short:
|
|
177
|
-
|
|
178
|
-
- Run `npx penpair doctor --json` to see where things stand; every failing check carries a `remedy`.
|
|
179
|
-
- Web is the default; use Docker only when the person asks for offline or local data.
|
|
180
|
-
- **The personal MCP key never goes through you.** The person pastes it themselves into `npx penpair connect` (a hidden prompt), or pipes it from their clipboard.
|
|
181
|
-
- Done means `doctor` reports `ok: true` and "editor and plugin" connected. Then `npx penpair skills install` gives you the rules for working in Penpot (`penpot-design`) and for judging a screen (`design-judgment`).
|
|
182
|
-
|
|
183
|
-
## Commands
|
|
184
|
-
|
|
185
|
-
`npx penpair --help` lists them all. The ones you need first:
|
|
186
|
-
|
|
187
|
-
| Command | What it does |
|
|
188
|
-
| --- | --- |
|
|
189
|
-
| `setup [--web \| --docker]` | The guided path from nothing to a working connection |
|
|
190
|
-
| `connect [--replace]` | Only the connection: your key (hidden), then waits for the editor |
|
|
191
|
-
| `doctor [--json]` | What works, what does not, and what to do about it |
|
|
192
|
-
| `pages`, `tree`, `audit`, `tokens dump` | Read your Penpot file |
|
|
193
|
-
| `export <shapeId> <out.png>` | A stable PNG of a frame |
|
|
194
|
-
| `baselines [--apply] [--update]` | Export a page's frames as approved images, with ready-made Reflection cases |
|
|
195
|
-
| `skills install` | The agent's rules, into `.skills/`, visible to Codex and Claude Code |
|
|
196
|
-
| `up`, `down`, `backup`, `restore` | Docker only |
|
|
197
|
-
|
|
198
|
-
## Good to know
|
|
199
|
-
|
|
200
|
-
- Penpot's plugin needs the **editor tab open and visible** while the assistant works, so penpair cannot run unattended overnight. It acts on one Penpot at a time, on the page you have open.
|
|
201
|
-
- Checked against Penpot 2.18.3 (Docker) and the hosted Penpot 2.18.4: the same tools and limits, and a full deep check on both ([compatibility](https://github.com/niklas-westman/penpair/blob/main/docs/compatibility.md)). Not proven yet: a large file with a shared library on the hosted service, paid plans, two editors in one file, and a full `.penpot` export and import round trip.
|
|
202
|
-
- penpair is early (0.1.0). Expect rough edges and quick fixes. The plan and the open questions are in [PLAN.md](https://github.com/niklas-westman/penpair/blob/main/PLAN.md).
|
|
203
|
-
|
|
204
|
-
## Documentation
|
|
205
|
-
|
|
206
|
-
- [Getting started](https://github.com/niklas-westman/penpair/blob/main/docs/getting-started.md): step by step, for designers and for whoever sets it up
|
|
207
|
-
- [Guide](https://github.com/niklas-westman/penpair/blob/main/docs/guide.md): extension points, instances, commands
|
|
208
|
-
- [Compatibility](https://github.com/niklas-westman/penpair/blob/main/docs/compatibility.md): what was checked on which Penpot
|
|
209
|
-
- [Releasing](https://github.com/niklas-westman/penpair/blob/main/docs/releasing.md), [Changelog](https://github.com/niklas-westman/penpair/blob/main/CHANGELOG.md)
|
|
210
|
-
|
|
211
|
-
penpair is the other half of [Reflection](https://github.com/niklas-westman/reflection): Reflection checks that an implementation matches its design; penpair holds and drives the design that everything is measured against.
|
|
212
|
-
|
|
213
|
-
## Contributing and security
|
|
214
|
-
|
|
215
|
-
Issues and small pull requests are welcome: see [CONTRIBUTING](https://github.com/niklas-westman/penpair/blob/main/CONTRIBUTING.md). Your Penpot MCP key is a password; read [SECURITY](https://github.com/niklas-westman/penpair/blob/main/SECURITY.md) before you report a problem.
|
|
216
|
-
|
|
217
|
-
## Licence
|
|
218
|
-
|
|
219
|
-
[MIT](https://github.com/niklas-westman/penpair/blob/main/LICENSE)
|
|
16
|
+
The guide is in the [repository](https://github.com/niklas-westman/penpair#readme).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type LocalProfile } from "./local-profile.js";
|
|
2
|
+
export interface CliOptions {
|
|
3
|
+
/** A way to run Penpot on this computer, from a package that provides one (`@penpair/docker`). Without it those commands explain where they come from. */
|
|
4
|
+
local?: LocalProfile;
|
|
5
|
+
}
|
|
6
|
+
/** Runs the command line and returns the exit code. The `penpair` command calls it with nothing extra; a package that adds a way to run Penpot calls it with its profile. */
|
|
7
|
+
export declare function runCli(argv: string[], options?: CliOptions): Promise<number>;
|