@plitzi/cli 0.38.3 → 0.38.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,201 @@
1
1
  # @plitzi/cli
2
2
 
3
+ ## 0.38.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 6ae4161: ## `plitzi push`: a self-hosted project back on its space
8
+
9
+ `plitzi create --from` and `plitzi pull` took a space out as a project and kept it in step; nothing put the project's
10
+ own changes back but a command per part, and no command at all for its pages. `plitzi push` is the way back
11
+ (`docs/en/projects-from-spaces.md`):
12
+
13
+ - **What changed, or what is named.** `plitzi push` sends what changed since the project last had the space — at a
14
+ terminal, offered as a ticked list to choose from (`↑/↓`, space, Enter); `plitzi push space functions` sends only
15
+ those parts: `space`, `functions`, `runtime`, `plugins`.
16
+ - **In order:** each changed plugin packed and uploaded (`pack plugin` + `upload plugin`, `--cdn`/`--bucket`), the
17
+ functions, the runtime, then the space — `src/space.ts` authored, the actions `src/actions.ts` serves and the manifests
18
+ in `src/connectors/` — as the space's draft. Never a published environment.
19
+ - **Never over the builder's work unseen.** The export now carries which state the draft is in (`SpaceExport.draft`),
20
+ recorded in `.plitzi/space.json` by `create --from` and `pull`; a push names it, and a draft edited since is refused
21
+ until `--force`. A project that never had the space may take one nobody has worked on; one holding work takes
22
+ `--force`.
23
+ - **Then `pull` follows it.** `.plitzi/space.json` records what was sent — only that, so a builder's change to a part
24
+ not pushed is still the next pull's — and a project that started on its own works with `pull` from then on.
25
+ - `@plitzi/sdk-shared/source`: `SpaceImport` / `SpaceImportResult` (`SPACE_IMPORT_FORMAT`), the push's one shape for
26
+ both ends, beside `SpaceExport`, which gains `draft`.
27
+ - `functions push`, `runtime push` and `upload plugin` keep their behaviour; their cores are what `push` runs.
28
+
29
+ ## A project's folders: `tmp/` for what it writes, `.plitzi/` committed, `public/` said to be public
30
+ - **`tmp/` is what a project writes for itself.** `@plitzi/sdk-server` keeps the plugins it builds in `tmp/.sdk-plugins`
31
+ and resized pictures in `tmp/images` by default (were `.sdk-plugins` and `.plitzi/images`). A project the CLI writes
32
+ puts the port it took (`tmp/dev-server.json`), the space as last authored while developing (`tmp/space.json`) and
33
+ Playwright's output (`tmp/visual`) there too, and its `.gitignore` ignores `tmp` — one line for all of it.
34
+ - **`.plitzi/` is committed.** The scaffold ignored it whole, so a clone lost `space.json`, `functions.json` and
35
+ `scaffold.json` — what `pull`, `push`, `functions push` and `upgrade` stand on. Now it holds only what the CLI records.
36
+ `npx plitzi upgrade --write` brings an existing project's `.gitignore` up.
37
+ - **No `space/offline-data.json`.** `npm run author` checks the space and writes nothing; the server asks it for the
38
+ documents with `--out tmp/space.json` while developing. The space is `src/space.ts`, and nothing beside it says
39
+ otherwise.
40
+ - **`public/` is on the internet.** The generated `README.md` and `AGENTS.md`, the CLI and authoring skills, the MCP
41
+ guide, `@plitzi/sdk-server`'s README and the docs now say so where data goes: never a secret, a key or data only
42
+ some visitors may read — in `public/`, or in a space's documents, which reach every visitor too.
43
+
44
+ ## An agent leaves the project clean
45
+
46
+ `AGENTS.md` gains **Keep the project clean** — nothing unused left behind, scratch work in `tmp/`, one of everything,
47
+ files a reader can find, and `author`, `typecheck`, `lint`, `format` and `check` passing — and the authoring skill's
48
+ review checklist a **Nothing left behind** section.
49
+
50
+ ## The dev tools' X-ray is the outlines too
51
+
52
+ The QA tab had **Outlines** (every element's box, its type and id when pointed at) and **X-ray** (the same boxes,
53
+ fainter, with what is wired to each). They are one tool now: the X-ray draws every box with its type and id, and marks
54
+ the wiring picked — all of it, one kind, or **Boxes only**.
55
+
56
+ ## A new space that says where to go
57
+
58
+ The space a new account and `plitzi create` start from keeps its welcome, and gains the three ways to change it — the
59
+ builder, code (with the command to run) and an agent — before the guides, which are shorter. Header, navigation, main
60
+ and footer are landmarks; the grid behind the top fades out instead of ending at an edge; the footer's line spans the
61
+ content; the links say their words and icon themselves, with no suggestion left.
62
+
63
+ ## Server data reads the same everywhere
64
+ - **One shape for a provider with a `query`.** A `runtime: 'server'` provider that only reads a JSON file the server
65
+ serves answered the body itself, while the same provider in the browser publishes `{ status, data }` — so moving it
66
+ to the server broke every binding. `publicFileResolver` and the builder's mock now answer `{ status, data }` too:
67
+ `<source>.data.<field>` whichever runtime.
68
+ - **Server data is on unless a space turns it off.** The page render read a missing `rsc` as on and the connector
69
+ resolver as off, so a server provider in a space the builder or the MCP made (neither writes `rsc`) resolved to
70
+ nothing, silently. Both read it as on now; only `rsc: { enabled: false }` turns it off (and is warned with a server
71
+ element). `authorSpace` writes `enabled: true` for a space that has one.
72
+ - **Never inside a component.** The page server resolves a page's tree and its layouts only, so a server provider in a
73
+ component rendered nothing, silently. Refused now (`server-provider-in-component`), with the fix: the provider on
74
+ the page, the rows handed in as a prop.
75
+ - **Bindings held to the data.** `authorSpace` takes `data` — what a provider's `query` answers — and
76
+ `publicData(folder)` (`@plitzi/sdk-authoring/node`) reads it from `public/`. A binding onto a path the file does not
77
+ have is warned (`path-not-in-data`) with the keys it does have. Generated projects pass it.
78
+ - **`plitzi check` reads the data too.** A binding that reads a path its provider's answer lacks, a provider that
79
+ failed (once, not as every element it left empty) and the rows each list rendered (`dataIssues`). `--ssr` names what
80
+ the server's HTML lacks that the hydrated page has. The dev tools' badge and panel (`data-plitzi-devtools`) are hidden
81
+ while `check` and `shot` look.
82
+
83
+ ## Authoring, clearer where agents tripped
84
+ - **`controls` on a button** (`aria-controls`): named by the element's id; authoring gives that element the anchor and
85
+ refuses an id the space lacks (`controls-unknown`); a saved document naming no anchor is warned
86
+ (`controls-no-anchor`). The accordion recipe uses it.
87
+ - **`live` on a container** (`aria-live`, `polite` or `assertive`) for words that change while a visitor reads them.
88
+ - **`subType: 'p'`** for a paragraph made of parts; `span-holds-block` covers it.
89
+ - **`formControl` types `search`, `url` and `tel`.**
90
+ - **Images:** no `src` draws the SDK's own placeholder (the CDN one answered 404); `resize: false` keeps a picture as
91
+ written; an SVG is never resized — `/_plitzi/img` redirects to it (`307`) instead of refusing it.
92
+ - **Messages:** an id taken by another page or layout names it and the id to write; a hyphenated source in a template
93
+ (`list_study-plans`) is explained as valid; an unknown CSS property says to write it in `customCss`; an unknown
94
+ variant on `custom` says it is never a prop of the component it hosts.
95
+ - **`plitzi explain` knows the helpers** — `bindTemplate`, `visibleWhen`, `variantFrom`, `activeOn`, `activeWhen`,
96
+ `when`, `named`, `scope`, `source`, `twig` — and `motion`, from the presets the SDK plays (`MOTION_ENTERS`,
97
+ `MOTION_TRIGGERS`, `MOTION_LOOPS`, now exported). `--list helpers` lists them.
98
+ - **`plitzi shot`** writes to `tmp/shots/` by default, and a full page shows the arrivals tied to the scroll as they
99
+ end. `inspectPage` no longer reports an image a hidden ancestor hides.
100
+ - **Style inspector:** `container-type` in Size.
101
+ - **New recipes:** `server-data.ts`, `accordion.ts`.
102
+
103
+ ## Matching a page to another, measured
104
+ - **A whole page is the whole page.** The SDK scrolls a pane of its own, so `shot`'s full-page picture was one
105
+ screen; the pane is unrolled now (`unrollPage`) — the viewport untouched, so a `100vh` hero stays one screen.
106
+ - **`plitzi shot --compare` says why, not only how much.** Every lazy picture on both pages is loaded before they are
107
+ taken (`loadImages`), so a section no longer swings between 23 % and 40 % on how many covers had arrived. Each section
108
+ is compared where it is on the other page (`alignPictures` over row profiles): a page 400 px longer is said once, with
109
+ the section the drift starts at, instead of a footer with the same styles reading 100 %. And the texts both pages
110
+ have are paired (`pageTexts`, `compareTexts`), each with what it does differently there —
111
+ `h1 "Learn CSS" — font-size 68px → 60px · y +19px` — position less the drift of its section. `comparePictures` takes
112
+ its options as an object now (`{ regions, tolerance, align }`).
113
+ - **`--scheme` is the space's theme.** A space whose default is dark painted dark under `--scheme light`, and the file
114
+ said light. `check` and `shot` now set the `theme` cookie a visitor's toggle writes; left out, the space's default is
115
+ pictured and the file is named by the theme actually painted.
116
+
117
+ ## `motion: { on: 'view' }` arrives once
118
+
119
+ `view` was tied to the scroll both ways: cards faded out again as the reader scrolled back up. Now it plays once, the
120
+ first time the element comes into view, with its `duration`, and stays — `revealOnView` (`@plitzi/sdk-shared/schema/
121
+ motionReveal`), one observer under the SDK's root, marks it `data-motion-seen`. The scroll-driven arrival is
122
+ `on: 'scroll'`. A page read without scripts shows them as they end (`@media (scripting: none)`); the builder's previews
123
+ and the MCP's local screenshots hold motion at its end. The builder's Motion tab has the three.
124
+
125
+ The builder's preview plays motion as the published page does. It held everything at its end, and nothing marked
126
+ the canvas `data-hydrated`, so neither the loops nor a space's own `[data-hydrated]` animations ever started there.
127
+ Now an arrival plays as it comes into view, and the loops and keyframes run; **▶** on the canvas being edited runs the
128
+ space's own `[data-hydrated]` loops too. The motion guide adds the rule this needs from a space: `[data-hydrated]` is for
129
+ loops; an entrance of its own that plays once (`transform`/`opacity` only) plays from the first paint with
130
+ `backwards`, as the presets do — gated on hydration it jumps when the page goes live, and a canvas that never goes
131
+ live holds it on its first frame.
132
+
133
+ ## Thirteen ways in, nine loops
134
+
135
+ `motion` grows from six arrivals and four loops to thirteen and nine, all still `opacity` and the transforms:
136
+
137
+ - **Bold arrivals:** `slide-up` (from well below), `zoom-in` (from 60%), `zoom-out` (settling from 125%), `tilt`
138
+ (rising as it turns straight), `spin-in` (half a turn as it grows).
139
+ - **Springy arrivals:** `pop` overshoots and settles; `bounce-in` falls into place and bounces.
140
+ - **Loops:** `bounce`, `wobble`, `orbit` (a slow circle round where it is), `heartbeat`, `drift`.
141
+
142
+ An arrival is now a list of frames (`MOTION_ENTER_FRAMES` in `@plitzi/sdk-shared/schema/motion`; `MOTION_ENTER_FROM`
143
+ is its first), and so is a loop (`MOTION_LOOP_FRAMES[name].frames`, replacing `transform`/`at`). Arrivals move the
144
+ individual `translate`, `scale` and `rotate`, so they still compose with an element's own `transform` and with a loop.
145
+ The builder's Motion tab previews every frame; authoring, the MCP, `explain motion` and GraphQL read the same lists.
146
+
147
+ An arrival from the side no longer scrolls the page sideways. `slide-left` starts 32px to the right of where it lands,
148
+ past a phone's 16px gutter, and until it had played the page could be dragged sideways. The SDK's root clips that way
149
+ now (`overflow-x: clip`, which keeps the window the scroller: sticky bars and scroll timelines are untouched), and the
150
+ builder's preview does the same. Content wider than the page is cut rather than scrolling the page — give it a box of
151
+ its own with `overflow-x: auto`.
152
+
153
+ **3D and the transform parts are style properties.** `transform-style`, `perspective-origin`, `backface-visibility`,
154
+ `translate`, `rotate` and `scale` join the vocabulary (they were refused as `css-property-unknown` and had to go in
155
+ `customCss`); `scale: 0.94` is a factor, not pixels.
156
+
157
+ **The builder's placeholder for an empty element is 24px** (was 50px), one size for every element that has one. It
158
+ is a minimum, so it also grew empty shapes with a size of their own — a dot, a planet — while editing; 24px is still
159
+ a box to see, pick and drop into.
160
+
161
+ The desktop app's window was blank: authoring, run in the renderer to build the rail, asked Vite's `process` shim for
162
+ a working directory it does not have. It reads one only where there is one now — and only when a class conflict is
163
+ reported, rather than for every element of every space it writes.
164
+
165
+ ## Authoring advice that knows where and when
166
+ - **`class-conflict` says where both declarations are:** `styles('md-menu-button') at src/components/dropdown.ts:12,
167
+ used by button at …, and styles('md-menu-button') at src/site/layout.ts:8, used by …` — `styles()` records its line as
168
+ an element factory does.
169
+ - **`repeated-shape` leaves controls alone.** Siblings that read different sources or write different state keys — a
170
+ menu for the language, one for the level — are not offered as a list.
171
+ - **`quiet: ['repeated-shape']`** on an element says a suggestion was left on purpose, and it is not offered again.
172
+ Only suggestions' codes (`quiet-unknown`). It is saved on the element (`definition.quiet`), so the builder's problems
173
+ panel, the MCP's `upsertElement`/`patchElement` and a project pulled back with `plitzi pull` all leave it alone.
174
+
175
+ ## What the docs say is what the code does
176
+
177
+ A pass across authoring's own messages, the skills, the MCP guide, `docs/en`, the CLI's README and the website, so an
178
+ agent reading two of them is never told two things:
179
+
180
+ - **A binding's `source` is the bound value, and only that.** With no value yet it fell back to the attribute's
181
+ current one — `sourceTo` — so `{{ source|default('…') }}` never took its default and `{{ source ? … }}` read an
182
+ element's placeholder ("Text") as an answer. It is `undefined` until the source has a value now; `sourceTo` is still
183
+ the value before.
184
+ - **`explain` writes a step the way its builder is called:** `delay(ms)`, `openModal('modal-id', data?)`,
185
+ `scrollBy('row-id', { x: '80%' })` — not `delay(…)` above the document's param names, which read as `delay({ time })`.
186
+ - **A page field nobody takes is told what it was probably for** — `seo` → `seoTitle` and `seoDescription` — instead of a
187
+ hint about keeping state appended to every one.
188
+ - Docs set straight: an element's id is `data-plitzi-el` in the page (`data-id` is the builder's, with debugging on);
189
+ a visibility condition writes nothing while its source has no value and is a yes or a no once it has one; `mobile`
190
+ is up to and including 48rem; `loadStrategy` defaults by type (`lazy` for a modal); the accessibility codes listed
191
+ in full everywhere (`embed-without-title`, `dropdown-without-control`, `controls-no-anchor`); the generated
192
+ `AGENTS.md` says a repeated block is a component and rows of data a list, as the skill does; `plitzi runtime` is
193
+ documented in the CLI's README, its skill and the website, which no longer says state across requests needs a
194
+ server of your own.
195
+
196
+ - Updated dependencies [6ae4161]
197
+ - @plitzi/sdk-authoring@0.38.4
198
+
3
199
  ## 0.38.3
4
200
 
5
201
  ### Patch Changes
package/README.md CHANGED
@@ -59,8 +59,8 @@ code (JSON, with the reason said, where one would not read back exactly), its fu
59
59
  runtime, and its files downloaded into `public/` with every CDN address rewritten — served by the project with nothing
60
60
  of Plitzi's. `.env` gets a signing key made for it and the names of the variables and credentials the space had, never
61
61
  their values. It signs in as you and needs a space you may change; the end of `create` says what came across
62
- differently. What the project was given is recorded in `.plitzi/space.json` — commit it — for [`pull`](#pull). See
63
- `docs/en/projects-from-spaces.md`.
62
+ differently. What the project was given is recorded in `.plitzi/space.json` — commit it — for [`pull`](#pull) and
63
+ [`push`](#push). See `docs/en/projects-from-spaces.md`.
64
64
 
65
65
  ## The package manager
66
66
 
@@ -126,6 +126,14 @@ when an install fails the CLI says which setting names it.
126
126
  save the most first. Not problems: the space authors either way. `npm run author -- --json` carries them in
127
127
  `suggestions`.
128
128
 
129
+ ## The folders that are not the source
130
+
131
+ | Folder | What it is | In git |
132
+ | --- | --- | --- |
133
+ | `public/` | Served to anyone who asks, as it is — data, pictures, a favicon. **It is on the internet**: never a secret, a key, a private document or data only some visitors may read | yes |
134
+ | `tmp/` | What the project writes for itself while it runs: the plugins the server builds (`tmp/.sdk-plugins`), resized pictures, the port it took (`tmp/dev-server.json`), the space as last authored, screenshots and test output. Rebuilt when missing | no |
135
+ | `.plitzi/` | What the CLI records about the project: the space it came from (`space.json`), the functions' working copy, the files `create` wrote — what `pull`, `push` and `upgrade` stand on | yes |
136
+
129
137
  ## `create --template blank` and `--template catalog`
130
138
 
131
139
  A space written in the project starts as the welcome tour, with a plugin of the project's own. `--template blank`
@@ -140,17 +148,28 @@ per part under `src/site/`. Both go with `--source local`.
140
148
  ```bash
141
149
  plitzi check / --width 1440,390 # is the page whole? in text, per width; --json for a tool
142
150
  plitzi check /products --state --element catalog-count # and what it holds: state, sources, one element
151
+ plitzi check / --ssr # and what the server's HTML lacks that the hydrated page has
143
152
  plitzi shot /about --width 390 --scheme dark
144
153
  plitzi shot / --frames 4 --every 500 # what moves: a marquee, an autoplay
145
- plitzi shot / --compare https://example.com --width 1440 # side by side, and how much differs by section
154
+ plitzi shot / --compare https://example.com --width 1440 # beside another site: what differs, and how
146
155
  ```
147
156
 
148
157
  Both run on the project's own Playwright against its running server (`npm start`), and refuse a port that answers as
149
158
  another project. `check` reports every element the space owes the page that is missing or hidden (with why), broken
150
- images, sideways scroll, text in the colour behind it, console errors and refused requests — a page's state in a few
151
- hundred tokens, where a screenshot costs thousands. `shot --compare` writes the two pictures side by side and the
152
- differences in red, and says the share that differs in each landmark of the page; `--frames` compares pictures taken
153
- one after another and names what moved. A project `create` writes has them as `npm run check` and `npm run shot`.
159
+ images, sideways scroll, text in the colour behind it, console errors, refused requests and failed flows — and the
160
+ page's data: a binding that reads a path its provider's answer lacks (with the keys it has), a provider that failed,
161
+ the rows each list rendered. A page's state in a few hundred tokens, where a screenshot costs thousands.
162
+
163
+ `--scheme` is the space's own theme, set as a visitor's toggle sets it (the `theme` cookie); left out, the space's
164
+ default, and `shot` names the file by the theme it was painted in. The dev tools' badge is hidden from both. A
165
+ full-page `shot` is the whole page — the pane the SDK scrolls in unrolled, every lazy picture loaded, every arrival
166
+ waiting for the scroll shown as it ends — in `tmp/shots/` unless `--out` says where.
167
+
168
+ `shot --compare` writes the two pictures side by side and the differences in red. It compares each section where it
169
+ is on the other page, so a page 400 px longer is said once, with the section the drift starts at; then it pairs the
170
+ texts both pages have and says what each does differently there — `h1 "Learn CSS" — font-size 68px → 60px · y +19px`.
171
+ `--frames` compares pictures taken one after another and names what moved. A project `create` writes has them as
172
+ `npm run check` and `npm run shot`.
154
173
 
155
174
  ## `fix`
156
175
 
@@ -200,18 +219,29 @@ plitzi explain navigate # a step: its params and the function that write
200
219
  plitzi explain onScroll # a trigger: what it hands its flow, and what fires it
201
220
  plitzi explain class-and-css # a problem's code: what was wrong, what to write instead
202
221
  plitzi explain content-attribute # a suggestion's code: what is written the long way, and the short one
203
- plitzi explain --list steps # every one of a kind: elements, steps, triggers, codes, transformers
222
+ plitzi explain bindTemplate # a helper: how it is written, what it is for, an example
223
+ plitzi explain motion # the arrivals, triggers and loops the SDK plays
224
+ plitzi explain --list steps # every one of a kind: elements, steps, triggers, codes, transformers, helpers
204
225
  ```
205
226
 
206
227
  What a name means when authoring, from the catalogues the checks themselves read — the answer to a question that would
207
228
  otherwise be a search through the SDK's types. `--json` answers in one object; over MCP it is `plitzi://explain/{name}`.
208
229
 
209
- ## `skills update`
230
+ ## `upgrade`
231
+
232
+ ```bash
233
+ plitzi upgrade # what this CLI would change in the project — shown, nothing written
234
+ plitzi upgrade skills --write # only the skills, each replaced whole from the packages installed
235
+ plitzi upgrade --write --take src/author.ts
236
+ ```
210
237
 
211
- The skills `create` copies into `.claude/skills/` say the version of the package they came from (`version:` in
212
- `SKILL.md`), and `npm run author` says when the authoring skill is older than the `@plitzi/sdk-authoring` installed.
213
- `plitzi skills update` replaces each Plitzi skill there with the one of the installed package — whole, so a reference it
214
- no longer has goes with it — and leaves any other skill alone.
238
+ A project brought up to the CLI it has now, part by part: `files` (the machinery — `author.ts`, `main.ts`, the
239
+ Playwright and lint configs, `AGENTS.md`), `packages` (`package.json` merged, `@plitzi/*` raised to this version, then
240
+ the install), `skills` (`.claude/skills/plitzi-*`, whole, so a reference a skill no longer has goes with it) and
241
+ `renames` (a name a version renamed, at its file and line). A file nobody changed since the CLI wrote it is replaced;
242
+ one the project made its own is a diff, left unless `--take` names it (`all` for every one). `update` is the same
243
+ command, and `plitzi skills update` is `upgrade skills --write`. `npm run author` says when the authoring skill is
244
+ older than the `@plitzi/sdk-authoring` installed.
215
245
 
216
246
  ## `data describe`
217
247
 
@@ -237,6 +267,30 @@ write **nothing** and name them. Both sides are compared as the project's Pretti
237
267
  `package.json` only gains the packages the space's code now asks for. It follows the version the project was made from —
238
268
  the draft, an environment's latest, or a pinned revision.
239
269
 
270
+ ## `push`
271
+
272
+ ```bash
273
+ plitzi push # at a terminal: tick what goes up — what changed is ticked already
274
+ plitzi push space functions # only these: space, functions, runtime, plugins
275
+ plitzi push space --force # replace the draft even though it was edited in the builder since
276
+ ```
277
+
278
+ The way back of `pull`: the project put on the space it works with, as its **draft**. With nobody at the terminal it
279
+ sends what changed since the project last had the space. Each part goes up as its own command sends it, in the order
280
+ that names come before what names them:
281
+
282
+ 1. `plugins` — every plugin whose source changed, packed and uploaded to the space's CDN (`--cdn`/`--bucket` when it
283
+ has several public buckets);
284
+ 2. `functions` — `functions/`, as `functions push`;
285
+ 3. `runtime` — the runtime module, as `runtime push`;
286
+ 4. `space` — `src/space.ts` authored, with the actions `src/actions.ts` serves and the connectors in `src/connectors/`.
287
+
288
+ The draft is never replaced unseen: when it was edited in the builder since the project's last pull or push, the push
289
+ is refused — pull first, or `--force`. A project that never had the space (one not made with `create --from`) may take a
290
+ space nobody has worked on yet; one that holds work asks for `--force` too. Always the space the CLI is connected to,
291
+ which must be the one the project came from, and never a published environment: publish in the builder. Afterwards
292
+ `.plitzi/space.json` records what was sent, so `pull` keeps working — on a project that started on its own too.
293
+
240
294
  ## `add plugin`
241
295
 
242
296
  Adds elements of your own to the project you are in — one, several at once (`add plugin seat-picker legend`), or one at